diff options
author | Stefan Wintermeyer <stefan.wintermeyer@amooma.de> | 2012-12-17 12:01:45 +0100 |
---|---|---|
committer | Stefan Wintermeyer <stefan.wintermeyer@amooma.de> | 2012-12-17 12:01:45 +0100 |
commit | b80bd744ad873f6fc43018bc4bfb90677de167bd (patch) | |
tree | 072c4b0e33d442528555b82c415f5e7a1712b2b0 | |
parent | 3e706c2025ecc5523e81ad649639ef2ff75e7bac (diff) |
Start of GS5.
1224 files changed, 61085 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf1d09a --- /dev/null +++ b/.gitignore @@ -0,0 +1,88 @@ +# ignore vim swap files: +*.swp + +.DS_Store +.bundle + +/db/*.sqlite3-journal +/db/*.sqlite3 +tmp/ +.DS_Store +/ERD.pdf +/ERD.png +/bin + +# Numerous always-ignore extensions +*.diff +*.err +*.orig +*.log +*.rej +*.swo +*.swp +*.vi +*~ + +# OS or Editor folders +.DS_Store +.cache +.project +.settings +nbproject +Thumbs.db + + +# Extra +.sass-cache +.livereload + +# Dreamweaver added files +_notes +dwsync.xml + +# Komodo +*.komodoproject +.komodotools + +# Folders to ignore +.hg +.svn +/db/*.db +/db/*.db-journal + +log/*.log +tmp/**/* +/.project +*.komodoproject +/.komodotools +.tmp*~ + +# Ignore fixtures. We use factory_girl. +/test/fixtures/*.yml + +ERD.pdf +ERD.png + +/vendor/cache +/vendor/bundle +.settings/* +/.idea + +# ignore Sass cache: +/.sass-cache +tmp/sass-cache +tmp/cache + +# directory created by "bundle install --path ." (not needed anyway): +/ruby + +# ignore wrappers generated by "bundle install --binstubs": +/bin + +/tmp + +# Images +public/uploads +# Freeswitch +misc/freeswitch/conf/freeswitch.serial +misc/freeswitch/scripts/ini/gateway_gateway1.ini @@ -0,0 +1,78 @@ +source 'http://rubygems.org' + +gem 'rails', '3.2.2' + +gem 'bcrypt-ruby' + +gem 'sqlite3' + +gem 'mysql2' + +gem 'cancan' + +gem 'state_machine' + +gem 'acts_as_list' + +# Nicer console output: +gem "hirb" + +gem "nokogiri" + +# Gems used only for assets and not required +# in production environments by default. +group :assets do + gem 'sass-rails', '~> 3.2.3' + gem 'coffee-rails', '~> 3.2.1' + gem 'compass-rails' + + # See https://github.com/sstephenson/execjs#readme for more supported runtimes + gem 'therubyracer' + + gem 'uglifier', '>= 1.0.3' + +end + +gem 'json' + +gem 'jquery-rails' + +group :development do + gem 'factory_girl_rails' + gem 'factory_girl' + gem "rails-erd", "~> 0.4.5" +end + +group :test do + gem 'factory_girl_rails' +end + +gem "haml" +gem "simple_form", '2.0.1' + +# Image Upload +gem 'carrierwave' +gem "mini_magick" + +# Pagination https://github.com/mislav/will_paginate/wiki/Installation +gem 'will_paginate' + +# DelayedJob +gem 'delayed_job' +gem 'delayed_job_active_record' + +# Private Pub http://railscasts.com/episodes/316-private-pub +gem 'private_pub' + +# https://github.com/iain/http_accept_language +gem 'http_accept_language' + +# https://github.com/weppos/breadcrumbs_on_rails +gem 'breadcrumbs_on_rails' + +# UUID Generator https://github.com/assaf/uuid +gem 'uuid' + +# Local Variables: +# mode: ruby +# End: diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..e07b89e --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,209 @@ +GEM + remote: http://rubygems.org/ + specs: + actionmailer (3.2.2) + actionpack (= 3.2.2) + mail (~> 2.4.0) + actionpack (3.2.2) + activemodel (= 3.2.2) + activesupport (= 3.2.2) + builder (~> 3.0.0) + erubis (~> 2.7.0) + journey (~> 1.0.1) + rack (~> 1.4.0) + rack-cache (~> 1.1) + rack-test (~> 0.6.1) + sprockets (~> 2.1.2) + activemodel (3.2.2) + activesupport (= 3.2.2) + builder (~> 3.0.0) + activerecord (3.2.2) + activemodel (= 3.2.2) + activesupport (= 3.2.2) + arel (~> 3.0.2) + tzinfo (~> 0.3.29) + activeresource (3.2.2) + activemodel (= 3.2.2) + activesupport (= 3.2.2) + activesupport (3.2.2) + i18n (~> 0.6) + multi_json (~> 1.0) + acts_as_list (0.1.5) + addressable (2.2.7) + arel (3.0.2) + bcrypt-ruby (3.0.1) + breadcrumbs_on_rails (2.2.0) + builder (3.0.0) + cancan (1.6.7) + carrierwave (0.5.8) + activesupport (~> 3.0) + chunky_png (1.2.5) + coffee-rails (3.2.2) + coffee-script (>= 2.2.0) + railties (~> 3.2.0) + coffee-script (2.2.0) + coffee-script-source + execjs + coffee-script-source (1.2.0) + compass (0.12.rc.1) + chunky_png (~> 1.2) + fssm (>= 0.2.7) + sass (~> 3.1) + compass-rails (1.0.0.rc.3) + compass (~> 0.12.rc.0) + cookiejar (0.3.0) + delayed_job (3.0.1) + activesupport (~> 3.0) + delayed_job_active_record (0.3.2) + activerecord (> 2.1.0) + delayed_job (~> 3.0.0) + em-http-request (1.0.1) + addressable (>= 2.2.3) + cookiejar + em-socksify + eventmachine (>= 1.0.0.beta.4) + http_parser.rb (>= 0.5.3) + em-socksify (0.1.0) + eventmachine + erubis (2.7.0) + eventmachine (1.0.0.beta.4) + execjs (1.3.0) + multi_json (~> 1.0) + factory_girl (2.6.1) + activesupport (>= 2.3.9) + factory_girl_rails (1.7.0) + factory_girl (~> 2.6.0) + railties (>= 3.0.0) + faye (0.8.0) + cookiejar (>= 0.3.0) + em-http-request (>= 0.3.0) + eventmachine (>= 0.12.0) + faye-websocket (>= 0.4.0) + rack (>= 1.0.0) + yajl-ruby (>= 1.0.0) + faye-websocket (0.4.1) + eventmachine (>= 0.12.0) + fssm (0.2.8.1) + haml (3.1.4) + hike (1.2.1) + hirb (0.6.1) + http_accept_language (1.0.2) + http_parser.rb (0.5.3) + i18n (0.6.0) + journey (1.0.3) + jquery-rails (2.0.1) + railties (>= 3.2.0, < 5.0) + thor (~> 0.14) + json (1.6.5) + libv8 (3.3.10.4) + macaddr (1.6.1) + systemu (~> 2.5.0) + mail (2.4.3) + i18n (>= 0.4.0) + mime-types (~> 1.16) + treetop (~> 1.4.8) + mime-types (1.17.2) + mini_magick (3.4) + subexec (~> 0.2.1) + multi_json (1.1.0) + mysql2 (0.3.11) + nokogiri (1.5.5) + polyglot (0.3.3) + private_pub (1.0.1) + faye + rack (1.4.1) + rack-cache (1.2) + rack (>= 0.4) + rack-ssl (1.3.2) + rack + rack-test (0.6.1) + rack (>= 1.0) + rails (3.2.2) + actionmailer (= 3.2.2) + actionpack (= 3.2.2) + activerecord (= 3.2.2) + activeresource (= 3.2.2) + activesupport (= 3.2.2) + bundler (~> 1.0) + railties (= 3.2.2) + rails-erd (0.4.5) + activerecord (~> 3.0) + activesupport (~> 3.0) + ruby-graphviz (~> 0.9.18) + railties (3.2.2) + actionpack (= 3.2.2) + activesupport (= 3.2.2) + rack-ssl (~> 1.3.2) + rake (>= 0.8.7) + rdoc (~> 3.4) + thor (~> 0.14.6) + rake (0.9.2.2) + rdoc (3.12) + json (~> 1.4) + ruby-graphviz (0.9.21) + sass (3.1.15) + sass-rails (3.2.4) + railties (~> 3.2.0) + sass (>= 3.1.10) + tilt (~> 1.3) + simple_form (2.0.1) + actionpack (~> 3.0) + activemodel (~> 3.0) + sprockets (2.1.2) + hike (~> 1.2) + rack (~> 1.0) + tilt (~> 1.1, != 1.3.0) + sqlite3 (1.3.5) + state_machine (1.1.2) + subexec (0.2.1) + systemu (2.5.1) + therubyracer (0.9.10) + libv8 (~> 3.3.10) + thor (0.14.6) + tilt (1.3.3) + treetop (1.4.10) + polyglot + polyglot (>= 0.3.1) + tzinfo (0.3.32) + uglifier (1.2.3) + execjs (>= 0.3.0) + multi_json (>= 1.0.2) + uuid (2.3.5) + macaddr (~> 1.0) + will_paginate (3.0.3) + yajl-ruby (1.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + acts_as_list + bcrypt-ruby + breadcrumbs_on_rails + cancan + carrierwave + coffee-rails (~> 3.2.1) + compass-rails + delayed_job + delayed_job_active_record + factory_girl + factory_girl_rails + haml + hirb + http_accept_language + jquery-rails + json + mini_magick + mysql2 + nokogiri + private_pub + rails (= 3.2.2) + rails-erd (~> 0.4.5) + sass-rails (~> 3.2.3) + simple_form (= 2.0.1) + sqlite3 + state_machine + therubyracer + uglifier (>= 1.0.3) + uuid + will_paginate diff --git a/Guardfile b/Guardfile new file mode 100644 index 0000000..4b273b3 --- /dev/null +++ b/Guardfile @@ -0,0 +1,13 @@ +# A sample Guardfile +# More info at https://github.com/guard/guard#readme + +guard 'livereload' do + watch(%r{app/.+\.(erb|haml)}) + watch(%r{app/helpers/.+\.rb}) + watch(%r{(public/|app/assets).+\.(css|js|html)}) + watch(%r{(public/|app/assets).+\.(css|js|html)}) + watch(%r{(public/|app/assets).*}) + watch(%r{(app/assets/.+\.css)\.s[ac]ss}) { |m| m[1] } + watch(%r{(app/assets/.+\.js)\.coffee}) { |m| m[1] } + watch(%r{config/locales/.+\.yml}) +end @@ -1,4 +1,16 @@ GS5 === -Gemeinschaft 5. A FreeSWITCH and Ruby on Rails based PBX.
\ No newline at end of file +Gemeinschaft 5 by the [AMOOMA GmbH](http://amooma.de) in Germany. It is a FreeSWITCH and Ruby on Rails based PBX. + +License +======= +We use this repository to develop and we'd like to invite everybody to add new features or find bugs. There for the repository is not a private one. But the code in this repository is not GPL! Please go to [http://amooma.de/gemeinschaft/gs5](http://amooma.de/gemeinschaft/gs5) for a copy of this software with an other license. + +DO NOT USE CODE FROM THIS REPO FOR A PRODUCTION SYSTEM! + +Development How-to +================== +There is a master and a develop branch in this repository. If you are familiar with [http://nvie.com/posts/a-successful-git-branching-model/](http://nvie.com/posts/a-successful-git-branching-model/) you are good to go. Please send a pull request and an e-mail to stefan.wintermeyer@amooma.de with some info about your code. Regular developer get access to the repository and a closed developer mailinglist. + +We only accept code which was written 100% by you and were you grant us the rights for this code.
\ No newline at end of file diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..cd4227d --- /dev/null +++ b/Rakefile @@ -0,0 +1,7 @@ +#!/usr/bin/env rake +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require File.expand_path('../config/application', __FILE__) + +Gemeinschaft42c::Application.load_tasks diff --git a/app/assets/images/amooma-logo.png b/app/assets/images/amooma-logo.png Binary files differnew file mode 100644 index 0000000..11096ff --- /dev/null +++ b/app/assets/images/amooma-logo.png diff --git a/app/assets/images/bg-body.png b/app/assets/images/bg-body.png Binary files differnew file mode 100644 index 0000000..777eff6 --- /dev/null +++ b/app/assets/images/bg-body.png diff --git a/app/assets/images/gradients/light-to-dark-blue-x63.png b/app/assets/images/gradients/light-to-dark-blue-x63.png Binary files differnew file mode 100644 index 0000000..7eb020f --- /dev/null +++ b/app/assets/images/gradients/light-to-dark-blue-x63.png diff --git a/app/assets/images/gradients/white-gray-x29-reverse.png b/app/assets/images/gradients/white-gray-x29-reverse.png Binary files differnew file mode 100644 index 0000000..7b7e879 --- /dev/null +++ b/app/assets/images/gradients/white-gray-x29-reverse.png diff --git a/app/assets/images/gradients/white-gray-x29.png b/app/assets/images/gradients/white-gray-x29.png Binary files differnew file mode 100644 index 0000000..a1671d0 --- /dev/null +++ b/app/assets/images/gradients/white-gray-x29.png diff --git a/app/assets/images/gradients/white-texture-x63.png b/app/assets/images/gradients/white-texture-x63.png Binary files differnew file mode 100644 index 0000000..1576e8f --- /dev/null +++ b/app/assets/images/gradients/white-texture-x63.png diff --git a/app/assets/images/icons/cellphone-32x.png b/app/assets/images/icons/cellphone-32x.png Binary files differnew file mode 100644 index 0000000..cfc41f5 --- /dev/null +++ b/app/assets/images/icons/cellphone-32x.png diff --git a/app/assets/images/icons/clock-32x.png b/app/assets/images/icons/clock-32x.png Binary files differnew file mode 100644 index 0000000..c076042 --- /dev/null +++ b/app/assets/images/icons/clock-32x.png diff --git a/app/assets/images/icons/cross-16x.png b/app/assets/images/icons/cross-16x.png Binary files differnew file mode 100644 index 0000000..e22ed6f --- /dev/null +++ b/app/assets/images/icons/cross-16x.png diff --git a/app/assets/images/icons/facebook-32x.png b/app/assets/images/icons/facebook-32x.png Binary files differnew file mode 100644 index 0000000..08fa0f7 --- /dev/null +++ b/app/assets/images/icons/facebook-32x.png diff --git a/app/assets/images/icons/fax-32x.png b/app/assets/images/icons/fax-32x.png Binary files differnew file mode 100644 index 0000000..b05ee59 --- /dev/null +++ b/app/assets/images/icons/fax-32x.png diff --git a/app/assets/images/icons/headphones-16x.png b/app/assets/images/icons/headphones-16x.png Binary files differnew file mode 100644 index 0000000..dee8346 --- /dev/null +++ b/app/assets/images/icons/headphones-16x.png diff --git a/app/assets/images/icons/headphones-32x.png b/app/assets/images/icons/headphones-32x.png Binary files differnew file mode 100644 index 0000000..89a5df7 --- /dev/null +++ b/app/assets/images/icons/headphones-32x.png diff --git a/app/assets/images/icons/house-32x.png b/app/assets/images/icons/house-32x.png Binary files differnew file mode 100644 index 0000000..b112915 --- /dev/null +++ b/app/assets/images/icons/house-32x.png diff --git a/app/assets/images/icons/mic-32x.png b/app/assets/images/icons/mic-32x.png Binary files differnew file mode 100644 index 0000000..30c4531 --- /dev/null +++ b/app/assets/images/icons/mic-32x.png diff --git a/app/assets/images/icons/microphone-16x.png b/app/assets/images/icons/microphone-16x.png Binary files differnew file mode 100644 index 0000000..b62422d --- /dev/null +++ b/app/assets/images/icons/microphone-16x.png diff --git a/app/assets/images/icons/microphone-32x.png b/app/assets/images/icons/microphone-32x.png Binary files differnew file mode 100644 index 0000000..30c4531 --- /dev/null +++ b/app/assets/images/icons/microphone-32x.png diff --git a/app/assets/images/icons/mute-16x.png b/app/assets/images/icons/mute-16x.png Binary files differnew file mode 100644 index 0000000..0656f3f --- /dev/null +++ b/app/assets/images/icons/mute-16x.png diff --git a/app/assets/images/icons/phone-down-32x.png b/app/assets/images/icons/phone-down-32x.png Binary files differnew file mode 100644 index 0000000..38c3560 --- /dev/null +++ b/app/assets/images/icons/phone-down-32x.png diff --git a/app/assets/images/icons/phone-mobile-32x.png b/app/assets/images/icons/phone-mobile-32x.png Binary files differnew file mode 100644 index 0000000..b373e1a --- /dev/null +++ b/app/assets/images/icons/phone-mobile-32x.png diff --git a/app/assets/images/icons/phone-up-32x.png b/app/assets/images/icons/phone-up-32x.png Binary files differnew file mode 100644 index 0000000..9b765c7 --- /dev/null +++ b/app/assets/images/icons/phone-up-32x.png diff --git a/app/assets/images/icons/search-13x16.png b/app/assets/images/icons/search-13x16.png Binary files differnew file mode 100644 index 0000000..16aa3c6 --- /dev/null +++ b/app/assets/images/icons/search-13x16.png diff --git a/app/assets/images/icons/skype-32x.png b/app/assets/images/icons/skype-32x.png Binary files differnew file mode 100644 index 0000000..c3b0978 --- /dev/null +++ b/app/assets/images/icons/skype-32x.png diff --git a/app/assets/images/icons/star-16x.png b/app/assets/images/icons/star-16x.png Binary files differnew file mode 100644 index 0000000..6b16932 --- /dev/null +++ b/app/assets/images/icons/star-16x.png diff --git a/app/assets/images/icons/suitcase-32x.png b/app/assets/images/icons/suitcase-32x.png Binary files differnew file mode 100644 index 0000000..f53daa9 --- /dev/null +++ b/app/assets/images/icons/suitcase-32x.png diff --git a/app/assets/images/icons/tag-16x.png b/app/assets/images/icons/tag-16x.png Binary files differnew file mode 100644 index 0000000..b4522d7 --- /dev/null +++ b/app/assets/images/icons/tag-16x.png diff --git a/app/assets/images/icons/twitter-32x.png b/app/assets/images/icons/twitter-32x.png Binary files differnew file mode 100644 index 0000000..51351a7 --- /dev/null +++ b/app/assets/images/icons/twitter-32x.png diff --git a/app/assets/images/icons/unmute-16x.png b/app/assets/images/icons/unmute-16x.png Binary files differnew file mode 100644 index 0000000..e9dfde0 --- /dev/null +++ b/app/assets/images/icons/unmute-16x.png diff --git a/app/assets/images/icons/user-16x.png b/app/assets/images/icons/user-16x.png Binary files differnew file mode 100644 index 0000000..909403a --- /dev/null +++ b/app/assets/images/icons/user-16x.png diff --git a/app/assets/images/icons/user-female-16x.png b/app/assets/images/icons/user-female-16x.png Binary files differnew file mode 100644 index 0000000..38dde34 --- /dev/null +++ b/app/assets/images/icons/user-female-16x.png diff --git a/app/assets/images/icons/user-male-16x.png b/app/assets/images/icons/user-male-16x.png Binary files differnew file mode 100644 index 0000000..e03fd0f --- /dev/null +++ b/app/assets/images/icons/user-male-16x.png diff --git a/app/assets/images/logo.png b/app/assets/images/logo.png Binary files differnew file mode 100644 index 0000000..e4432b8 --- /dev/null +++ b/app/assets/images/logo.png diff --git a/app/assets/images/rails.png b/app/assets/images/rails.png Binary files differnew file mode 100644 index 0000000..d5edc04 --- /dev/null +++ b/app/assets/images/rails.png diff --git a/app/assets/images/stubs/user-36x.jpg b/app/assets/images/stubs/user-36x.jpg Binary files differnew file mode 100644 index 0000000..8a391a0 --- /dev/null +++ b/app/assets/images/stubs/user-36x.jpg diff --git a/app/assets/images/user.png b/app/assets/images/user.png Binary files differnew file mode 100644 index 0000000..c0e33c7 --- /dev/null +++ b/app/assets/images/user.png diff --git a/app/assets/javascripts/api/rows.js.coffee b/app/assets/javascripts/api/rows.js.coffee new file mode 100644 index 0000000..7615679 --- /dev/null +++ b/app/assets/javascripts/api/rows.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js new file mode 100644 index 0000000..2e0b0d8 --- /dev/null +++ b/app/assets/javascripts/application.js @@ -0,0 +1,13 @@ +// This is a manifest file that'll be compiled into including all the files listed below. +// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically +// be included in the compiled file accessible from http://example.com/assets/application.js +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// the compiled file. +// +//= require jquery +//= require jquery_ujs +//= require core +//= require vendor/modernizr-2.0.6.min.js +//= require vendor/jquery.condom.js +//= require vendor/jquery.survival-kit +//= require softkeys diff --git a/app/assets/javascripts/config_siemens.js.coffee b/app/assets/javascripts/config_siemens.js.coffee new file mode 100644 index 0000000..7615679 --- /dev/null +++ b/app/assets/javascripts/config_siemens.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/javascripts/core.coffee b/app/assets/javascripts/core.coffee new file mode 100644 index 0000000..af12aa7 --- /dev/null +++ b/app/assets/javascripts/core.coffee @@ -0,0 +1,5 @@ +$(document).ready -> + $sk = $.ns('sk') + $sk('.search-box').searchBox() + $sk('.simple_form').simpleForms() +
\ No newline at end of file diff --git a/app/assets/javascripts/page.js.coffee b/app/assets/javascripts/page.js.coffee new file mode 100644 index 0000000..7615679 --- /dev/null +++ b/app/assets/javascripts/page.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/javascripts/softkeys.js.coffee b/app/assets/javascripts/softkeys.js.coffee new file mode 100644 index 0000000..23d5233 --- /dev/null +++ b/app/assets/javascripts/softkeys.js.coffee @@ -0,0 +1,25 @@ +jQuery -> + function_name = $('#softkey_softkey_function_id :selected').text() + + if function_name == call_forwarding_function_name + $('#softkey_call_forward_id').parent().show() + $('#softkey_number').parent().hide() + else + $('#softkey_call_forward_id').parent().hide() + if (function_name == hold_function_name || function_name == deactivated_function_name) + $('#softkey_number').parent().hide() + else + $('#softkey_number').parent().show() + + $('#softkey_softkey_function_id').change -> + $('#softkey_label').parent().show() + function_name = $('#softkey_softkey_function_id :selected').text() + if function_name == call_forwarding_function_name + $('#softkey_call_forward_id').parent().show("slow") + $('#softkey_number').parent().hide("slow") + else + $('#softkey_call_forward_id').parent().hide("slow") + if (function_name == hold_function_name || function_name == deactivated_function_name) + $('#softkey_number').parent().hide("slow") + else + $('#softkey_number').parent().show("slow") diff --git a/app/assets/javascripts/vendor/autoresize.jquery.js b/app/assets/javascripts/vendor/autoresize.jquery.js new file mode 100644 index 0000000..28cec5d --- /dev/null +++ b/app/assets/javascripts/vendor/autoresize.jquery.js @@ -0,0 +1,94 @@ +/* + * jQuery autoResize (textarea auto-resizer) + * @copyright James Padolsey http://james.padolsey.com + * @version 1.04 + */ + +(function($){ + $.fn.autoResize = function(options) { + // Just some abstracted details, + // to make plugin users happy: + var settings = $.extend({ + onResize : function(){}, + animate : true, + animateDuration : 150, + animateCallback : function(){}, + extraSpace : 20, + limit: 1000 + }, options); + + // Only textarea's auto-resize: + this.filter('textarea').each(function(){ + + // Get rid of scrollbars and disable WebKit resizing: + var textarea = $(this).css({resize:'none','overflow-y':'hidden'}), + + // Cache original height, for use later: + origHeight = textarea.height(), + + // Need clone of textarea, hidden off screen: + clone = (function(){ + + // Properties which may effect space taken up by chracters: + var props = ['height','width','lineHeight','textDecoration','letterSpacing'], + propOb = {}; + + // Create object of styles to apply: + $.each(props, function(i, prop){ + propOb[prop] = textarea.css(prop); + }); + + // Clone the actual textarea removing unique properties + // and insert before original textarea: + return textarea.clone().removeAttr('id').removeAttr('name').css({ + position: 'absolute', + top: 0, + left: -9999 + }).css(propOb).attr('tabIndex','-1').insertBefore(textarea); + + })(), + lastScrollTop = null, + updateSize = function() { + + // Prepare the clone: + clone.height(0).val($(this).val()).scrollTop(10000); + + // Find the height of text: + var scrollTop = Math.max(clone.scrollTop(), origHeight) + settings.extraSpace, + toChange = $(this).add(clone); + + // Don't do anything if scrollTip hasen't changed: + if (lastScrollTop === scrollTop) { return; } + lastScrollTop = scrollTop; + + // Check for limit: + if ( scrollTop >= settings.limit ) { + $(this).css('overflow-y',''); + return; + } + // Fire off callback: + settings.onResize.call(this); + + // Either animate or directly apply height: + settings.animate && textarea.css('display') === 'block' ? + toChange.stop().animate({height:scrollTop}, settings.animateDuration, settings.animateCallback) + : toChange.height(scrollTop); + }; + + // Bind namespaced handlers to appropriate events: + textarea + .unbind('.dynSiz') + .bind('keyup.dynSiz', updateSize) + .bind('keydown.dynSiz', updateSize) + .bind('change.dynSiz', updateSize); + + }); + + // Chain: + return this; + + }; + + + +})(jQuery); diff --git a/app/assets/javascripts/vendor/fancybox/jquery.easing-1.3.pack.js b/app/assets/javascripts/vendor/fancybox/jquery.easing-1.3.pack.js new file mode 100755 index 0000000..ef74321 --- /dev/null +++ b/app/assets/javascripts/vendor/fancybox/jquery.easing-1.3.pack.js @@ -0,0 +1,205 @@ +/* + * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ + * + * Uses the built in easing capabilities added In jQuery 1.1 + * to offer multiple easing options + * + * TERMS OF USE - jQuery Easing + * + * Open source under the BSD License. + * + * Copyright © 2008 George McGinley Smith + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the author nor the names of contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * +*/ + +// t: current time, b: begInnIng value, c: change In value, d: duration +jQuery.easing['jswing'] = jQuery.easing['swing']; + +jQuery.extend( jQuery.easing, +{ + def: 'easeOutQuad', + swing: function (x, t, b, c, d) { + //alert(jQuery.easing.default); + return jQuery.easing[jQuery.easing.def](x, t, b, c, d); + }, + easeInQuad: function (x, t, b, c, d) { + return c*(t/=d)*t + b; + }, + easeOutQuad: function (x, t, b, c, d) { + return -c *(t/=d)*(t-2) + b; + }, + easeInOutQuad: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t + b; + return -c/2 * ((--t)*(t-2) - 1) + b; + }, + easeInCubic: function (x, t, b, c, d) { + return c*(t/=d)*t*t + b; + }, + easeOutCubic: function (x, t, b, c, d) { + return c*((t=t/d-1)*t*t + 1) + b; + }, + easeInOutCubic: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t + b; + return c/2*((t-=2)*t*t + 2) + b; + }, + easeInQuart: function (x, t, b, c, d) { + return c*(t/=d)*t*t*t + b; + }, + easeOutQuart: function (x, t, b, c, d) { + return -c * ((t=t/d-1)*t*t*t - 1) + b; + }, + easeInOutQuart: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t*t + b; + return -c/2 * ((t-=2)*t*t*t - 2) + b; + }, + easeInQuint: function (x, t, b, c, d) { + return c*(t/=d)*t*t*t*t + b; + }, + easeOutQuint: function (x, t, b, c, d) { + return c*((t=t/d-1)*t*t*t*t + 1) + b; + }, + easeInOutQuint: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; + return c/2*((t-=2)*t*t*t*t + 2) + b; + }, + easeInSine: function (x, t, b, c, d) { + return -c * Math.cos(t/d * (Math.PI/2)) + c + b; + }, + easeOutSine: function (x, t, b, c, d) { + return c * Math.sin(t/d * (Math.PI/2)) + b; + }, + easeInOutSine: function (x, t, b, c, d) { + return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; + }, + easeInExpo: function (x, t, b, c, d) { + return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; + }, + easeOutExpo: function (x, t, b, c, d) { + return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; + }, + easeInOutExpo: function (x, t, b, c, d) { + if (t==0) return b; + if (t==d) return b+c; + if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; + return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; + }, + easeInCirc: function (x, t, b, c, d) { + return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; + }, + easeOutCirc: function (x, t, b, c, d) { + return c * Math.sqrt(1 - (t=t/d-1)*t) + b; + }, + easeInOutCirc: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; + return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; + }, + easeInElastic: function (x, t, b, c, d) { + var s=1.70158;var p=0;var a=c; + if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; + if (a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; + }, + easeOutElastic: function (x, t, b, c, d) { + var s=1.70158;var p=0;var a=c; + if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; + if (a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; + }, + easeInOutElastic: function (x, t, b, c, d) { + var s=1.70158;var p=0;var a=c; + if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); + if (a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; + return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; + }, + easeInBack: function (x, t, b, c, d, s) { + if (s == undefined) s = 1.70158; + return c*(t/=d)*t*((s+1)*t - s) + b; + }, + easeOutBack: function (x, t, b, c, d, s) { + if (s == undefined) s = 1.70158; + return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; + }, + easeInOutBack: function (x, t, b, c, d, s) { + if (s == undefined) s = 1.70158; + if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; + return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; + }, + easeInBounce: function (x, t, b, c, d) { + return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b; + }, + easeOutBounce: function (x, t, b, c, d) { + if ((t/=d) < (1/2.75)) { + return c*(7.5625*t*t) + b; + } else if (t < (2/2.75)) { + return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; + } else if (t < (2.5/2.75)) { + return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; + } else { + return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; + } + }, + easeInOutBounce: function (x, t, b, c, d) { + if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b; + return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b; + } +}); + +/* + * + * TERMS OF USE - EASING EQUATIONS + * + * Open source under the BSD License. + * + * Copyright © 2001 Robert Penner + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the author nor the names of contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */
\ No newline at end of file diff --git a/app/assets/javascripts/vendor/fancybox/jquery.fancybox-1.3.4.pack.js b/app/assets/javascripts/vendor/fancybox/jquery.fancybox-1.3.4.pack.js new file mode 100755 index 0000000..1373ed0 --- /dev/null +++ b/app/assets/javascripts/vendor/fancybox/jquery.fancybox-1.3.4.pack.js @@ -0,0 +1,46 @@ +/* + * FancyBox - jQuery Plugin + * Simple and fancy lightbox alternative + * + * Examples and documentation at: http://fancybox.net + * + * Copyright (c) 2008 - 2010 Janis Skarnelis + * That said, it is hardly a one-person project. Many people have submitted bugs, code, and offered their advice freely. Their support is greatly appreciated. + * + * Version: 1.3.4 (11/11/2010) + * Requires: jQuery v1.3+ + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + */ + +;(function(b){var m,t,u,f,D,j,E,n,z,A,q=0,e={},o=[],p=0,d={},l=[],G=null,v=new Image,J=/\.(jpg|gif|png|bmp|jpeg)(.*)?$/i,W=/[^\.]\.(swf)\s*$/i,K,L=1,y=0,s="",r,i,h=false,B=b.extend(b("<div/>")[0],{prop:0}),M=b.browser.msie&&b.browser.version<7&&!window.XMLHttpRequest,N=function(){t.hide();v.onerror=v.onload=null;G&&G.abort();m.empty()},O=function(){if(false===e.onError(o,q,e)){t.hide();h=false}else{e.titleShow=false;e.width="auto";e.height="auto";m.html('<p id="fancybox-error">The requested content cannot be loaded.<br />Please try again later.</p>'); +F()}},I=function(){var a=o[q],c,g,k,C,P,w;N();e=b.extend({},b.fn.fancybox.defaults,typeof b(a).data("fancybox")=="undefined"?e:b(a).data("fancybox"));w=e.onStart(o,q,e);if(w===false)h=false;else{if(typeof w=="object")e=b.extend(e,w);k=e.title||(a.nodeName?b(a).attr("title"):a.title)||"";if(a.nodeName&&!e.orig)e.orig=b(a).children("img:first").length?b(a).children("img:first"):b(a);if(k===""&&e.orig&&e.titleFromAlt)k=e.orig.attr("alt");c=e.href||(a.nodeName?b(a).attr("href"):a.href)||null;if(/^(?:javascript)/i.test(c)|| +c=="#")c=null;if(e.type){g=e.type;if(!c)c=e.content}else if(e.content)g="html";else if(c)g=c.match(J)?"image":c.match(W)?"swf":b(a).hasClass("iframe")?"iframe":c.indexOf("#")===0?"inline":"ajax";if(g){if(g=="inline"){a=c.substr(c.indexOf("#"));g=b(a).length>0?"inline":"ajax"}e.type=g;e.href=c;e.title=k;if(e.autoDimensions)if(e.type=="html"||e.type=="inline"||e.type=="ajax"){e.width="auto";e.height="auto"}else e.autoDimensions=false;if(e.modal){e.overlayShow=true;e.hideOnOverlayClick=false;e.hideOnContentClick= +false;e.enableEscapeButton=false;e.showCloseButton=false}e.padding=parseInt(e.padding,10);e.margin=parseInt(e.margin,10);m.css("padding",e.padding+e.margin);b(".fancybox-inline-tmp").unbind("fancybox-cancel").bind("fancybox-change",function(){b(this).replaceWith(j.children())});switch(g){case "html":m.html(e.content);F();break;case "inline":if(b(a).parent().is("#fancybox-content")===true){h=false;break}b('<div class="fancybox-inline-tmp" />').hide().insertBefore(b(a)).bind("fancybox-cleanup",function(){b(this).replaceWith(j.children())}).bind("fancybox-cancel", +function(){b(this).replaceWith(m.children())});b(a).appendTo(m);F();break;case "image":h=false;b.fancybox.showActivity();v=new Image;v.onerror=function(){O()};v.onload=function(){h=true;v.onerror=v.onload=null;e.width=v.width;e.height=v.height;b("<img />").attr({id:"fancybox-img",src:v.src,alt:e.title}).appendTo(m);Q()};v.src=c;break;case "swf":e.scrolling="no";C='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'+e.width+'" height="'+e.height+'"><param name="movie" value="'+c+ +'"></param>';P="";b.each(e.swf,function(x,H){C+='<param name="'+x+'" value="'+H+'"></param>';P+=" "+x+'="'+H+'"'});C+='<embed src="'+c+'" type="application/x-shockwave-flash" width="'+e.width+'" height="'+e.height+'"'+P+"></embed></object>";m.html(C);F();break;case "ajax":h=false;b.fancybox.showActivity();e.ajax.win=e.ajax.success;G=b.ajax(b.extend({},e.ajax,{url:c,data:e.ajax.data||{},error:function(x){x.status>0&&O()},success:function(x,H,R){if((typeof R=="object"?R:G).status==200){if(typeof e.ajax.win== +"function"){w=e.ajax.win(c,x,H,R);if(w===false){t.hide();return}else if(typeof w=="string"||typeof w=="object")x=w}m.html(x);F()}}}));break;case "iframe":Q()}}else O()}},F=function(){var a=e.width,c=e.height;a=a.toString().indexOf("%")>-1?parseInt((b(window).width()-e.margin*2)*parseFloat(a)/100,10)+"px":a=="auto"?"auto":a+"px";c=c.toString().indexOf("%")>-1?parseInt((b(window).height()-e.margin*2)*parseFloat(c)/100,10)+"px":c=="auto"?"auto":c+"px";m.wrapInner('<div style="width:'+a+";height:"+c+ +";overflow: "+(e.scrolling=="auto"?"auto":e.scrolling=="yes"?"scroll":"hidden")+';position:relative;"></div>');e.width=m.width();e.height=m.height();Q()},Q=function(){var a,c;t.hide();if(f.is(":visible")&&false===d.onCleanup(l,p,d)){b.event.trigger("fancybox-cancel");h=false}else{h=true;b(j.add(u)).unbind();b(window).unbind("resize.fb scroll.fb");b(document).unbind("keydown.fb");f.is(":visible")&&d.titlePosition!=="outside"&&f.css("height",f.height());l=o;p=q;d=e;if(d.overlayShow){u.css({"background-color":d.overlayColor, +opacity:d.overlayOpacity,cursor:d.hideOnOverlayClick?"pointer":"auto",height:b(document).height()});if(!u.is(":visible")){M&&b("select:not(#fancybox-tmp select)").filter(function(){return this.style.visibility!=="hidden"}).css({visibility:"hidden"}).one("fancybox-cleanup",function(){this.style.visibility="inherit"});u.show()}}else u.hide();i=X();s=d.title||"";y=0;n.empty().removeAttr("style").removeClass();if(d.titleShow!==false){if(b.isFunction(d.titleFormat))a=d.titleFormat(s,l,p,d);else a=s&&s.length? +d.titlePosition=="float"?'<table id="fancybox-title-float-wrap" cellpadding="0" cellspacing="0"><tr><td id="fancybox-title-float-left"></td><td id="fancybox-title-float-main">'+s+'</td><td id="fancybox-title-float-right"></td></tr></table>':'<div id="fancybox-title-'+d.titlePosition+'">'+s+"</div>":false;s=a;if(!(!s||s==="")){n.addClass("fancybox-title-"+d.titlePosition).html(s).appendTo("body").show();switch(d.titlePosition){case "inside":n.css({width:i.width-d.padding*2,marginLeft:d.padding,marginRight:d.padding}); +y=n.outerHeight(true);n.appendTo(D);i.height+=y;break;case "over":n.css({marginLeft:d.padding,width:i.width-d.padding*2,bottom:d.padding}).appendTo(D);break;case "float":n.css("left",parseInt((n.width()-i.width-40)/2,10)*-1).appendTo(f);break;default:n.css({width:i.width-d.padding*2,paddingLeft:d.padding,paddingRight:d.padding}).appendTo(f)}}}n.hide();if(f.is(":visible")){b(E.add(z).add(A)).hide();a=f.position();r={top:a.top,left:a.left,width:f.width(),height:f.height()};c=r.width==i.width&&r.height== +i.height;j.fadeTo(d.changeFade,0.3,function(){var g=function(){j.html(m.contents()).fadeTo(d.changeFade,1,S)};b.event.trigger("fancybox-change");j.empty().removeAttr("filter").css({"border-width":d.padding,width:i.width-d.padding*2,height:e.autoDimensions?"auto":i.height-y-d.padding*2});if(c)g();else{B.prop=0;b(B).animate({prop:1},{duration:d.changeSpeed,easing:d.easingChange,step:T,complete:g})}})}else{f.removeAttr("style");j.css("border-width",d.padding);if(d.transitionIn=="elastic"){r=V();j.html(m.contents()); +f.show();if(d.opacity)i.opacity=0;B.prop=0;b(B).animate({prop:1},{duration:d.speedIn,easing:d.easingIn,step:T,complete:S})}else{d.titlePosition=="inside"&&y>0&&n.show();j.css({width:i.width-d.padding*2,height:e.autoDimensions?"auto":i.height-y-d.padding*2}).html(m.contents());f.css(i).fadeIn(d.transitionIn=="none"?0:d.speedIn,S)}}}},Y=function(){if(d.enableEscapeButton||d.enableKeyboardNav)b(document).bind("keydown.fb",function(a){if(a.keyCode==27&&d.enableEscapeButton){a.preventDefault();b.fancybox.close()}else if((a.keyCode== +37||a.keyCode==39)&&d.enableKeyboardNav&&a.target.tagName!=="INPUT"&&a.target.tagName!=="TEXTAREA"&&a.target.tagName!=="SELECT"){a.preventDefault();b.fancybox[a.keyCode==37?"prev":"next"]()}});if(d.showNavArrows){if(d.cyclic&&l.length>1||p!==0)z.show();if(d.cyclic&&l.length>1||p!=l.length-1)A.show()}else{z.hide();A.hide()}},S=function(){if(!b.support.opacity){j.get(0).style.removeAttribute("filter");f.get(0).style.removeAttribute("filter")}e.autoDimensions&&j.css("height","auto");f.css("height","auto"); +s&&s.length&&n.show();d.showCloseButton&&E.show();Y();d.hideOnContentClick&&j.bind("click",b.fancybox.close);d.hideOnOverlayClick&&u.bind("click",b.fancybox.close);b(window).bind("resize.fb",b.fancybox.resize);d.centerOnScroll&&b(window).bind("scroll.fb",b.fancybox.center);if(d.type=="iframe")b('<iframe id="fancybox-frame" name="fancybox-frame'+(new Date).getTime()+'" frameborder="0" hspace="0" '+(b.browser.msie?'allowtransparency="true""':"")+' scrolling="'+e.scrolling+'" src="'+d.href+'"></iframe>').appendTo(j); +f.show();h=false;b.fancybox.center();d.onComplete(l,p,d);var a,c;if(l.length-1>p){a=l[p+1].href;if(typeof a!=="undefined"&&a.match(J)){c=new Image;c.src=a}}if(p>0){a=l[p-1].href;if(typeof a!=="undefined"&&a.match(J)){c=new Image;c.src=a}}},T=function(a){var c={width:parseInt(r.width+(i.width-r.width)*a,10),height:parseInt(r.height+(i.height-r.height)*a,10),top:parseInt(r.top+(i.top-r.top)*a,10),left:parseInt(r.left+(i.left-r.left)*a,10)};if(typeof i.opacity!=="undefined")c.opacity=a<0.5?0.5:a;f.css(c); +j.css({width:c.width-d.padding*2,height:c.height-y*a-d.padding*2})},U=function(){return[b(window).width()-d.margin*2,b(window).height()-d.margin*2,b(document).scrollLeft()+d.margin,b(document).scrollTop()+d.margin]},X=function(){var a=U(),c={},g=d.autoScale,k=d.padding*2;c.width=d.width.toString().indexOf("%")>-1?parseInt(a[0]*parseFloat(d.width)/100,10):d.width+k;c.height=d.height.toString().indexOf("%")>-1?parseInt(a[1]*parseFloat(d.height)/100,10):d.height+k;if(g&&(c.width>a[0]||c.height>a[1]))if(e.type== +"image"||e.type=="swf"){g=d.width/d.height;if(c.width>a[0]){c.width=a[0];c.height=parseInt((c.width-k)/g+k,10)}if(c.height>a[1]){c.height=a[1];c.width=parseInt((c.height-k)*g+k,10)}}else{c.width=Math.min(c.width,a[0]);c.height=Math.min(c.height,a[1])}c.top=parseInt(Math.max(a[3]-20,a[3]+(a[1]-c.height-40)*0.5),10);c.left=parseInt(Math.max(a[2]-20,a[2]+(a[0]-c.width-40)*0.5),10);return c},V=function(){var a=e.orig?b(e.orig):false,c={};if(a&&a.length){c=a.offset();c.top+=parseInt(a.css("paddingTop"), +10)||0;c.left+=parseInt(a.css("paddingLeft"),10)||0;c.top+=parseInt(a.css("border-top-width"),10)||0;c.left+=parseInt(a.css("border-left-width"),10)||0;c.width=a.width();c.height=a.height();c={width:c.width+d.padding*2,height:c.height+d.padding*2,top:c.top-d.padding-20,left:c.left-d.padding-20}}else{a=U();c={width:d.padding*2,height:d.padding*2,top:parseInt(a[3]+a[1]*0.5,10),left:parseInt(a[2]+a[0]*0.5,10)}}return c},Z=function(){if(t.is(":visible")){b("div",t).css("top",L*-40+"px");L=(L+1)%12}else clearInterval(K)}; +b.fn.fancybox=function(a){if(!b(this).length)return this;b(this).data("fancybox",b.extend({},a,b.metadata?b(this).metadata():{})).unbind("click.fb").bind("click.fb",function(c){c.preventDefault();if(!h){h=true;b(this).blur();o=[];q=0;c=b(this).attr("rel")||"";if(!c||c==""||c==="nofollow")o.push(this);else{o=b("a[rel="+c+"], area[rel="+c+"]");q=o.index(this)}I()}});return this};b.fancybox=function(a,c){var g;if(!h){h=true;g=typeof c!=="undefined"?c:{};o=[];q=parseInt(g.index,10)||0;if(b.isArray(a)){for(var k= +0,C=a.length;k<C;k++)if(typeof a[k]=="object")b(a[k]).data("fancybox",b.extend({},g,a[k]));else a[k]=b({}).data("fancybox",b.extend({content:a[k]},g));o=jQuery.merge(o,a)}else{if(typeof a=="object")b(a).data("fancybox",b.extend({},g,a));else a=b({}).data("fancybox",b.extend({content:a},g));o.push(a)}if(q>o.length||q<0)q=0;I()}};b.fancybox.showActivity=function(){clearInterval(K);t.show();K=setInterval(Z,66)};b.fancybox.hideActivity=function(){t.hide()};b.fancybox.next=function(){return b.fancybox.pos(p+ +1)};b.fancybox.prev=function(){return b.fancybox.pos(p-1)};b.fancybox.pos=function(a){if(!h){a=parseInt(a);o=l;if(a>-1&&a<l.length){q=a;I()}else if(d.cyclic&&l.length>1){q=a>=l.length?0:l.length-1;I()}}};b.fancybox.cancel=function(){if(!h){h=true;b.event.trigger("fancybox-cancel");N();e.onCancel(o,q,e);h=false}};b.fancybox.close=function(){function a(){u.fadeOut("fast");n.empty().hide();f.hide();b.event.trigger("fancybox-cleanup");j.empty();d.onClosed(l,p,d);l=e=[];p=q=0;d=e={};h=false}if(!(h||f.is(":hidden"))){h= +true;if(d&&false===d.onCleanup(l,p,d))h=false;else{N();b(E.add(z).add(A)).hide();b(j.add(u)).unbind();b(window).unbind("resize.fb scroll.fb");b(document).unbind("keydown.fb");j.find("iframe").attr("src",M&&/^https/i.test(window.location.href||"")?"javascript:void(false)":"about:blank");d.titlePosition!=="inside"&&n.empty();f.stop();if(d.transitionOut=="elastic"){r=V();var c=f.position();i={top:c.top,left:c.left,width:f.width(),height:f.height()};if(d.opacity)i.opacity=1;n.empty().hide();B.prop=1; +b(B).animate({prop:0},{duration:d.speedOut,easing:d.easingOut,step:T,complete:a})}else f.fadeOut(d.transitionOut=="none"?0:d.speedOut,a)}}};b.fancybox.resize=function(){u.is(":visible")&&u.css("height",b(document).height());b.fancybox.center(true)};b.fancybox.center=function(a){var c,g;if(!h){g=a===true?1:0;c=U();!g&&(f.width()>c[0]||f.height()>c[1])||f.stop().animate({top:parseInt(Math.max(c[3]-20,c[3]+(c[1]-j.height()-40)*0.5-d.padding)),left:parseInt(Math.max(c[2]-20,c[2]+(c[0]-j.width()-40)*0.5- +d.padding))},typeof a=="number"?a:200)}};b.fancybox.init=function(){if(!b("#fancybox-wrap").length){b("body").append(m=b('<div id="fancybox-tmp"></div>'),t=b('<div id="fancybox-loading"><div></div></div>'),u=b('<div id="fancybox-overlay"></div>'),f=b('<div id="fancybox-wrap"></div>'));D=b('<div id="fancybox-outer"></div>').append('<div class="fancybox-bg" id="fancybox-bg-n"></div><div class="fancybox-bg" id="fancybox-bg-ne"></div><div class="fancybox-bg" id="fancybox-bg-e"></div><div class="fancybox-bg" id="fancybox-bg-se"></div><div class="fancybox-bg" id="fancybox-bg-s"></div><div class="fancybox-bg" id="fancybox-bg-sw"></div><div class="fancybox-bg" id="fancybox-bg-w"></div><div class="fancybox-bg" id="fancybox-bg-nw"></div>').appendTo(f); +D.append(j=b('<div id="fancybox-content"></div>'),E=b('<a id="fancybox-close"></a>'),n=b('<div id="fancybox-title"></div>'),z=b('<a href="javascript:;" id="fancybox-left"><span class="fancy-ico" id="fancybox-left-ico"></span></a>'),A=b('<a href="javascript:;" id="fancybox-right"><span class="fancy-ico" id="fancybox-right-ico"></span></a>'));E.click(b.fancybox.close);t.click(b.fancybox.cancel);z.click(function(a){a.preventDefault();b.fancybox.prev()});A.click(function(a){a.preventDefault();b.fancybox.next()}); +b.fn.mousewheel&&f.bind("mousewheel.fb",function(a,c){if(h)a.preventDefault();else if(b(a.target).get(0).clientHeight==0||b(a.target).get(0).scrollHeight===b(a.target).get(0).clientHeight){a.preventDefault();b.fancybox[c>0?"prev":"next"]()}});b.support.opacity||f.addClass("fancybox-ie");if(M){t.addClass("fancybox-ie6");f.addClass("fancybox-ie6");b('<iframe id="fancybox-hide-sel-frame" src="'+(/^https/i.test(window.location.href||"")?"javascript:void(false)":"about:blank")+'" scrolling="no" border="0" frameborder="0" tabindex="-1"></iframe>').prependTo(D)}}}; +b.fn.fancybox.defaults={padding:10,margin:40,opacity:false,modal:false,cyclic:false,scrolling:"auto",width:560,height:340,autoScale:true,autoDimensions:true,centerOnScroll:false,ajax:{},swf:{wmode:"transparent"},hideOnOverlayClick:true,hideOnContentClick:false,overlayShow:true,overlayOpacity:0.7,overlayColor:"#777",titleShow:true,titlePosition:"float",titleFormat:null,titleFromAlt:false,transitionIn:"fade",transitionOut:"fade",speedIn:300,speedOut:300,changeSpeed:300,changeFade:"fast",easingIn:"swing", +easingOut:"swing",showCloseButton:true,showNavArrows:true,enableEscapeButton:true,enableKeyboardNav:true,onStart:function(){},onCancel:function(){},onComplete:function(){},onCleanup:function(){},onClosed:function(){},onError:function(){}};b(document).ready(function(){b.fancybox.init()})})(jQuery);
\ No newline at end of file diff --git a/app/assets/javascripts/vendor/fancybox/jquery.mousewheel-3.0.4.pack.js b/app/assets/javascripts/vendor/fancybox/jquery.mousewheel-3.0.4.pack.js new file mode 100755 index 0000000..cb66588 --- /dev/null +++ b/app/assets/javascripts/vendor/fancybox/jquery.mousewheel-3.0.4.pack.js @@ -0,0 +1,14 @@ +/*! Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net) +* Licensed under the MIT License (LICENSE.txt). +* +* Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers. +* Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix. +* Thanks to: Seamus Leahy for adding deltaX and deltaY +* +* Version: 3.0.4 +* +* Requires: 1.2.2+ +*/ + +(function(d){function g(a){var b=a||window.event,i=[].slice.call(arguments,1),c=0,h=0,e=0;a=d.event.fix(b);a.type="mousewheel";if(a.wheelDelta)c=a.wheelDelta/120;if(a.detail)c=-a.detail/3;e=c;if(b.axis!==undefined&&b.axis===b.HORIZONTAL_AXIS){e=0;h=-1*c}if(b.wheelDeltaY!==undefined)e=b.wheelDeltaY/120;if(b.wheelDeltaX!==undefined)h=-1*b.wheelDeltaX/120;i.unshift(a,c,h,e);return d.event.handle.apply(this,i)}var f=["DOMMouseScroll","mousewheel"];d.event.special.mousewheel={setup:function(){if(this.addEventListener)for(var a= +f.length;a;)this.addEventListener(f[--a],g,false);else this.onmousewheel=g},teardown:function(){if(this.removeEventListener)for(var a=f.length;a;)this.removeEventListener(f[--a],g,false);else this.onmousewheel=null}};d.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})})(jQuery);
\ No newline at end of file diff --git a/app/assets/javascripts/vendor/html5boilerplate.js b/app/assets/javascripts/vendor/html5boilerplate.js new file mode 100644 index 0000000..7cb21b1 --- /dev/null +++ b/app/assets/javascripts/vendor/html5boilerplate.js @@ -0,0 +1,20 @@ + +// usage: log('inside coolFunc', this, arguments); +// paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/ +window.log = function(){ + log.history = log.history || []; // store logs to an array for reference + log.history.push(arguments); + if(this.console) { + arguments.callee = arguments.callee.caller; + var newarr = [].slice.call(arguments); + (typeof console.log === 'object' ? log.apply.call(console.log, console, newarr) : console.log.apply(console, newarr)); + } +}; + +// make it safe to use console.log always +(function(b){function c(){}for(var d="assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,timeStamp,profile,profileEnd,time,timeEnd,trace,warn".split(","),a;a=d.pop();){b[a]=b[a]||c}})((function(){try +{console.log();return window.console;}catch(err){return window.console={};}})()); + + +// place any jQuery/helper plugins in here, instead of separate, slower script files. + diff --git a/app/assets/javascripts/vendor/jquery-1.6.2.min.js b/app/assets/javascripts/vendor/jquery-1.6.2.min.js new file mode 100755 index 0000000..48590ec --- /dev/null +++ b/app/assets/javascripts/vendor/jquery-1.6.2.min.js @@ -0,0 +1,18 @@ +/*! + * jQuery JavaScript Library v1.6.2 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Thu Jun 30 14:16:56 2011 -0400 + */ +(function(a,b){function cv(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cs(a){if(!cg[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ch||(ch=c.createElement("iframe"),ch.frameBorder=ch.width=ch.height=0),b.appendChild(ch);if(!ci||!ch.createElement)ci=(ch.contentWindow||ch.contentDocument).document,ci.write((c.compatMode==="CSS1Compat"?"<!doctype html>":"")+"<html><body>"),ci.close();d=ci.createElement(a),ci.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ch)}cg[a]=e}return cg[a]}function cr(a,b){var c={};f.each(cm.concat.apply([],cm.slice(0,b)),function(){c[this]=a});return c}function cq(){cn=b}function cp(){setTimeout(cq,0);return cn=f.now()}function cf(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ce(){try{return new a.XMLHttpRequest}catch(b){}}function b$(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function bZ(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function bY(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bC.test(a)?d(a,e):bY(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)bY(a+"["+e+"]",b[e],c,d);else d(a,b)}function bX(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bR,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bX(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bX(a,c,d,e,"*",g));return l}function bW(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bN),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bA(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?bv:bw;if(d>0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bx(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function bm(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(be,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bl(a){f.nodeName(a,"input")?bk(a):"getElementsByTagName"in a&&f.grep(a.getElementsByTagName("input"),bk)}function bk(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bj(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bi(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bh(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i<j;i++)f.event.add(b,h+(g[h][i].namespace?".":"")+g[h][i].namespace,g[h][i],g[h][i].data)}}}}function bg(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function W(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(R.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(a,b){return(a&&a!=="*"?a+".":"")+b.replace(z,"`").replace(A,"&")}function M(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;i<s.length;i++)g=s[i],g.origType.replace(x,"")===a.type?q.push(g.selector):s.splice(i--,1);e=f(a.target).closest(q,a.currentTarget);for(j=0,k=e.length;j<k;j++){m=e[j];for(i=0;i<s.length;i++){g=s[i];if(m.selector===g.selector&&(!n||n.test(g.namespace))&&!m.elem.disabled){h=m.elem,d=null;if(g.preType==="mouseenter"||g.preType==="mouseleave")a.type=g.preType,d=f(a.relatedTarget).closest(g.selector)[0],d&&f.contains(h,d)&&(d=h);(!d||d!==h)&&p.push({elem:h,handleObj:g,level:m.level})}}}for(j=0,k=p.length;j<k;j++){e=p[j];if(c&&e.level>c)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function K(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function E(){return!0}function D(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"$1-$2").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z])/ig,x=function(a,b){return b.toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!A){A=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a);return c===b||D.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(b,c,d){a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),d=c.documentElement,(!d||!d.nodeName||d.nodeName==="parsererror")&&e.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b){if(H)return H.call(b,a);for(var c=0,d=b.length;c<d;c++)if(b[c]===a)return c;return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=s.exec(a)||t.exec(a)||u.exec(a)||a.indexOf("compatible")<0&&v.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g="done fail isResolved isRejected promise then always pipe".split(" "),h=[].slice;f.extend({_Deferred:function(){var a=[],b,c,d,e={done:function(){if(!d){var c=arguments,g,h,i,j,k;b&&(k=b,b=0);for(g=0,h=c.length;g<h;g++)i=c[g],j=f.type(i),j==="array"?e.done.apply(e,i):j==="function"&&a.push(i);k&&e.resolveWith(k[0],k[1])}return this},resolveWith:function(e,f){if(!d&&!b&&!c){f=f||[],c=1;try{while(a[0])a.shift().apply(e,f)}finally{b=[e,f],c=0}}return this},resolve:function(){e.resolveWith(this,arguments);return this},isResolved:function(){return!!c||!!b},cancel:function(){d=1,a=[];return this}};return e},Deferred:function(a){var b=f._Deferred(),c=f._Deferred(),d;f.extend(b,{then:function(a,c){b.done(a).fail(c);return this},always:function(){return b.done.apply(b,arguments).fail.apply(this,arguments)},fail:c.done,rejectWith:c.resolveWith,reject:c.resolve,isRejected:c.isResolved,pipe:function(a,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[c,"reject"]},function(a,c){var e=c[0],g=c[1],h;f.isFunction(e)?b[a](function(){h=e.apply(this,arguments),h&&f.isFunction(h.promise)?h.promise().then(d.resolve,d.reject):d[g](h)}):b[a](d[g])})}).promise()},promise:function(a){if(a==null){if(d)return d;d=a={}}var c=g.length;while(c--)a[g[c]]=b[g[c]];return a}}),b.done(c.cancel).fail(b.cancel),delete b.cancel,a&&a.call(b,b);return b},when:function(a){function i(a){return function(c){b[a]=arguments.length>1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c<d;c++)b[c]&&f.isFunction(b[c].promise)?b[c].promise().then(i(c),g.reject):--e;e||g.resolveWith(g,b)}else g!==a&&g.resolveWith(g,d?[a]:[]);return g.promise()}}),f.support=function(){var a=c.createElement("div"),b=c.documentElement,d,e,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;a.setAttribute("className","t"),a.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.firstChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0},m&&f.extend(p,{position:"absolute",left:-1e3,top:-1e3});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="<div style='width:4px;'></div>",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0),o.innerHTML="",n.removeChild(o);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([a-z])([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g=f.expando,h=typeof c=="string",i,j=a.nodeType,k=j?f.cache:a,l=j?a[f.expando]:a[f.expando]&&f.expando;if((!l||e&&l&&!k[l][g])&&h&&d===b)return;l||(j?a[f.expando]=l=++f.uuid:l=f.expando),k[l]||(k[l]={},j||(k[l].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?k[l][g]=f.extend(k[l][g],c):k[l]=f.extend(k[l],c);i=k[l],e&&(i[g]||(i[g]={}),i=i[g]),d!==b&&(i[f.camelCase(c)]=d);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[f.camelCase(c)]||i[c]:i}},removeData:function(b,c,d){if(!!f.acceptData(b)){var e=f.expando,g=b.nodeType,h=g?f.cache:b,i=g?b[f.expando]:f.expando;if(!h[i])return;if(c){var j=d?h[i][e]:h[i];if(j){delete j[c];if(!l(j))return}}if(d){delete h[i][e];if(!l(h[i]))return}var k=h[i][e];f.support.deleteExpando||h!=a?delete h[i]:h[i]=null,k?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=k):g&&(f.support.deleteExpando?delete b[f.expando]:b.removeAttribute?b.removeAttribute(f.expando):b[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h<i;h++)g=e[h].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),k(this[0],g,d[g]))}}return d}if(typeof a=="object")return this.each(function(){f.data(this,a)});var j=a.split(".");j[1]=j[1]?"."+j[1]:"";if(c===b){d=this.triggerHandler("getData"+j[1]+"!",[j[0]]),d===b&&this.length&&(d=f.data(this[0],a),d=k(this[0],a,d));return d===b&&j[1]?this.data(j[0]):d}return this.each(function(){var b=f(this),d=[j[0],c];b.triggerHandler("setData"+j[1]+"!",d),f.data(this,a,c),b.triggerHandler("changeData"+j[1]+"!",d)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,c){a&&(c=(c||"fx")+"mark",f.data(a,c,(f.data(a,c,b,!0)||0)+1,!0))},_unmark:function(a,c,d){a!==!0&&(d=c,c=a,a=!1);if(c){d=d||"fx";var e=d+"mark",g=a?0:(f.data(c,e,b,!0)||1)-1;g?f.data(c,e,g,!0):(f.removeData(c,e,!0),m(c,d,"mark"))}},queue:function(a,c,d){if(a){c=(c||"fx")+"queue";var e=f.data(a,c,b,!0);d&&(!e||f.isArray(d)?e=f.data(a,c,f.makeArray(d),!0):e.push(d));return e||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e;d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),d.call(a,function(){f.dequeue(a,b)})),c.length||(f.removeData(a,b+"queue",!0),m(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(){var c=this;setTimeout(function(){f.dequeue(c,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f._Deferred(),!0))h++,l.done(m);m();return d.promise()}});var n=/[\n\t\r]/g,o=/\s+/,p=/\r/g,q=/^(?:button|input)$/i,r=/^(?:button|input|object|select|textarea)$/i,s=/^a(?:rea)?$/i,t=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,u=/\:|^on/,v,w;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(o);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(o);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(n," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(o);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ";for(var c=0,d=this.length;c<d;c++)if((" "+this[c].className+" ").replace(n," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;d=e.value;return typeof d=="string"?d.replace(p,""):d==null?"":d}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h<i;h++){var j=e[h];if(j.selected&&(f.support.optDisabled?!j.disabled:j.getAttribute("disabled")===null)&&(!j.parentNode.disabled||!f.nodeName(j.parentNode,"optgroup"))){b=f(j).val();if(g)return b;d.push(b)}}if(g&&!d.length&&e.length)return f(e[c]).val();return d},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);j&&(c=f.attrFix[c]||c,i=f.attrHooks[c],i||(t.test(c)?i=w:v&&c!=="className"&&(f.nodeName(a,"form")||u.test(c))&&(i=v)));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j&&(h=i.get(a,c))!==null)return h;h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.support.getSetAttribute?a.removeAttribute(b):(f.attr(a,b,""),a.removeAttributeNode(a.getAttributeNode(b))),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},tabIndex:{get:function(a){var c=a.getAttributeNode("tabIndex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}},value:{get:function(a,b){if(v&&f.nodeName(a,"button"))return v.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(v&&f.nodeName(a,"button"))return v.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);i&&(c=f.propFix[c]||c,h=f.propHooks[c]);return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==b?g:a[c]},propHooks:{}}),w={get:function(a,c){return f.prop(a,c)?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},f.support.getSetAttribute||(f.attrFix=f.propFix,v=f.attrHooks.name=f.attrHooks.title=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,c){var d=a.getAttributeNode(c);if(d){d.nodeValue=b;return b}}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var x=/\.(.*)$/,y=/^(?:textarea|input|select)$/i,z=/\./g,A=/ /g,B=/[^\w\s.|`]/g,C=function(a){return a.replace(B,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=D;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=D);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),C).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j<p.length;j++){q=p[j];if(l||n.test(q.namespace))f.event.remove(a,r,q.handler,j),p.splice(j--,1)}continue}o=f.event.special[h]||{};for(j=e||0;j<p.length;j++){q=p[j];if(d.guid===q.guid){if(l||n.test(q.namespace))e==null&&p.splice(j--,1),o.remove&&o.remove.call(a,q);if(e!=null)break}}if(p.length===0||e!=null&&p.length===1)(!o.teardown||o.teardown.call(a,m)===!1)&&f.removeEvent(a,h,s.handle),g=null,delete t[h]}if(f.isEmptyObject(t)){var u=s.handle;u&&(u.elem=null),delete s.events,delete s.handle,f.isEmptyObject(s)&&f.removeData(a,b,!0)}}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){var h=c.type||c,i=[],j;h.indexOf("!")>=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i. +shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d!=null?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h<i;h++){var j=d[h];if(e||c.namespace_re.test(j.namespace)){c.handler=j.handler,c.data=j.data,c.handleObj=j;var k=j.handler.apply(this,g);k!==b&&(c.result=k,k===!1&&(c.preventDefault(),c.stopPropagation()));if(c.isImmediatePropagationStopped())break}}return c.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(a){if(a[f.expando])return a;var d=a;a=f.Event(d);for(var e=this.props.length,g;e;)g=this.props[--e],a[g]=d[g];a.target||(a.target=a.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),!a.relatedTarget&&a.fromElement&&(a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement);if(a.pageX==null&&a.clientX!=null){var h=a.target.ownerDocument||c,i=h.documentElement,j=h.body;a.pageX=a.clientX+(i&&i.scrollLeft||j&&j.scrollLeft||0)-(i&&i.clientLeft||j&&j.clientLeft||0),a.pageY=a.clientY+(i&&i.scrollTop||j&&j.scrollTop||0)-(i&&i.clientTop||j&&j.clientTop||0)}a.which==null&&(a.charCode!=null||a.keyCode!=null)&&(a.which=a.charCode!=null?a.charCode:a.keyCode),!a.metaKey&&a.ctrlKey&&(a.metaKey=a.ctrlKey),!a.which&&a.button!==b&&(a.which=a.button&1?1:a.button&2?3:a.button&4?2:0);return a},guid:1e8,proxy:f.proxy,special:{ready:{setup:f.bindReady,teardown:f.noop},live:{add:function(a){f.event.add(this,N(a.origType,a.selector),f.extend({},a,{handler:M,guid:a.handler.guid}))},remove:function(a){f.event.remove(this,N(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}}},f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!this.preventDefault)return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?E:D):this.type=a,b&&f.extend(this,b),this.timeStamp=f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=E;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=E;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=E,this.stopPropagation()},isDefaultPrevented:D,isPropagationStopped:D,isImmediatePropagationStopped:D};var F=function(a){var b=a.relatedTarget,c=!1,d=a.type;a.type=a.data,b!==this&&(b&&(c=f.contains(this,b)),c||(f.event.handle.apply(this,arguments),a.type=d))},G=function(a){a.type=a.data,f.event.handle.apply(this,arguments)};f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={setup:function(c){f.event.add(this,b,c&&c.selector?G:F,a)},teardown:function(a){f.event.remove(this,b,a&&a.selector?G:F)}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(a,b){if(!f.nodeName(this,"form"))f.event.add(this,"click.specialSubmit",function(a){var b=a.target,c=b.type;(c==="submit"||c==="image")&&f(b).closest("form").length&&K("submit",this,arguments)}),f.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,c=b.type;(c==="text"||c==="password")&&f(b).closest("form").length&&a.keyCode===13&&K("submit",this,arguments)});else return!1},teardown:function(a){f.event.remove(this,".specialSubmit")}});if(!f.support.changeBubbles){var H,I=function(a){var b=a.type,c=a.value;b==="radio"||b==="checkbox"?c=a.checked:b==="select-multiple"?c=a.selectedIndex>-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},J=function(c){var d=c.target,e,g;if(!!y.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=I(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:J,beforedeactivate:J,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&J.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&J.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",I(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in H)f.event.add(this,c+".specialChange",H[c]);return y.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return y.test(this.nodeName)}},H=f.event.special.change.filters,H.focus=H.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i<j;i++)f.event.add(this[i],a,g,d);return this}}),f.fn.extend({unbind:function(a,b){if(typeof a=="object"&&!a.preventDefault)for(var c in a)this.unbind(c,a[c]);else for(var d=0,e=this.length;d<e;d++)f.event.remove(this[d],a,b);return this},delegate:function(a,b,c,d){return this.live(b,c,d,a)},undelegate:function(a,b,c){return arguments.length===0?this.unbind("live"):this.die(b,null,c,a)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f.data(this,"lastToggle"+a.guid)||0)%d;f.data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var L={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};f.each(["live","die"],function(a,c){f.fn[c]=function(a,d,e,g){var h,i=0,j,k,l,m=g||this.selector,n=g?this:f(this.context);if(typeof a=="object"&&!a.preventDefault){for(var o in a)n[c](o,d,a[o],m);return this}if(c==="die"&&!a&&g&&g.charAt(0)==="."){n.unbind(g);return this}if(d===!1||f.isFunction(d))e=d||D,d=b;a=(a||"").split(" ");while((h=a[i++])!=null){j=x.exec(h),k="",j&&(k=j[0],h=h.replace(x,""));if(h==="hover"){a.push("mouseenter"+k,"mouseleave"+k);continue}l=h,L[h]?(a.push(L[h]+k),h=h+k):h=(L[h]||h)+k;if(c==="live")for(var p=0,q=n.length;p<q;p++)f.event.add(n[p],"live."+N(h,m),{data:d,selector:m,handler:e,origType:h,origHandler:e,preType:l});else n.unbind("live."+N(h,m),e)}return this}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}if(i.nodeType===1){f||(i.sizcache=c,i.sizset=g);if(typeof b!="string"){if(i===b){j=!0;break}}else if(k.filter(b,[i]).length>0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}i.nodeType===1&&!f&&(i.sizcache=c,i.sizset=g);if(i.nodeName.toLowerCase()===b){j=i;break}i=i[a]}d[g]=j}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},k.matches=function(a,b){return k(a,null,null,b)},k.matchesSelector=function(a,b){return k(b,null,null,[a]).length>0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e<f;e++){var g,h=l.order[e];if(g=l.leftMatch[h].exec(a)){var j=g[1];g.splice(1,1);if(j.substr(j.length-1)!=="\\"){g[1]=(g[1]||"").replace(i,""),d=l.find[h](g,b,c);if(d!=null){a=a.replace(l.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},k.filter=function(a,c,d,e){var f,g,h=a,i=[],j=c,m=c&&c[0]&&k.isXML(c[0]);while(a&&c.length){for(var n in l.filter)if((f=l.leftMatch[n].exec(a))!=null&&f[2]){var o,p,q=l.filter[n],r=f[1];g=!1,f.splice(1,1);if(r.substr(r.length-1)==="\\")continue;j===i&&(i=[]);if(l.preFilter[n]){f=l.preFilter[n](f,j,d,i,e,m);if(!f)g=o=!0;else if(f===!0)continue}if(f)for(var s=0;(p=j[s])!=null;s++)if(p){o=q(p,f,s,j);var t=e^!!o;d&&o!=null?t?g=!0:j[s]=!1:t&&(i.push(p),g=!0)}if(o!==b){d||(j=i),a=a.replace(l.match[n],"");if(!g)return[];break}}if(a===h)if(g==null)k.error(a);else break;h=a}return j},k.error=function(a){throw"Syntax error, unrecognized expression: "+a};var l=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!j.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&k.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&k.filter(b,a,!0)}},"":function(a,b,c){var e,f=d++,g=u;typeof b=="string"&&!j.test(b)&&(b=b.toLowerCase(),e=b,g=t),g("parentNode",b,f,a,e,c)},"~":function(a,b,c){var e,f=d++,g=u;typeof b=="string"&&!j.test(b)&&(b=b.toLowerCase(),e=b,g=t),g("previousSibling",b,f,a,e,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(i,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}k.error(e)},CHILD:function(a,b){var c=b[1],d=a;switch(c){case"only":case"first":while(d=d.previousSibling)if(d.nodeType===1)return!1;if(c==="first")return!0;d=a;case"last":while(d=d.nextSibling)if(d.nodeType===1)return!1;return!0;case"nth":var e=b[2],f=b[3];if(e===1&&f===0)return!0;var g=b[0],h=a.parentNode;if(h&&(h.sizcache!==g||!a.nodeIndex)){var i=0;for(d=h.firstChild;d;d=d.nextSibling)d.nodeType===1&&(d.nodeIndex=++i);h.sizcache=g}var j=a.nodeIndex-f;return e===0?j===0:j%e===0&&j/e>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c<f;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var r,s;c.documentElement.compareDocumentPosition?r=function(a,b){if(a===b){g=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(r=function(a,b){if(a===b){g=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],h=a.parentNode,i=b.parentNode,j=h;if(h===i)return s(a,b);if(!h)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return s(e[k],f[k]);return k===c?s(a,f[k],-1):s(e[k],b,1)},s=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),k.getText=function(a){var b="",c;for(var d=0;a[d];d++)c=a[d],c.nodeType===3||c.nodeType===4?b+=c.nodeValue:c.nodeType!==8&&(b+=k.getText(c.childNodes));return b},function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g<h;g++)k(a,f[g],d);return k.filter(e,d)};f.find=k,f.expr=k.selectors,f.expr[":"]=f.expr.filters,f.unique=k.uniqueSort,f.text=k.getText,f.isXMLDoc=k.isXML,f.contains=k.contains}();var O=/Until$/,P=/^(?:parents|prevUntil|prevAll)/,Q=/,/,R=/^.[^:#\[\.,]*$/,S=Array.prototype.slice,T=f.expr.match.POS,U={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(W(this,a,!1),"not",a)},filter:function(a){return this.pushStack(W(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d<e;d++)i=a[d],j[i]||(j[i]=T.test(i)?f(i,b||this.context):i);while(g&&g.ownerDocument&&g!==b){for(i in j)h=j[i],(h.jquery?h.index(g)>-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=T.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(l?l.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a=="string")return f.inArray(this[0],a?f(a):this.parent().children());return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(V(c[0])||V(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=S.call(arguments);O.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!U[a]?f.unique(e):e,(this.length>1||Q.test(d))&&P.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var X=/ jQuery\d+="(?:\d+|null)"/g,Y=/^\s+/,Z=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,$=/<([\w:]+)/,_=/<tbody/i,ba=/<|&#?\w+;/,bb=/<(?:script|object|embed|option|style)/i,bc=/checked\s*(?:[^=]|=\s*.checked.)/i,bd=/\/(java|ecma)script/i,be=/^\s*<!(?:\[CDATA\[|\-\-)/,bf={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};bf.optgroup=bf.option,bf.tbody=bf.tfoot=bf.colgroup=bf.caption=bf.thead,bf.th=bf.td,f.support.htmlSerialize||(bf._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(X,""):null;if(typeof a=="string"&&!bb.test(a)&&(f.support.leadingWhitespace||!Y.test(a))&&!bf[($.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Z,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bc.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bg(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,bm)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i;b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof a[0]=="string"&&a[0].length<512&&i===c&&a[0].charAt(0)==="<"&&!bb.test(a[0])&&(f.support.checkClone||!bc.test(a[0]))&&(g=!0,h=f.fragments[a[0]],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[a[0]]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j +)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bi(a,d),e=bj(a),g=bj(d);for(h=0;e[h];++h)bi(e[h],g[h])}if(b){bh(a,d);if(c){e=bj(a),g=bj(d);for(h=0;e[h];++h)bh(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!ba.test(k))k=b.createTextNode(k);else{k=k.replace(Z,"<$1></$2>");var l=($.exec(k)||["",""])[1].toLowerCase(),m=bf[l]||bf._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=_.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&Y.test(k)&&o.insertBefore(b.createTextNode(Y.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bl(k[i]);else bl(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||bd.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.expando,g=f.event.special,h=f.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&f.noData[j.nodeName.toLowerCase()])continue;c=j[f.expando];if(c){b=d[c]&&d[c][e];if(b&&b.events){for(var k in b.events)g[k]?f.event.remove(j,k):f.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[f.expando]:j.removeAttribute&&j.removeAttribute(f.expando),delete d[c]}}}});var bn=/alpha\([^)]*\)/i,bo=/opacity=([^)]*)/,bp=/([A-Z]|^ms)/g,bq=/^-?\d+(?:px)?$/i,br=/^-?\d/,bs=/^[+\-]=/,bt=/[^+\-\.\de]+/g,bu={position:"absolute",visibility:"hidden",display:"block"},bv=["Left","Right"],bw=["Top","Bottom"],bx,by,bz;f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bx(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d;if(h==="number"&&isNaN(d)||d==null)return;h==="string"&&bs.test(d)&&(d=+d.replace(bt,"")+parseFloat(f.css(a,c)),h="number"),h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bx)return bx(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){if(a.offsetWidth!==0)return bA(a,b,d);f.swap(a,bu,function(){e=bA(a,b,d)});return e}},set:function(a,b){if(!bq.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bo.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle;c.zoom=1;var e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.filter=bn.test(g)?g.replace(bn,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bx(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(by=function(a,c){var d,e,g;c=c.replace(bp,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bz=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bq.test(d)&&br.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bx=by||bz,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bB=/%20/g,bC=/\[\]$/,bD=/\r?\n/g,bE=/#.*$/,bF=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bG=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bH=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,bI=/^(?:GET|HEAD)$/,bJ=/^\/\//,bK=/\?/,bL=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bM=/^(?:select|textarea)/i,bN=/\s+/,bO=/([?&])_=[^&]*/,bP=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bQ=f.fn.load,bR={},bS={},bT,bU;try{bT=e.href}catch(bV){bT=c.createElement("a"),bT.href="",bT=bT.href}bU=bP.exec(bT.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bQ)return bQ.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bL,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bM.test(this.nodeName)||bG.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bD,"\r\n")}}):{name:b.name,value:c.replace(bD,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?f.extend(!0,a,f.ajaxSettings,b):(b=a,a=f.extend(!0,f.ajaxSettings,b));for(var c in{context:1,url:1})c in b?a[c]=b[c]:c in f.ajaxSettings&&(a[c]=f.ajaxSettings[c]);return a},ajaxSettings:{url:bT,isLocal:bH.test(bU[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML}},ajaxPrefilter:bW(bR),ajaxTransport:bW(bS),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a?4:0;var o,r,u,w=l?bZ(d,v,l):b,x,y;if(a>=200&&a<300||a===304){if(d.ifModified){if(x=v.getResponseHeader("Last-Modified"))f.lastModified[k]=x;if(y=v.getResponseHeader("Etag"))f.etag[k]=y}if(a===304)c="notmodified",o=!0;else try{r=b$(d,w),c="success",o=!0}catch(z){c="parsererror",u=z}}else{u=c;if(!c||a)c="error",a<0&&(a=0)}v.status=a,v.statusText=c,o?h.resolveWith(e,[r,c,v]):h.rejectWith(e,[v,c,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,c]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bF.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bE,"").replace(bJ,bU[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bN),d.crossDomain==null&&(r=bP.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bU[1]&&r[2]==bU[2]&&(r[3]||(r[1]==="http:"?80:443))==(bU[3]||(bU[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bX(bR,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bI.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bK.test(d.url)?"&":"?")+d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bO,"$1_="+x);d.url=y+(y===d.url?(bK.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", */*; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bX(bS,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){status<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)bY(g,a[g],c,e);return d.join("&").replace(bB,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var b_=f.now(),ca=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+b_++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ca.test(b.url)||e&&ca.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ca,l),b.url===j&&(e&&(k=k.replace(ca,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cb=a.ActiveXObject?function(){for(var a in cd)cd[a](0,1)}:!1,cc=0,cd;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ce()||cf()}:ce,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cb&&delete cd[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cc,cb&&(cd||(cd={},f(a).unload(cb)),cd[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cg={},ch,ci,cj=/^(?:toggle|show|hide)$/,ck=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cl,cm=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cn,co=a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cr("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",cs(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cr("hide",3),a,b,c);for(var d=0,e=this.length;d<e;d++)if(this[d].style){var g=f.css(this[d],"display");g!=="none"&&!f._data(this[d],"olddisplay")&&f._data(this[d],"olddisplay",g)}for(d=0;d<e;d++)this[d].style&&(this[d].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cr("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return this[e.queue===!1?"each":"queue"](function(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(f.support.inlineBlockNeedsLayout?(j=cs(this.nodeName),j==="inline"?this.style.display="inline-block":(this.style.display="inline",this.style.zoom=1)):this.style.display="inline-block"))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)k=new f.fx(this,b,i),h=a[i],cj.test(h)?k[h==="toggle"?d?"show":"hide":h]():(l=ck.exec(h),m=k.cur(),l?(n=parseFloat(l[2]),o=l[3]||(f.cssNumber[i]?"":"px"),o!=="px"&&(f.style(this,i,(n||1)+o),m=(n||1)/k.cur()*m,f.style(this,i,m+o)),l[1]&&(n=(l[1]==="-="?-1:1)*n+m),k.custom(m,n,o)):k.custom(m,h,""));return!0})},stop:function(a,b){a&&this.queue([]),this.each(function(){var a=f.timers,c=a.length;b||f._unmark(!0,this);while(c--)a[c].elem===this&&(b&&a[c](!0),a.splice(c,1))}),b||this.dequeue();return this}}),f.each({slideDown:cr("show",1),slideUp:cr("hide",1),slideToggle:cr("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default,d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue!==!1?f.dequeue(this):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,b,c){function h(a){return d.step(a)}var d=this,e=f.fx,g;this.startTime=cn||cp(),this.start=a,this.end=b,this.unit=c||this.unit||(f.cssNumber[this.prop]?"":"px"),this.now=this.start,this.pos=this.state=0,h.elem=this.elem,h()&&f.timers.push(h)&&!cl&&(co?(cl=!0,g=function(){cl&&(co(g),e.tick())},co(g)):cl=setInterval(e.tick,e.interval))},show:function(){this.options.orig[this.prop]=f.style(this.elem,this.prop),this.options.show=!0,this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b=cn||cp(),c=!0,d=this.elem,e=this.options,g,h;if(a||b>=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){for(var a=f.timers,b=0;b<a.length;++b)a[b]()||a.splice(b--,1);a.length||f.fx.stop()},interval:13,stop:function(){clearInterval(cl),cl=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit:a.elem[a.prop]=a.now}}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var ct=/^t(?:able|d|h)$/i,cu=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cv(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);f.offset.initialize();var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.offset.supportsFixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.offset.doesNotAddBorder&&(!f.offset.doesAddBorderForTableAndCells||!ct.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.offset.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.offset.supportsFixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={initialize:function(){var a=c.body,b=c.createElement("div"),d,e,g,h,i=parseFloat(f.css(a,"marginTop"))||0,j="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cu.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cu.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cv(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cv(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a&&a.style?parseFloat(f.css(a,d,"padding")):null},f.fn["outer"+c]=function(a){var b=this[0];return b&&b.style?parseFloat(f.css(b,d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c];return e.document.compatMode==="CSS1Compat"&&g||e.document.body["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var h=f.css(e,d),i=parseFloat(h);return f.isNaN(i)?h:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window);
\ No newline at end of file diff --git a/app/assets/javascripts/vendor/jquery.condom.js b/app/assets/javascripts/vendor/jquery.condom.js new file mode 100644 index 0000000..5d24a7e --- /dev/null +++ b/app/assets/javascripts/vendor/jquery.condom.js @@ -0,0 +1,52 @@ +/* + * jQuery Condom (Use namespaces to protect your global integrity.) + * Version 0.0.3 + * + * Copyright (c) 2011 Mario "Kuroir" Ricalde (http://kuroir.com) + * & Micha Niskin (micha@thinkminimo.com) + * Licensed jointly under the GPL and MIT licenses, + * choose which one suits your project best! + */ +(function($) { + var methods = {}; + $.ns = function(ns) { + // Define namespace if it doesn't exist. + methods[ns] = methods[ns] || {}; + + // Get reference to a namespaced jQ object + function nsfun(selector, context) { + return $(selector, context).ns(ns); + } + + // Allows you to add methods ala jQuery.fn (useful to namespace premade plugins) + nsfun.fn = methods[ns]; + + // Add a method. + nsfun.add = function(fname, fn) { + var new_funcs = typeof fname == "object" ? fname : {}; + // One method. + if (new_funcs !== fname) + new_funcs[fname] = fn; + // Group of methods. + $.each(new_funcs, function(fname, fn) { + methods[ns][fname] = function() { + fn.apply(this, arguments); + return this; + }; + }); + return this; + }; + + // Get methods. + nsfun.methods = function() { + return $.extend({}, methods[ns]); + }; + + return nsfun; + }; + // The only function that touches $.fn + $.fn.ns = function(ns) { + if (methods[ns]) $.extend(this, methods[ns]); + return this; + }; +})(jQuery); diff --git a/app/assets/javascripts/vendor/jquery.easy-slider-1.7.js b/app/assets/javascripts/vendor/jquery.easy-slider-1.7.js new file mode 100644 index 0000000..ff5518f --- /dev/null +++ b/app/assets/javascripts/vendor/jquery.easy-slider-1.7.js @@ -0,0 +1,225 @@ +/* + * Easy Slider 1.7 - jQuery plugin + * written by Alen Grakalic + * http://cssglobe.com/post/4004/easy-slider-15-the-easiest-jquery-plugin-for-sliding + * + * Copyright (c) 2009 Alen Grakalic (http://cssglobe.com) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * Built for jQuery library + * http://jquery.com + * + */ + +/* + * markup example for $("#slider").easySlider(); + * + * <div id="slider"> + * <ul> + * <li><img src="images/01.jpg" alt="" /></li> + * <li><img src="images/02.jpg" alt="" /></li> + * <li><img src="images/03.jpg" alt="" /></li> + * <li><img src="images/04.jpg" alt="" /></li> + * <li><img src="images/05.jpg" alt="" /></li> + * </ul> + * </div> + * + */ + +(function($) { + + $.fn.easySlider = function(options){ + + // default configuration properties + var defaults = { + prevId: 'prevBtn', + prevText: 'Previous', + nextId: 'nextBtn', + nextText: 'Next', + controlsShow: true, + controlsBefore: '', + controlsAfter: '', + controlsFade: true, + firstId: 'firstBtn', + firstText: 'First', + firstShow: false, + lastId: 'lastBtn', + lastText: 'Last', + lastShow: false, + vertical: false, + speed: 800, + auto: false, + pause: 2000, + continuous: false, + numeric: false, + numericId: 'controls' + }; + + var options = $.extend(defaults, options); + + this.each(function() { + var obj = $(this); + var s = $("li", obj).length; + var w = $("li", obj).width(); + var h = $("li", obj).height(); + var clickable = true; + obj.width(w); + obj.height(h); + obj.css("overflow","hidden"); + var ts = s-1; + var t = 0; + $("ul", obj).css('width',s*w); + + if(options.continuous){ + $("ul", obj).prepend($("ul li:last-child", obj).clone().css("margin-left","-"+ w +"px")); + $("ul", obj).append($("ul li:nth-child(2)", obj).clone()); + $("ul", obj).css('width',(s+1)*w); + }; + + if(!options.vertical) $("li", obj).css('float','left'); + + if(options.controlsShow){ + var html = options.controlsBefore; + if(options.numeric){ + html += '<ol id="'+ options.numericId +'"></ol>'; + } else { + if(options.firstShow) html += '<span id="'+ options.firstId +'"><a href=\"javascript:void(0);\">'+ options.firstText +'</a></span>'; + html += ' <span id="'+ options.prevId +'"><a href=\"javascript:void(0);\">'+ options.prevText +'</a></span>'; + html += ' <span id="'+ options.nextId +'"><a href=\"javascript:void(0);\">'+ options.nextText +'</a></span>'; + if(options.lastShow) html += ' <span id="'+ options.lastId +'"><a href=\"javascript:void(0);\">'+ options.lastText +'</a></span>'; + }; + + html += options.controlsAfter; + $(obj).after(html); + }; + + if(options.numeric){ + for(var i=0;i<s;i++){ + $(document.createElement("li")) + .attr('id',options.numericId + (i+1)) + .html('<a rel='+ i +' href=\"javascript:void(0);\">'+ (i+1) +'</a>') + .appendTo($("#"+ options.numericId)) + .click(function(){ + animate($("a",$(this)).attr('rel'),true); + }); + }; + } else { + $("a","#"+options.nextId).click(function(){ + animate("next",true); + }); + $("a","#"+options.prevId).click(function(){ + animate("prev",true); + }); + $("a","#"+options.firstId).click(function(){ + animate("first",true); + }); + $("a","#"+options.lastId).click(function(){ + animate("last",true); + }); + }; + + function setCurrent(i){ + i = parseInt(i)+1; + $("li", "#" + options.numericId).removeClass("current"); + $("li#" + options.numericId + i).addClass("current"); + }; + + function adjust(){ + if(t>ts) t=0; + if(t<0) t=ts; + if(!options.vertical) { + $("ul",obj).css("margin-left",(t*w*-1)); + } else { + $("ul",obj).css("margin-left",(t*h*-1)); + } + clickable = true; + if(options.numeric) setCurrent(t); + }; + + function animate(dir,clicked){ + if (clickable){ + clickable = false; + var ot = t; + switch(dir){ + case "next": + t = (ot>=ts) ? (options.continuous ? t+1 : ts) : t+1; + break; + case "prev": + t = (t<=0) ? (options.continuous ? t-1 : 0) : t-1; + break; + case "first": + t = 0; + break; + case "last": + t = ts; + break; + default: + t = dir; + break; + }; + var diff = Math.abs(ot-t); + var speed = diff*options.speed; + if(!options.vertical) { + p = (t*w*-1); + $("ul",obj).animate( + { marginLeft: p }, + { queue:false, duration:speed, complete:adjust } + ); + } else { + p = (t*h*-1); + $("ul",obj).animate( + { marginTop: p }, + { queue:false, duration:speed, complete:adjust } + ); + }; + + if(!options.continuous && options.controlsFade){ + if(t==ts){ + $("a","#"+options.nextId).hide(); + $("a","#"+options.lastId).hide(); + } else { + $("a","#"+options.nextId).show(); + $("a","#"+options.lastId).show(); + }; + if(t==0){ + $("a","#"+options.prevId).hide(); + $("a","#"+options.firstId).hide(); + } else { + $("a","#"+options.prevId).show(); + $("a","#"+options.firstId).show(); + }; + }; + + if(clicked) clearTimeout(timeout); + if(options.auto && dir=="next" && !clicked){; + timeout = setTimeout(function(){ + animate("next",false); + },diff*options.speed+options.pause); + }; + + }; + + }; + // init + var timeout; + if(options.auto){; + timeout = setTimeout(function(){ + animate("next",false); + },options.pause); + }; + + if(options.numeric) setCurrent(0); + + if(!options.continuous && options.controlsFade){ + $("a","#"+options.prevId).hide(); + $("a","#"+options.firstId).hide(); + }; + + }); + + }; + +})(jQuery); + + diff --git a/app/assets/javascripts/vendor/jquery.survival-kit.coffee b/app/assets/javascripts/vendor/jquery.survival-kit.coffee new file mode 100644 index 0000000..654e167 --- /dev/null +++ b/app/assets/javascripts/vendor/jquery.survival-kit.coffee @@ -0,0 +1,36 @@ +Array.prototype.empty = -> + if this.length <= 0 + return true + else + return false + +$.ns('sk').add { + # Search Box Helper + searchBox: -> + input = $('input.text', this) + default_mes = input.val() + input.focus(-> + if input.val() == default_mes + input.val '' + ).blur(-> + if input.val() == '' + input.val default_mes + ) + + # Simple Form Style Helper. + simpleForms: -> + max = 0 + labels = $("div:not(.boolean) > label", this) + hints = $("div:not(.boolean) > .hint", this) + labels.each -> + if $(this).width() > max + max = $(this).width() + $('> .hint.padded', this).css 'padding-left' : max + + # Get the horizontal-spacing (set on the css.) + horizontal_spacing = parseInt(labels.first().css('margin-right')) + + hints.css 'padding-left' : (max + horizontal_spacing) + $('.actions', this).css 'padding-left' : (max + horizontal_spacing) + labels.width(max) +} diff --git a/app/assets/javascripts/vendor/jquery.tmpl.js b/app/assets/javascripts/vendor/jquery.tmpl.js new file mode 100644 index 0000000..a819d61 --- /dev/null +++ b/app/assets/javascripts/vendor/jquery.tmpl.js @@ -0,0 +1,486 @@ +/* + * jQuery Templating Plugin + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + */ +(function( jQuery, undefined ){ + var oldManip = jQuery.fn.domManip, tmplItmAtt = "_tmplitem", htmlExpr = /^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /, + newTmplItems = {}, wrappedItems = {}, appendToTmplItems, topTmplItem = { key: 0, data: {} }, itemKey = 0, cloneIndex = 0, stack = []; + + function newTmplItem( options, parentItem, fn, data ) { + // Returns a template item data structure for a new rendered instance of a template (a 'template item'). + // The content field is a hierarchical array of strings and nested items (to be + // removed and replaced by nodes field of dom elements, once inserted in DOM). + var newItem = { + data: data || (parentItem ? parentItem.data : {}), + _wrap: parentItem ? parentItem._wrap : null, + tmpl: null, + parent: parentItem || null, + nodes: [], + calls: tiCalls, + nest: tiNest, + wrap: tiWrap, + html: tiHtml, + update: tiUpdate + }; + if ( options ) { + jQuery.extend( newItem, options, { nodes: [], parent: parentItem } ); + } + if ( fn ) { + // Build the hierarchical content to be used during insertion into DOM + newItem.tmpl = fn; + newItem._ctnt = newItem._ctnt || newItem.tmpl( jQuery, newItem ); + newItem.key = ++itemKey; + // Keep track of new template item, until it is stored as jQuery Data on DOM element + (stack.length ? wrappedItems : newTmplItems)[itemKey] = newItem; + } + return newItem; + } + + // Override appendTo etc., in order to provide support for targeting multiple elements. (This code would disappear if integrated in jquery core). + jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" + }, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var ret = [], insert = jQuery( selector ), elems, i, l, tmplItems, + parent = this.length === 1 && this[0].parentNode; + + appendToTmplItems = newTmplItems || {}; + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + ret = this; + } else { + for ( i = 0, l = insert.length; i < l; i++ ) { + cloneIndex = i; + elems = (i > 0 ? this.clone(true) : this).get(); + jQuery.fn[ original ].apply( jQuery(insert[i]), elems ); + ret = ret.concat( elems ); + } + cloneIndex = 0; + ret = this.pushStack( ret, name, insert.selector ); + } + tmplItems = appendToTmplItems; + appendToTmplItems = null; + jQuery.tmpl.complete( tmplItems ); + return ret; + }; + }); + + jQuery.fn.extend({ + // Use first wrapped element as template markup. + // Return wrapped set of template items, obtained by rendering template against data. + tmpl: function( data, options, parentItem ) { + return jQuery.tmpl( this[0], data, options, parentItem ); + }, + + // Find which rendered template item the first wrapped DOM element belongs to + tmplItem: function() { + return jQuery.tmplItem( this[0] ); + }, + + // Consider the first wrapped element as a template declaration, and get the compiled template or store it as a named template. + template: function( name ) { + return jQuery.template( name, this[0] ); + }, + + domManip: function( args, table, callback, options ) { + // This appears to be a bug in the appendTo, etc. implementation + // it should be doing .call() instead of .apply(). See #6227 + if ( args[0] && args[0].nodeType ) { + var dmArgs = jQuery.makeArray( arguments ), argsLength = args.length, i = 0, tmplItem; + while ( i < argsLength && !(tmplItem = jQuery.data( args[i++], "tmplItem" ))) {} + if ( argsLength > 1 ) { + dmArgs[0] = [jQuery.makeArray( args )]; + } + if ( tmplItem && cloneIndex ) { + dmArgs[2] = function( fragClone ) { + // Handler called by oldManip when rendered template has been inserted into DOM. + jQuery.tmpl.afterManip( this, fragClone, callback ); + }; + } + oldManip.apply( this, dmArgs ); + } else { + oldManip.apply( this, arguments ); + } + cloneIndex = 0; + if ( !appendToTmplItems ) { + jQuery.tmpl.complete( newTmplItems ); + } + return this; + } + }); + + jQuery.extend({ + // Return wrapped set of template items, obtained by rendering template against data. + tmpl: function( tmpl, data, options, parentItem ) { + var ret, topLevel = !parentItem; + if ( topLevel ) { + // This is a top-level tmpl call (not from a nested template using {{tmpl}}) + parentItem = topTmplItem; + tmpl = jQuery.template[tmpl] || jQuery.template( null, tmpl ); + wrappedItems = {}; // Any wrapped items will be rebuilt, since this is top level + } else if ( !tmpl ) { + // The template item is already associated with DOM - this is a refresh. + // Re-evaluate rendered template for the parentItem + tmpl = parentItem.tmpl; + newTmplItems[parentItem.key] = parentItem; + parentItem.nodes = []; + if ( parentItem.wrapped ) { + updateWrapped( parentItem, parentItem.wrapped ); + } + // Rebuild, without creating a new template item + return jQuery( build( parentItem, null, parentItem.tmpl( jQuery, parentItem ) )); + } + if ( !tmpl ) { + return []; // Could throw... + } + if ( typeof data === "function" ) { + data = data.call( parentItem || {} ); + } + if ( options && options.wrapped ) { + updateWrapped( options, options.wrapped ); + } + ret = jQuery.isArray( data ) ? + jQuery.map( data, function( dataItem ) { + return dataItem ? newTmplItem( options, parentItem, tmpl, dataItem ) : null; + }) : + [ newTmplItem( options, parentItem, tmpl, data ) ]; + return topLevel ? jQuery( build( parentItem, null, ret ) ) : ret; + }, + + // Return rendered template item for an element. + tmplItem: function( elem ) { + var tmplItem; + if ( elem instanceof jQuery ) { + elem = elem[0]; + } + while ( elem && elem.nodeType === 1 && !(tmplItem = jQuery.data( elem, "tmplItem" )) && (elem = elem.parentNode) ) {} + return tmplItem || topTmplItem; + }, + + // Set: + // Use $.template( name, tmpl ) to cache a named template, + // where tmpl is a template string, a script element or a jQuery instance wrapping a script element, etc. + // Use $( "selector" ).template( name ) to provide access by name to a script block template declaration. + + // Get: + // Use $.template( name ) to access a cached template. + // Also $( selectorToScriptBlock ).template(), or $.template( null, templateString ) + // will return the compiled template, without adding a name reference. + // If templateString includes at least one HTML tag, $.template( templateString ) is equivalent + // to $.template( null, templateString ) + template: function( name, tmpl ) { + if (tmpl) { + // Compile template and associate with name + if ( typeof tmpl === "string" ) { + // This is an HTML string being passed directly in. + tmpl = buildTmplFn( tmpl ) + } else if ( tmpl instanceof jQuery ) { + tmpl = tmpl[0] || {}; + } + if ( tmpl.nodeType ) { + // If this is a template block, use cached copy, or generate tmpl function and cache. + tmpl = jQuery.data( tmpl, "tmpl" ) || jQuery.data( tmpl, "tmpl", buildTmplFn( tmpl.innerHTML )); + } + return typeof name === "string" ? (jQuery.template[name] = tmpl) : tmpl; + } + // Return named compiled template + return name ? (typeof name !== "string" ? jQuery.template( null, name ): + (jQuery.template[name] || + // If not in map, treat as a selector. (If integrated with core, use quickExpr.exec) + jQuery.template( null, htmlExpr.test( name ) ? name : jQuery( name )))) : null; + }, + + encode: function( text ) { + // Do HTML encoding replacing < > & and ' and " by corresponding entities. + return ("" + text).split("<").join("<").split(">").join(">").split('"').join(""").split("'").join("'"); + } + }); + + jQuery.extend( jQuery.tmpl, { + tag: { + "tmpl": { + _default: { $2: "null" }, + open: "if($notnull_1){_=_.concat($item.nest($1,$2));}" + // tmpl target parameter can be of type function, so use $1, not $1a (so not auto detection of functions) + // This means that {{tmpl foo}} treats foo as a template (which IS a function). + // Explicit parens can be used if foo is a function that returns a template: {{tmpl foo()}}. + }, + "wrap": { + _default: { $2: "null" }, + open: "$item.calls(_,$1,$2);_=[];", + close: "call=$item.calls();_=call._.concat($item.wrap(call,_));" + }, + "each": { + _default: { $2: "$index, $value" }, + open: "if($notnull_1){$.each($1a,function($2){with(this){", + close: "}});}" + }, + "if": { + open: "if(($notnull_1) && $1a){", + close: "}" + }, + "else": { + _default: { $1: "true" }, + open: "}else if(($notnull_1) && $1a){" + }, + "html": { + // Unecoded expression evaluation. + open: "if($notnull_1){_.push($1a);}" + }, + "=": { + // Encoded expression evaluation. Abbreviated form is ${}. + _default: { $1: "$data" }, + open: "if($notnull_1){_.push($.encode($1a));}" + }, + "!": { + // Comment tag. Skipped by parser + open: "" + } + }, + + // This stub can be overridden, e.g. in jquery.tmplPlus for providing rendered events + complete: function( items ) { + newTmplItems = {}; + }, + + // Call this from code which overrides domManip, or equivalent + // Manage cloning/storing template items etc. + afterManip: function afterManip( elem, fragClone, callback ) { + // Provides cloned fragment ready for fixup prior to and after insertion into DOM + var content = fragClone.nodeType === 11 ? + jQuery.makeArray(fragClone.childNodes) : + fragClone.nodeType === 1 ? [fragClone] : []; + + // Return fragment to original caller (e.g. append) for DOM insertion + callback.call( elem, fragClone ); + + // Fragment has been inserted:- Add inserted nodes to tmplItem data structure. Replace inserted element annotations by jQuery.data. + storeTmplItems( content ); + cloneIndex++; + } + }); + + //========================== Private helper functions, used by code above ========================== + + function build( tmplItem, nested, content ) { + // Convert hierarchical content into flat string array + // and finally return array of fragments ready for DOM insertion + var frag, ret = content ? jQuery.map( content, function( item ) { + return (typeof item === "string") ? + // Insert template item annotations, to be converted to jQuery.data( "tmplItem" ) when elems are inserted into DOM. + (tmplItem.key ? item.replace( /(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g, "$1 " + tmplItmAtt + "=\"" + tmplItem.key + "\" $2" ) : item) : + // This is a child template item. Build nested template. + build( item, tmplItem, item._ctnt ); + }) : + // If content is not defined, insert tmplItem directly. Not a template item. May be a string, or a string array, e.g. from {{html $item.html()}}. + tmplItem; + if ( nested ) { + return ret; + } + + // top-level template + ret = ret.join(""); + + // Support templates which have initial or final text nodes, or consist only of text + // Also support HTML entities within the HTML markup. + ret.replace( /^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/, function( all, before, middle, after) { + frag = jQuery( middle ).get(); + + storeTmplItems( frag ); + if ( before ) { + frag = unencode( before ).concat(frag); + } + if ( after ) { + frag = frag.concat(unencode( after )); + } + }); + return frag ? frag : unencode( ret ); + } + + function unencode( text ) { + // Use createElement, since createTextNode will not render HTML entities correctly + var el = document.createElement( "div" ); + el.innerHTML = text; + return jQuery.makeArray(el.childNodes); + } + + // Generate a reusable function that will serve to render a template against data + function buildTmplFn( markup ) { + return new Function("jQuery","$item", + "var $=jQuery,call,_=[],$data=$item.data;" + + + // Introduce the data as local variables using with(){} + "with($data){_.push('" + + + // Convert the template into pure JavaScript + jQuery.trim(markup) + .replace( /([\\'])/g, "\\$1" ) + .replace( /[\r\t\n]/g, " " ) + .replace( /\$\{([^\}]*)\}/g, "{{= $1}}" ) + .replace( /\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g, + function( all, slash, type, fnargs, target, parens, args ) { + var tag = jQuery.tmpl.tag[ type ], def, expr, exprAutoFnDetect; + if ( !tag ) { + throw "Template command not found: " + type; + } + def = tag._default || []; + if ( parens && !/\w$/.test(target)) { + target += parens; + parens = ""; + } + if ( target ) { + target = unescape( target ); + args = args ? ("," + unescape( args ) + ")") : (parens ? ")" : ""); + // Support for target being things like a.toLowerCase(); + // In that case don't call with template item as 'this' pointer. Just evaluate... + expr = parens ? (target.indexOf(".") > -1 ? target + parens : ("(" + target + ").call($item" + args)) : target; + exprAutoFnDetect = parens ? expr : "(typeof(" + target + ")==='function'?(" + target + ").call($item):(" + target + "))"; + } else { + exprAutoFnDetect = expr = def.$1 || "null"; + } + fnargs = unescape( fnargs ); + return "');" + + tag[ slash ? "close" : "open" ] + .split( "$notnull_1" ).join( target ? "typeof(" + target + ")!=='undefined' && (" + target + ")!=null" : "true" ) + .split( "$1a" ).join( exprAutoFnDetect ) + .split( "$1" ).join( expr ) + .split( "$2" ).join( fnargs ? + fnargs.replace( /\s*([^\(]+)\s*(\((.*?)\))?/g, function( all, name, parens, params ) { + params = params ? ("," + params + ")") : (parens ? ")" : ""); + return params ? ("(" + name + ").call($item" + params) : all; + }) + : (def.$2||"") + ) + + "_.push('"; + }) + + "');}return _;" + ); + } + function updateWrapped( options, wrapped ) { + // Build the wrapped content. + options._wrap = build( options, true, + // Suport imperative scenario in which options.wrapped can be set to a selector or an HTML string. + jQuery.isArray( wrapped ) ? wrapped : [htmlExpr.test( wrapped ) ? wrapped : jQuery( wrapped ).html()] + ).join(""); + } + + function unescape( args ) { + return args ? args.replace( /\\'/g, "'").replace(/\\\\/g, "\\" ) : null; + } + function outerHtml( elem ) { + var div = document.createElement("div"); + div.appendChild( elem.cloneNode(true) ); + return div.innerHTML; + } + + // Store template items in jQuery.data(), ensuring a unique tmplItem data data structure for each rendered template instance. + function storeTmplItems( content ) { + var keySuffix = "_" + cloneIndex, elem, elems, newClonedItems = {}, i, l, m; + for ( i = 0, l = content.length; i < l; i++ ) { + if ( (elem = content[i]).nodeType !== 1 ) { + continue; + } + elems = elem.getElementsByTagName("*"); + for ( m = elems.length - 1; m >= 0; m-- ) { + processItemKey( elems[m] ); + } + processItemKey( elem ); + } + function processItemKey( el ) { + var pntKey, pntNode = el, pntItem, tmplItem, key; + // Ensure that each rendered template inserted into the DOM has its own template item, + if ( (key = el.getAttribute( tmplItmAtt ))) { + while ( pntNode.parentNode && (pntNode = pntNode.parentNode).nodeType === 1 && !(pntKey = pntNode.getAttribute( tmplItmAtt ))) { } + if ( pntKey !== key ) { + // The next ancestor with a _tmplitem expando is on a different key than this one. + // So this is a top-level element within this template item + // Set pntNode to the key of the parentNode, or to 0 if pntNode.parentNode is null, or pntNode is a fragment. + pntNode = pntNode.parentNode ? (pntNode.nodeType === 11 ? 0 : (pntNode.getAttribute( tmplItmAtt ) || 0)) : 0; + if ( !(tmplItem = newTmplItems[key]) ) { + // The item is for wrapped content, and was copied from the temporary parent wrappedItem. + tmplItem = wrappedItems[key]; + tmplItem = newTmplItem( tmplItem, newTmplItems[pntNode]||wrappedItems[pntNode], null, true ); + tmplItem.key = ++itemKey; + newTmplItems[itemKey] = tmplItem; + } + if ( cloneIndex ) { + cloneTmplItem( key ); + } + } + el.removeAttribute( tmplItmAtt ); + } else if ( cloneIndex && (tmplItem = jQuery.data( el, "tmplItem" )) ) { + // This was a rendered element, cloned during append or appendTo etc. + // TmplItem stored in jQuery data has already been cloned in cloneCopyEvent. We must replace it with a fresh cloned tmplItem. + cloneTmplItem( tmplItem.key ); + newTmplItems[tmplItem.key] = tmplItem; + pntNode = jQuery.data( el.parentNode, "tmplItem" ); + pntNode = pntNode ? pntNode.key : 0; + } + if ( tmplItem ) { + pntItem = tmplItem; + // Find the template item of the parent element. + // (Using !=, not !==, since pntItem.key is number, and pntNode may be a string) + while ( pntItem && pntItem.key != pntNode ) { + // Add this element as a top-level node for this rendered template item, as well as for any + // ancestor items between this item and the item of its parent element + pntItem.nodes.push( el ); + pntItem = pntItem.parent; + } + // Delete content built during rendering - reduce API surface area and memory use, and avoid exposing of stale data after rendering... + delete tmplItem._ctnt; + delete tmplItem._wrap; + // Store template item as jQuery data on the element + jQuery.data( el, "tmplItem", tmplItem ); + } + function cloneTmplItem( key ) { + key = key + keySuffix; + tmplItem = newClonedItems[key] = + (newClonedItems[key] || newTmplItem( tmplItem, newTmplItems[tmplItem.parent.key + keySuffix] || tmplItem.parent, null, true )); + } + } + } + + //---- Helper functions for template item ---- + + function tiCalls( content, tmpl, data, options ) { + if ( !content ) { + return stack.pop(); + } + stack.push({ _: content, tmpl: tmpl, item:this, data: data, options: options }); + } + + function tiNest( tmpl, data, options ) { + // nested template, using {{tmpl}} tag + return jQuery.tmpl( jQuery.template( tmpl ), data, options, this ); + } + + function tiWrap( call, wrapped ) { + // nested template, using {{wrap}} tag + var options = call.options || {}; + options.wrapped = wrapped; + // Apply the template, which may incorporate wrapped content, + return jQuery.tmpl( jQuery.template( call.tmpl ), call.data, options, call.item ); + } + + function tiHtml( filter, textOnly ) { + var wrapped = this._wrap; + return jQuery.map( + jQuery( jQuery.isArray( wrapped ) ? wrapped.join("") : wrapped ).filter( filter || "*" ), + function(e) { + return textOnly ? + e.innerText || e.textContent : + e.outerHTML || outerHtml(e); + }); + } + + function tiUpdate() { + var coll = this.nodes; + jQuery.tmpl( null, null, null, this).insertBefore( coll[0] ); + jQuery( coll ).remove(); + } +})( jQuery );
\ No newline at end of file diff --git a/app/assets/javascripts/vendor/modernizr-2.0.6.min.js b/app/assets/javascripts/vendor/modernizr-2.0.6.min.js new file mode 100755 index 0000000..52cd7e1 --- /dev/null +++ b/app/assets/javascripts/vendor/modernizr-2.0.6.min.js @@ -0,0 +1,1116 @@ +/*! + * Modernizr v2.0.6 + * http://www.modernizr.com + * + * Copyright (c) 2009-2011 Faruk Ates, Paul Irish, Alex Sexton + * Dual-licensed under the BSD or MIT licenses: www.modernizr.com/license/ + */ + +/* + * Modernizr tests which native CSS3 and HTML5 features are available in + * the current UA and makes the results available to you in two ways: + * as properties on a global Modernizr object, and as classes on the + * <html> element. This information allows you to progressively enhance + * your pages with a granular level of control over the experience. + * + * Modernizr has an optional (not included) conditional resource loader + * called Modernizr.load(), based on Yepnope.js (yepnopejs.com). + * To get a build that includes Modernizr.load(), as well as choosing + * which tests to include, go to www.modernizr.com/download/ + * + * Authors Faruk Ates, Paul Irish, Alex Sexton, + * Contributors Ryan Seddon, Ben Alman + */ + +window.Modernizr = (function( window, document, undefined ) { + + var version = '2.0.6', + + Modernizr = {}, + + // option for enabling the HTML classes to be added + enableClasses = true, + + docElement = document.documentElement, + docHead = document.head || document.getElementsByTagName('head')[0], + + /** + * Create our "modernizr" element that we do most feature tests on. + */ + mod = 'modernizr', + modElem = document.createElement(mod), + mStyle = modElem.style, + + /** + * Create the input element for various Web Forms feature tests. + */ + inputElem = document.createElement('input'), + + smile = ':)', + + toString = Object.prototype.toString, + + // List of property values to set for css tests. See ticket #21 + prefixes = ' -webkit- -moz- -o- -ms- -khtml- '.split(' '), + + // Following spec is to expose vendor-specific style properties as: + // elem.style.WebkitBorderRadius + // and the following would be incorrect: + // elem.style.webkitBorderRadius + + // Webkit ghosts their properties in lowercase but Opera & Moz do not. + // Microsoft foregoes prefixes entirely <= IE8, but appears to + // use a lowercase `ms` instead of the correct `Ms` in IE9 + + // More here: http://github.com/Modernizr/Modernizr/issues/issue/21 + domPrefixes = 'Webkit Moz O ms Khtml'.split(' '), + + ns = {'svg': 'http://www.w3.org/2000/svg'}, + + tests = {}, + inputs = {}, + attrs = {}, + + classes = [], + + featureName, // used in testing loop + + + // Inject element with style element and some CSS rules + injectElementWithStyles = function( rule, callback, nodes, testnames ) { + + var style, ret, node, + div = document.createElement('div'); + + if ( parseInt(nodes, 10) ) { + // In order not to give false positives we create a node for each test + // This also allows the method to scale for unspecified uses + while ( nodes-- ) { + node = document.createElement('div'); + node.id = testnames ? testnames[nodes] : mod + (nodes + 1); + div.appendChild(node); + } + } + + // <style> elements in IE6-9 are considered 'NoScope' elements and therefore will be removed + // when injected with innerHTML. To get around this you need to prepend the 'NoScope' element + // with a 'scoped' element, in our case the soft-hyphen entity as it won't mess with our measurements. + // http://msdn.microsoft.com/en-us/library/ms533897%28VS.85%29.aspx + style = ['­', '<style>', rule, '</style>'].join(''); + div.id = mod; + div.innerHTML += style; + docElement.appendChild(div); + + ret = callback(div, rule); + div.parentNode.removeChild(div); + + return !!ret; + + }, + + + // adapted from matchMedia polyfill + // by Scott Jehl and Paul Irish + // gist.github.com/786768 + testMediaQuery = function( mq ) { + + if ( window.matchMedia ) { + return matchMedia(mq).matches; + } + + var bool; + + injectElementWithStyles('@media ' + mq + ' { #' + mod + ' { position: absolute; } }', function( node ) { + bool = (window.getComputedStyle ? + getComputedStyle(node, null) : + node.currentStyle)['position'] == 'absolute'; + }); + + return bool; + + }, + + + /** + * isEventSupported determines if a given element supports the given event + * function from http://yura.thinkweb2.com/isEventSupported/ + */ + isEventSupported = (function() { + + var TAGNAMES = { + 'select': 'input', 'change': 'input', + 'submit': 'form', 'reset': 'form', + 'error': 'img', 'load': 'img', 'abort': 'img' + }; + + function isEventSupported( eventName, element ) { + + element = element || document.createElement(TAGNAMES[eventName] || 'div'); + eventName = 'on' + eventName; + + // When using `setAttribute`, IE skips "unload", WebKit skips "unload" and "resize", whereas `in` "catches" those + var isSupported = eventName in element; + + if ( !isSupported ) { + // If it has no `setAttribute` (i.e. doesn't implement Node interface), try generic element + if ( !element.setAttribute ) { + element = document.createElement('div'); + } + if ( element.setAttribute && element.removeAttribute ) { + element.setAttribute(eventName, ''); + isSupported = is(element[eventName], 'function'); + + // If property was created, "remove it" (by setting value to `undefined`) + if ( !is(element[eventName], undefined) ) { + element[eventName] = undefined; + } + element.removeAttribute(eventName); + } + } + + element = null; + return isSupported; + } + return isEventSupported; + })(); + + // hasOwnProperty shim by kangax needed for Safari 2.0 support + var _hasOwnProperty = ({}).hasOwnProperty, hasOwnProperty; + if ( !is(_hasOwnProperty, undefined) && !is(_hasOwnProperty.call, undefined) ) { + hasOwnProperty = function (object, property) { + return _hasOwnProperty.call(object, property); + }; + } + else { + hasOwnProperty = function (object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */ + return ((property in object) && is(object.constructor.prototype[property], undefined)); + }; + } + + /** + * setCss applies given styles to the Modernizr DOM node. + */ + function setCss( str ) { + mStyle.cssText = str; + } + + /** + * setCssAll extrapolates all vendor-specific css strings. + */ + function setCssAll( str1, str2 ) { + return setCss(prefixes.join(str1 + ';') + ( str2 || '' )); + } + + /** + * is returns a boolean for if typeof obj is exactly type. + */ + function is( obj, type ) { + return typeof obj === type; + } + + /** + * contains returns a boolean for if substr is found within str. + */ + function contains( str, substr ) { + return !!~('' + str).indexOf(substr); + } + + /** + * testProps is a generic CSS / DOM property test; if a browser supports + * a certain property, it won't return undefined for it. + * A supported CSS property returns empty string when its not yet set. + */ + function testProps( props, prefixed ) { + for ( var i in props ) { + if ( mStyle[ props[i] ] !== undefined ) { + return prefixed == 'pfx' ? props[i] : true; + } + } + return false; + } + + /** + * testPropsAll tests a list of DOM properties we want to check against. + * We specify literally ALL possible (known and/or likely) properties on + * the element including the non-vendor prefixed one, for forward- + * compatibility. + */ + function testPropsAll( prop, prefixed ) { + + var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1), + props = (prop + ' ' + domPrefixes.join(ucProp + ' ') + ucProp).split(' '); + + return testProps(props, prefixed); + } + + /** + * testBundle tests a list of CSS features that require element and style injection. + * By bundling them together we can reduce the need to touch the DOM multiple times. + */ + /*>>testBundle*/ + var testBundle = (function( styles, tests ) { + var style = styles.join(''), + len = tests.length; + + injectElementWithStyles(style, function( node, rule ) { + var style = document.styleSheets[document.styleSheets.length - 1], + // IE8 will bork if you create a custom build that excludes both fontface and generatedcontent tests. + // So we check for cssRules and that there is a rule available + // More here: https://github.com/Modernizr/Modernizr/issues/288 & https://github.com/Modernizr/Modernizr/issues/293 + cssText = style.cssRules && style.cssRules[0] ? style.cssRules[0].cssText : style.cssText || "", + children = node.childNodes, hash = {}; + + while ( len-- ) { + hash[children[len].id] = children[len]; + } + + /*>>touch*/ Modernizr['touch'] = ('ontouchstart' in window) || hash['touch'].offsetTop === 9; /*>>touch*/ + /*>>csstransforms3d*/ Modernizr['csstransforms3d'] = hash['csstransforms3d'].offsetLeft === 9; /*>>csstransforms3d*/ + /*>>generatedcontent*/Modernizr['generatedcontent'] = hash['generatedcontent'].offsetHeight >= 1; /*>>generatedcontent*/ + /*>>fontface*/ Modernizr['fontface'] = /src/i.test(cssText) && + cssText.indexOf(rule.split(' ')[0]) === 0; /*>>fontface*/ + }, len, tests); + + })([ + // Pass in styles to be injected into document + /*>>fontface*/ '@font-face {font-family:"font";src:url("https://")}' /*>>fontface*/ + + /*>>touch*/ ,['@media (',prefixes.join('touch-enabled),('),mod,')', + '{#touch{top:9px;position:absolute}}'].join('') /*>>touch*/ + + /*>>csstransforms3d*/ ,['@media (',prefixes.join('transform-3d),('),mod,')', + '{#csstransforms3d{left:9px;position:absolute}}'].join('')/*>>csstransforms3d*/ + + /*>>generatedcontent*/,['#generatedcontent:after{content:"',smile,'";visibility:hidden}'].join('') /*>>generatedcontent*/ + ], + [ + /*>>fontface*/ 'fontface' /*>>fontface*/ + /*>>touch*/ ,'touch' /*>>touch*/ + /*>>csstransforms3d*/ ,'csstransforms3d' /*>>csstransforms3d*/ + /*>>generatedcontent*/,'generatedcontent' /*>>generatedcontent*/ + + ]);/*>>testBundle*/ + + + /** + * Tests + * ----- + */ + + tests['flexbox'] = function() { + /** + * setPrefixedValueCSS sets the property of a specified element + * adding vendor prefixes to the VALUE of the property. + * @param {Element} element + * @param {string} property The property name. This will not be prefixed. + * @param {string} value The value of the property. This WILL be prefixed. + * @param {string=} extra Additional CSS to append unmodified to the end of + * the CSS string. + */ + function setPrefixedValueCSS( element, property, value, extra ) { + property += ':'; + element.style.cssText = (property + prefixes.join(value + ';' + property)).slice(0, -property.length) + (extra || ''); + } + + /** + * setPrefixedPropertyCSS sets the property of a specified element + * adding vendor prefixes to the NAME of the property. + * @param {Element} element + * @param {string} property The property name. This WILL be prefixed. + * @param {string} value The value of the property. This will not be prefixed. + * @param {string=} extra Additional CSS to append unmodified to the end of + * the CSS string. + */ + function setPrefixedPropertyCSS( element, property, value, extra ) { + element.style.cssText = prefixes.join(property + ':' + value + ';') + (extra || ''); + } + + var c = document.createElement('div'), + elem = document.createElement('div'); + + setPrefixedValueCSS(c, 'display', 'box', 'width:42px;padding:0;'); + setPrefixedPropertyCSS(elem, 'box-flex', '1', 'width:10px;'); + + c.appendChild(elem); + docElement.appendChild(c); + + var ret = elem.offsetWidth === 42; + + c.removeChild(elem); + docElement.removeChild(c); + + return ret; + }; + + // On the S60 and BB Storm, getContext exists, but always returns undefined + // http://github.com/Modernizr/Modernizr/issues/issue/97/ + + tests['canvas'] = function() { + var elem = document.createElement('canvas'); + return !!(elem.getContext && elem.getContext('2d')); + }; + + tests['canvastext'] = function() { + return !!(Modernizr['canvas'] && is(document.createElement('canvas').getContext('2d').fillText, 'function')); + }; + + // This WebGL test may false positive. + // But really it's quite impossible to know whether webgl will succeed until after you create the context. + // You might have hardware that can support a 100x100 webgl canvas, but will not support a 1000x1000 webgl + // canvas. So this feature inference is weak, but intentionally so. + + // It is known to false positive in FF4 with certain hardware and the iPad 2. + + tests['webgl'] = function() { + return !!window.WebGLRenderingContext; + }; + + /* + * The Modernizr.touch test only indicates if the browser supports + * touch events, which does not necessarily reflect a touchscreen + * device, as evidenced by tablets running Windows 7 or, alas, + * the Palm Pre / WebOS (touch) phones. + * + * Additionally, Chrome (desktop) used to lie about its support on this, + * but that has since been rectified: http://crbug.com/36415 + * + * We also test for Firefox 4 Multitouch Support. + * + * For more info, see: http://modernizr.github.com/Modernizr/touch.html + */ + + tests['touch'] = function() { + return Modernizr['touch']; + }; + + /** + * geolocation tests for the new Geolocation API specification. + * This test is a standards compliant-only test; for more complete + * testing, including a Google Gears fallback, please see: + * http://code.google.com/p/geo-location-javascript/ + * or view a fallback solution using google's geo API: + * http://gist.github.com/366184 + */ + tests['geolocation'] = function() { + return !!navigator.geolocation; + }; + + // Per 1.6: + // This used to be Modernizr.crosswindowmessaging but the longer + // name has been deprecated in favor of a shorter and property-matching one. + // The old API is still available in 1.6, but as of 2.0 will throw a warning, + // and in the first release thereafter disappear entirely. + tests['postmessage'] = function() { + return !!window.postMessage; + }; + + // Web SQL database detection is tricky: + + // In chrome incognito mode, openDatabase is truthy, but using it will + // throw an exception: http://crbug.com/42380 + // We can create a dummy database, but there is no way to delete it afterwards. + + // Meanwhile, Safari users can get prompted on any database creation. + // If they do, any page with Modernizr will give them a prompt: + // http://github.com/Modernizr/Modernizr/issues/closed#issue/113 + + // We have chosen to allow the Chrome incognito false positive, so that Modernizr + // doesn't litter the web with these test databases. As a developer, you'll have + // to account for this gotcha yourself. + tests['websqldatabase'] = function() { + var result = !!window.openDatabase; + /* if (result){ + try { + result = !!openDatabase( mod + "testdb", "1.0", mod + "testdb", 2e4); + } catch(e) { + } + } */ + return result; + }; + + // Vendors had inconsistent prefixing with the experimental Indexed DB: + // - Webkit's implementation is accessible through webkitIndexedDB + // - Firefox shipped moz_indexedDB before FF4b9, but since then has been mozIndexedDB + // For speed, we don't test the legacy (and beta-only) indexedDB + tests['indexedDB'] = function() { + for ( var i = -1, len = domPrefixes.length; ++i < len; ){ + if ( window[domPrefixes[i].toLowerCase() + 'IndexedDB'] ){ + return true; + } + } + return !!window.indexedDB; + }; + + // documentMode logic from YUI to filter out IE8 Compat Mode + // which false positives. + tests['hashchange'] = function() { + return isEventSupported('hashchange', window) && (document.documentMode === undefined || document.documentMode > 7); + }; + + // Per 1.6: + // This used to be Modernizr.historymanagement but the longer + // name has been deprecated in favor of a shorter and property-matching one. + // The old API is still available in 1.6, but as of 2.0 will throw a warning, + // and in the first release thereafter disappear entirely. + tests['history'] = function() { + return !!(window.history && history.pushState); + }; + + tests['draganddrop'] = function() { + return isEventSupported('dragstart') && isEventSupported('drop'); + }; + + // Mozilla is targeting to land MozWebSocket for FF6 + // bugzil.la/659324 + tests['websockets'] = function() { + for ( var i = -1, len = domPrefixes.length; ++i < len; ){ + if ( window[domPrefixes[i] + 'WebSocket'] ){ + return true; + } + } + return 'WebSocket' in window; + }; + + + // http://css-tricks.com/rgba-browser-support/ + tests['rgba'] = function() { + // Set an rgba() color and check the returned value + + setCss('background-color:rgba(150,255,150,.5)'); + + return contains(mStyle.backgroundColor, 'rgba'); + }; + + tests['hsla'] = function() { + // Same as rgba(), in fact, browsers re-map hsla() to rgba() internally, + // except IE9 who retains it as hsla + + setCss('background-color:hsla(120,40%,100%,.5)'); + + return contains(mStyle.backgroundColor, 'rgba') || contains(mStyle.backgroundColor, 'hsla'); + }; + + tests['multiplebgs'] = function() { + // Setting multiple images AND a color on the background shorthand property + // and then querying the style.background property value for the number of + // occurrences of "url(" is a reliable method for detecting ACTUAL support for this! + + setCss('background:url(https://),url(https://),red url(https://)'); + + // If the UA supports multiple backgrounds, there should be three occurrences + // of the string "url(" in the return value for elemStyle.background + + return /(url\s*\(.*?){3}/.test(mStyle.background); + }; + + + // In testing support for a given CSS property, it's legit to test: + // `elem.style[styleName] !== undefined` + // If the property is supported it will return an empty string, + // if unsupported it will return undefined. + + // We'll take advantage of this quick test and skip setting a style + // on our modernizr element, but instead just testing undefined vs + // empty string. + + + tests['backgroundsize'] = function() { + return testPropsAll('backgroundSize'); + }; + + tests['borderimage'] = function() { + return testPropsAll('borderImage'); + }; + + + // Super comprehensive table about all the unique implementations of + // border-radius: http://muddledramblings.com/table-of-css3-border-radius-compliance + + tests['borderradius'] = function() { + return testPropsAll('borderRadius'); + }; + + // WebOS unfortunately false positives on this test. + tests['boxshadow'] = function() { + return testPropsAll('boxShadow'); + }; + + // FF3.0 will false positive on this test + tests['textshadow'] = function() { + return document.createElement('div').style.textShadow === ''; + }; + + + tests['opacity'] = function() { + // Browsers that actually have CSS Opacity implemented have done so + // according to spec, which means their return values are within the + // range of [0.0,1.0] - including the leading zero. + + setCssAll('opacity:.55'); + + // The non-literal . in this regex is intentional: + // German Chrome returns this value as 0,55 + // https://github.com/Modernizr/Modernizr/issues/#issue/59/comment/516632 + return /^0.55$/.test(mStyle.opacity); + }; + + + tests['cssanimations'] = function() { + return testPropsAll('animationName'); + }; + + + tests['csscolumns'] = function() { + return testPropsAll('columnCount'); + }; + + + tests['cssgradients'] = function() { + /** + * For CSS Gradients syntax, please see: + * http://webkit.org/blog/175/introducing-css-gradients/ + * https://developer.mozilla.org/en/CSS/-moz-linear-gradient + * https://developer.mozilla.org/en/CSS/-moz-radial-gradient + * http://dev.w3.org/csswg/css3-images/#gradients- + */ + + var str1 = 'background-image:', + str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));', + str3 = 'linear-gradient(left top,#9f9, white);'; + + setCss( + (str1 + prefixes.join(str2 + str1) + prefixes.join(str3 + str1)).slice(0, -str1.length) + ); + + return contains(mStyle.backgroundImage, 'gradient'); + }; + + + tests['cssreflections'] = function() { + return testPropsAll('boxReflect'); + }; + + + tests['csstransforms'] = function() { + return !!testProps(['transformProperty', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform']); + }; + + + tests['csstransforms3d'] = function() { + + var ret = !!testProps(['perspectiveProperty', 'WebkitPerspective', 'MozPerspective', 'OPerspective', 'msPerspective']); + + // Webkit’s 3D transforms are passed off to the browser's own graphics renderer. + // It works fine in Safari on Leopard and Snow Leopard, but not in Chrome in + // some conditions. As a result, Webkit typically recognizes the syntax but + // will sometimes throw a false positive, thus we must do a more thorough check: + if ( ret && 'webkitPerspective' in docElement.style ) { + + // Webkit allows this media query to succeed only if the feature is enabled. + // `@media (transform-3d),(-o-transform-3d),(-moz-transform-3d),(-ms-transform-3d),(-webkit-transform-3d),(modernizr){ ... }` + ret = Modernizr['csstransforms3d']; + } + return ret; + }; + + + tests['csstransitions'] = function() { + return testPropsAll('transitionProperty'); + }; + + + /*>>fontface*/ + // @font-face detection routine by Diego Perini + // http://javascript.nwbox.com/CSSSupport/ + tests['fontface'] = function() { + return Modernizr['fontface']; + }; + /*>>fontface*/ + + // CSS generated content detection + tests['generatedcontent'] = function() { + return Modernizr['generatedcontent']; + }; + + + + // These tests evaluate support of the video/audio elements, as well as + // testing what types of content they support. + // + // We're using the Boolean constructor here, so that we can extend the value + // e.g. Modernizr.video // true + // Modernizr.video.ogg // 'probably' + // + // Codec values from : http://github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845 + // thx to NielsLeenheer and zcorpan + + // Note: in FF 3.5.1 and 3.5.0, "no" was a return value instead of empty string. + // Modernizr does not normalize for that. + + tests['video'] = function() { + var elem = document.createElement('video'), + bool = false; + + // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224 + try { + if ( bool = !!elem.canPlayType ) { + bool = new Boolean(bool); + bool.ogg = elem.canPlayType('video/ogg; codecs="theora"'); + + // Workaround required for IE9, which doesn't report video support without audio codec specified. + // bug 599718 @ msft connect + var h264 = 'video/mp4; codecs="avc1.42E01E'; + bool.h264 = elem.canPlayType(h264 + '"') || elem.canPlayType(h264 + ', mp4a.40.2"'); + + bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"'); + } + + } catch(e) { } + + return bool; + }; + + tests['audio'] = function() { + var elem = document.createElement('audio'), + bool = false; + + try { + if ( bool = !!elem.canPlayType ) { + bool = new Boolean(bool); + bool.ogg = elem.canPlayType('audio/ogg; codecs="vorbis"'); + bool.mp3 = elem.canPlayType('audio/mpeg;'); + + // Mimetypes accepted: + // https://developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements + // http://bit.ly/iphoneoscodecs + bool.wav = elem.canPlayType('audio/wav; codecs="1"'); + bool.m4a = elem.canPlayType('audio/x-m4a;') || elem.canPlayType('audio/aac;'); + } + } catch(e) { } + + return bool; + }; + + + // Firefox has made these tests rather unfun. + + // In FF4, if disabled, window.localStorage should === null. + + // Normally, we could not test that directly and need to do a + // `('localStorage' in window) && ` test first because otherwise Firefox will + // throw http://bugzil.la/365772 if cookies are disabled + + // However, in Firefox 4 betas, if dom.storage.enabled == false, just mentioning + // the property will throw an exception. http://bugzil.la/599479 + // This looks to be fixed for FF4 Final. + + // Because we are forced to try/catch this, we'll go aggressive. + + // FWIW: IE8 Compat mode supports these features completely: + // http://www.quirksmode.org/dom/html5.html + // But IE8 doesn't support either with local files + + tests['localstorage'] = function() { + try { + return !!localStorage.getItem; + } catch(e) { + return false; + } + }; + + tests['sessionstorage'] = function() { + try { + return !!sessionStorage.getItem; + } catch(e){ + return false; + } + }; + + + tests['webworkers'] = function() { + return !!window.Worker; + }; + + + tests['applicationcache'] = function() { + return !!window.applicationCache; + }; + + + // Thanks to Erik Dahlstrom + tests['svg'] = function() { + return !!document.createElementNS && !!document.createElementNS(ns.svg, 'svg').createSVGRect; + }; + + // specifically for SVG inline in HTML, not within XHTML + // test page: paulirish.com/demo/inline-svg + tests['inlinesvg'] = function() { + var div = document.createElement('div'); + div.innerHTML = '<svg/>'; + return (div.firstChild && div.firstChild.namespaceURI) == ns.svg; + }; + + // Thanks to F1lt3r and lucideer, ticket #35 + tests['smil'] = function() { + return !!document.createElementNS && /SVG/.test(toString.call(document.createElementNS(ns.svg, 'animate'))); + }; + + tests['svgclippaths'] = function() { + // Possibly returns a false positive in Safari 3.2? + return !!document.createElementNS && /SVG/.test(toString.call(document.createElementNS(ns.svg, 'clipPath'))); + }; + + // input features and input types go directly onto the ret object, bypassing the tests loop. + // Hold this guy to execute in a moment. + function webforms() { + // Run through HTML5's new input attributes to see if the UA understands any. + // We're using f which is the <input> element created early on + // Mike Taylr has created a comprehensive resource for testing these attributes + // when applied to all input types: + // http://miketaylr.com/code/input-type-attr.html + // spec: http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary + + // Only input placeholder is tested while textarea's placeholder is not. + // Currently Safari 4 and Opera 11 have support only for the input placeholder + // Both tests are available in feature-detects/forms-placeholder.js + Modernizr['input'] = (function( props ) { + for ( var i = 0, len = props.length; i < len; i++ ) { + attrs[ props[i] ] = !!(props[i] in inputElem); + } + return attrs; + })('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' ')); + + // Run through HTML5's new input types to see if the UA understands any. + // This is put behind the tests runloop because it doesn't return a + // true/false like all the other tests; instead, it returns an object + // containing each input type with its corresponding true/false value + + // Big thanks to @miketaylr for the html5 forms expertise. http://miketaylr.com/ + Modernizr['inputtypes'] = (function(props) { + + for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) { + + inputElem.setAttribute('type', inputElemType = props[i]); + bool = inputElem.type !== 'text'; + + // We first check to see if the type we give it sticks.. + // If the type does, we feed it a textual value, which shouldn't be valid. + // If the value doesn't stick, we know there's input sanitization which infers a custom UI + if ( bool ) { + + inputElem.value = smile; + inputElem.style.cssText = 'position:absolute;visibility:hidden;'; + + if ( /^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined ) { + + docElement.appendChild(inputElem); + defaultView = document.defaultView; + + // Safari 2-4 allows the smiley as a value, despite making a slider + bool = defaultView.getComputedStyle && + defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' && + // Mobile android web browser has false positive, so must + // check the height to see if the widget is actually there. + (inputElem.offsetHeight !== 0); + + docElement.removeChild(inputElem); + + } else if ( /^(search|tel)$/.test(inputElemType) ){ + // Spec doesnt define any special parsing or detectable UI + // behaviors so we pass these through as true + + // Interestingly, opera fails the earlier test, so it doesn't + // even make it here. + + } else if ( /^(url|email)$/.test(inputElemType) ) { + // Real url and email support comes with prebaked validation. + bool = inputElem.checkValidity && inputElem.checkValidity() === false; + + } else if ( /^color$/.test(inputElemType) ) { + // chuck into DOM and force reflow for Opera bug in 11.00 + // github.com/Modernizr/Modernizr/issues#issue/159 + docElement.appendChild(inputElem); + docElement.offsetWidth; + bool = inputElem.value != smile; + docElement.removeChild(inputElem); + + } else { + // If the upgraded input compontent rejects the :) text, we got a winner + bool = inputElem.value != smile; + } + } + + inputs[ props[i] ] = !!bool; + } + return inputs; + })('search tel url email datetime date month week time datetime-local number range color'.split(' ')); + } + + + // End of test definitions + // ----------------------- + + + + // Run through all tests and detect their support in the current UA. + // todo: hypothetically we could be doing an array of tests and use a basic loop here. + for ( var feature in tests ) { + if ( hasOwnProperty(tests, feature) ) { + // run the test, throw the return value into the Modernizr, + // then based on that boolean, define an appropriate className + // and push it into an array of classes we'll join later. + featureName = feature.toLowerCase(); + Modernizr[featureName] = tests[feature](); + + classes.push((Modernizr[featureName] ? '' : 'no-') + featureName); + } + } + + // input tests need to run. + Modernizr.input || webforms(); + + + /** + * addTest allows the user to define their own feature tests + * the result will be added onto the Modernizr object, + * as well as an appropriate className set on the html element + * + * @param feature - String naming the feature + * @param test - Function returning true if feature is supported, false if not + */ + Modernizr.addTest = function ( feature, test ) { + if ( typeof feature == "object" ) { + for ( var key in feature ) { + if ( hasOwnProperty( feature, key ) ) { + Modernizr.addTest( key, feature[ key ] ); + } + } + } else { + + feature = feature.toLowerCase(); + + if ( Modernizr[feature] !== undefined ) { + // we're going to quit if you're trying to overwrite an existing test + // if we were to allow it, we'd do this: + // var re = new RegExp("\\b(no-)?" + feature + "\\b"); + // docElement.className = docElement.className.replace( re, '' ); + // but, no rly, stuff 'em. + return; + } + + test = typeof test == "boolean" ? test : !!test(); + + docElement.className += ' ' + (test ? '' : 'no-') + feature; + Modernizr[feature] = test; + + } + + return Modernizr; // allow chaining. + }; + + + // Reset modElem.cssText to nothing to reduce memory footprint. + setCss(''); + modElem = inputElem = null; + + //>>BEGIN IEPP + // Enable HTML 5 elements for styling (and printing) in IE. + if ( window.attachEvent && (function(){ var elem = document.createElement('div'); + elem.innerHTML = '<elem></elem>'; + return elem.childNodes.length !== 1; })() ) { + + // iepp v2 by @jon_neal & afarkas : github.com/aFarkas/iepp/ + (function(win, doc) { + win.iepp = win.iepp || {}; + var iepp = win.iepp, + elems = iepp.html5elements || 'abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video', + elemsArr = elems.split('|'), + elemsArrLen = elemsArr.length, + elemRegExp = new RegExp('(^|\\s)('+elems+')', 'gi'), + tagRegExp = new RegExp('<(\/*)('+elems+')', 'gi'), + filterReg = /^\s*[\{\}]\s*$/, + ruleRegExp = new RegExp('(^|[^\\n]*?\\s)('+elems+')([^\\n]*)({[\\n\\w\\W]*?})', 'gi'), + docFrag = doc.createDocumentFragment(), + html = doc.documentElement, + head = html.firstChild, + bodyElem = doc.createElement('body'), + styleElem = doc.createElement('style'), + printMedias = /print|all/, + body; + function shim(doc) { + var a = -1; + while (++a < elemsArrLen) + // Use createElement so IE allows HTML5-named elements in a document + doc.createElement(elemsArr[a]); + } + + iepp.getCSS = function(styleSheetList, mediaType) { + if(styleSheetList+'' === undefined){return '';} + var a = -1, + len = styleSheetList.length, + styleSheet, + cssTextArr = []; + while (++a < len) { + styleSheet = styleSheetList[a]; + //currently no test for disabled/alternate stylesheets + if(styleSheet.disabled){continue;} + mediaType = styleSheet.media || mediaType; + // Get css from all non-screen stylesheets and their imports + if (printMedias.test(mediaType)) cssTextArr.push(iepp.getCSS(styleSheet.imports, mediaType), styleSheet.cssText); + //reset mediaType to all with every new *not imported* stylesheet + mediaType = 'all'; + } + return cssTextArr.join(''); + }; + + iepp.parseCSS = function(cssText) { + var cssTextArr = [], + rule; + while ((rule = ruleRegExp.exec(cssText)) != null){ + // Replace all html5 element references with iepp substitute classnames + cssTextArr.push(( (filterReg.exec(rule[1]) ? '\n' : rule[1]) +rule[2]+rule[3]).replace(elemRegExp, '$1.iepp_$2')+rule[4]); + } + return cssTextArr.join('\n'); + }; + + iepp.writeHTML = function() { + var a = -1; + body = body || doc.body; + while (++a < elemsArrLen) { + var nodeList = doc.getElementsByTagName(elemsArr[a]), + nodeListLen = nodeList.length, + b = -1; + while (++b < nodeListLen) + if (nodeList[b].className.indexOf('iepp_') < 0) + // Append iepp substitute classnames to all html5 elements + nodeList[b].className += ' iepp_'+elemsArr[a]; + } + docFrag.appendChild(body); + html.appendChild(bodyElem); + // Write iepp substitute print-safe document + bodyElem.className = body.className; + bodyElem.id = body.id; + // Replace HTML5 elements with <font> which is print-safe and shouldn't conflict since it isn't part of html5 + bodyElem.innerHTML = body.innerHTML.replace(tagRegExp, '<$1font'); + }; + + + iepp._beforePrint = function() { + // Write iepp custom print CSS + styleElem.styleSheet.cssText = iepp.parseCSS(iepp.getCSS(doc.styleSheets, 'all')); + iepp.writeHTML(); + }; + + iepp.restoreHTML = function(){ + // Undo everything done in onbeforeprint + bodyElem.innerHTML = ''; + html.removeChild(bodyElem); + html.appendChild(body); + }; + + iepp._afterPrint = function(){ + // Undo everything done in onbeforeprint + iepp.restoreHTML(); + styleElem.styleSheet.cssText = ''; + }; + + + + // Shim the document and iepp fragment + shim(doc); + shim(docFrag); + + // + if(iepp.disablePP){return;} + + // Add iepp custom print style element + head.insertBefore(styleElem, head.firstChild); + styleElem.media = 'print'; + styleElem.className = 'iepp-printshim'; + win.attachEvent( + 'onbeforeprint', + iepp._beforePrint + ); + win.attachEvent( + 'onafterprint', + iepp._afterPrint + ); + })(window, document); + } + //>>END IEPP + + // Assign private properties to the return object with prefix + Modernizr._version = version; + + // expose these for the plugin API. Look in the source for how to join() them against your input + Modernizr._prefixes = prefixes; + Modernizr._domPrefixes = domPrefixes; + + // Modernizr.mq tests a given media query, live against the current state of the window + // A few important notes: + // * If a browser does not support media queries at all (eg. oldIE) the mq() will always return false + // * A max-width or orientation query will be evaluated against the current state, which may change later. + // * You must specify values. Eg. If you are testing support for the min-width media query use: + // Modernizr.mq('(min-width:0)') + // usage: + // Modernizr.mq('only screen and (max-width:768)') + Modernizr.mq = testMediaQuery; + + // Modernizr.hasEvent() detects support for a given event, with an optional element to test on + // Modernizr.hasEvent('gesturestart', elem) + Modernizr.hasEvent = isEventSupported; + + // Modernizr.testProp() investigates whether a given style property is recognized + // Note that the property names must be provided in the camelCase variant. + // Modernizr.testProp('pointerEvents') + Modernizr.testProp = function(prop){ + return testProps([prop]); + }; + + // Modernizr.testAllProps() investigates whether a given style property, + // or any of its vendor-prefixed variants, is recognized + // Note that the property names must be provided in the camelCase variant. + // Modernizr.testAllProps('boxSizing') + Modernizr.testAllProps = testPropsAll; + + + + // Modernizr.testStyles() allows you to add custom styles to the document and test an element afterwards + // Modernizr.testStyles('#modernizr { position:absolute }', function(elem, rule){ ... }) + Modernizr.testStyles = injectElementWithStyles; + + + // Modernizr.prefixed() returns the prefixed or nonprefixed property name variant of your input + // Modernizr.prefixed('boxSizing') // 'MozBoxSizing' + + // Properties must be passed as dom-style camelcase, rather than `box-sizing` hypentated style. + // Return values will also be the camelCase variant, if you need to translate that to hypenated style use: + // + // str.replace(/([A-Z])/g, function(str,m1){ return '-' + m1.toLowerCase(); }).replace(/^ms-/,'-ms-'); + + // If you're trying to ascertain which transition end event to bind to, you might do something like... + // + // var transEndEventNames = { + // 'WebkitTransition' : 'webkitTransitionEnd', + // 'MozTransition' : 'transitionend', + // 'OTransition' : 'oTransitionEnd', + // 'msTransition' : 'msTransitionEnd', // maybe? + // 'transition' : 'transitionEnd' + // }, + // transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ]; + + Modernizr.prefixed = function(prop){ + return testPropsAll(prop, 'pfx'); + }; + + + + // Remove "no-js" class from <html> element, if it exists: + docElement.className = docElement.className.replace(/\bno-js\b/, '') + + // Add the new classes to the <html> element. + + (enableClasses ? ' js ' + classes.join(' ') : ''); + + return Modernizr; + +})(this, this.document);
\ No newline at end of file diff --git a/app/assets/stylesheets/api/rows.css.scss b/app/assets/stylesheets/api/rows.css.scss new file mode 100644 index 0000000..4fad3d5 --- /dev/null +++ b/app/assets/stylesheets/api/rows.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the api/rows controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/app/layouts/_app.scss b/app/assets/stylesheets/app/layouts/_app.scss new file mode 100644 index 0000000..abbe8b1 --- /dev/null +++ b/app/assets/stylesheets/app/layouts/_app.scss @@ -0,0 +1,24 @@ +// Application Layout +// ---------------------------------------- +// TODO: This generates huge selectors.. +.app { + + #main { + @include center-container(1000px, 0, 0 8px); + @include clearfix; + @include debug(gray); + } + + $sidebar: 215px; + $content: 758px; + + .container { + & > aside { + @include float(left, $sidebar, green); + } + + & > .content { + @include float(right, $content, blue); + } + } +}
\ No newline at end of file diff --git a/app/assets/stylesheets/app/layouts/_conference.scss b/app/assets/stylesheets/app/layouts/_conference.scss new file mode 100644 index 0000000..18fb232 --- /dev/null +++ b/app/assets/stylesheets/app/layouts/_conference.scss @@ -0,0 +1,136 @@ +// Conference +// ---------------------------------------- + +// The Usual Box with Mute, Cross +.actor { + @include clearfix; + @include gradient(#FDFDFD, #EFEFEF); + @include size(auto, 50px,6px, 1px 0 1px 0); + border-bottom:1px solid #E3E3E8; + border-top:1px solid #FFF; + img { + @extend .ext-bradius-inner; + float:left; + margin-right:6px; + } + .info { + .name { + display:block; + font-size:size(15px); + width:188px; + } + .status { + color:#818181; + } + float:left; + } + .voice-actions { + float:right; + } +} + +// Audio Controls. +.voice-actions { + padding:10px 0; + text-align:right; + width:100px; + .make.speaker { + @include image-replace('icons/microphone-16x.png'); + display:inline-block; + } + .make.listener { + @include image-replace('icons/headphones-16x.png'); + display:inline-block; + } + + .voice { + &.muted { + @include image-replace('icons/mute-16x.png'); + display:inline-block; + } + &.unmuted { + @include image-replace('icons/unmute-16x.png'); + display:inline-block; + } + } + .remove { + @include image-replace('icons/cross-16x.png'); + display:inline-block; + } + a { + margin:0 5px; + opacity:0.6; + &:hover { opacity:1.0;} + } +} + + +.conference { + @include pie-clearfix(); + .panel { + @include box-shadow(1px 1px 0px #FFF inset, 1px 1px 1px #EDEDED); + background:#F7F7F7; + border:1px solid #E3E3E8; + float:left; + margin: 0 10px; + width: 303px; + &.speakers { + header { @include gradient(#FFF, #F3F3DE); } + h3 { background:transparent image-url('icons/microphone-32x.png') left top no-repeat; } + } + &.listeners { + header { @include gradient(#FFF, #DCEAF2); } + h3 { background:transparent image-url('icons/headphones-32x.png') left top no-repeat; } + } + &.log { + header { @include gradient(#FFF, #E7E7E7); } + h3 { background:transparent image-url('icons/clock-32x.png') left top no-repeat; } + } + } + .first { margin-left:0;} + .last { margin-right:0;} + header { + @include box-shadow(1px 1px 0px #FFF inset, 0px 1px 0px #FFF); + border-bottom:1px solid #E3E3E8; + padding:10px; + } + h3 { + font-size:size(24px); + font-weight:normal; + margin:0; + opacity:0.8; + padding:2px 2px 2px 43px; + } + .message { + @include gradient( #EEEEEE, #fff); + border-bottom:1px solid #E3E3E8; + padding:10px; + input { + @include input-effects; + @include size(283px, auto, 5px, 1px solid #E3E3E8 ); + color:#696969; + } + } + .actors, .messages { + height:290px; // This should be X times the amount of items + overflow:auto; + } +} + +.log { + .messages { + color:#484848; + div { + background:#FFF; + border-bottom:1px solid #E3E3E8; + border-top:1px solid #FFF; + padding:5px 10px; + } + .status { + background:#F5F5F5; + } + .name { + font-weight:bold; + } + } +}
\ No newline at end of file diff --git a/app/assets/stylesheets/app/layouts/_phone-book-entry.scss b/app/assets/stylesheets/app/layouts/_phone-book-entry.scss new file mode 100644 index 0000000..f9e2345 --- /dev/null +++ b/app/assets/stylesheets/app/layouts/_phone-book-entry.scss @@ -0,0 +1,176 @@ +// Phone Book Entry Show +// ---------------------------------------- +// Icons: http://www.iconfinder.com/search/?q=iconset%3Acc_mono_icon_set +//http://www.iconfinder.com/search/?q=iconset%3AHelveticons_Social +section.phone-book-entry { + @include clearfix; + a { + @include link-colors(#49494D, $link-color); + } + .sidebar { + @include size(220px, auto, none, 0 1px 0 0); + border-right:1px solid #E3E3E8; + float:left; + } + .content { + float:right; + width:716px; + + } + + .username { + font-size: 46px; + font-weight:normal; + margin: 0 0 10px 0; + } + .personal { + margin-top:5px; + span { + margin-right:10px; + padding-left:22px; + } + .nickname { + background:transparent image-url('icons/user-16x.png') left 1px no-repeat; + } + .birthday { + background:transparent image-url('icons/star-16x.png') left -1px no-repeat;; + } + } + + .work { + font-size: 18px; + } + + .tags { + background:transparent image-url('icons/tag-16x.png') left 0px no-repeat; + margin-top:10px; + padding-left:22px; + } + + + .activity { + & > h2 { + font-size: 27px; + font-weight: normal; + } + textarea { + border-color: #E3E3E8; + height: 15px; + padding: 12px; + width: 689px; + } + .entry { + @include clearfix; + background-position: 12px 9px; + border-bottom: 1px solid #E3E3E8; + font-size: 16px; + + padding: 12px 0px 15px 55px; + .motive { + + display: block; + float: left; + line-height: 25px; + width: 529px; + + } + .timestamp { + color: #929298; + float: right; + font-size: 12px; + line-height: 26px; + } + } + } + + + // Icons + .home { + background:transparent image-url('icons/house-32x.png') left 2px no-repeat; + } + .office { + background:transparent image-url('icons/suitcase-32x.png') left 2px no-repeat; + } + .cellphone { + background:transparent image-url('icons/phone-mobile-32x.png') left 2px no-repeat; + } + .phone { + background:transparent image-url('icons/phone-up-32x.png') left 2px no-repeat; + } + .phone-down { + background:transparent image-url('icons/phone-down-32x.png') left 2px no-repeat; + } + .fax { + background:transparent image-url('icons/fax-32x.png') left 2px no-repeat; + } + .skype { + background:transparent image-url('icons/skype-32x.png') left 2px no-repeat; + } + .twitter { + background:transparent image-url('icons/twitter-32x.png') left 2px no-repeat; + } + .voice-message { + background:transparent image-url('icons/mic-32x.png') left 2px no-repeat; + } + + + + .comment { + @include clearfix; + border-bottom:1px solid #E3E3E8; + padding: 11px; + .display { + float:left; + overflow:hidden; + } + .info { + margin-bottom:4px; + } + .info,.body { + padding-left:47px; + } + .commenter { + font-size: 17px; + font-weight: bold; + } + .time { + color:#929298; + } + .info { + display:block; + } + } + + + .display { + @include border-radius(10px 0 10px 0); + } + .description { + margin:10px 0; + } + .widget { + border-top:1px solid #E3E3E8; + padding:23px 0; + width:200px; // Width of the image + div { + margin:10px 0; + padding-left:51px; + a { + color:#4B4B4B; + display:block; + font-size:size(17px); + font-weight:bold; + } + span { + color:#E3E3E8; + font-size:size(12px); + } + } + + &.adresses { + strong { + display:block; + } + } + } +}
\ No newline at end of file diff --git a/app/assets/stylesheets/app/pages/_phone_book.scss b/app/assets/stylesheets/app/pages/_phone_book.scss new file mode 100644 index 0000000..3670e8e --- /dev/null +++ b/app/assets/stylesheets/app/pages/_phone_book.scss @@ -0,0 +1,25 @@ +header.tabular-nav { @extend .ext-bradius-t; } +footer.tabular-nav { @extend .ext-bradius-b; } + +.tabular-nav { + @include clearfix; + line-height: 41px; + padding: 0 14px; + @include gradient(#69ABFB, #75AAEC); + .pagination { + float:right; + } + nav { + float:left; + margin-top: 1px; + @include size(395px, auto); + ol { + @include inline-list; + a { + font-weight:bold; + color:#fff; + font-size: 14px; + } + } + } +}
\ No newline at end of file diff --git a/app/assets/stylesheets/app/shared/_contents.scss b/app/assets/stylesheets/app/shared/_contents.scss new file mode 100644 index 0000000..8f88802 --- /dev/null +++ b/app/assets/stylesheets/app/shared/_contents.scss @@ -0,0 +1,374 @@ +// Contents +// ----------------------------------------´ + +// Wrap Everything +#container { + margin:0 auto; + width:$site-total-width; +} + +// Global Layout. +#content { + @extend .ext-bradius; + @include gradient(#fff, #FAF9FA); + border:1px solid #C0C0CA; + .light { + @extend .ext-bradius; + border:1px solid #ECECEF; + padding:20px; + } +} + + +// Content Header Object +// ---------------------------------------- + +.breadcrumbs { + color:$border-dark-color; + font-size:size(12px); + margin-bottom: 5px; + a { + @include link-colors(#8A8A91, $link-hover-color, #8A8A91, #8A8A91); + } +} + +header.main { + border-bottom:1px solid $border-dark-color; + margin-bottom:20px; + h1,h2 { + color:#49494D; + margin-top:0px; + } +} + + +// Flash Notice Object +// ---------------------------------------- +.flash { + @extend .ext-bradius; + @include box-shadow(0px 1px 2px darken(#F0F0F3, 20%)); + font-weight:bold; + margin-bottom:$vertical-margin; + position:relative; + .sign { + $sign-height: 26px; + @include pos-y-center($sign-height); + font-family:Georgia; + font-size:23px; + height:$sign-height; + } + .light { + @extend .ext-texture; + background-position:left bottom; + padding:15px 21px; + } + .message { + line-height:18px; + padding-left:26px; + } + + &.notice, &.flash { + @include text-shadow(1px 1px 1px #EDF3FA); + background:#CCE4FF; + color:#37547B; + } + + &.warning, &.alert { + $border-bottom: #EE9C00; + $border-light:#EDCC37; + @include text-shadow(1px 1px 1px #FEF6D7); + background:#FFE070; + border:{ + bottom:1px solid $border-bottom; + top:1px solid lighten(saturate($border-light, 5%), 9%); + }; + color:#7A2300; + } +} + + +// Scaffolding Tables +// ---------------------------------------- +table { + th { + color: #6A747B; + font-size: 14px; + font-weight: bold; + padding: 10px 8px; + } + .odd { + background:#F4F6FB; + border-bottom:1px solid #EDEFF8; + } + td { + padding:8px; + } +} + + +// Pagination Object +// ---------------------------------------- +.pagination { @include pagination(); } + + +// Entries Object. +// ---------------------------------------- +header.entries-nav { @extend .ext-bradius-inner-t; } +footer.entries-nav { @extend .ext-bradius-inner-b; } + +.entries-nav { + @include box-shadow( + inset 0px 1px 0px #aad2ff, // Light Top + 0 -1px 0px #A3C9E7, // Shadow Top + inset 0 -1px 2px #518CBC // Shadow Bottom + ); + @include clearfix; + @include gradient(#69ABFB, #75AAEC); + line-height: 41px; + padding: 0 14px; + nav { + color:#5490C3; + float:left; + margin-top: 1px; + width:540px; + } + // ABC + ol { + @include inline-list; + a { + @include link-colors(#fff, #FFFFC2); + @include text-shadow(0 2px 0px #5D96CC); + font-size: 14px; + font-weight:bold; + padding:0 3px; + } + } + .pagination { + float:right; + } +} + + +// Phone Book Entry +// ---------------------------------------- + +// Title Extension +.ext-pbe-big { display:block; font-size:size(18px); font-weight:bold; } + +.phone-book-entries { + table { margin:0;} +} +// Listing Version +tr.phone-book-entry { + @include clearfix; + color:#7F7F7F; + td { + vertical-align:middle; + } + &.odd { + background:#F4F6FB; + border-bottom:1px solid #EDEFF8; + } + img { + opacity:0.8; + } + // All Link colors + a { + @include link-colors(#577DA2, $link-color); + } + .name { + @extend .ext-pbe-big; + } + .company { + font-size:size(16px); + } + .user { + @include size(34%); + } + .phone { + @extend .ext-pbe-big; + } + .contact { + @include size(28%); + } + .extra { + @include size(38%); + } +} + +// Call History Entry +// ---------------------------------------- + +.call-history-entries { + table { margin:0;} +} + +// Listing Version +tr.call-history-entry { + @include clearfix; + color:#7F7F7F; + td { + vertical-align:middle; + } + &.odd { + background:#F4F6FB; + border-bottom:1px solid #EDEFF8; + } + img { + opacity:0.8; + } + // All Link colors + a { + @include link-colors(#577DA2, $link-color); + } + .select_box { + @include size(2%); + } + .thumbnail { + @include size(5%); + } + .time { + @include size(15%); + } + .user { + @include size(45%); + } + .status { + @include size(15%); + } + .actions { + @include size(5%); + } + .name { + @extend .ext-pbe-big; + } + .phone { + @extend .ext-pbe-big; + } + .call-forwarded { + background:transparent image-url('icons/gs_forward_16x.png') no-repeat; + padding-left: 20px; + } + .call-placed { + background:transparent image-url('icons/gs_placed_16x.png') no-repeat; + padding-left: 20px; + } + .call-received { + background:transparent image-url('icons/gs_received_16x.png') no-repeat; + padding-left: 20px; + } + .call-missed { + background:transparent image-url('icons/gs_missed_16x.png') no-repeat; + padding-left: 20px; + } + .voicemail { + background:transparent image-url('icons/gs_envelope_16x.png') no-repeat; + padding-left: 20px; + } + .duration { + @extend .ext-pbe-big; + } +} + +// Voicemail Messages Entry +// ---------------------------------------- + +.voicemail-messages-entries { + table { margin:0;} +} + +// Listing Version +tr.voicemail-messages-entry { + @include clearfix; + color:#7F7F7F; + td { + vertical-align:middle; + } + &.odd { + background:#F4F6FB; + border-bottom:1px solid #EDEFF8; + } + img { + opacity:0.8; + } + // All Link colors + a { + @include link-colors(#577DA2, $link-color); + } + .select_box { + @include size(2%); + } + .time { + @include size(15%); + } + .folder { + @include size(10%); + } + .user { + @include size(40%); + } + .status { + @include size(5%); + } + .actions { + @include size(5%); + } + .name { + @extend .ext-pbe-big; + } + .phone { + @extend .ext-pbe-big; + } + .voicemail-received { + background:transparent image-url('icons/gs_received_16x.png') no-repeat; + padding-left: 20px; + } + .voicemail-read { + background:transparent image-url('icons/gs_envelope_16x.png') no-repeat; + padding-left: 20px; + } + .duration { + @extend .ext-pbe-big; + } +} + + +// users#show +// ---------------------------------------- +#user-show { + @include clearfix; + .display { + float:left; + } + aside { + float:left; + padding-top:5px; + width: 250px; + p { + margin-left:70+26px; + } + } + section { + float:right; + width:650px; + h2:first-child { + margin-top:0; + } + } +} + + +// Simple Form Error Messgaes +// ----------------------------------------´ +p.error_notification { + @include gradient(mix(#EB6565, #994F5D, 60%), #994F5D); + border: 1px solid #7B404A; + border-radius: 4px; + box-shadow: inset 1px 1px 0px darken(#f3a0a0, 10%),inset -1px -1px 0px darken(#eb6565, 3%); + color:#FFF; + font-weight: bold; + margin: 0 0 0 158px; + padding: 10px 15px; + text-shadow: 2px 2px 1px #82434E; + width: 282px; +}
\ No newline at end of file diff --git a/app/assets/stylesheets/app/shared/_footers.scss b/app/assets/stylesheets/app/shared/_footers.scss new file mode 100644 index 0000000..be0f54f --- /dev/null +++ b/app/assets/stylesheets/app/shared/_footers.scss @@ -0,0 +1,90 @@ +// Footers +// ---------------------------------------- + +//@include sticky-footer(54px, "#container", "#footer-spacing", "footer"); + +footer#main { + @include clearfix; + position:relative; + margin:$vertical-margin 0; + @include gradient(#fff, #FAF9FA); + border:1px solid #C0C0CA; + @extend .ext-bradius; + .light { + padding:10px 20px; + border:1px solid #ECECEF; + @extend .ext-bradius; + } + + ul { + @include clearfix; + list-style:none; + float:left; + margin:0; + li { + float:left; + margin-bottom: 0px; + } + a { + @include debug; + line-height:43px; + padding: 10px 20px; + border:{ + left:1px solid $border-dark-color; + }; + } + li:first-child a{ + border-left:none; + } +// @include horizontal-navigation( +// /*$height : */ 32px, +// /*$color : */ red, +// /*$hover-color : */ red, +// /*$active-color : */ red, +// /*$text-shadow : */ #530008, +// /*$bg : */ none, +// /*$bg-hover : */ none, +// /*$bg-active : */ none, +// /*$box-shadow : */ none, +// /*$box-shadow-hover : */ none, +// /*$box-shadow-active : */ none, +// /*$border-light : */ #E3E3E8, +// /*$border-shadow : */ lighten(#E3E3E8,5%), +// /*$padding : */ 0 10px, +// /*$margin : */ none, +// /*\border-radius : */ none, +// /*$font-weight : */ none, +// /*$font-size : */ 14px, +// /*$tab-space : */ none +// ); + } + +} + +.amooma-logo { + background:#FAF9FA; + @include debug; + float:right; + padding:7px 7px 7px 16px; + @include border-radius(0 $global-border-radius $global-border-radius 0); + border-left:1px solid $border-dark-color; + @include box-shadow(-1px -1px 1px #FFFFFF); + a { + @include image-replace('amooma-logo.png'); + float:right; + opacity:0.6; + margin: 5px 5px 0 0; + &:hover { + opacity:1; + } + } + span { + @include debug; + font-size:11px; + color:#74748B; + float:left; + line-height:30px; + margin-right:10px; + } + +}
\ No newline at end of file diff --git a/app/assets/stylesheets/app/shared/_handheld.scss b/app/assets/stylesheets/app/shared/_handheld.scss new file mode 100644 index 0000000..1efc5e9 --- /dev/null +++ b/app/assets/stylesheets/app/shared/_handheld.scss @@ -0,0 +1,25 @@ +// +// Media queries for responsive design. +// +// These follow after primary styles so they will successfully override. +// + +@media all and (orientation:portrait) { + // Style adjustments for portrait mode goes here + +} + +@media all and (orientation:landscape) { + // Style adjustments for landscape mode goes here + +} + +// Grade-A Mobile Browsers (Opera Mobile, Mobile Safari, Android Chrome) +// consider this: www.cloudfour.com/css-media-query-for-mobile-is-fools-gold/ +@media screen and (max-device-width: 480px) { + + + // Uncomment if you don't want iOS and WinMobile to mobile-optimize the text for you: j.mp/textsizeadjust + // html { -webkit-text-size-adjust:none; -ms-text-size-adjust:none; } +} + diff --git a/app/assets/stylesheets/app/shared/_headers.scss b/app/assets/stylesheets/app/shared/_headers.scss new file mode 100644 index 0000000..6f8f0fe --- /dev/null +++ b/app/assets/stylesheets/app/shared/_headers.scss @@ -0,0 +1,145 @@ +// Headers +// ---------------------------------------- + +// -- File Variables ---------------------- + +$border-light:#74B8EA; +$background: #F0F0F3; +$user-context-width:227px; + + +// ---------------------------------------- + +.ext-texture { + background:transparent image-url('gradients/white-texture-x63.png') left top repeat-x; +} + +.ext-header { + max-width:$site-total-width - 2px; // Minus the borders + height:63px; + @extend .ext-bradius; +} + + +// Header Object +// ---------------------------------------- + +header#main { + @extend .ext-header; + background:$background image-url('gradients/light-to-dark-blue-x63.png') left top repeat-x; + margin:$vertical-margin 0; + border:1px solid $border-light; + border-bottom:1px solid #518CBC; + border-top:1px solid lighten(saturate($border-light, 5%), 9%); + @include box-shadow(0px 1px 2px darken(#F0F0F3, 20%)); + position:relative; + overflow:hidden; + .light { + @extend .ext-header; + @extend .ext-texture; + height:62px; + border-bottom:1px solid $border-light; + + // Used as a spacer for the width + padding:0 $user-context-width 0 0; + } + + // Resizable Navigation Items. + span { + @include debug; + float:left; + margin-left:15px; + font-size:size(17px); + line-height:62px; + display:inline; + float:left; + margin-right:7px; + } + + .message { + margin-left:206px; + } + + a { + @include text-shadow(1px 1px 0px darken(#518CBC, 12%)); + color:#fff; + &:active, &:hover { + color:#FFFF70; + text-decoration:none; + } + } +} + +// User Context Object +// ---------------------------------------- + +.user-context { + @include pos(0 10px); + @include size(200px); + padding-left:12px; + text-align:center; + font-size:size(17px); + line-height:62px; + color:#DDDDDD; + border-left:1px solid $border-light; + @include box-shadow(-1px -1px 0px #3A91DE); + // Logged out version of this little guy. + .display { + @extend .ext-bradius-inner; + @include box-shadow(0 -1px 0px #518CBC, 0 1px 0 #74B8EA); + vertical-align: middle; + margin: -2px 7px 0 0; + } + .logged-out { + @include box-shadow(none); + } + .user { + width: 170px; + display: block; + text-align:left; + } + .logout { + display: block; + position: absolute; + top: 0; + right: 0; + } +} + + +// Logo Object +// ---------------------------------------- + +.gemeinschaft-logo { + @include logo('logo.png'); + @include pos(2px 0 0 10px); + @include debug; +} + + +// Search Box Object +// ---------------------------------------- + +.search-box { + @extend .ext-bradius-inner; + float: left; + margin-left: 200px; + margin-top: 18px; + border-bottom: 1px solid #74B7EB; + position: relative; + input.text { + z-index: 0; + @extend .ext-bradius-inner; + background: #fff image-url('gradients/white-gray-x29-reverse.png') left top repeat-x; + line-height: 25px; + height: 25px; + border: 1px solid #3472B2; + @include size(160px, 25px, 0 23px 0 12px); + } + input[type="submit"] { + @include pos(5px 6px 0 0); + @include image-replace('icons/search-13x16.png'); + border:0; + } +} + diff --git a/app/assets/stylesheets/app/shared/_ie.scss b/app/assets/stylesheets/app/shared/_ie.scss new file mode 100644 index 0000000..afbe7e4 --- /dev/null +++ b/app/assets/stylesheets/app/shared/_ie.scss @@ -0,0 +1,7 @@ +// Internet Explorer Hate File. +// ---------------------------------------- +// Here you'll find all css which it's focused at a specific browser. +@if in-compatibility-mode() { + .ie7 {} + .ie8 {} +}
\ No newline at end of file diff --git a/app/assets/stylesheets/app/shared/_media.scss b/app/assets/stylesheets/app/shared/_media.scss new file mode 100644 index 0000000..c528a1f --- /dev/null +++ b/app/assets/stylesheets/app/shared/_media.scss @@ -0,0 +1,16 @@ +// Survival ✚ Kit +// Normalize.css + +//PLACEHOLDER Media Queries for Responsive Design. +//These override the primary ('mobile first') styles +//Modify as content requires. + +@media only screen and (min-width: 480px) { + /* Style adjustments for viewports 480px and over go here */ + +} + +@media only screen and (min-width: 768px) { + /* Style adjustments for viewports 768px and over go here */ + +}
\ No newline at end of file diff --git a/app/assets/stylesheets/app/shared/_print.scss b/app/assets/stylesheets/app/shared/_print.scss new file mode 100644 index 0000000..c8594e4 --- /dev/null +++ b/app/assets/stylesheets/app/shared/_print.scss @@ -0,0 +1,17 @@ +// Survival ✚ Kit +// Normalize.css + +@media print { + * { background: transparent !important; color: black !important; text-shadow: none !important; filter:none !important; -ms-filter: none !important; } /* Black prints faster: h5bp.com/s */ + a, a:visited { text-decoration: underline; } + a[href]:after { content: " (" attr(href) ")"; } + abbr[title]:after { content: " (" attr(title) ")"; } + .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } /* Don't show links for images, or javascript/internal links */ + pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } + thead { display: table-header-group; } /* h5bp.com/t */ + tr, img { page-break-inside: avoid; } + img { max-width: 100% !important; } + @page { margin: 0.5cm; } + p, h2, h3 { orphans: 3; widows: 3; } + h2, h3 { page-break-after: avoid; } +}
\ No newline at end of file diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss new file mode 100644 index 0000000..32e54f4 --- /dev/null +++ b/app/assets/stylesheets/application.css.scss @@ -0,0 +1,117 @@ +// Survival ✚ Kit [ Bootstrapper File ] + +// Dependencies +// ---------------------------------------- +@import "vendor/survival-kit/secure"; +@import "compass"; +@import "vendor/boilerplate-1.0/reset"; +@import "vendor/survival-kit/loader"; +@import "vendor/fancy-buttons/fancy-buttons"; + + +// Project Variables +// ---------------------------------------- +// Use @include debug; to show a color overlay on the element when this is set to true. +$debug : false; +// Typography +$base-font-family : "Helvetica Neue", Arial, Helvetica, sans-serif; +$base-font-size : 13px; +$base-line-height : 1.231; +$font-color : #222; +$link-color : #00e; +$link-hover-color : #06e; +$link-visited-color : #551a8b; +// ETC +$hr-color : #ccc; +// Selection +$selected-font-color : #fff; +$selected-background-color : #0084AC; +// Lists +$list-margin : 1em 0; +$list-padding : 0 0 0 2em; +// Container Width +$container-width : 1000px; +// Use @if in-compatibility-mode() to add conditional CSS (useful for mixins). +$compatibility-mode : true, ie7 ie8 ie9 ff2 chrome9; + +// -- Project Variables Overrides --------- + +$site-total-width : 1000px; +$vertical-margin : 15px; +$global-border-radius : 8px; +$global-inner-border-radius : 6px; + + +// -- Colors ------------------------------ +$link-color:#388DDA; +$border-dark-color: #E3E3E8; + +// Hooks +// ---------------------------------------- +// Mixins that are called from the Library files to add some extra styling. + +@mixin sk-html() { } + +@mixin sk-body() { + background:#F0F3F3 image-url('bg-body.png') left top repeat; +} + +// h1, h2, h3, h4, h5, h6 +@mixin sk-header-tags() { + font-weight:bold; +} + + +// Global Styling +// ---------------------------------------- +// Calculate all H# Tags. +@include htags-sizes($base-font-size + 20); +@include simple-forms(auto, block-hints no-stars ); +ul { + ul { + margin:0; + } +} + +// Global Classes for extension. +// ---------------------------------------- +// Here you place classes which are used as extensions across all the project. +// Prefix them with .ext- + + +// The default fancy button, used across the SK. +// @TODO: Turn this into a mixin @include sk-button(small/medium/big); +.sk-button, .button { + @include fancy-button(#1E81D5); +} +a.button { margin:10px 0;} + +// Border Radius +// ---------------------------------------- +.ext-bradius { @include border-radius($global-border-radius); } +.ext-bradius-inner-t { @include border-radius($global-inner-border-radius $global-inner-border-radius 0 0); } +.ext-bradius-inner-b { @include border-radius(0 0 $global-inner-border-radius $global-inner-border-radius); } +.ext-bradius-inner { @include border-radius($global-inner-border-radius); } + + +// The Partials +// ---------------------------------------- +// Base styles thanks to html5boilerplate. This one uses the Hooks defined before. +@import "vendor/boilerplate-1.0/styles"; + +// -- Shared Partials + // Headers and related material go here. + @import "app/shared/headers"; + // Footers and related material go here. + @import "app/shared/footers"; + // Content and Related Material go here. + @import "app/shared/contents"; + +// -- Layouts Partials + // The most general one goes first + @import "app/layouts/app"; + @import "app/layouts/phone-book-entry"; + @import "app/layouts/conference"; + + // Compatibility.. oh jeez. + @import "app/shared/ie";
\ No newline at end of file diff --git a/app/assets/stylesheets/scaffolds.css.scss b/app/assets/stylesheets/scaffolds.css.scss new file mode 100644 index 0000000..05188f0 --- /dev/null +++ b/app/assets/stylesheets/scaffolds.css.scss @@ -0,0 +1,56 @@ +body { + background-color: #fff; + color: #333; + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; } + +p, ol, ul, td { + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; } + +pre { + background-color: #eee; + padding: 10px; + font-size: 11px; } + +a { + color: #000; + &:visited { + color: #666; } + &:hover { + color: #fff; + background-color: #000; } } + +div { + &.field, &.actions { + margin-bottom: 10px; } } + +#notice { + color: green; } + +.field_with_errors { + padding: 2px; + background-color: red; + display: table; } + +#error_explanation { + width: 450px; + border: 2px solid red; + padding: 7px; + padding-bottom: 0; + margin-bottom: 20px; + background-color: #f0f0f0; + h2 { + text-align: left; + font-weight: bold; + padding: 5px 5px 5px 15px; + font-size: 12px; + margin: -7px; + margin-bottom: 0px; + background-color: #c00; + color: #fff; } + ul li { + font-size: 12px; + list-style: square; } } diff --git a/app/assets/stylesheets/vendor/README b/app/assets/stylesheets/vendor/README new file mode 100644 index 0000000..016b5fa --- /dev/null +++ b/app/assets/stylesheets/vendor/README @@ -0,0 +1 @@ +Here you should place the files that are not part of your project, but you use them at some point.
\ No newline at end of file diff --git a/app/assets/stylesheets/vendor/boilerplate-1.0/README b/app/assets/stylesheets/vendor/boilerplate-1.0/README new file mode 100644 index 0000000..a5aa5b1 --- /dev/null +++ b/app/assets/stylesheets/vendor/boilerplate-1.0/README @@ -0,0 +1,15 @@ +HTML5 ✰ Boilerplate (ac92ae7a) + +style.css contains a reset, font normalization and some base styles. + +Credit is left where credit is due. +Much inspiration was taken from these projects: +- yui.yahooapis.com/2.8.1/build/base/base.css +- camendesign.com/design/ +- praegnanz.de/weblog/htmlcssjs-kickstart + +Implementation to Compass as part of Survival Kit by Mario "Kuroir" Ricalde. + +Notes: + + Not implementing Non-semantic helper classes. Use Compass builts-in.
\ No newline at end of file diff --git a/app/assets/stylesheets/vendor/boilerplate-1.0/_reset.scss b/app/assets/stylesheets/vendor/boilerplate-1.0/_reset.scss new file mode 100644 index 0000000..efd1ac6 --- /dev/null +++ b/app/assets/stylesheets/vendor/boilerplate-1.0/_reset.scss @@ -0,0 +1,37 @@ +// html5doctor.com Reset Stylesheet (Eric Meyer's Reset Reloaded + HTML5 baseline) +// v1.6.1 2010-09-17 | Authors: Eric Meyer & Richard Clark +// html5doctor.com/html-5-reset-stylesheet/ +html, body, div, span, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, +small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, figcaption, figure, +footer, header, hgroup, menu, nav, section, summary, +time, mark, audio, video{ + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} + +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} + +blockquote, q { quotes: none; } + +blockquote:before, blockquote:after, +q:before, q:after { content: ""; content: none; } + +del { text-decoration: line-through; } + +abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; } + +table { border-collapse: collapse; border-spacing: 0; } + +input, select { vertical-align: middle; } diff --git a/app/assets/stylesheets/vendor/boilerplate-1.0/_styles.scss b/app/assets/stylesheets/vendor/boilerplate-1.0/_styles.scss new file mode 100644 index 0000000..3852329 --- /dev/null +++ b/app/assets/stylesheets/vendor/boilerplate-1.0/_styles.scss @@ -0,0 +1,171 @@ +// HTML5 ✰ Boilerplate +// +// style.css contains a reset, font normalization and some base styles. +// +// Credit is left where credit is due. +// Much inspiration was taken from these projects: +// - yui.yahooapis.com/2.8.1/build/base/base.css +// - camendesign.com/design/ +// - praegnanz.de/weblog/htmlcssjs-kickstart +// +// Modified to fit Survival ✚ Kit + + html { + @include sk-html; + overflow-y: scroll; + } + + +// Sections (body, section, nav, article, aside, h1..6, header, footer, address) +// ---------------------------------------- + + body, select, input, textarea { color: $font-color; font-family: $base-font-family; } + + body { @include sk-body; font-size: $base-font-size; line-height: $base-line-height; } + + +// Grouping Content (p, hr, pre, blockquote, ol, ul, li, dl, dt, dt, dd, figure, figcaption, div) +// ---------------------------------------- + + p { margin: 0 0 1em 0;} + li { margin-bottom: (1em / 2);} + + hr { border: 0; border-top: 1px solid $hr-color; display: block; height: 1px; margin: 1em 0; padding: 0; } + + blockquote { color: #666; font-style: italic; margin: 1.5em; } + + // normalize monospace sizing + // meyerweb.com/eric/thoughts/2010/02/12/fixed-monospace-sizing/ + // en.wikipedia.org/wiki/MediaWiki_talk:Common.css/Archive_11#Teletype_style_fix_for_Chrome + pre { + // www.pathf.com/blogs/2008/05/formatting-quoted-code-in-blog-posts-css21-white-space-pre-wrap/ + white-space: pre; white-space: pre-wrap; word-wrap: break-word; + padding: 15px; + } + + pre, code, kbd, samp { font-family: monospace, sans-serif; } + + // Lists + ul, ol { margin:$list-margin; padding:$list-padding;} + + ol { list-style-type: decimal; } + + // Remove margin from navigation lists. + nav ul, + nav li { list-style:none; list-style-image: none; margin: 0; } + + // Lists + dl { margin: 0 0 1.5em 0; } + + dl dt { font-weight: bold; } + + dd { margin-left: 1.5em;} + + +// Text Level Semantics (a, em, strong, small, s, cite, q, dfn, abbr, time, code, var, samp, kbd, sub, i, b, u, mark, ruby, rt, rp, bdi, bdo, span, br, wbr) +// ---------------------------------------- + + // Accessible focus treatment: people.opera.com/patrickl/experiments/keyboard/test + a {text-decoration:none;} + + a:hover, a:active { outline: none; text-decoration:underline;} + + a, a:active, a:visited { color: $link-color; } + + a:hover { color: $link-hover-color; } + + // Headers (h1, h2, etc) have no default font-size or margin; define those yourself + h1, h2, h3, h4, h5, h6 { @include sk-header-tags; } + + // j.mp/webkit-tap-highlight-color + a:link { -webkit-tap-highlight-color: #FF5E99; } + + small { font-size: 85%; } + + strong, b, th, dfn { font-weight: bold; } + + em, i { font-style:italic; } + + mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; } + + abbr, + acronym { border-bottom: 1px dotted #666; } + + address { font-style: italic; margin: 0 0 1.5em; } + + // Set sub, sup without affecting line-height: gist.github.com/413930 + sub, sup { font-size: 75%; line-height: 0; position: relative; } + + sup { top: -0.5em; } + + sub { bottom: -0.25em; } + + +// Embedded Content (img) +// ---------------------------------------- + + // Bicubic resizing for non-native sized IMG: + // code.flickr.com/blog/2008/11/12/on-ui-quality-the-little-things-client-side-image-resizing/ + .ie7 img { -ms-interpolation-mode: bicubic; } + + +// Tabular Data (table, caption, colgroup, col, tbody, thead, tfoot, tr, td, th) +// ---------------------------------------- + + table { margin-bottom: 1.4em; width:100%; } + + th { font-weight: bold; } + + th,td,caption { padding: 4px 10px 4px 5px; text-align: left; } + + +// Edits (ins, del) +// ---------------------------------------- + + ins { background-color: #ff9; color: #000; text-decoration: none; } + + del { color:#666; } + + +// Forms +// ---------------------------------------- + + select, input, textarea, button { font: 99% $base-font-family; outline:none;} + + td { vertical-align: top; } + + textarea { overflow: auto; } + + // Align checkboxes, radios, text inputs with their label by: Thierry Koblentz tjkdesign.com/ez-css/css/base.css + input[type="radio"] { vertical-align: text-bottom; } + + input[type="checkbox"] { vertical-align: bottom; } + + // Hand cursor on clickable input elements + + label, input[type="button"], input[type="submit"], input[type="image"], button { cursor: pointer; } + + // Webkit browsers add a 2px margin outside the chrome of form elements + button, input, select, textarea { margin: 0; } + + + // required:valid and required:invalid moved to form.scss + + // Make buttons play nice in IE: + // www.viget.com/inspire/styling-the-button-element-in-internet-explorer/ + button { overflow: visible; width: auto; } + + @if in-compatibility-mode() { + .ie7 input[type="checkbox"] { vertical-align: baseline; } + + .ie6 input { vertical-align: text-bottom; } + + .ie6 legend, .ie7 legend { margin-left: -7px; } + } + + +// Etc. +// ---------------------------------------- + + ::-moz-selection{ background: $selected-background-color; color:$selected-font-color; text-shadow: none; } + ::selection { background:$selected-background-color; color:$selected-font-color; text-shadow: none; } diff --git a/app/assets/stylesheets/vendor/boilerplate-2.0/README b/app/assets/stylesheets/vendor/boilerplate-2.0/README new file mode 100644 index 0000000..c9cd066 --- /dev/null +++ b/app/assets/stylesheets/vendor/boilerplate-2.0/README @@ -0,0 +1,16 @@ +HTML5 ✰ Boilerplate 2.0 (7467f9c0417a0c1f9863e2d000aad73f34836ef2) + +style.css contains a reset, font normalization and some base styles. + +Credit is left where credit is due. +Much inspiration was taken from these projects: +- yui.yahooapis.com/2.8.1/build/base/base.css +- camendesign.com/design/ +- praegnanz.de/weblog/htmlcssjs-kickstart + +Implementation to Compass as part of Survival Kit by Mario "Kuroir" Ricalde. + +Notes: + + - Not implementing Non-semantic helper classes. Use Compass builts-in. + - 1.0 and 2.0 are very similar. With 2.0 you save a couple of bytes.. maybe not worth the change?
\ No newline at end of file diff --git a/app/assets/stylesheets/vendor/boilerplate-2.0/_styles.scss b/app/assets/stylesheets/vendor/boilerplate-2.0/_styles.scss new file mode 100644 index 0000000..6268a35 --- /dev/null +++ b/app/assets/stylesheets/vendor/boilerplate-2.0/_styles.scss @@ -0,0 +1,209 @@ +// +// HTML5 ✰ Boilerplate +// +// What follows is the result of much research on cross-browser styling. +// Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal, +// Kroc Camen, and the H5BP dev community and team. +// +// Detailed information about this CSS: h5bp.com/css +// +// ==|== normalize ========================================================== +// + + +// ========================================================================== +// HTML5 display definitions +// ========================================================================== + +article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { display: block; } +audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; } +audio:not([controls]) { display: none; } +[hidden] { display: none; } + +// ========================================================================== +// Base +// ========================================================================== + +// +// 1. Correct text resizing oddly in IE6/7 when body font-size is set using em units +// 2. Force vertical scrollbar in non-IE +// 3. Prevent iOS text size adjust on device orientation change, without disabling user zoom: h5bp.com/g +// + +html { font-size: 100%; overflow-y: scroll; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } + +body { margin: 0; font-size: $base-font-size; line-height: $base-line-height; } + +body, button, input, select, textarea { font-family: $base-font-family; color: $font-color; } + +// +// Remove text-shadow in selection highlight: h5bp.com/i +// These selection declarations have to be separate +// Also: hot pink! (or customize the background color to match your design) +// + +::-moz-selection { background: $selected-background-color; color: $selected-font-color; text-shadow: none; } +::selection { background: $selected-background-color; color: $selected-font-color; text-shadow: none; } + + +// ========================================================================== +// Links +// ========================================================================== + +a { color: $link-color; } +a:visited { color: $link-visited-color; } +a:hover { color: $link-hover-color; } +a:focus { outline: thin dotted; } + +/* Improve readability when focused and hovered in all browsers: h5bp.com/h */ +a:hover, a:active { outline: 0; } + + +// ========================================================================== +// Typography +// ========================================================================== + +abbr[title] { border-bottom: 1px dotted; } + +b, strong { font-weight: bold; } + +i, em { font-style:italic;} + +blockquote { margin: 1em 40px; } + +dfn { font-style: italic; } + +hr { display: block; height: 1px; border: 0; border-top: 1px solid $hr-color; margin: 1em 0; padding: 0; } + +ins { background: #ff9; color: #000; text-decoration: none; } + +mark { background: #ff0; color: #000; font-style: italic; font-weight: bold; } + +// Redeclare monospace font family: h5bp.com/j +pre, code, kbd, samp { font-family: monospace, monospace; _font-family: 'courier new', monospace; font-size: 1em; } + +// Improve readability of pre-formatted text in all browsers +pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; } + +q { quotes: none; } +q:before, q:after { content: ""; content: none; } + +small { font-size: 85%; } + +// Position subscript and superscript content without affecting line-height: h5bp.com/k +sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } +sup { top: -0.5em; } +sub { bottom: -0.25em; } + + +// ========================================================================== +// Lists +// ========================================================================== +dl {margin:$list-margin;} +ul, ol { margin: $list-margin; padding: $list-padding; } +dd { margin: 0 0 0 40px; } +nav ul, nav ol { list-style: none; list-style-image: none; margin: 0; padding: 0; } + + +// ========================================================================== +// Embedded content +// ========================================================================== + +// +// 1. Improve image quality when scaled in IE7: h5bp.com/d +// 2. Remove the gap between images and borders on image containers: h5bp.com/e +// + +img { border: 0; -ms-interpolation-mode: bicubic; vertical-align: middle; } + +// +// Correct overflow not hidden in IE9 +// + +svg:not(:root) { overflow: hidden; } + + +// ========================================================================== +// Figures +// ========================================================================== + +figure { margin: 0; } + + +// ========================================================================== +// Forms +// ========================================================================== + +form { margin: 0; } +fieldset { border: 0; margin: 0; padding: 0; } + +// Indicate that 'label' will shift focus to the associated form element +label { cursor: pointer; } + +// +// 1. Correct color not inheriting in IE6/7/8/9 +// 2. Correct alignment displayed oddly in IE6/7 +// + +legend { border: 0; *margin-left: -7px; padding: 0; } + +// +// 1. Correct font-size not inheriting in all browsers +// 2. Remove margins in FF3/4 S5 Chrome +// 3. Define consistent vertical alignment display in all browsers +// + +button, input, select, textarea { font-size: 100%; margin: 0; vertical-align: baseline; *vertical-align: middle; } + +// +// 1. Define line-height as normal to match FF3/4 (set using !important in the UA stylesheet) +// 2. Correct inner spacing displayed oddly in IE6/7 +// + +button, input { line-height: normal; *overflow: visible; } + +// +// Reintroduce inner spacing in 'table' to avoid overlap and whitespace issues in IE6/7 +// + +table button, table input { *overflow: auto; } + +// +// 1. Display hand cursor for clickable form elements +// 2. Allow styling of clickable form elements in iOS +// + +button, input[type="button"], input[type="reset"], input[type="submit"] { cursor: pointer; -webkit-appearance: button; } + +// +// Consistent box sizing and appearance +// + +input[type="checkbox"], input[type="radio"] { box-sizing: border-box; } +input[type="search"] { -webkit-appearance: textfield; -moz-box-sizing: content-box; -webkit-box-sizing: content-box; box-sizing: content-box; } +input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } + +// +// Remove inner padding and border in FF3/4: h5bp.com/l +// + +button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } + +// +// 1. Remove default vertical scrollbar in IE6/7/8/9 +// 2. Allow only vertical resizing +// + +textarea { overflow: auto; vertical-align: top; resize: vertical; } + +// Colors for form validity +input:valid, textarea:valid { } +input:invalid, textarea:invalid { background-color: #f0dddd; } + + +// ========================================================================== +// Tables +// ========================================================================== + +table { border-collapse: collapse; border-spacing: 0; } +td { vertical-align: top; }
\ No newline at end of file diff --git a/app/assets/stylesheets/vendor/easy-slider/_numeric.scss b/app/assets/stylesheets/vendor/easy-slider/_numeric.scss new file mode 100644 index 0000000..db61e78 --- /dev/null +++ b/app/assets/stylesheets/vendor/easy-slider/_numeric.scss @@ -0,0 +1,44 @@ +// +// @TODO: Add docs to easy-slider-numeric! +// +@mixin easy-slider-numeric($width, $height, $selector:'#slider') { + #{$selector} { + & ul, & li { + margin:0; + padding:0; + list-style:none; + } + & li { + width:$width; + height:$height; + overflow:hidden; + } + } + @include _numeric-controls(); +} + +// You can override this function to alter the appearance of the numeric controls. +@mixin _numeric-controls() { + #controls{ + margin:10px 0; + line-height:28px; + list-style:none; + text-align:right; + li { + @include inline-block; + margin:0 0 0 10px; + } + .current a { + background:#FFFFFF; + color:#C80111; + @include box-shadow(0px 0px 3px #B2B2B2); + padding:6px 11px; // Simulate "hover" + } + a { + padding:5px 10px; + background:#F5F5F5; + border: 1px solid #AEAEAE; + color: #7F7F7F; + } + } +}
\ No newline at end of file diff --git a/app/assets/stylesheets/vendor/facebox/_facebox.scss b/app/assets/stylesheets/vendor/facebox/_facebox.scss new file mode 100644 index 0000000..53e612d --- /dev/null +++ b/app/assets/stylesheets/vendor/facebox/_facebox.scss @@ -0,0 +1,85 @@ +$facebox-overlay: #000 !default; +#facebox { + left: 0; + position: fixed; + text-align: left; + top: 0; + z-index: 100; +} + + +#facebox .popup{ + border:9px solid rgba(0, 157, 214, 0.8); + border-radius:5px; + -moz-border-radius:5px; + -webkit-border-radius:5px; + box-shadow:0 0 18px rgba(0,0,0,0.4); + -moz-box-shadow:0 0 18px rgba(0,0,0,0.4); + -webkit-box-shadow:0 0 18px rgba(0,0,0,0.4); + position:relative; +} + +#facebox .content { + background: #fff; + border-radius:4px; + -moz-border-radius:4px; + -webkit-border-radius:4px; + display:table; + min-width: 370px; + padding: 10px; +} + +#facebox .content > p:first-child{ + margin-top:0; +} +#facebox .content > p:last-child{ + margin-bottom:0; +} + +#facebox .close{ + padding:2px; + position:absolute; + right:5px; + top:5px; + z-index:101; +} +#facebox .close img{ + opacity:0.3; +} +#facebox .close:hover img{ + opacity:1.0; +} + +#facebox .loading { + text-align: center; +} + +#facebox .image { + text-align: center; +} + +#facebox img { + border: 0; + margin: 0; +} + +#facebox_overlay { + height:100%; + left: 0px; + position: fixed; + top: 0px; + width:100%; +} + +.facebox_hide { + z-index:-100; +} + +.facebox_overlayBG { + background-color: $facebox-overlay; + z-index: 99; +} + +#facebox h1{ + margin: 0 0 10px 0; +} diff --git a/app/assets/stylesheets/vendor/fancy-box/README b/app/assets/stylesheets/vendor/fancy-box/README new file mode 100644 index 0000000..70212dd --- /dev/null +++ b/app/assets/stylesheets/vendor/fancy-box/README @@ -0,0 +1,4 @@ +Fancybox 1.3.4 (2010/11/11) +Licensed under both MIT and GPL licenses + +http://fancybox.net/
\ No newline at end of file diff --git a/app/assets/stylesheets/vendor/fancy-box/_fancy-box.scss b/app/assets/stylesheets/vendor/fancy-box/_fancy-box.scss new file mode 100755 index 0000000..7ec2644 --- /dev/null +++ b/app/assets/stylesheets/vendor/fancy-box/_fancy-box.scss @@ -0,0 +1,336 @@ +//
+// FancyBox - jQuery Plugin
+// Simple and fancy lightbox alternative
+//
+// Examples and documentation at: http://fancybox.net
+//
+// Copyright (c) 2008 - 2010 Janis Skarnelis
+// That said, it is hardly a one-person project. Many people have submitted bugs, code, and offered their advice freely. Their support is greatly appreciated.
+//
+// Version: 1.3.4 (11/11/2010)
+// Requires: jQuery v1.3+
+//
+// Dual licensed under the MIT and GPL licenses:
+// http://www.opensource.org/licenses/mit-license.php
+// http://www.gnu.org/licenses/gpl.html
+//
+
+#fancybox-loading {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ width: 40px;
+ height: 40px;
+ margin-top: -20px;
+ margin-left: -20px;
+ cursor: pointer;
+ overflow: hidden;
+ z-index: 1104;
+ display: none;
+}
+
+#fancybox-loading div {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 40px;
+ height: 480px;
+ background-image: image-url('vendor/fancy-box/fancybox.png');
+}
+
+#fancybox-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ z-index: 1100;
+ display: none;
+}
+
+#fancybox-tmp {
+ padding: 0;
+ margin: 0;
+ border: 0;
+ overflow: auto;
+ display: none;
+}
+
+#fancybox-wrap {
+ position: absolute;
+ top: 0;
+ left: 0;
+ padding: 20px;
+ z-index: 1101;
+ outline: none;
+ display: none;
+}
+
+#fancybox-outer {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ background: #fff;
+}
+
+#fancybox-content {
+ width: 0;
+ height: 0;
+ padding: 0;
+ outline: none;
+ position: relative;
+ overflow: hidden;
+ z-index: 1102;
+ border: 0px solid #fff;
+}
+
+#fancybox-hide-sel-frame {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: transparent;
+ z-index: 1101;
+}
+
+#fancybox-close {
+ position: absolute;
+ top: -15px;
+ right: -15px;
+ width: 30px;
+ height: 30px;
+ background: transparent image-url('vendor/fancy-box/fancybox.png') -40px 0px;
+ cursor: pointer;
+ z-index: 1103;
+ display: none;
+}
+
+#fancybox-error {
+ color: #444;
+ font: normal 12px/20px Arial;
+ padding: 14px;
+ margin: 0;
+}
+
+#fancybox-img {
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ margin: 0;
+ border: none;
+ outline: none;
+ line-height: 0;
+ vertical-align: top;
+}
+
+#fancybox-frame {
+ width: 100%;
+ height: 100%;
+ border: none;
+ display: block;
+}
+
+#fancybox-left, #fancybox-right {
+ position: absolute;
+ bottom: 0px;
+ height: 100%;
+ width: 35%;
+ cursor: pointer;
+ outline: none;
+ background: transparent image-url('vendor/fancy-box/blank.gif');
+ z-index: 1102;
+ display: none;
+}
+
+#fancybox-left {
+ left: 0px;
+}
+
+#fancybox-right {
+ right: 0px;
+}
+
+#fancybox-left-ico, #fancybox-right-ico {
+ position: absolute;
+ top: 50%;
+ left: -9999px;
+ width: 30px;
+ height: 30px;
+ margin-top: -15px;
+ cursor: pointer;
+ z-index: 1102;
+ display: block;
+}
+
+#fancybox-left-ico {
+ background-image: image-url('vendor/fancy-box/fancybox.png');
+ background-position: -40px -30px;
+}
+
+#fancybox-right-ico {
+ background-image: image-url('vendor/fancy-box/fancybox.png');
+ background-position: -40px -60px;
+}
+
+#fancybox-left:hover, #fancybox-right:hover {
+ visibility: visible; /* IE6 */
+}
+
+#fancybox-left:hover span {
+ left: 20px;
+}
+
+#fancybox-right:hover span {
+ left: auto;
+ right: 20px;
+}
+
+.fancybox-bg {
+ position: absolute;
+ padding: 0;
+ margin: 0;
+ border: 0;
+ width: 20px;
+ height: 20px;
+ z-index: 1001;
+}
+
+#fancybox-bg-n {
+ top: -20px;
+ left: 0;
+ width: 100%;
+ background-image: image-url('vendor/fancy-box/fancybox-x.png');
+}
+
+#fancybox-bg-ne {
+ top: -20px;
+ right: -20px;
+ background-image: image-url('vendor/fancy-box/fancybox.png');
+ background-position: -40px -162px;
+}
+
+#fancybox-bg-e {
+ top: 0;
+ right: -20px;
+ height: 100%;
+ background-image: image-url('vendor/fancy-box/fancybox-y.png');
+ background-position: -20px 0px;
+}
+
+#fancybox-bg-se {
+ bottom: -20px;
+ right: -20px;
+ background-image: image-url('vendor/fancy-box/fancybox.png');
+ background-position: -40px -182px;
+}
+
+#fancybox-bg-s {
+ bottom: -20px;
+ left: 0;
+ width: 100%;
+ background-image: image-url('vendor/fancy-box/fancybox-x.png');
+ background-position: 0px -20px;
+}
+
+#fancybox-bg-sw {
+ bottom: -20px;
+ left: -20px;
+ background-image: image-url('vendor/fancy-box/fancybox.png');
+ background-position: -40px -142px;
+}
+
+#fancybox-bg-w {
+ top: 0;
+ left: -20px;
+ height: 100%;
+ background-image: image-url('vendor/fancy-box/fancybox-y.png');
+}
+
+#fancybox-bg-nw {
+ top: -20px;
+ left: -20px;
+ background-image: image-url('vendor/fancy-box/fancybox.png');
+ background-position: -40px -122px;
+}
+
+#fancybox-title {
+ font-family: Helvetica;
+ font-size: 12px;
+ z-index: 1102;
+}
+
+.fancybox-title-inside {
+ padding-bottom: 10px;
+ text-align: center;
+ color: #333;
+ background: #fff;
+ position: relative;
+}
+
+.fancybox-title-outside {
+ padding-top: 10px;
+ color: #fff;
+}
+
+.fancybox-title-over {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ color: #FFF;
+ text-align: left;
+}
+
+#fancybox-title-over {
+ padding: 10px;
+ background-image: image-url('vendor/fancy-box/fancy_title_over.png');
+ display: block;
+}
+
+.fancybox-title-float {
+ position: absolute;
+ left: 0;
+ bottom: -20px;
+ height: 32px;
+}
+
+#fancybox-title-float-wrap {
+ border: none;
+ border-collapse: collapse;
+ width: auto;
+}
+
+#fancybox-title-float-wrap td {
+ border: none;
+ white-space: nowrap;
+}
+
+#fancybox-title-float-left {
+ padding: 0 0 0 15px;
+ background: image-url('vendor/fancy-box/fancybox.png') -40px -90px no-repeat;
+}
+
+#fancybox-title-float-main {
+ color: #FFF;
+ line-height: 29px;
+ font-weight: bold;
+ padding: 0 0 3px 0;
+ background: image-url('vendor/fancy-box/fancybox-x.png') 0px -40px;
+}
+
+#fancybox-title-float-right {
+ padding: 0 0 0 15px;
+ background: image-url('vendor/fancy-box/fancybox.png') -55px -90px no-repeat;
+}
+
+/* IE6, IE7, IE8 */
+
+.fancybox-ie .fancybox-bg { background: transparent !important; }
+
+.fancybox-ie #fancybox-bg-n { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_n.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-ne { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_ne.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-e { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_e.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-se { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_se.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-s { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_s.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-sw { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_sw.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-w { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_w.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-nw { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_nw.png', sizingMethod='scale'); }
\ No newline at end of file diff --git a/app/assets/stylesheets/vendor/fancy-buttons/README b/app/assets/stylesheets/vendor/fancy-buttons/README new file mode 100644 index 0000000..9ee6cc3 --- /dev/null +++ b/app/assets/stylesheets/vendor/fancy-buttons/README @@ -0,0 +1,3 @@ + Fancy Buttons by imathis + https://github.com/imathis/fancy-buttons + License: MIT
\ No newline at end of file diff --git a/app/assets/stylesheets/vendor/fancy-buttons/_fancy-buttons.scss b/app/assets/stylesheets/vendor/fancy-buttons/_fancy-buttons.scss new file mode 100644 index 0000000..2e85caf --- /dev/null +++ b/app/assets/stylesheets/vendor/fancy-buttons/_fancy-buttons.scss @@ -0,0 +1,195 @@ +@import "compass/css3/gradient"; +@import "compass/css3/border-radius"; +@import "compass/css3/opacity"; +@import "compass/css3/text-shadow"; +@import "compass/css3/box-shadow"; +@import "compass/css3/background-clip"; +@import "fancy-gradient"; + +$fb-gradient-style: glossy !default; +$fb-invert-on-click: 1 !default; +$fb-font-size: 18px !default; +$fb-color: #444444 !default; +$fb-font-weight: bold !default; +$fb-border-width: 1px !default; +$fb-radius: 6px !default; +$fb-light-text: white !default; +$fb-dark-text: #222222 !default; +$fb-gradient: 1 !default; +$fb-image-path: image-url("vendor/fancy-buttons/button_bg.png") !default; +$fb-allow-disabled: false !default; +$fb-line-height: 1.2em !default; + +// Make a fancy button. +@mixin fancy-button($color: $fb-color, $font-size: $fb-font-size, $radius: $fb-radius, $border-width: $fb-border-width) { + @include fancy-button-structure($font-size, $radius, $border-width); + @include fancy-button-colors($color); +} + +// Style the button's colors, picking the most appropriate color set for the base color. +@mixin fancy-button-colors($color: $fb-color, $hover: 0, $active: 0, $fb-allow-disabled: $fb-allow-disabled) { + @include fb-color($color, "default"); + &:hover, &:focus { + @if $hover == 0 { + @include fb-color(darken($color, 3), "hover", $color); } + @else { + @include fb-color($hover, "hover"); } } + &:active { + @if $active == 0 { + @include fb-color(darken($color, 6), "active", $color); + @include box-shadow(darken($color, 15) 0 0.08em 0.2em 1px inset); } + @else { + @include fb-color($active, "active"); + @include box-shadow(darken($active, 9) 0 0.08em 0.1em 1px inset); } } + @include box-shadow(rgba(white, lightness($color) / 100) 0 0 0.1em 1px inset); + @include background-clip(padding-box); + @if $fb-allow-disabled { + &.disabled, &[disabled] { + @include disable-fancy-button($color); + } + } +} + +@mixin fancy-button-allow-disable($color: $fb-color, $font-size: $fb-font-size, $radius: $fb-radius, $border-width: $fb-border-width) { + $fb-disable-allowed: $fb-allow-disabled; + $fb-allow-disabled: true; + @include fancy-button-structure($font-size, $radius, $border-width); + @include fancy-button-colors-matte($color); + $fb-allow-disabled: $fb-disable-allowed; +} + +@mixin fancy-button-matte($color: $fb-color, $font-size: $fb-font-size, $radius: $fb-radius, $border-width: $fb-border-width) { + @include fancy-button-structure($font-size, $radius, $border-width); + @include fancy-button-colors-matte($color); +} + +@mixin fancy-button-custom($color: $fb-color, $font-size: $fb-font-size, $radius: $fb-radius, $border-width: $fb-border-width) { + @include fancy-button-structure($font-size, $radius, $border-width); + @include fancy-button-colors-custom($color, $font-size, $radius, $border-width); +} + +@mixin fancy-button-colors-matte($color: $fb-color, $hover: 0, $active: 0) { + $fb-current-style: $fb-gradient-style; + $fb-gradient-style: matte; + @include fancy-button-colors($color, $hover, $active); + $fb-gradient-style: $fb-current-style; +} + +@mixin fancy-button-colors-custom($color: $fb-color, $hover: 0, $active: 0) { + $fb-current-style: $fb-gradient-style; + $fb-gradient-style: custom; + @include fancy-button-colors($color, $hover, $active); + $fb-gradient-style: $fb-current-style; +} + +// Default state color settings +@mixin fb-color($color, $state, $lumins: $color) { + $gradient-top: lighten($color, 15); + $gradient-bottom: darken($color, 6); + $border-color: darken($color, 8); + @if $fb-invert-on-click != 0 { + $border-color: darken($color, 15); } + @if saturation($color) > 0 { + $color: saturate($color, 40); } + @else if lightness($lumins) >= lightness(#aaaaaa) { + $color: lighten($color, 20); } + @include fb-state-colors($color, $gradient-top, $gradient-bottom, $border-color, $state, $lumins); +} + +// Apply the button colors specified for the button state into which it is mixed. +@mixin fb-state-colors($color, $gradient-top, $gradient-bottom, $border, $state, $lumins: $color) { + background-color: $color; + @if $fb-gradient != 0 { + @if $fb-gradient-style == "glossy" { + @if $state == "active" { + @include fancy-gradient-active($gradient-top, $gradient-bottom); } + @else { + @include fancy-gradient($gradient-top, $gradient-bottom); } } + @else if $fb-gradient-style == "matte" { + @if $state == "active" { + @include fancy-matte-gradient-active($gradient-top, $gradient-bottom); } + @else { + @include fancy-matte-gradient($gradient-top, $gradient-bottom); } } + @else if $fb-gradient-style == "custom" { + @if $state == "active" { + @include custom-fancy-gradient-active($gradient-top, $gradient-bottom); } + @else { + @include custom-fancy-gradient($gradient-top, $gradient-bottom); } } } + border: { + color: $border; }; + $text-shadow-settings: unquote("0px 1px 1px"); + @if $fb-invert-on-click != 0 and $state == "active" { + $text-shadow-settings: unquote("0px -1px -1px"); } + @if lightness($lumins) < lightness(#aaaaaa) { + text-shadow: darken($color, 25) $text-shadow-settings; + &, &:visited { + color: $fb-light-text; } } + @else { + text-shadow: lighten($color, 15) $text-shadow-settings; + &, &:visited { + color: $fb-dark-text; } } +} + +@mixin fancy-button-text-colors($color, $hover: $color, $active: $color, $fb-allow-disabled: $fb-allow-disabled) { + &, &:visited { + color: $color; } + &:hover, &:focus { + color: $hover; } + &:active { + color: $active; } + @if $fb-allow-disabled { + &.disabled, &[disabled] { + color: $color; } } +} + +// Layout the button's box +@mixin fancy-button-structure($font-size: $fb-font-size, $radius: $fb-radius, $border-width: $fb-border-width, $line-height: $fb-line-height) { + @extend .fancy-button-reset-base-class; + @include fancy-button-size($font-size, $radius, $border-width, $line-height); +} + +@mixin fancy-button-size($font-size: $fb-font-size, $radius: $fb-radius, $border-width: $fb-border-width, $line-height: $fb-line-height) { + // better padding for smaller buttons + $v-padding: 0.3em; + $h-padding: 1em; + @if $radius > 0 { + @include border-radius($radius); } + font-size: $font-size; + line-height: $line-height; + @include fancy-button-padding($v-padding, $h-padding, $border-width); +} + +@mixin fancy-button-padding($v-padding, $h-padding, $border-width: $fb-border-width) { + padding: $v-padding $h-padding; + border-width: $border-width; +} + +// Reset the button's important properties to make sure they behave correctly +@mixin fb-reset($font-weight: $fb-font-weight) { + font-family: "Lucida Grande", Lucida, Arial, sans-serif; + background: #{$fb-image-path} repeat-x bottom left; + margin: 0; + width: auto; + overflow: visible; + display: inline-block; + cursor: pointer; + text-decoration: none; + border-style: solid; + font-weight: $font-weight; + &::-moz-focus-inner { + border: none; + padding: 0; } + &:focus { + outline: none; } +} + +@mixin disable-fancy-button($color: $fb-color, $opacity: 0.7) { + @include fb-color($color, "default"); + @include opacity($opacity); + @include box-shadow(none); + cursor: default !important; +} + +.fancy-button-reset-base-class { + @include fb-reset; +} diff --git a/app/assets/stylesheets/vendor/fancy-buttons/_fancy-gradient.scss b/app/assets/stylesheets/vendor/fancy-buttons/_fancy-gradient.scss new file mode 100644 index 0000000..da0baa9 --- /dev/null +++ b/app/assets/stylesheets/vendor/fancy-buttons/_fancy-gradient.scss @@ -0,0 +1,28 @@ +@mixin fancy-gradient($color1, $color2) { + $top_shine: lighten($color1, 18); + $bottom_glow: lighten($color2, 10); + $top_middle: $color1; + $middle: lighten($color2, 3); + $bottom_middle: $color2; + @include background-image(linear-gradient($top_shine, $top_middle 10%, $middle 50%, $bottom_middle 50%, $bottom_glow)); } + +@mixin fancy-gradient-active($color1, $color2) { + $top: lighten($color2, 6); + $bottom: lighten($color2, 14); + $top_middle: lighten($color2, 8); + $middle: lighten($color2, 4); + $bottom_middle: lighten($color2, 1); + @include background-image(linear-gradient($top, $top_middle 30%, $middle 50%, $bottom_middle 50%, $bottom)); } + +@mixin fancy-matte-gradient($color1, $color2) { + @include background-image(linear-gradient($color1, $color2)); } + +@mixin fancy-matte-gradient-active($color1, $color2) { + $top: lighten($color2, 5); + $bottom: lighten($color2, 15); + $middle: lighten($color2, 8); + @include background-image(linear-gradient($top, $middle 40%, $middle 85%, $bottom)); } + +/* incase an inverted custom gradient isn't specified */ +@mixin custom-fancy-gradient-active($color1, $color2) { + @include custom-fancy-gradient($color1, $color2); } diff --git a/app/assets/stylesheets/vendor/survival-kit/_blog.scss b/app/assets/stylesheets/vendor/survival-kit/_blog.scss new file mode 100644 index 0000000..5bec255 --- /dev/null +++ b/app/assets/stylesheets/vendor/survival-kit/_blog.scss @@ -0,0 +1,99 @@ +// Survival ✚ Kit + +// News Item +// ---------------------------------------- +// <div class="blog-item"> +// <h6 class="date">14 de Julio 2010</h6> +// <h3><a href="#">Lorem My Ipsum</a></h3> +// <img src="image.jpg" width="194" height="146" alt="Blog Thumb"/> +// <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras nec ipsum magna. Duis porttitor, felis quis eleifend vehicula, mauris mi varius nibh, sit amet iaculis magna magna vitae justo...</p> +// <p class="read-more"><a href="#">Nota Completa</a></p> +// </div> +@mixin news-item($date:#383838, $header:#1491EE) { + @include clearfix; + .date { + color:$date; + font:{ + size:11px; + weight:normal; + }; + } + img { + float:left; + padding: 4px 14px 50px 0; + } + p { + color:$link-color; + } + h3 { + margin-bottom:8px; + a{ + color:$header; + font-size:15px; + font-weight:bold; + text-decoration:none; + } + } +} + + +// Pagination Styling +// ---------------------------------------- +// <div class="pagination"> +// <span class="previous_page disabled">← Previous</span> +// <em>1</em> +// <a rel="next" href="/?page=2">2</a> +// <a href="/?page=3">3</a> +// <a href="/?page=4">4</a> +// <a class="next_page" rel="next" href="/?page=2">Next →</a> +// </div> + +// Notes for Later +//$active-state: (border (1px solid red), height 300px, ..[infinite]); +// Would Output: +// border: 1px solid red; heigh: 300px; + +// Normal, hover, active, disabled +//$pagination-font-weights: normal bold normal; +//$pagination-font-colors: #7F7F7F yellow #FFFFFF #4C7DB5; +//$pagination-borders:none (1px solid #4C7DB5) (none) (1px solid #D0D0D0); +//$pagination-backgrounds: #F5F5F5 #FFFFFF none none; +// color, background, border, weight +//@include pagination(#7F7F7F yellow #FFFFFF #4C7DB5, #F5F5F5 #FFFFFF none none, none (1px solid #4C7DB5) (none) (1px solid #D0D0D0), ); + +@mixin pagination() { + text-align:center; + * { + @include border-radius(4px); + } + .current { + font-weight:bold; + color:#0090BC; + font-size:14px; + padding: 3px 8px; + margin-right:2px; + } + .disabled { + color:#518CBC; + border:1px solid #518CBC; + } + a { + padding: 3px 8px; + @include gradient(#80DFFF, #3BBBE7); + @include box-shadow(0 2px 0px #EBEBEB); + text-decoration:none; + color: #FFF; + font-weight: bold; + border:1px solid #4DC6EF; + &:active { + @include box-shadow(0px 1px #96C5FA, inset 0px 1px #5D96CC); + background:#F6FAFC !important; + } + &:hover { + background:#D0F0FC; + border:1px solid #4DC6EF; + color:$link-hover-color; + @include box-shadow(none); + } + } +}
\ No newline at end of file diff --git a/app/assets/stylesheets/vendor/survival-kit/_effects.scss b/app/assets/stylesheets/vendor/survival-kit/_effects.scss new file mode 100644 index 0000000..488a83a --- /dev/null +++ b/app/assets/stylesheets/vendor/survival-kit/_effects.scss @@ -0,0 +1,97 @@ +// Survival ✚ Kit + +// Add a Bendy shadow to a squar element. +// @author Chris Eppstein +@mixin bendy-shadow($width, $angle: 5deg, $color: rgba(#333, 0.5)) { + @include box-shadow(0 10px 5px -5px $color); + position: relative; + z-index: 1; + &:before, &:after { + @include box-shadow(0 10px 10px 1px $color); + bottom: 2px; + content: ""; + height: 10px; + position: absolute; + width: $width / 2; + z-index: -1; + } + &:before { + @include rotate(-$angle); + left: 10px; + } + &:after { + @include rotate($angle); + right: 10px; + } +} + +// Sexy button ! +@mixin shiny-button($light-color: #92CE2F, $dark-mix-color: #32D17C, $mix-percent: 40%) { + // Params + $bg-light: $light-color; + $bg-dark: darken(mix($dark-mix-color, $bg-light, $mix-percent), 13%); + + $border-inset-color: $bg-light; + $border-inside-light: lighten($border-inset-color, 13%); + $border-inside-dark: $border-inset-color; + + $border-outside: darken($bg-dark, 9%); + $box-shadow: rgba(35, 35, 35, 0.2); + $text-shadow: darken($bg-dark, 7%); + + @extend .bradius-inner; + @include box-shadow(inset 1px 1px 0px $border-inside-light, inset -1px -1px 0px $border-inside-dark); + @include gradient($bg-light, $bg-dark); + @include text-shadow(2px 2px 1px $text-shadow); + border:1px solid $border-outside; + color:#FFF !important; + + font-size:size(13px); + font-weight: bold; + padding: 9px 60px; + text-decoration: none; + text-decoration: none !important; + &:hover { + @include box-shadow(inset 0px 0px 1px $border-inside-light, 0px 2px 1px $box-shadow); + @include gradient(lighten($bg-light, 6%), lighten($bg-dark, 6%)); + } + &:active { + @include box-shadow(inset 0px 2px 3px $bg-dark); + background: mix($bg-light, $bg-dark, 50%); + } + &.small { + font-size: 12px; + padding: 7px 22px; + } +} + +// +// @TODO: Add docs to shiny-button-colors! +// +@mixin shiny-button-colors($light-color: #92CE2F, $dark-mix-color: #32D17C, $mix-percent: 40%) { + // Params + $bg-light: $light-color; + $bg-dark: darken(mix($dark-mix-color, $bg-light, $mix-percent), 13%); + + $border-inset-color: $bg-light; + $border-inside-light: lighten($border-inset-color, 13%); + $border-inside-dark: $border-inset-color; + + $border-outside: darken($bg-dark, 9%); + $box-shadow: rgba(35, 35, 35, 0.2); + $text-shadow: darken($bg-dark, 7%); + + + @include box-shadow(inset 1px 1px 0px $border-inside-light, inset -1px -1px 0px $border-inside-dark); + @include gradient($bg-light, $bg-dark); + @include text-shadow(2px 2px 1px $text-shadow); + border:1px solid $border-outside; + &:hover { + @include box-shadow(inset 0px 0px 1px $border-inside-light, 0px 2px 1px $box-shadow); + @include gradient(lighten($bg-light, 6%), lighten($bg-dark, 6%)); + } + &:active { + @include box-shadow(inset 0px 2px 3px $bg-dark); + background: mix($bg-light, $bg-dark, 50%); + } +}
\ No newline at end of file diff --git a/app/assets/stylesheets/vendor/survival-kit/_forms.scss b/app/assets/stylesheets/vendor/survival-kit/_forms.scss new file mode 100644 index 0000000..7e82b87 --- /dev/null +++ b/app/assets/stylesheets/vendor/survival-kit/_forms.scss @@ -0,0 +1,313 @@ +// Survival ✚ Kit + +// A simple search box, generic. +// If $width contains a second argument, it won't output the width to the parent element, allowing you to use box-size. +// +// <form action="#" accept-charset="utf-8" class="search-box"> +// <input type="text" class="text" value="Search..." name="q" /> +// <input type="submit" class="button" value="" /> +// </form> +@mixin search-box-simple { + // Preferences + $width: 210px; + $height: 27px; + $font-size: 12px; + + background: #FFF; + overflow: hidden; + height: $height; + width: $width; + + // Style + border:1px solid #4BC5ED; + margin-top: -3px; + @extend .bradius-inner; + @include gradient(#FFF, #F5F5F5); + + &.active { + @include box-shadow(0px 1px 2px transparentize(#000, 0.8)); + background:#FFF; + } + + // Calculations + $button-width: 27px; + $input-width: $width - $button-width - 2px; + + input, button { + background:transparent; + border: 0; + font-size: $font-size; + outline: none; + } + .text { + @include size($input-width, $height, 7px 10px); + color: #777; + float: left; + line-height: $height - (7px * 2); + } + button, .search { + cursor: pointer; + display: block; + float:right; + height: $height; + padding:0; + width: $button-width; + } + .search { + background:transparent image-url('redesign/vendor/survival-kit/search-13x16.png') center center no-repeat; + } +} + +// Search Box Simple dimention override +@mixin search-box-simple-size($width, $height, $button-width: 27px) { + $input-width: $width - $button-width - 2px; + height: $height; + width: $width; + .text { + @include size($input-width, $height, 7px 10px); + line-height: $height - (7px * 2); + } + button, .search { + height: $height; + width: $button-width; + } +} + + +// Inputs. +$input-shadow : inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.2) !default; +$input-hover-color : #7DBEF1 !default; +$input-hover-shadow : 0 0 6px #7DBEF1 !default; + +// +// Adds the Input state effects +// +@mixin input-effects() { + @if $input-shadow { @include box-shadow($input-shadow); } + border:1px solid #CCCCCC; + outline: 0; + &:focus { + @if $input-hover-shadow { + @include box-shadow($input-hover-shadow); + } + border:1px solid $input-hover-color; + } +} + +// +// Forms Styles (Survival Kit) +// This styles are meant to be used with Simple_Forms (Rails) +// Usage: +// simple-forms(default, option-1 option2) +// Options: +// block-hints : display the hints right after the inut field. +@mixin simple-forms($selector : "simple_form", $opts:false) { + // Setup + $size-modifier : 0px; + $input-width : 300px; + $input-font-size : $base-font-size + $size-modifier; + $vertical-spacing : 7px; + $horizontal-spacing : 10px; + $label-width :148px; + + // 7px = base padding at 0 size modifier. + $vertical-field-padding : floor((7px + $size-modifier) + ($size-modifier / 4.4) * 2); + $horizontal-field-padding: 6px; + + // Colors. + $hint-color : #6E6E6E; + + @if $selector == auto or $selector == default { + $selector: 'simple_form'; + } + + .hidden { display: none; } + // Force $opts into a list goddamnit. + $opts: join($opts, herp derp); + + .#{$selector} { + @include debug; + .hint { + @include debug(green); + display:inline-block; + padding:$vertical-field-padding 0 $vertical-field-padding ($label-width + $horizontal-spacing); + } + + // Fix a issue with the spacing. + input.date { + label { + width: 145px !important; + } + } + + + label { + @include debug(green); + vertical-align:middle; + width:$label-width; // double line labels. + display:inline-block; // works with already inline displayed items. + margin:0 $horizontal-spacing 0 0; + padding: $vertical-field-padding 0; + line-height:$input-font-size + ($input-font-size * 0.26); + text-align: right; + abbr { + @include debug(yellow); + @if index($opts, no-stars) { + display:none; + } @else { + color:#E62500; + float: right; + margin-left: $horizontal-spacing; + } + } + &.boolean, &.collection_radio_buttons { padding:$horizontal-spacing/2; width: auto;} + } + + .ext-sfr { + @include debug(yellow); + display: inline-block; + vertical-align: middle; + width: $label-width; + } + + .input { + @include debug(blue); + padding:$vertical-spacing 0; + .hint { + @extend .ext-sfr; + color: #8A8A8A; + display: block; + font-size: size(11px); + padding: 2px 0 0 ($label-width + $horizontal-spacing); + width: $input-width + ($horizontal-field-padding * 2) + $horizontal-spacing; + } + &.boolean { + padding: 2px 0 0 ($label-width + $horizontal-spacing); + } + } + + select { + border:1px solid #CCCCCC; + outline:none; + // floor(Font Size * Line Height) + (Vertical Input Padding * 2) + 1px) + $calc: floor(($input-font-size * $base-line-height ) + ($vertical-field-padding * 2)) + (1px); + height: $calc + 1px; + padding:(6px + $size-modifier) * $base-line-height ; + &:focus { + border:1px solid $input-hover-color; + } + } + + // Needs to be nested so it doesn't collide with date selects. + .select select, .country select { + width:$input-width + ($horizontal-field-padding * 2); + } + textarea, input[type=text], input[type=password], input[type=email] { + font-size:$input-font-size; + padding: $vertical-field-padding $horizontal-field-padding; + vertical-align:top; + width:$input-width; + // Input Effects + @include input-effects; + } + + textarea { + height:80px; + max-width:$input-width; + } + + input { + &.check_boxes, &.radio, &.boolean { + vertical-align:middle; + } + } + + .submit, .padded { + padding-left: $label-width + $horizontal-spacing; + } + + .form-actions { + background: #F7F7F7; + border-top: 1px solid #DDD; + padding: 17px 0px 18px $label-width + $horizontal-spacing; + } + + // Simple Form Button for the forms. + .button { + @extend .sk-button; + } + + + // Errors @todo: this should be in its own section. + span.error, .error { + @extend .ext-sfr; + color: #D65C5C; + font-size: 12px; + margin-left: 10px; + } + + #error_explanation { + @include box-shadow(#D4D4D4 0 0 10px); + background: #FFEBD6; + border: 1px solid #FFB36C; + color:#895334; + margin:$vertical-spacing * 4 0; + padding: 10px 14px; + h2 { + @include header-size(18px); + color:#AE4910; + margin-top:0; + } + } + + + // Colors for form validity + input:valid, textarea:valid {} + + input:invalid, textarea:invalid { + $error-color: #FF6161; + box-shadow:$input-shadow, inset -7px 0px 0px lighten($error-color, 15%) !important; + &:focus { + @if $input-hover-shadow { + @include box-shadow($input-hover-shadow, inset -7px 0px 0px $error-color !important ); + } + } + } + } + + // Rails 3 wraps errors in Divs + .field_with_errors { + display:inline; + } + + // Make this compatible when you have no javascript loaded! + @if not index($opts, no-browser-support) { + .ie7 { + select { margin-top:15px;} + } + } +} + +// Allows you to have different widths for different layouts. +@mixin simple-form-width($width:false, $label-width:false) { + $horizontal-field-padding : 6px; + $horizontal-spacing : 3px; + $input-width : $width; + + @if $width { + textarea, input[type=text], input[type=password] { + max-width: $width; + width:$width; + } + + .select select, .country select { + width:$input-width + ($horizontal-field-padding * 2); + } + } + + @if $label-width { + .input .hint { + width:$label-width; + } + } +}
\ No newline at end of file diff --git a/app/assets/stylesheets/vendor/survival-kit/_headers.scss b/app/assets/stylesheets/vendor/survival-kit/_headers.scss new file mode 100644 index 0000000..8b99808 --- /dev/null +++ b/app/assets/stylesheets/vendor/survival-kit/_headers.scss @@ -0,0 +1,36 @@ +// Survival ✚ Kit + +// Sets the font size specified in pixels using percents so that the base +// font size changes and 1em has the correct value. When nesting font size +// declarations, within the DOM tree, the base_font_size must be the parent's +// effective font-size in pixels. +// Usage Examples: +// .big +// +font-size(16px) +// .bigger +// +font-size(18px) +// .big .bigger +// +font-size(18px, 16px) +// +// For more information see the table found at http://developer.yahoo.com/yui/3/cssfonts/#fontsize +// From: compass-html5-boilerplate gem. + +@function size($size, $base-font-size: $base-font-size) { + @return ceil(percentage($size / $base-font-size)); +} + + +// Calculate margin and line height according to the given size. +@mixin header-size($size) { + font-size: size($size); +} + +// Calculate the Header based on the H1 Max size. +@mixin htags-sizes($max) { + $per: $max * 0.10; + @for $i from 1 through 6 { + h#{$i} { + @include header-size($max - ($per * $i) ); + } + } +}
\ No newline at end of file diff --git a/app/assets/stylesheets/vendor/survival-kit/_images.scss b/app/assets/stylesheets/vendor/survival-kit/_images.scss new file mode 100644 index 0000000..36e67cd --- /dev/null +++ b/app/assets/stylesheets/vendor/survival-kit/_images.scss @@ -0,0 +1,121 @@ +// Survival ✚ Kit + +@import "compass/typography/text/replacement"; + +// Replace an A tag with an a background-image. +// @var $image string path to the image +// @var $inline boolean embed via data. +@mixin image-link($image, $inline:false) { + @include image-background($image, $inline); + cursor:pointer; + @extend .ext-hide-text; +} + +// Replace an A tag with an a background-image sprite. You need to provide it with +// the dimentions of the image and the x-pos/y-pos +// +// @var $image string path to the image, inherit is useful. +// @var $height width +// @var $height pixels +// @var $x-pos pixels +// @var $y-pos pixels +// @var $inline boolean embed via data. +@mixin image-sprite-link($image, $width, $height, $x-pos, $y-pos, $inline: false) { + @include sk-background(transparent, $image, $x-pos, $y-pos, no-repeat, $inline); + width:$width; + height:$height; + @extend .ext-hide-text; +} + +// Replace a Header>a tag with a background image. Made specifically for logos. +// @var $image string path to the image +// @var $inline boolean embed via data. +@mixin logo($image, $inline:false) { + @include no-mp; + width: image-width($image); + height: image-height($image); + a { + @include image-link($image, $inline); + &:hover { opacity: 0.7;} + } +} + +// Area for a header link, meant to be used when it inherits a background image. +// This should be invoked on the H1-6 Tag and not in the link, the needed structure is:2 +// <h1><a href=""></a></h1> +@mixin logo-area($width, $height, $debugging: false) { + @include no-mp; + width:$width; + height:$height; + a { + @include link-area($width, $height, $debugging); + } +} + +// An area which should be clickable. It's meant to be a low level mixin, you should +// use the alternatuves. +// - debugging enables a background color to know the position. +@mixin link-area($width, $height, $debugging: false) { + width:$width; + height:$height; + @if $debugging { + @include debug($debugging); + } + @extend .ext-hide-text; +} + +// Mixin for quickly replacing images for any given element. +// @var $image string path to the image +// @var $inline boolean embed via data. +@mixin image-replace($image, $inline:false) { + @include image-background($image, $inline); + @extend .ext-hide-text; +} +@mixin image-replace-url($image, $width, $height) { + background:transparent url($image) left top no-repeat; + @include link-area($width, $height); +} + +// Just adds the image as a background and sets the width/height accordingly. +// @var $image string path to the image +// @var $inline boolean embed via data. +@mixin image-background($image, $inline:false) { + @include sk-background(transparent, $image, no-repeat, top, left, $inline); + width: image-width($image); + height: image-height($image); +} + +// Add a background by passing the exact same parameters as a normal one. With +// one more parameter $inline. Which will use inline-image and add backward +// compatibility to IE7 via *background. +// +// @var $color +// @var $image string can be a path to an image or inherit (will insert tags separately) +// @var $horizontal +// @var $vertical +// @var $repeat +// @var $inline +@mixin sk-background($color, $image, $horizontal, $vertical, $repeat, $inline: false) { + @if $image == inherit { + background-color: $color; + background-repeat: $repeat; + background-position: $horizontal $vertical; + } @else { + @if $inline == true { + background : $color inline-image($image) $horizontal $vertical $repeat; + *background : $color image-url($image) $horizontal $vertical $repeat; + } @else { + background: $color image-url($image) $horizontal $vertical $repeat; + } + } +} + + +// Common styles needed by our Image Mixins. +// Depends on Compass. +.ext-hide-text { + @include hide-text; + display:block; + direction: ltr; + outline:none; +} diff --git a/app/assets/stylesheets/vendor/survival-kit/_lists.scss b/app/assets/stylesheets/vendor/survival-kit/_lists.scss new file mode 100644 index 0000000..ea9670e --- /dev/null +++ b/app/assets/stylesheets/vendor/survival-kit/_lists.scss @@ -0,0 +1,37 @@ +// Survival ✚ Kit + +// Add docs to float-list! +@mixin float-list($side:left) { + @include no-mp; + list-style-type: none; + li { float:$side; } +} + +$tc-begin-color : #000 !default; +$tc-end-color : lighten(#646464, 30) !default; +$tc-base-font-size : 11px !default; +$tc-max-font-size : 20px !default; +$tc-how-many : 10 !default; +@mixin tag-cloud($tc-begin-color, $tc-end-color, $tc-base-font-size, $tc-max-font-size, $tc-how-many) { + $font-calculations : $tc-base-font-size; + + li { + display:inline; + background:none; + padding:0 2px; + } + + a { + // Stops words from breaking. + display:inline-block; + } + + @for $i from 1 through $tc-how-many { + // The last item gets the max-font size. + $font-calculations: round($font-calculations + (($tc-max-font-size - $tc-base-font-size) / $tc-how-many)); + a.tag-#{$i} { + font-size:$font-calculations; + color: mix($tc-end-color, $tc-begin-color, ( $i * (100 / $tc-how-many) )); + } + } +} diff --git a/app/assets/stylesheets/vendor/survival-kit/_loader.scss b/app/assets/stylesheets/vendor/survival-kit/_loader.scss new file mode 100644 index 0000000..c09a018 --- /dev/null +++ b/app/assets/stylesheets/vendor/survival-kit/_loader.scss @@ -0,0 +1,11 @@ +// Survival ✚ Kit + +// Load all the Libraries. +@import "blog"; +@import "forms"; +@import "images"; +@import "lists"; +@import "navigation"; +@import "tools"; +@import "headers"; +@import "effects"; diff --git a/app/assets/stylesheets/vendor/survival-kit/_navigation.scss b/app/assets/stylesheets/vendor/survival-kit/_navigation.scss new file mode 100644 index 0000000..5e6f13d --- /dev/null +++ b/app/assets/stylesheets/vendor/survival-kit/_navigation.scss @@ -0,0 +1,230 @@ +// Survival ✚ Kit + +// Horizontal Navigation Low-level Method. +// +// It's meant to be called from other predifined mixins to avoid calling so many variables per call. +// Used from ul/ol +@mixin horizontal-navigation( + $height, + $color, + $hover-color, + $active-color, + $text-shadow, + $bg, + $bg-hover, + $bg-active, + $box-shadow, + $box-shadow-hover, + $box-shadow-active, + $border-left, + $border-right, + $padding, + $margin, + $border-radius, + $font-weight, + $font-size, + $tab-space // Sets a tabbing space. + ) { + // $bg none or transparent will remove the background. + @if $tab-space == none { $tab-space:0;} + @if $bg == none { $bg:transparent;} + @if $bg-active == auto { $bg-active:$bg-hover; } + @if $active-color == auto { $active-color:$hover-color; } + + // Border Calculation + // ---------------------------------------- + // Check if borders are set to anything but none / auto. + @if $border-left != none and $border-right != none and $border-left != auto and $border-right != auto { + // Borders where explicitly set. + @include _sk-nav-borders($border-left, $border-right); + } @else if $border-left == auto and $border-right == auto and $bg != transparent{ + // Borders calculated magically. + @include _sk-nav-borders(lighten($bg, 10%), darken($bg, 10%)); + } + + height:$height; // instead of clearfix, to keep shadows alive. + margin: 0; + list-style:none; + + // Links and input + li, a { + display:block; + float:left; // this can make it inline or block level. + line-height:$height; + } + + + a { + @if $font-weight != none { + font-weight: $font-weight; + } + @if $padding != none { + padding:$padding; + } + @if $margin != none { + margin:$margin; + } + @if $font-size != none { + font-size:$font-size; + } + + text-decoration:none; + color:$color; + + @if $bg != transparent { + background:$bg; + } + + @if $box-shadow != none { + @include box-shadow($box-shadow); + } + + @if $text-shadow != none { + @include text-shadow($text-shadow, 1px, 1px, 1px); + } + + @if $border-radius != none { + @include border-radius($border-radius); + } + + // Feature for tabs. + @if $tab-space != 0 { + margin-top: -($tab-space); + } + + // States + // ---------------------------------------- + + &:hover{ + @include _sk-nav-effects($hover-color, $bg-hover, $text-shadow, $box-shadow-hover, $bg-hover); + text-decoration:none; + } + + &:visited { + color:$active-color; + } + &.active { + @include _sk-nav-effects($active-color, $bg-active, $text-shadow, $box-shadow-active, $bg-hover); + // Add tab space. + @if $tab-space != 0 { + height:$height + $tab-space; + } + } + } +} + +// Mixin used to generate Background effects by the horizontal-navigation mixin. +@mixin _sk-nav-effects($color, $bg, $text-shadow, $box-shadow, $bg-hover) { + @if $color != auto { + color:$color; + } + @if $bg != transparent { + @if $bg-hover == auto { + background:darken($bg,3%); + } @else { + background:$bg; + } + @if $box-shadow != none{ + @include box-shadow($box-shadow); + } + } + // Remove the text shadow of hover. + @if $text-shadow != none { + @include text-shadow(none); + } +} + +// Low level mixin. +// Invoked by other mixins. +// +// @var $left the left border. +// @var $right the right border +@mixin _sk-nav-borders($left, $right) { + li:first-child, li.first { + border-left:1px solid $right; + }// li:first-child + li:last-child, li.last { + border-right:1px solid $left; + } + a { + border:{ + left: 1px solid $left; + right: 1px solid $right; + }; + &.active, &.active:hover { + border:{ + left:1px solid transparent; + right:1px solid transparent; + }; + } + &:hover { + border:{ + left:1px solid transparent; + right:1px solid transparent; + }; + } + } +} + +// +// @TODO: Add docs to tabs! +// +@mixin navigation-classes($opts: tabs) { + $opts: join($opts, force list); + .nav { + list-style: none; + margin-bottom: $base-line-height; + margin-left: 0; + } + + // Make links block level + .nav > li > a { + display: block; + } + .nav > li > a:hover { + background-color: #EEEEEE; + text-decoration: none; + } + + // Common styles + .nav-tabs { + @extend .nav; + @include pie-clearfix(); + } + .nav-tabs > li, .nav-pills > li { + float: left; + } + .nav-tabs > li > a { + line-height: 14px; + margin-right: 2px; + padding-left: 12px; + padding-right: 12px; // keeps the overall height an even number + } + + .nav-tabs { + border-bottom: 1px solid #ddd; + } + + .nav-tabs > li { + margin-bottom: -1px; + } + + .nav-tabs > li > a { + @include border-radius(4px 4px 0 0); + border: 1px solid transparent; + padding-bottom: 9px; + padding-top: 9px; + &:hover { + border-color: #EEEEEE #EEEEEE #ddd; + } + } + .nav-tabs > .active { + a, a:hover { + background-color: #FFF; + border: 1px solid #ddd; + border-bottom-color: transparent; + color: gray; + cursor: default; + } + } +}
\ No newline at end of file diff --git a/app/assets/stylesheets/vendor/survival-kit/_secure.scss b/app/assets/stylesheets/vendor/survival-kit/_secure.scss new file mode 100644 index 0000000..f08dd11 --- /dev/null +++ b/app/assets/stylesheets/vendor/survival-kit/_secure.scss @@ -0,0 +1,3 @@ +/*! + This is a compiled file. +*/
\ No newline at end of file diff --git a/app/assets/stylesheets/vendor/survival-kit/_tools.scss b/app/assets/stylesheets/vendor/survival-kit/_tools.scss new file mode 100644 index 0000000..e753dfe --- /dev/null +++ b/app/assets/stylesheets/vendor/survival-kit/_tools.scss @@ -0,0 +1,267 @@ +// Survival ✚ Kit +$container-width : 1000px !default; +$compatibility-mode : true, ie ie7 ie8 ie9 ff2 chrome9 !default; + +// Function to know if we're in compatibility mode, if $version is set it'll return if there's a match for that browser. +@function in-compatibility-mode($version: false) { + @if $compatibility-mode { + @if $version { + @return index(nth($compatibility-mode, 2), $version); + } @else { + @return nth($compatibility-mode, 1); + } + } @else { + @return false; + } +} + +// Shortcut to remove margin an padding. +// Used on several @mixins. +@mixin no-mp($extend:false) { + @if $extend { + @extend .no-mp; + } @else { + margin:0; + padding:0; + } +} +// Sometimes it's better to extend a class. +.no-mp { + margin:0; + padding:0; +} + +// Center an element. +@mixin center-container($container-width, $vertical-margin:0, $padding:0) { + margin:$vertical-margin auto; + @if $padding == 0 { + width:$container-width; + } @else { + @include size($container-width, auto, $padding); + } +} + +// Inline Block CrossBrowser. +// Disregards FF2 and IE6 +@mixin inline-block { + display: inline-block; + @if in-compatibility-mode(ie7) { + zoom: 1; + *display:inline; + } +} + +// Shortcut to set absolute positioning. +@mixin pos($pos, $debug: false) { + @if length($pos) == 1 { + $pos: $pos 0 0 0; + } + @if length($pos) == 2 { + $pos: nth($pos,1) nth($pos,2) 0 0; + } + @if length($pos) == 3 { + $pos: nth($pos,1) nth($pos,2) nth($pos, 3) 0; + } + position:absolute; + @if "#{nth($pos, 1)}" != "0" { top: nth($pos, 1); } + @if "#{nth($pos, 2)}" != "0" { right: nth($pos, 2); } + @if "#{nth($pos, 3)}" != "0" { bottom: nth($pos, 3); } + @if "#{nth($pos, 4)}" != "0" { left: nth($pos, 4); } + @if $debug { @include debug($debug); } +} + +// Center a absolute element horizontally; optional offset. +@mixin pos-x-center($width, $offset:0) { + @include pos(0 50% 0 50%); + margin-left:$offset - ( $width / 2 ); + width:$width; +} + +// Center a absolute element vertically; optional offset. +@mixin pos-y-center($height, $offset:0) { + @include pos(50% 0 50% 0); + height:$height; + margin-top:$offset - ( $height / 2 ); +} + +// Set a debug variable. +@mixin debug($color:red) { + @if $debug != false { + @if $color == true { $color:red; } + background: rgba($color, 0.2); // incompatible with IE. + } +} + +// Class available to center container to 1000px +.w, .pagewidth { + @include center-container($container-width); +} + +// Crossbrowser linear gradient. +// Compatible Browsers: FF3.6+ Saf4+ Chrome IE6-IE9 +// @author SitePoint +@mixin background-gradient($from, $to, $start: top, $end: bottom, $fallback:$from, $ie:false) { + @include gradient($from, $to, $start, $end, $fallback); +} +@mixin gradient($from, $to, $start: top, $end: bottom, $fallback:$from){ + background-color: $fallback; + + @if $end == bottom and $start == top { + @if $start == 0 { + background-image: -webkit-gradient(linear, left $start, left bottom, from($from), to($to)); + } @else { + background-image: -webkit-gradient(linear, $start, left bottom, from($from), to($to)); + } + background-image: -webkit-linear-gradient($start, $from, $to); + background-image: -moz-linear-gradient($start, $from, $to); + background-image: -ms-linear-gradient($start, $from, $to); + background-image: -o-linear-gradient($start, $from, $to); + @if in-compatibility-mode() { + filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#{ie-hex-str($from)}', EndColorStr='#{ie-hex-str($to)}'); + } + } @else if $end == bottom { + background-image: -webkit-gradient(linear, left $start, 0 $end, from($from), to($to)); + background-image: -webkit-linear-gradient(top, $from $start+px, $to); + background-image: -moz-linear-gradient(top, $from $start+px, $to); + background-image: -ms-linear-gradient(top, $from $start+px, $to); + background-image: -o-linear-gradient(top, $from $start+px, $to); + + } @else { + background-image: -webkit-gradient(linear, left $start, 0 $end, from($from), to($to)); + background-image: -webkit-linear-gradient(top, $from $start+px, $to $end+px); + background-image: -moz-linear-gradient(top, $from $start+px, $to $end+px); + background-image: -ms-linear-gradient(top, $from $start+px, $to $end+px); + background-image: -o-linear-gradient(top, $from $start+px, $to $end+px); + // No IE support for positioned gradients + } +} + +// Mixin that allows you to set the size of the box to a fixed width/height +// taking into consideration the padding and borders for you. +// +// Examples: +// @include size(100px, 100px, 10px, 5px solid red); +// Will render a 100x100. +// +// $width: Pixel value for width +// $height: Pixel value for height +// $padding: Padding accepts: 1px or 1px 2px or 1px 2px 3px 4px +// $border: Border, accepts 1px solid #000 or 1px or 1px 2px or 1px 2px 3px 4px +// When passing a border declaration (1px solid #000) it'll add the CSS for you. +@mixin size($width, $height:auto, $padding: none, $border:none) { + // Prepare the borders, accept the following: + // 1px solid #000 or 1px or 1px 2px or 1px 2px 3px 4px + @if true { + @if $border == none { + $border:0; + } + + $border-len: length($border); + // Standardize padding to a list with 4 items. + @if $border-len == 3 { + border:$border; + $bw: nth($border, 1); + $border: $bw $bw $bw $bw; + } @else if $border-len == 1 { + $border: $border $border $border $border; + } @else if $border-len == 2 { + $border: join($border, $border); + } + } + + // Prepare padding, accept the following: + // 1px or 1px 2px or 1px 2px 3px 4px + @if true { + @if $padding == none { + $padding:0; + } @else { + padding:$padding; + } + + // Standardize padding to a list with 4 items. + $padding-len: length($padding); + @if $padding-len == 1 { + $padding: $padding $padding $padding $padding; + } @else if $padding-len == 2 { + $padding: join($padding, $padding); + } + } + + @if $width != auto { + width: $width - (nth($padding, 2) + nth($padding, 4)) - (nth($border, 2) + nth($border, 4)); + } + @if $height != auto { + height: $height - (nth($padding, 1) + nth($padding, 3)) - (nth($border, 1) + nth($border, 3)); + } +} + +// Float an element with a given width and a direction. Third parameter allows easy debugging. +// Yes, we override Compass :( +// @TODO: Make it use box-size and allow padding. +@mixin float($side, $size:auto, $debug-color:false) { + @if $size != auto { + @if length($size) == 1 { + width:$size; + } @else { + height:nth($size, 2); + width:nth($size, 1); + } + + } + @if in-compatibility-mode(ie6) { + display:inline; + } + float: $side; + @if $debug-color and $debug { + @include debug($debug-color); + } +} + +@mixin transition($property: all, $time: 400ms, $easing: ease-out){ + transition: $property $time $easing; + -moz-transition: $property $time $easing; + -ms-transition: $property $time $easing; + -o-transition: $property $time $easing; + -webkit-transition: $property $time $easing; +} + +// Calculate the Golden Ratio of a given value. +// ---------------------------------------- +@function golden-ratio($size, $type) { + $big : round($size / 1.61803); + $small : $size - $big; + @return if($type == large, $big, $small); +} + + +// +// @TODO: Add docs to link-colors! +// +@mixin link-colors($normal, $hover: false, $active: false, $visited: false, $focus: false) { + @if $normal == default { + $hover: $link-hover-color; + $normal: $link-color; + $visited: $link-visited-color; + } + color: $normal; + @if $visited { + &:visited { + color: $visited; } } + @if $focus { + &:focus { + color: $focus; } } + @if $hover { + &:hover { + color: $hover; } } + @if $active { + &:active { + color: $active; } } +} + +// Substract the Body to the Container width to get the sidebar. +@function sidebar($body-width, $container-width-over: false) { + @if $container-width-over == false { + $container-width-over : $container-width; + } + @return $container-width - $body-width; +}
\ No newline at end of file diff --git a/app/controllers/access_authorizations_controller.rb b/app/controllers/access_authorizations_controller.rb new file mode 100644 index 0000000..54365e7 --- /dev/null +++ b/app/controllers/access_authorizations_controller.rb @@ -0,0 +1,68 @@ +class AccessAuthorizationsController < ApplicationController + load_and_authorize_resource :callthrough + load_and_authorize_resource :access_authorization, :through => [:callthrough] + + before_filter :set_parent_and_path_methods + before_filter :spread_breadcrumbs + + def index + end + + def show + end + + def new + @access_authorization = @parent.access_authorizations.build + @access_authorization.name = generate_a_new_name(@parent, @access_authorization) + @access_authorization.phone_numbers.build + @access_authorization.login = random_pin + random_pin + @access_authorization.pin = random_pin + end + + def create + @access_authorization = @parent.access_authorizations.build(params[:access_authorization]) + if @access_authorization.save + redirect_to @show_path_method.(@parent, @access_authorization), :notice => t('access_authorizations.controller.successfuly_created') + else + render :new + end + end + + def edit + end + + def update + if @access_authorization.update_attributes(params[:access_authorization]) + redirect_to @show_path_method.(@parent, @access_authorization), :notice => t('access_authorizations.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @access_authorization.destroy + redirect_to @index_path_method.(@parent), :notice => t('access_authorizations.controller.successfuly_destroyed') + end + + private + + def set_parent_and_path_methods + @parent = @callthrough + @show_path_method = method( :"#{@parent.class.name.underscore}_access_authorization_path" ) + @index_path_method = method( :"#{@parent.class.name.underscore}_access_authorizations_path" ) + @new_path_method = method( :"new_#{@parent.class.name.underscore}_access_authorization_path" ) + @edit_path_method = method( :"edit_#{@parent.class.name.underscore}_access_authorization_path" ) + end + + def spread_breadcrumbs + if @callthrough + add_breadcrumb t("#{@parent.class.name.underscore.pluralize}.index.page_title"), tenant_callthroughs_path(@callthrough.tenant) + add_breadcrumb @callthrough, tenant_callthrough_path(@callthrough.tenant, @callthrough) + add_breadcrumb t("access_authorizations.index.page_title"), callthrough_access_authorizations_path(@callthrough) + if @access_authorization && !@access_authorization.new_record? + add_breadcrumb @access_authorization, callthrough_access_authorization_path(@callthrough, @access_authorization) + end + end + end + +end diff --git a/app/controllers/acd_agents_controller.rb b/app/controllers/acd_agents_controller.rb new file mode 100644 index 0000000..1d119b3 --- /dev/null +++ b/app/controllers/acd_agents_controller.rb @@ -0,0 +1,73 @@ +class AcdAgentsController < ApplicationController + load_and_authorize_resource :automatic_call_distributor + load_and_authorize_resource :acd_agent, :through => [:automatic_call_distributor] + + before_filter :spread_breadcrumbs + + def index + if params[:active] + if params[:active].downcase == 'true' + @acd_agents = @acd_agents.where(:active => true) + elsif params[:active].downcase == 'false' + @acd_agents = @acd_agents.where(:active => false) + end + end + end + + def show + @acd_agent = AcdAgent.find(params[:id]) + end + + def new + @acd_agent = @automatic_call_distributor.acd_agents.build + i = @automatic_call_distributor.acd_agents.count + loop do + i += 1 + break unless @automatic_call_distributor.acd_agents.where(:name => "#{t('acd_agents.name')} #{i}").count > 0 + end + @acd_agent.name = "#{t('acd_agents.name')} #{i}" + @acd_agent.status = 'active' + @acd_agent.calls_answered = 0 + end + + def create + @acd_agent = @automatic_call_distributor.acd_agents.build(params[:acd_agent]) + if @acd_agent.save + redirect_to automatic_call_distributor_acd_agent_path(@automatic_call_distributor, @acd_agent), :notice => t('acd_agents.controller.successfuly_created') + else + render :new + end + end + + def edit + @acd_agent = AcdAgent.find(params[:id]) + end + + def update + @acd_agent = AcdAgent.find(params[:id]) + if @acd_agent.update_attributes(params[:acd_agent]) + redirect_to automatic_call_distributor_acd_agent_path(@automatic_call_distributor, @acd_agent), :notice => t('acd_agents.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @acd_agent = AcdAgent.find(params[:id]) + @acd_agent.destroy + redirect_to automatic_call_distributor_acd_agents_path(@automatic_call_distributor), :notice => t('acd_agents.controller.successfuly_destroyed') + end + + def spread_breadcrumbs + if @automatic_call_distributor.automatic_call_distributorable.class == User + add_breadcrumb t("#{@automatic_call_distributor.automatic_call_distributorable.class.name.underscore.pluralize}.index.page_title"), method( :"tenant_#{@automatic_call_distributor.automatic_call_distributorable.class.name.underscore.pluralize}_path" ).(@automatic_call_distributor.tenant) + add_breadcrumb @automatic_call_distributor.automatic_call_distributorable, method( :"tenant_#{@automatic_call_distributor.automatic_call_distributorable.class.name.underscore}_path" ).(@automatic_call_distributor.tenant, @automatic_call_distributor.automatic_call_distributorable) + end + add_breadcrumb t("automatic_call_distributors.index.page_title"), method( :"#{@automatic_call_distributor.automatic_call_distributorable.class.name.underscore}_automatic_call_distributors_path" ).(@automatic_call_distributor.automatic_call_distributorable) + add_breadcrumb @automatic_call_distributor, method( :"#{@automatic_call_distributor.automatic_call_distributorable.class.name.underscore}_automatic_call_distributor_path" ).(@automatic_call_distributor.automatic_call_distributorable, @automatic_call_distributor) + add_breadcrumb t("acd_agents.index.page_title"), automatic_call_distributor_acd_agents_path(@automatic_call_distributor) + if @acd_agent && !@acd_agent.new_record? + add_breadcrumb @acd_agent, automatic_call_distributor_acd_agent_path(@automatic_call_distributor, @acd_agent) + end + end +end diff --git a/app/controllers/acd_callers_controller.rb b/app/controllers/acd_callers_controller.rb new file mode 100644 index 0000000..ab58064 --- /dev/null +++ b/app/controllers/acd_callers_controller.rb @@ -0,0 +1,41 @@ +class AcdCallersController < ApplicationController + def index + @acd_callers = AcdCaller.all + end + + def show + @acd_caller = AcdCaller.find(params[:id]) + end + + def new + @acd_caller = AcdCaller.new + end + + def create + @acd_caller = AcdCaller.new(params[:acd_caller]) + if @acd_caller.save + redirect_to @acd_caller, :notice => t('acd_callers.controller.successfuly_created') + else + render :new + end + end + + def edit + @acd_caller = AcdCaller.find(params[:id]) + end + + def update + @acd_caller = AcdCaller.find(params[:id]) + if @acd_caller.update_attributes(params[:acd_caller]) + redirect_to @acd_caller, :notice => t('acd_callers.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @acd_caller = AcdCaller.find(params[:id]) + @acd_caller.destroy + redirect_to acd_callers_url, :notice => t('acd_callers.controller.successfuly_destroyed') + end +end diff --git a/app/controllers/addresses_controller.rb b/app/controllers/addresses_controller.rb new file mode 100644 index 0000000..a70b1f4 --- /dev/null +++ b/app/controllers/addresses_controller.rb @@ -0,0 +1,41 @@ +class AddressesController < ApplicationController + def index + @addresses = Address.all + end + + def show + @address = Address.find(params[:id]) + end + + def new + @address = Address.new + end + + def create + @address = Address.new(params[:address]) + if @address.save + redirect_to @address, :notice => t('addresses.controller.successfuly_created') + else + render :new + end + end + + def edit + @address = Address.find(params[:id]) + end + + def update + @address = Address.find(params[:id]) + if @address.update_attributes(params[:address]) + redirect_to @address, :notice => t('addresses.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @address = Address.find(params[:id]) + @address.destroy + redirect_to addresses_url, :notice => t('addresses.controller.successfuly_destroyed') + end +end diff --git a/app/controllers/api/rows_controller.rb b/app/controllers/api/rows_controller.rb new file mode 100644 index 0000000..6e815eb --- /dev/null +++ b/app/controllers/api/rows_controller.rb @@ -0,0 +1,91 @@ +class Api::RowsController < ApplicationController + before_filter :check_remote_ip_address_whitelist + + def index + @rows = Api::Row.all + + respond_to do |format| + format.xml { render xml: @rows } + end + end + + def show + if params[:user_name] + @row = Api::Row.find_by_user_name(params[:user_name]) + else + @row = Api::Row.find(params[:id]) + end + + respond_to do |format| + format.xml { render xml: @row } + end + end + + def new + @row = Api::Row.new + + respond_to do |format| + format.xml { render xml: @row } + end + end + + def edit + if params[:user_name] + @row = Api::Row.find_by_user_name(params[:user_name]) + else + @row = Api::Row.find(params[:id]) + end + end + + def create + @row = Api::Row.new(params[:row]) + + respond_to do |format| + if @row.save + @row.create_a_new_gemeinschaft_user + + format.xml { render xml: @row, status: :created, location: @row } + else + format.xml { render xml: @row.errors, status: :unprocessable_entity } + end + end + end + + def update + if params[:user_name] + @row = Api::Row.find_by_user_name(params[:user_name]) + else + @row = Api::Row.find(params[:id]) + end + + respond_to do |format| + if @row.update_attributes(params[:row]) + @row.update_user_data + format.xml { head :no_content } + else + format.xml { render xml: @row.errors, status: :unprocessable_entity } + end + end + end + + def destroy + if params[:user_name] + @row = Api::Row.find_by_user_name(params[:user_name]) + else + @row = Api::Row.find(params[:id]) + end + @row.destroy + + respond_to do |format| + format.xml { head :no_content } + end + end + + private + + def check_remote_ip_address_whitelist + if !(REMOTE_IP_ADDRESS_WHITELIST.empty? or REMOTE_IP_ADDRESS_WHITELIST.include?(ENV['REMOTE_ADDR'])) + redirect_to root_url + end + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb new file mode 100644 index 0000000..c675f5c --- /dev/null +++ b/app/controllers/application_controller.rb @@ -0,0 +1,161 @@ +class ApplicationController < ActionController::Base + + protect_from_forgery + + before_filter :set_locale + + before_filter :go_to_setup_if_new_installation + before_filter :home_breadcrumb + + helper_method :current_user + + helper_method :guess_local_ip_address + helper_method :guess_local_host + + helper_method :'have_https?' + + helper_method :random_pin + + + #TODO Add check_authorization. See + # https://github.com/ryanb/cancan + # https://github.com/ryanb/cancan/wiki/Ensure-Authorization + # and Gemeinschaft 4 + + # Generate a new name for an Object + # + def generate_a_new_name(parent, child = nil) + if child + i = parent.send(child.class.name.underscore.pluralize).count + loop do + i += 1 + if I18n.t("#{child.class.name.underscore.pluralize}.new_name_scaffold").include?('translation missing') + @guess_a_new_name = I18n.t(child.class.name.underscore.pluralize + '.name') + " #{i}" + else + @guess_a_new_name = I18n.t("#{child.class.name.underscore.pluralize}.new_name_scaffold", :counter => i.to_s) + end + break unless parent.send(child.class.name.underscore.pluralize).where(:name => "#{@guess_a_new_name}").count > 0 + end + else + i = parent.class.count + loop do + i += 1 + if I18n.t("#{parent.class.name.underscore.pluralize}.new_name_scaffold").include?('translation missing') + @guess_a_new_name = I18n.t(parent.class.name.underscore.pluralize + '.name') + " #{i}" + else + @guess_a_new_name = I18n.t("#{parent.class.name.underscore.pluralize}.new_name_scaffold", :counter => i.to_s) + end + break unless parent.class.where(:name => "#{@guess_a_new_name}").count > 0 + end + end + return @guess_a_new_name + end + + # Generate a new random PIN + # + def random_pin + if MINIMUM_PIN_LENGTH > 0 + (1..MINIMUM_PIN_LENGTH).map{|i| (0 .. 9).to_a.sample}.join + end + end + + # return the IP address (preferred) or hostname at which the + # current request arrived + def server_host + return ( + request.env['SERVER_ADDR'] || + request.env['SERVER_NAME'] || + request.env['HTTP_HOST'] + ) + end + + def have_https? + return Connectivity::port_open?( server_host(), 443 ) + end + + + def guess_local_ip_address + ret = nil + begin + ipsocket_addr_info = UDPSocket.open {|s| s.connect("255.255.255.254", 1); s.addr(false) } + ret = ipsocket_addr_info.last if ipsocket_addr_info + rescue + end + return ret + end + + def guess_local_host + ret = guess_local_ip_address() + if ! ret + begin + if request + ret = request.env['SERVER_NAME'] + end + rescue + end + end + if ret && [ + '', + 'localhost', + '127.0.0.1', + '0.0.0.0', + ].include?(ret) + ret = nil + end + return ret + end + + rescue_from CanCan::AccessDenied do |exception| + if @current_user + redirect_to root_url, :alert => 'Access denied! Please ask your admin to grant you the necessary rights.' + else + if Tenant.count == 0 && User.count == 0 + # This is a brand new system. We need to run a setup first. + redirect_to wizards_new_initial_setup_path + else + # You need to login first. + redirect_to log_in_path, :alert => 'Access denied! You need to login first.' + end + end + end + + private + + def current_user + begin + @current_user ||= User.find(session[:user_id]) if session[:user_id] + rescue ActiveRecord::RecordNotFound + session[:user_id] = nil + end + @current_user + end + + def go_to_setup_if_new_installation + if Rails.env != 'test' + if GemeinschaftSetup.all.count == 0 + redirect_to new_gemeinschaft_setup_path + end + end + end + + def home_breadcrumb + if current_user + if current_user && Tenant.find(current_user.current_tenant_id) + add_breadcrumb( current_user.current_tenant, tenant_path(current_user.current_tenant) ) + else + add_breadcrumb I18n.t('pages.controller.index.name'), :root_path + end + end + end + + def set_locale + if current_user && Language.find(current_user.language_id) + I18n.locale = current_user.language.code.downcase + else + logger.debug "* Accept-Language: #{request.env['HTTP_ACCEPT_LANGUAGE']}" + I18n.locale = request.compatible_language_from(Language.all.map{|x| x.code}) + end + logger.debug "* Locale set to '#{I18n.locale}'" + end + +end diff --git a/app/controllers/automatic_call_distributors_controller.rb b/app/controllers/automatic_call_distributors_controller.rb new file mode 100644 index 0000000..cc0c7e6 --- /dev/null +++ b/app/controllers/automatic_call_distributors_controller.rb @@ -0,0 +1,100 @@ +class AutomaticCallDistributorsController < ApplicationController + DEFAULT_STRATEGY = 'round_robin' + DEFAULT_MAX_CALLERS = 50 + DEFAULT_AGENT_TIMEOUT = 20 + DEFAULT_RETRY_TIMEOUT = 10 + DEFAULT_JOIN = 'agents_active' + DEFAULT_LEAVE = 'no_agents_active' + + load_resource :user + load_resource :tenant + load_and_authorize_resource :automatic_call_distributor, :through => [:user, :tenant ] + + before_filter :set_and_authorize_parent + before_filter :spread_breadcrumbs + + def index + @automatic_call_distributors = AutomaticCallDistributor.all + end + + def show + @automatic_call_distributor = AutomaticCallDistributor.find(params[:id]) + end + + def new + i = @parent.automatic_call_distributors.count + loop do + i += 1 + break unless @parent.automatic_call_distributors.where(:name => "#{t('automatic_call_distributors.name')} #{i}").count > 0 + end + @strategies = AutomaticCallDistributor::STRATEGIES.collect {|r| [ t("automatic_call_distributors.strategies.#{r.to_s}"), r.to_s ] } + @join_on = AutomaticCallDistributor::JOIN_ON.collect {|r| [ t("automatic_call_distributors.join_on.#{r.to_s}"), r.to_s ] } + @leave_on = AutomaticCallDistributor::LEAVE_ON.collect {|r| [ t("automatic_call_distributors.leave_on.#{r.to_s}"), r.to_s ] } + @automatic_call_distributor = @parent.automatic_call_distributors.build( + :name => "#{t('automatic_call_distributors.name')} #{i}", + :strategy => DEFAULT_STRATEGY, + :max_callers => DEFAULT_MAX_CALLERS, + :retry_timeout => DEFAULT_RETRY_TIMEOUT, + :agent_timeout => DEFAULT_AGENT_TIMEOUT, + :join => DEFAULT_JOIN, + :leave => DEFAULT_LEAVE, + ) + + end + + def create + @automatic_call_distributor = @parent.automatic_call_distributors.build(params[:automatic_call_distributor]) + if @automatic_call_distributor.save + m = method( :"#{@parent.class.name.underscore}_automatic_call_distributor_path" ) + redirect_to m.( @parent, @automatic_call_distributor ), :notice => t('automatic_call_distributors.controller.successfuly_created', :resource => @parent) + else + render :new + end + end + + def edit + @strategies = AutomaticCallDistributor::STRATEGIES.collect {|r| [ t("automatic_call_distributors.strategies.#{r.to_s}"), r.to_s ] } + @join_on = AutomaticCallDistributor::JOIN_ON.collect {|r| [ t("automatic_call_distributors.join_on.#{r.to_s}"), r.to_s ] } + @leave_on = AutomaticCallDistributor::LEAVE_ON.collect {|r| [ t("automatic_call_distributors.leave_on.#{r.to_s}"), r.to_s ] } + @automatic_call_distributor = AutomaticCallDistributor.find(params[:id]) + end + + def update + if @automatic_call_distributor.update_attributes(params[:automatic_call_distributor]) + m = method( :"#{@parent.class.name.underscore}_automatic_call_distributor_path" ) + redirect_to m.( @parent, @automatic_call_distributor ), :notice => t('automatic_call_distributors.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @automatic_call_distributor = AutomaticCallDistributor.find(params[:id]) + @automatic_call_distributor.destroy + m = method( :"#{@parent.class.name.underscore}_automatic_call_distributors_url" ) + redirect_to m.( @parent ), :notice => t('automatic_call_distributors.controller.successfuly_destroyed') + end + + private + def set_and_authorize_parent + @parent = @user || @tenant + authorize! :read, @parent + end + + def spread_breadcrumbs + if @user + add_breadcrumb t("users.index.page_title"), tenant_users_path(@user.current_tenant) + add_breadcrumb @user, tenant_user_path(@user.current_tenant, @user) + add_breadcrumb t("automatic_call_distributors.index.page_title"), user_automatic_call_distributors_path(@user) + if @automatic_call_distributor && !@automatic_call_distributor.new_record? + add_breadcrumb @automatic_call_distributor, user_automatic_call_distributor_path(@user, @automatic_call_distributor) + end + end + if @tenant + add_breadcrumb t("automatic_call_distributors.index.page_title"), tenant_automatic_call_distributors_path(@tenant) + if @automatic_call_distributor && !@automatic_call_distributor.new_record? + add_breadcrumb @automatic_call_distributor, tenant_automatic_call_distributor_path(@tenant, @automatic_call_distributor) + end + end + end +end diff --git a/app/controllers/call_forwards_controller.rb b/app/controllers/call_forwards_controller.rb new file mode 100644 index 0000000..5321b35 --- /dev/null +++ b/app/controllers/call_forwards_controller.rb @@ -0,0 +1,127 @@ +class CallForwardsController < ApplicationController + load_and_authorize_resource :phone_number + load_and_authorize_resource :call_forward, :through => [:phone_number] + + before_filter :spread_breadcrumbs + + class CallForwardingDestination + attr_accessor :id, :label + + def to_s + return label + end + end + + + def index + end + + def show + end + + def new + @call_forward = @phone_number.call_forwards.build + @call_forward.depth = DEFAULT_CALL_FORWARD_DEPTH + @call_forward.active = true + @call_forwarding_destinations = call_forwarding_destination_types() + @call_forward.destination = CALLFORWARD_DESTINATION_DEFAULT.to_s if defined?(CALLFORWARD_DESTINATION_DEFAULT) + + @available_call_forward_cases = [] + CallForwardCase.all.each do |available_call_forward_case| + if GuiFunction.display?("call_forward_case_#{available_call_forward_case.value}_field_in_call_forward_form", @current_user) + @available_call_forward_cases << available_call_forward_case + end + end + + if @phone_number.call_forwards.where( + :call_forward_case_id => CallForwardCase.find_by_value('noanswer').id, + :active => true + ).count == 0 + @call_forward.call_forward_case_id = CallForwardCase.find_by_value('noanswer').id + @call_forward.timeout = 45 + end + end + + def create + @call_forward = @phone_number.call_forwards.build( params[:call_forward] ) + + if @call_forward.save + redirect_to phone_number_call_forward_path( @phone_number, @call_forward ), :notice => t('call_forwards.controller.successfuly_created') + else + @available_call_forward_cases = CallForwardCase.all + render :new + end + end + + def edit + @available_call_forward_cases = CallForwardCase.all + @call_forwarding_destinations = call_forwarding_destination_types() + end + + def update + @available_call_forward_cases = CallForwardCase.all + if @call_forward.update_attributes(params[:call_forward]) + redirect_to phone_number_call_forward_path( @phone_number, @call_forward ), :notice => t('call_forwards.controller.successfuly_updated') + else + @call_forwarding_destinations = call_forwarding_destination_types() + render :edit + end + end + + def destroy + @call_forward.destroy + redirect_to phone_number_call_forwards_path( @phone_number ), :notice => t('call_forwards.controller.successfuly_destroyed') + end + + private + def spread_breadcrumbs + if @phone_number && @phone_number.phone_numberable_type == 'SipAccount' + @sip_account = @phone_number.phone_numberable + if @sip_account.sip_accountable_type == 'User' + @user = @phone_number.phone_numberable.sip_accountable + add_breadcrumb t("users.index.page_title"), tenant_users_path(@user.current_tenant) + add_breadcrumb @user, tenant_users_path(@user.current_tenant, @user) + add_breadcrumb t("sip_accounts.index.page_title"), user_sip_accounts_path(@user) + add_breadcrumb @sip_account, user_sip_account_path(@user, @sip_account) + end + if @sip_account.sip_accountable_type == 'Tenant' + @tenant = @sip_account.sip_accountable + add_breadcrumb t("sip_accounts.index.page_title"), tenant_sip_accounts_path(@tenant) + add_breadcrumb @sip_account, tenant_sip_account_path(@tenant, @sip_account) + end + add_breadcrumb t("phone_numbers.index.page_title"), sip_account_phone_numbers_path(@sip_account) + add_breadcrumb @phone_number, sip_account_phone_number_path(@sip_account, @phone_number) + add_breadcrumb t("call_forwards.index.page_title"), phone_number_call_forwards_path(@phone_number) + if @call_forward && !@call_forward.new_record? + add_breadcrumb @call_forward, phone_number_call_forward_path(@phone_number, @call_forward) + end + end + end + + def call_forwarding_destination_types + + phone_number_destination = CallForwardingDestination.new() + phone_number_destination.id = ':PhoneNumber' + phone_number_destination.label = 'Phone Number' + voice_mail_destination = CallForwardingDestination.new() + voice_mail_destination.id = ':Voicemail' + voice_mail_destination.label = 'Voice Mail' + + call_forwarding_destinations = [ + phone_number_destination, + voice_mail_destination, + ] + + if GuiFunction.display?('huntgroup_in_destination_field_in_call_forward_form', @current_user) + HuntGroup.all.each do |hunt_group| + hunt_group_destination = CallForwardingDestination.new() + hunt_group_destination.id = "#{hunt_group.id}:HuntGroup" + hunt_group_destination.label = "HuntGroup: #{hunt_group.to_s}" + call_forwarding_destinations.push(hunt_group_destination) + end + end + + return call_forwarding_destinations + end + +end diff --git a/app/controllers/call_histories_controller.rb b/app/controllers/call_histories_controller.rb new file mode 100644 index 0000000..f956f88 --- /dev/null +++ b/app/controllers/call_histories_controller.rb @@ -0,0 +1,100 @@ +class CallHistoriesController < ApplicationController + + load_resource :sip_account + + before_filter :set_and_authorize_parent + before_filter :spread_breadcrumbs + + before_filter { |controller| + if ! params[:type].blank? then + @type = params[:type].to_s + end + + if ! params[:page].blank? then + @pagination_page_number = params[:page].to_i + end + } + + def index + hunt_group_member_ids = PhoneNumber.where(:phone_numberable_type => 'HuntGroupMember', :number => @sip_account.phone_numbers.map {|a| a.number}).map {|a| a.phone_numberable_id} + hunt_group_ids = HuntGroupMember.where(:id => hunt_group_member_ids, :active => true).map {|a| a.hunt_group_id} + calls = CallHistory.where('(call_historyable_type = "SipAccount" AND call_historyable_id = ?) OR (call_historyable_type = "HuntGroup" AND call_historyable_id IN (?))', @sip_account.id, hunt_group_ids).order('start_stamp DESC') + + @call_histories = calls.paginate( + :page => @pagination_page_number, + :per_page => DEFAULT_PAGINATION_ENTRIES_PER_PAGE + ) + + @calls_count = calls.count + @calls_received_count = calls.where(:entry_type => 'received').count + @calls_dialed_count = calls.where(:entry_type => 'dialed').count + @calls_missed_count = calls.where(:entry_type => 'missed').count + @calls_forwarded_count = calls.where(:entry_type => 'forwarded').count + + if ! @type.blank? + @call_histories = @call_histories.where(:entry_type => @type) + end + end + + + def destroy + @call_history = CallHistory.where(:id => params[:id]).first + if can?(:destroy, @call_history) + @call_history.destroy + m = method( :"#{@parent.class.name.underscore}_call_histories_url" ) + redirect_to m.(), :notice => t('call_histories.controller.successfuly_destroyed') + end + end + + def destroy_multiple + if ! params[:selected_ids].blank? then + result = @sip_account.call_histories.where(:id => params[:selected_ids]).destroy_all(); + end + + m = method( :"#{@parent.class.name.underscore}_call_histories_url" ) + if result + redirect_to m.(), :notice => t('call_histories.controller.successfuly_destroyed') + else + redirect_to m.() + end + end + + def call + @call_history = CallHistory.where(:id => params[:id]).first + if can?(:call, @call_history) && @sip_account.registration + phone_number = @call_history.display_number + if ! phone_number.blank? + @sip_account.call(phone_number) + end + end + redirect_to(:back) + end + + private + def set_and_authorize_parent + @parent = @sip_account + + authorize! :read, @parent + + @show_path_method = method( :"#{@parent.class.name.underscore}_call_history_path" ) + @index_path_method = method( :"#{@parent.class.name.underscore}_call_histories_path" ) + @new_path_method = method( :"new_#{@parent.class.name.underscore}_call_history_path" ) + @edit_path_method = method( :"edit_#{@parent.class.name.underscore}_call_history_path" ) + end + + def spread_breadcrumbs + if @parent.class == SipAccount + if @sip_account.sip_accountable.class == User + add_breadcrumb t("#{@sip_account.sip_accountable.class.name.underscore.pluralize}.index.page_title"), method( :"tenant_#{@sip_account.sip_accountable.class.name.underscore.pluralize}_path" ).(@sip_account.tenant) + add_breadcrumb @sip_account.sip_accountable, method( :"tenant_#{@sip_account.sip_accountable.class.name.underscore}_path" ).(@sip_account.tenant, @sip_account.sip_accountable) + end + add_breadcrumb t("sip_accounts.index.page_title"), method( :"#{@sip_account.sip_accountable.class.name.underscore}_sip_accounts_path" ).(@sip_account.sip_accountable) + add_breadcrumb @sip_account, method( :"#{@sip_account.sip_accountable.class.name.underscore}_sip_account_path" ).(@sip_account.sip_accountable, @sip_account) + add_breadcrumb t("call_histories.index.page_title"), sip_account_call_histories_path(@sip_account) + if @call_history && !@call_history.new_record? + add_breadcrumb @call_history, sip_account_call_history_path(@sip_account, @call_history) + end + end + end + +end diff --git a/app/controllers/calls_controller.rb b/app/controllers/calls_controller.rb new file mode 100644 index 0000000..d5f3b42 --- /dev/null +++ b/app/controllers/calls_controller.rb @@ -0,0 +1,6 @@ +class CallsController < ApplicationController + + def index + @calls = Call.all + end +end diff --git a/app/controllers/callthroughs_controller.rb b/app/controllers/callthroughs_controller.rb new file mode 100644 index 0000000..f489622 --- /dev/null +++ b/app/controllers/callthroughs_controller.rb @@ -0,0 +1,75 @@ +class CallthroughsController < ApplicationController + load_and_authorize_resource :tenant + load_and_authorize_resource :callthrough, :through => [:tenant] + + before_filter :set_parent_and_path_methods + before_filter :spread_breadcrumbs + + def index + end + + def show + end + + def new + @callthrough = @tenant.callthroughs.build + @callthrough.name = generate_a_new_name(@tenant, @callthrough) + @callthrough.phone_numbers.build + @callthrough.access_authorizations.build(:name => "#{t('access_authorizations.name')} #{@callthrough.access_authorizations.count + 1}", :pin => random_pin).phone_numbers.build + @callthrough.whitelists.build.phone_numbers.build + end + + def create + @callthrough = @tenant.callthroughs.build(params[:callthrough]) + if @callthrough.save + redirect_to tenant_callthrough_path(@tenant, @callthrough), :notice => t('callthroughs.controller.successfuly_created') + else + @callthrough.phone_numbers.build if @callthrough.phone_numbers.size == 0 + render :new + end + end + + def edit + @callthrough.phone_numbers.build + @callthrough.access_authorizations.build.phone_numbers.build + if @callthrough.whitelisted_phone_numbers.count == 0 + if @callthrough.whitelists.count == 0 + @callthrough.whitelists.build.phone_numbers.build + else + @callthrough.whitelists.first.phone_numbers.build + end + end + end + + def update + if @callthrough.update_attributes(params[:callthrough]) + redirect_to tenant_callthrough_path(@tenant, @callthrough), :notice => t('callthroughs.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @callthrough.destroy + redirect_to tenant_callthroughs_path(@tenant), :notice => t('callthroughs.controller.successfuly_destroyed') + end + + private + + def set_parent_and_path_methods + @parent = @tenant + @show_path_method = method( :"#{@parent.class.name.underscore}_callthrough_path" ) + @index_path_method = method( :"#{@parent.class.name.underscore}_callthroughs_path" ) + @new_path_method = method( :"new_#{@parent.class.name.underscore}_callthrough_path" ) + @edit_path_method = method( :"edit_#{@parent.class.name.underscore}_callthrough_path" ) + end + + def spread_breadcrumbs + if @parent && @parent.class == Tenant + add_breadcrumb t("callthroughs.name").pluralize, tenant_callthroughs_path(@parent) + if @callthrough && !@callthrough.new_record? + add_breadcrumb @callthrough, tenant_callthrough_path(@parent, @callthrough) + end + end + end +end diff --git a/app/controllers/conference_invitees_controller.rb b/app/controllers/conference_invitees_controller.rb new file mode 100644 index 0000000..ce55b5a --- /dev/null +++ b/app/controllers/conference_invitees_controller.rb @@ -0,0 +1,93 @@ +class ConferenceInviteesController < ApplicationController + load_and_authorize_resource :conference + load_and_authorize_resource :conference_invitee, :through => [:conference] + + before_filter :spread_breadcrumbs + + def index + end + + def show + end + + def new + @conference_invitee = @conference.conference_invitees.build + @conference_invitee.speaker = true + @conference_invitee.moderator = false + @phone_number = @conference_invitee.build_phone_number + end + + def create + @conference_invitee = @conference.conference_invitees.build(params[:conference_invitee]) + + # Try to find this phone_number in phone_books the current_user can read. + # Save the found entry as phone_book_entry. + # + @conference_invitee.phone_number.parse_and_split_number! + phone_numbers = PhoneNumber.where(:number => @conference_invitee.phone_number.number). + where(:phone_numberable_type => 'PhoneBookEntry') + phone_numbers.each do |phone_number| + phone_book = phone_number.phone_numberable.phone_book + if can?(:read, phone_book) + @conference_invitee.phone_book_entry = phone_number.phone_numberable + break + end + end + + if @conference_invitee.save + # m = method( :"#{@parent_in_route.class.name.underscore}_path" ) + # redirect_to m.( @parent_in_route ), :notice => t('conference_invitees.controller.successfuly_created', :resource => @conference_invitees) + m = method( :"#{@conference_invitee.conference.conferenceable_type.underscore}_conference_path") + redirect_to m.( @conference_invitee.conference.conferenceable, @conference_invitee.conference), :notice => t('conference_invitees.controller.successfuly_created', :resource => @conference_invitees) + else + render :new + end + end + + def edit + authorize! :edit, @parent_in_route + end + + def update + if @conference_invitee.update_attributes(params[:conference_invitee]) + redirect_to @conference_invitee, :notice => t('conference_invitees.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @conference_invitee.destroy + redirect_to conference_invitees_url, :notice => t('conference_invitees.controller.successfuly_destroyed') + end + + private + + def spread_breadcrumbs + if @conference + @parent = @conference.conferenceable + if @parent && @parent.class == User + @user = @parent + add_breadcrumb t("users.index.page_title"), tenant_users_path(@user.current_tenant) + add_breadcrumb @user, tenant_users_path(@user.current_tenant, @user) + add_breadcrumb t("conferences.index.page_title"), user_conferences_path(@user) + if @conference && !@conference.new_record? + add_breadcrumb @conference, user_conference_path(@user, @conference) + end + end + if @parent && @parent.class == Tenant + @tenant = @parent + add_breadcrumb t("conferences.index.page_title"), tenant_conferences_path(@tenant) + if @conference && !@conference.new_record? + add_breadcrumb @conference, tenant_conference_path(@tenant, @conference) + end + end + + add_breadcrumb t("conference_invitees.index.page_title"), conference_conference_invitees_path(@conference) + if @conference_invitee && !@conference_invitee.new_record? + add_breadcrumb @conference_invitee, conference_conference_invitee_path(@conference, @conference_invitee) + end + end + end + +end diff --git a/app/controllers/conferences_controller.rb b/app/controllers/conferences_controller.rb new file mode 100644 index 0000000..302a23b --- /dev/null +++ b/app/controllers/conferences_controller.rb @@ -0,0 +1,82 @@ +class ConferencesController < ApplicationController + load_resource :user + load_resource :tenant + load_and_authorize_resource :conference, :through => [:user, :tenant] + + before_filter :set_and_authorize_parent + before_filter :spread_breadcrumbs + + def index + end + + def show + @phone_numbers = @conference.phone_numbers + end + + def new + @conference = @parent.conferences.build + @conference.name = generate_a_new_name(@parent, @conference) + @conference.start = nil + @conference.end = nil + @conference.open_for_anybody = true + @conference.max_members = DEFAULT_MAX_CONFERENCE_MEMBERS + @conference.pin = random_pin + + @conference.open_for_anybody = true + @conference.announce_new_member_by_name = true + @conference.announce_left_member_by_name = true + end + + def create + @conference = @parent.conferences.build(params[:conference]) + if @conference.save + m = method( :"#{@parent.class.name.underscore}_conference_path" ) + redirect_to m.( @parent, @conference ), :notice => t('conferences.controller.successfuly_created') + else + render :new + end + end + + def edit + end + + def update + if @conference.update_attributes(params[:conference]) + m = method( :"#{@parent.class.name.underscore}_conference_path" ) + redirect_to m.( @parent, @conference ), :notice => t('conferences.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @conference.destroy + m = method( :"#{@parent.class.name.underscore}_conferences_url" ) + redirect_to m.( @parent ), :notice => t('conferences.controller.successfuly_destroyed') + end + + private + + def set_and_authorize_parent + @parent = @tenant || @user + authorize! :read, @parent + end + + def spread_breadcrumbs + if @parent && @parent.class == User + add_breadcrumb t("users.index.page_title"), tenant_users_path(@user.current_tenant) + add_breadcrumb @user, tenant_user_path(@user.current_tenant, @user) + add_breadcrumb t("conferences.index.page_title"), user_conferences_path(@user) + if @conference && !@conference.new_record? + add_breadcrumb @conference, user_conference_path(@user, @conference) + end + end + if @parent && @parent.class == Tenant + add_breadcrumb t("conferences.index.page_title"), tenant_conferences_path(@tenant) + if @conference && !@conference.new_record? + add_breadcrumb @conference, tenant_conference_path(@tenant, @conference) + end + end + end + +end diff --git a/app/controllers/config_polycom_controller.rb b/app/controllers/config_polycom_controller.rb new file mode 100644 index 0000000..9d44e51 --- /dev/null +++ b/app/controllers/config_polycom_controller.rb @@ -0,0 +1,330 @@ +class ConfigPolycomController < ApplicationController + MAX_SIP_ACCOUNTS_COUNT = 11 + MAX_SOFTKEYS_COUNT = 12 + MAX_DIRECTORY_ENTRIES = 20 + SIP_DEFAULT_PORT = 5060 + + skip_authorization_check + + before_filter { |controller| + if ! params[:mac_address].blank? then + @mac_address = params[:mac_address].upcase.gsub(/[^0-9A-F]/,'') + @phone = Phone.where({ :mac_address => @mac_address }).first + elsif ! params[:phone].blank? then + @phone = Phone.where({ :id => params[:phone].to_i }).first + end + + if ! @phone + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- Phone not found -->", + ) + return false + end + + if ! params[:sip_account].blank? + @sip_account = @phone.sip_accounts.where({ :id => params[:sip_account].to_i }).first + if ! @sip_account + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- SipAccount not found -->", + ) + return false + end + end + + if ! params[:type].blank? + @type = params[:type].to_s.strip.downcase + end + } + + def config_files + if params[:mac_address].blank? then + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- MAC not specified -->", + ) + end + + respond_to { |format| + format.any { + self.formats = [ :xml ] + render + } + } + end + + def settings + if ! request.env['HTTP_USER_AGENT'].index('polycom') + Rails.logger.info "---> User-Agent indicates not a Polycom phone (#{request.env['HTTP_USER_AGENT'].inspect})" + else + Rails.logger.info "---> Phone #{@mac_address.inspect}, IP address #{request.remote_ip.inspect}" + @phone.update_attributes({ :ip_address => request.remote_ip }) + end + + xml_applications_url = "#{request.protocol}#{request.host_with_port}/config_polycom/#{@phone.id}/0" + + @settings = { + 'device.sntp.serverName' => 'pool.ntp.org', + 'device.sntp.gmtOffset' => 3600, + 'up.welcomeSoundOnWarmBootEnabled' => 0, + 'up.welcomeSoundEnabled' => 0, + 'bg.hiRes.color.selection' => '2,1', + 'msg.mwi.1.callBackMode' => 'contact', + 'msg.mwi.1.callBack' => 'f-vmcheck', + 'feature.enhancedFeatureKeys.enabled' => 1, + 'softkey.feature.basicCallManagement.redundant' => 0, + 'softkey.feature.buddies' => 0, + 'softkey.feature.callers' => 0, + 'softkey.feature.directories' => 0, + 'softkey.feature.endcall' => 1, + 'softkey.feature.forward' => 0, + 'softkey.feature.mystatus' => 0, + 'softkey.feature.newcall' => 0, + 'call.directedCallPickupMethod' => 'legacy', + 'call.directedCallPickupString' => 'f-ia-', + 'call.advancedMissedCalls.enabled' => 0, + 'lineKey.reassignment.enabled' => 1, + 'lineKey.1.category' => 'Line', + 'lineKey.1.index' => 1, + } + + for key_index in 2..42 + @settings["lineKey.#{key_index}.category"] = 'Unassigned' + end + + for ring_class in 1..17 + @settings["se.rt.custom#{ring_class}.name"] = "Ringer#{ring_class-1}" + @settings["se.rt.custom#{ring_class}.ringer"] = "ringer#{ring_class}" + end + @settings["se.rt.custom1.type"] = 'visual' + + for ring_class in 1..17 + @settings["voIpProt.SIP.alertInfo.#{ring_class}.class"] = "custom#{ring_class}" + @settings["voIpProt.SIP.alertInfo.#{ring_class}.value"] = "Ringer#{ring_class-1}" + end + + softkey_index = 1 + blf_index = 0 + + @phone.sip_accounts.each do |sip_account| + sip_account_index = 0 + if (sip_account.sip_accountable_type == @phone.phoneable_type) and (sip_account.sip_accountable_id == @phone.phoneable_id) + sip_account_index += 1 + if sip_account_index == 1 + xml_applications_url = "#{request.protocol}#{request.host_with_port}/config_polycom/#{@phone.id}/#{sip_account.id}" + @settings['voIpProt.SIP.outboundProxy.address'] = sip_account.host + @settings['voIpProt.SIP.outboundProxy.port'] = SIP_DEFAULT_PORT + end + + @settings["reg.#{sip_account_index}.address"] = "#{sip_account.auth_name}@#{sip_account.host}" + @settings["reg.#{sip_account_index}.auth.password"] = sip_account.password + @settings["reg.#{sip_account_index}.auth.userId"] = sip_account.auth_name + @settings["reg.#{sip_account_index}.displayName"] = 'Call' + @settings["reg.#{sip_account_index}.label"] = sip_account.caller_name + @settings["voIpProt.server.#{sip_account_index}.address"] = sip_account.host + @settings["voIpProt.server.#{sip_account_index}.port"] = SIP_DEFAULT_PORT + @settings["call.missedCallTracking.#{sip_account_index}.enabled"] = 0 + + sip_account.softkeys.order(:position).each do |softkey| + softkey_index += 1 + if softkey.softkey_function + softkey_function = softkey.softkey_function.name + end + case softkey_function + when 'blf' + blf_index += 1 + @settings["lineKey.#{softkey_index}.category"] = 'BLF' + @settings["attendant.resourceList.#{blf_index}.address"] = "#{softkey.number}@#{sip_account.host}" + @settings["attendant.resourceList.#{blf_index}.label"] = softkey.label + end + end + end + end + + @settings['mb.idleDisplay.home'] = "#{xml_applications_url}/idle_screen.xml" + @settings['mb.idleDisplay.refresh'] = 60 + + @settings['efk.efklist.1.mname'] = "directory" + @settings['efk.efklist.1.status'] = 1 + @settings['efk.efklist.1.action.string'] = "#{xml_applications_url}/phone_book.xml" + @settings['efk.efklist.2.mname'] = "callhistory" + @settings['efk.efklist.2.status'] = 1 + @settings['efk.efklist.2.action.string'] = "#{xml_applications_url}/call_history.xml" + @settings['efk.efklist.3.mname'] = "applications" + @settings['efk.efklist.3.status'] = 1 + @settings['efk.efklist.3.action.string'] = "#{xml_applications_url}/applications.xml" + + @settings['softkey.1.action'] = "#{xml_applications_url}/phone_book.xml" + @settings['softkey.1.enable'] = 1 + @settings['softkey.1.insert'] = 1 + @settings['softkey.1.label'] = 'Directory' + @settings['softkey.1.precede'] = 1 + @settings['softkey.1.use.active'] = 1 + @settings['softkey.1.use.alerting'] = 0 + @settings['softkey.1.use.dialtone'] = 1 + @settings['softkey.1.use.hold'] = 1 + @settings['softkey.1.use.idle'] = 1 + @settings['softkey.1.use.proceeding'] = 0 + @settings['softkey.1.use.setup'] = 0 + @settings['softkey.2.action'] = "#{xml_applications_url}/call_history.xml" + @settings['softkey.2.enable'] = 1 + @settings['softkey.2.insert'] = 2 + @settings['softkey.2.label'] = 'Call History' + @settings['softkey.2.precede'] = 1 + @settings['softkey.2.use.active'] = 1 + @settings['softkey.2.use.alerting'] = 0 + @settings['softkey.2.use.dialtone'] = 1 + @settings['softkey.2.use.hold'] = 1 + @settings['softkey.2.use.idle'] = 1 + @settings['softkey.2.use.proceeding'] = 0 + @settings['softkey.2.use.setup'] = 0 + + respond_to { |format| + format.any { + self.formats = [ :xml ] + render + } + } + end + + + def settings_directory + respond_to { |format| + format.any { + self.formats = [ :xml ] + render + } + } + end + + + def call_history + + if ! @sip_account + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- SipAccount not found -->", + ) + return + end + + if ['dialed', 'missed', 'received'].include? @type + @phone_xml_object = { + :name => "call_history", + :title => @type.titleize, + :entries => [] + } + + calls = @sip_account.call_histories.where(:entry_type => @type).order('start_stamp DESC').limit(MAX_DIRECTORY_ENTRIES) + + calls.each do |call| + display_name = call.display_name + phone_number = call.display_number + phone_book_entry = call.phone_book_entry_by_number(phone_number) + if display_name.blank? + display_name = phone_book_entry.to_s + end + + @phone_xml_object[:entries].push({ + :selected => false, + :number => phone_number, + :date => call.start_stamp.strftime('%d.%m %H:%M'), + :text => display_name, + :url => "tel:#{phone_number}", + }) + end + else + base_url = "#{request.protocol}#{request.host_with_port}#{request.fullpath.split("?")[0]}" + @phone_xml_object = { + :name => 'call_history_menu', + :title => 'Call History Lists', + :entries => [ + {:text => 'Missed Calls', :url => "#{base_url}?&type=missed", :selected => false}, + {:text => 'Received Calls', :url => "#{base_url}?&type=received", :selected => false}, + {:text => 'Placed Calls', :url => "#{base_url}?&type=dialed", :selected => false}, + ] + } + end + + respond_to { |format| + format.any { + self.formats = [ :xml ] + render :action => "_#{@phone_xml_object[:name]}", :content_type => Mime::HTML + } + } + + end + + + def phone_book + + if ! @sip_account + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- SipAccount not found -->", + ) + return + end + + @phone_xml_object = { + :name => 'phone_book', + :title => "Phone Book".strip, + :entries => [], + :softkeys => [], + } + + phone_books = Array.new() + phone_books = phone_books + @sip_account.sip_accountable.try(:phone_books).all + if @sip_account.sip_accountable.class == User + phone_books = phone_books + @sip_account.sip_accountable.try(:current_tenant).try(:phone_books).all + end + + phone_book_ids = Array.new() + phone_books.each do |phone_book| + phone_book_ids << phone_book.id + end + + PhoneBookEntry.where(:phone_book_id => phone_book_ids).order(:last_name).order(:first_name).limit(MAX_DIRECTORY_ENTRIES).each do |phone_book_entry| + phone_numbers_count = 0 + phone_book_entry.phone_numbers.each do |phone_number| + phone_numbers_count += 1 + if phone_numbers_count > 1 + entry_name = '' + else + entry_name = phone_book_entry.to_s + end + + @phone_xml_object[:entries] << { :text => entry_name, :type => phone_number.name, :number => phone_number.number, :url => "tel:#{phone_number.number}" } + end + end + + respond_to { |format| + format.any { + self.formats = [ :xml ] + render :action => "_#{@phone_xml_object[:name]}", :content_type => Mime::HTML + } + } + + end + + + def idle_screen + respond_to { |format| + format.any { + self.formats = [ :xml ] + render :action => "idle_screen", :content_type => Mime::HTML + } + } + end +end diff --git a/app/controllers/config_siemens_controller.rb b/app/controllers/config_siemens_controller.rb new file mode 100644 index 0000000..f398b1a --- /dev/null +++ b/app/controllers/config_siemens_controller.rb @@ -0,0 +1,1239 @@ +require 'nokogiri' +#doc.search('Message/ItemList').each do |a| puts a.children end +class ConfigSiemensController < ApplicationController +#TODO Authentication + # No access for admins though as this contains personal data. + + # We can't use load_and_authorize_resource() here because + # ConfigSiemensController isn't a resource. + # We can try client certificates + MAX_DIRECTORY_ENTRIES = 20 + + skip_authorization_check + + before_filter { |controller| + if ! params[:phone].blank? + @phone = Phone.where({ :id => params[:phone].to_i }).first + end + + if ! params[:sip_account].blank? + @sip_account = SipAccount.where({ :id => params[:sip_account].to_i }).first + end + + if ! @sip_account && @phone + @sip_account = @phone.sip_accounts.where(:sip_accountable_id => @phone.phoneable_id, :sip_accountable_type => @phone.phoneable_type).first + end + } + + + def index + os40_keys=6 + os60_keys=8 + os80_keys=9 + doc = Nokogiri::XML(request.body.read) + #logger.debug("#{params[:WorkpointMessage].to_xml}") + #logger.debug("#{params[:WorkpointMessage][:Message][:ItemList].to_xml}") + @phone_items=Hash.new + contact_reason = params[:WorkpointMessage][:Message][:ReasonForContact] + fragment = params[:WorkpointMessage][:Message][:fragment] + + reply_status = doc.search('Message/ReasonForContact').first[:status] + reply_action = doc.search('Message/ReasonForContact').first[:action] + + doc.search('Message/ItemList/Item').each do |post_item| + @phone_items[post_item[:name]]=post_item.children.to_s + end + if @phone_items['mac-addr'] + mac_address = @phone_items['mac-addr'] + end + phone_type = @phone_items['device-type'] + if phone_type == "OpenStage 40" + max_keys = (os40_keys) * 2 + elsif phone_type == "OpenStage 60" + max_keys = (os60_keys) * 2 + elsif phone_type == "OpenStage 80" + max_keys = (os80_keys) * 2 + else + max_keys = 0 + end + + blf_keys_max = max_keys / 2 + shift_key_position = blf_keys_max + + #logger.debug(request.body.read) + if mac_address + @phone = Phone.find_by_mac_address(mac_address.gsub(':','').upcase) + + if ! @phone && PROVISIONING_AUTO_ADD_PHONE + tenant = Tenant.where(:id => PROVISIONING_AUTO_TENANT_ID).first + if ! tenant + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- Tenant not found -->", + ) + return + end + + @phone = tenant.phones.build + @phone.mac_address = mac_address + @phone.hot_deskable = true + @phone.phone_model = PhoneModel.where('name LIKE ?', "#{phone_type}").first + if ! @phone.save + render( + :status => 500, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- #{@phone.errors.messages.inspect} -->", + ) + return + end + + if ! PROVISIONING_AUTO_ADD_SIP_ACCOUNT + return + end + + caller_name_index = 0 + sip_account_last = tenant.sip_accounts.where('caller_name LIKE ?', "#{PROVISIONING_AUTO_SIP_ACCOUNT_CALLER_PREFIX}%").sort { |item1, item2| + item1.caller_name.gsub(/[^0-9]/, '').to_i <=> item2.caller_name.gsub(/[^0-9]/, '').to_i + }.last + + if sip_account_last + caller_name_index = sip_account_last.caller_name.gsub(/[^0-9]/, '').to_i + end + caller_name_index = caller_name_index + 1 + + @sip_account = tenant.sip_accounts.build + @sip_account.caller_name = "#{PROVISIONING_AUTO_SIP_ACCOUNT_CALLER_PREFIX}#{caller_name_index}" + @sip_account.call_waiting = CALL_WAITING + @sip_account.clir = DEFAULT_CLIR_SETTING + @sip_account.clip = DEFAULT_CLIP_SETTING + @sip_account.voicemail_pin = random_pin + @sip_account.callforward_rules_act_per_sip_account = CALLFORWARD_RULES_ACT_PER_SIP_ACCOUNT_DEFAULT + @sip_account.hotdeskable = false + loop do + @sip_account.auth_name = SecureRandom.hex(DEFAULT_LENGTH_SIP_AUTH_NAME) + break unless SipAccount.exists?(:auth_name => @sip_account.auth_name) + end + @sip_account.password = SecureRandom.hex(DEFAULT_LENGTH_SIP_PASSWORD) + + if ! @sip_account.save + render( + :status => 500, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- #{@sip_account.errors.messages.inspect} -->", + ) + return + end + + phone_sip_account = PhoneSipAccount.new() + phone_sip_account.phone_id = @phone.id + phone_sip_account.sip_account_id = @sip_account.id + + if ! phone_sip_account.save + render( + :status => 500, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- #{phone_sip_account.errors.messages.inspect} -->", + ) + return + end + end + end + + + country = 'US' + language = 'en' + if ! @phone.nil? + @phone.update_attributes(:ip_address => request.remote_ip) + @sip_account = @phone.sip_accounts.where(:sip_accountable_type => @phone.phoneable_type, + :sip_accountable_id => @phone.phoneable_id).first + + if @phone.phoneable + if @phone.phoneable_type == 'Tenant' + tenant = @phone.phoneable + language = tenant.language.code + elsif @phone.phoneable_type == 'User' + language = @phone.phoneable.language.code + tenant = @phone.phoneable.current_tenant + end + end + + if tenant && tenant.country + country_map = { + '61' => 'AU', # Australia + '43' => 'AT', # Austria + '86' => 'CN', # China + '45' => 'DK', # Denmark + '33' => 'FA', # France + '49' => 'DE', # Germany + '44' => 'GB', # Great Britain + '91' => 'IN', # India + '39' => 'IT', # Italy + '81' => 'JP', # Japan + '52' => 'MX', # Mexico + '31' => 'NL', # Netherlands + '47' => 'NO', # Norway + '64' => 'NZ', # New Zealand + '34' => 'ES', # Spain + '46' => 'SE', # Sweden + '41' => 'CH', # Switzerland + } + if country_map.include?(tenant.country.country_code) + country = country_map[tenant.country.country_code] + end + end + end + + if ! @phone.nil? && ! @sip_account.blank? + #logger.debug(@phone_items) + @my_nonce = params[:WorkpointMessage][:Message][:nonce] + @new_settings = Array.new + + @new_settings << ['dhcp', nil, 'true'] + @new_settings << ['hostname', nil, mac_address.gsub(':', '') ] + #@new_settings << ['e164-hostname', nil, 'false'] + @new_settings << ['mobility-enabled', nil, 'false'] + @new_settings << ['mobility-password-on-logoff', nil, 'false'] + @new_settings << ['e164', nil, @sip_account.auth_name] + @new_settings << ['sip-user-id', nil, @sip_account.auth_name] + #Not supported + #@new_settings << ['reg-id', nil, @sip_account.auth_name] + #@new_settings << ['reg-number', nil, @sip_account.auth_name] + #@new_settings << ['fully-qualified-phone-no', nil, @sip_account.auth_name] + @new_settings << ['sip-pwd', nil, @sip_account.password] + @new_settings << ['sip-name', nil, @sip_account.caller_name] + @new_settings << ['register-by-name', nil, 'false'] + @new_settings << ['display-id-unicode', nil, @sip_account.caller_name] + @new_settings << ['use-display-id', nil, 'true'] + @new_settings << ['reg-addr', nil, @sip_account.sip_domain.host] + @new_settings << ['reg-port', nil, '5060'] + @new_settings << ['registrar-addr', nil, @sip_account.sip_domain.host] + @new_settings << ['registrar-port', nil, '5060'] + #@new_settings << ['outbound-proxy', nil, @sip_account.sip_domain.host] + @new_settings << ['outbound-proxy-user', nil, @sip_account.auth_name] + #@new_settings << ['sgnl-gateway-addr', nil, @sip_account.sip_domain.host] + #@new_settings << ['sgnl-gateway-port', nil, '5060' ] + @new_settings << ['sgnl-gateway-addr-user', nil, @sip_account.sip_domain.host] + @new_settings << ['sgnl-gateway-port-user', nil, '5060'] + @new_settings << ['sgnl-route', nil, '0' ] + @new_settings << ['mwi-e164', nil, '' ] + @new_settings << ['rtp-base-port', nil, '5004'] + @new_settings << ['default-domain', nil, '' ] + #@new_settings << ['sip-transport', nil, '0' ] + @new_settings << ['sip-transport-user', nil, '0' ] + @new_settings << ['server-type', nil, '0' ] + @new_settings << ['session-timer', nil, 'false' ] + @new_settings << ['session-duration', nil, '3600' ] + @new_settings << ['reg-ttl', nil, '3600' ] + @new_settings << ['realm', nil, @sip_account.sip_domain.realm] + @new_settings << ['emergency-e164', nil, '0110' ] + @new_settings << ['voice-mail-e164', nil, 'f-vmcheck'] + @new_settings << ['auto-answer', nil, 'false'] + @new_settings << ['beep-on-auto-answer', nil, 'true'] + @new_settings << ['auto-reconnect', nil, 'false' ] + @new_settings << ['beep-on-auto-reconnect', nil, 'true'] + #@new_settings << ['permit-decline-call', nil, 'true'] + @new_settings << ['transfer-on-ring', nil, 'false' ] + @new_settings << ['join-allowed-in-conference', nil, 'true'] + @new_settings << ['pickup-group-uri', nil, "f-ig-#{@sip_account.id}"] + @new_settings << ['pickup-group-uri', nil, '' ] + @new_settings << ['hot-line-warm-line-digits', nil, '' ] + @new_settings << ['initial-digit-timer', nil, '30' ] + @new_settings << ['conference-factory-uri', nil, ''] + @new_settings << ['callback-busy-allow', nil, 'false'] + @new_settings << ['callback-busy-code', nil, '' ] + @new_settings << ['callback-ring-allow', nil, 'false'] + @new_settings << ['callback-ring-code', nil, ''] + @new_settings << ['callback-cancel-code', nil, ''] + @new_settings << ['park-server', nil, ''] + #OPTIMIZE Callwaiting + @new_settings << ['call-waiting-enabled', nil, 'true'] + @new_settings << ['qos-layer2', nil, 'true'] + @new_settings << ['l2qos-voice', nil, '5' ] + @new_settings << ['l2qos-signalling', nil, '3' ] + @new_settings << ['l2qos-default', nil, '0'] + @new_settings << ['qos-layer3', nil, 'true'] + @new_settings << ['l3qos-voice', nil, '46'] + @new_settings << ['l3qos-signalling', nil, '26'] + @new_settings << ['vlan-method', nil, '1'] + #OPTIMIZE Timezone settings + @new_settings << ['time', nil, Time.new.localtime.to_i] + @new_settings << ['sntp-addr', nil, 'NULL'] + @new_settings << ['sntp-tz-offset', nil, (Time.new.utc_offset/60).to_i] + @new_settings << ['daylight-save', nil, 'true'] + @new_settings << ['daylight-save-minutes', nil, '0'] + @new_settings << ['auto-daylight-save', nil, 'true'] + @new_settings << ['daylight-save-zone-id', nil, '9'] + #OPTIMIZE Use SNMP? + @new_settings << ['snmp-trap-addr', nil, 'NULL'] + @new_settings << ['snmp-trap-port', nil, '162'] + @new_settings << ['snmp-trap-pwd', nil, 'snmp' ] + @new_settings << ['snmp-traps-active', nil, 'false' ] + @new_settings << ['diagnostic-trap-addr', nil, 'NULL'] + @new_settings << ['diagnostic-trap-port', nil, '162'] + @new_settings << ['diagnostic-trap-pwd', nil, 'snmp' ] + @new_settings << ['diagnostic-traps-active', nil, 'false' ] + @new_settings << ['diagnostic-snmp-active', nil, 'false'] + @new_settings << ['qdc-collection-unit-addr', nil, 'NULL'] + @new_settings << ['qdc-collection-unit-port', nil, '12010'] + + @new_settings << ['qdc-trap-pwd', nil, 'QOSDC'] + @new_settings << ['qdc-snmp-active', nil, 'false'] + @new_settings << ['qdc-qcu-active', nil, 'false'] + @new_settings << ['snmp-queries-allowed', nil, 'false'] + #@new_settings << ['snmp-pwd', nil, ''] + @new_settings << ['disable-microphone', nil, 'false'] + @new_settings << ['loudspeech-enabled', nil, 'true'] + @new_settings << ['audio-silence-suppression', nil, 'false'] + + @new_settings << ['port1', nil, '0' ] # 0=Automatic (speed) + @new_settings << ['port2', nil, '0' ] + @new_settings << ['port2-mode', nil, '1' ] + @new_settings << ['port2-auto-mdix-enabled', nil, 'true' ] + @new_settings << ['originating-line-preference', nil, '0'] + @new_settings << ['terminating-line-preference', nil, '0'] + @new_settings << ['line-key-operating-mode', nil, '0'] + @new_settings << ['line-rollover-type', nil, '2'] + @new_settings << ['line-rollover-volume', nil, '5' ]# 1-5 + @new_settings << ['line-registration-leds', nil, 'true'] + @new_settings << ['keyset-use-focus', nil, 'true' ] + @new_settings << ['keyset-remote-forward-ind', nil, 'true'] + @new_settings << ['keyset-reservation-timer', nil, '60' ] # 0-300 + @new_settings << ['dial-plan-enabled', nil, 'false' ] + @new_settings << ['Canonical-dialing-international-prefix', nil, ''] + @new_settings << ['Canonical-dialing-local-country-code', nil, ''] + @new_settings << ['Canonical-dialing-national-prefix', nil, ''] + @new_settings << ['Canonical-dialing-local-area-code', nil, ''] + @new_settings << ['Canonical-dialing-local-node', nil, ''] + @new_settings << ['Canonical-dialing-external-access', nil, '0'] + @new_settings << ['Canonical-dialing-operator-code', nil, ''] + @new_settings << ['Canonical-dialing-emergency-number', nil, ''] + @new_settings << ['Canonical-dialing-dial-needs-access-code', nil, '0'] + @new_settings << ['Canonical-dialing-dial-needs-intGWcode', nil, '1'] + @new_settings << ['Canonical-dialing-min-local-number-length', nil, '10'] + @new_settings << ['Canonical-dialing-extension-initial-digits', nil, ''] + @new_settings << ['Canonical-dialing-dial-internal-form', nil, '0' ] + @new_settings << ['Canonical-dialing-dial-external-form', nil, '0' ] + #@new_settings << ['Canonical-lookup-local-code', nil, '' ] + #@new_settings << ['Canonical-lookup-international-code', nil, ''] + @new_settings << ['hot-keypad-dialing', nil, 'false'] + @new_settings << ['ldap-transport', nil, '0'] + @new_settings << ['ldap-server-address', nil, 'NULL' ] + @new_settings << ['ldap-server-port', nil, '389' ] + @new_settings << ['ldap-authentication', nil, '1'] + @new_settings << ['ldap-user', nil, 'NULL' ] + @new_settings << ['ldap-pwd', nil, 'NULL' ] + @new_settings << ['ldap-max-responses', nil, '25'] + @new_settings << ['backup-addr', nil, 'NULL'] + @new_settings << ['backup-registration', nil, 'false'] + @new_settings << ['qdc-qcu-active', nil, 'false' ] + @new_settings << ['min-admin-passw-length', nil, '6' ] + #@new_settings << ['default-locked-config-menus', nil, 'true' ] + @new_settings << ['locked-config-menus', nil, 'true' ] + #@new_settings << ['default-locked-local-function-menus', nil, 'true' ] + @new_settings << ['locked-local-function-menus', nil, 'true' ] + #@new_settings << ['dls-mode-secure', nil, '0' ] + @new_settings << ['dls-chunk-size', nil, '9492'] + #@new_settings << ['default-passw-policy', nil, 'false'] + @new_settings << ['deflect-destination', nil, ''] + @new_settings << ['display-skin', nil, (@phone.phoneable_type == 'User' ? '0' : '1')] + @new_settings << ['enable-bluetooth-interface', nil, 'true'] + @new_settings << ['usb-access-enabled', nil, 'false' ] + @new_settings << ['usb-backup-enabled', nil, 'false' ] + @new_settings << ['line-button-mode', nil, '0' ] + @new_settings << ['lock-forwarding', nil, '' ] + @new_settings << ['loudspeaker-function-mode', nil, '0' ] + @new_settings << ['max-pin-retries', nil, '10' ] + @new_settings << ['screensaver-enabled', nil, 'false' ] + @new_settings << ['inactivity-timeout', nil, '60' ] + @new_settings << ['not-used-timeout', nil, '5' ] + @new_settings << ['passw-char-set', nil, '0' ] + @new_settings << ['refuse-call', nil, 'true' ] + @new_settings << ['restart-password', nil, '124816'] + #OPTIMIZE clock format + @new_settings << ['time-format', nil, '0' ]# 1=12 h + @new_settings << ['uaCSTA-enabled', nil, 'false' ] + @new_settings << ['enable-test-interface', nil, 'false'] + @new_settings << ['enable-WBM', nil, 'true'] + #@new_settings << ['pixelsaver-timeout', nil, '2' ]# 2 hours? + #@new_settings << ['voice-message-dial-tone', nil, '' ] + #@new_settings << ['call-pickup-allowed', nil, 'true' ] + @new_settings << ['group-pickup-tone-allowed', nil, 'true'] + @new_settings << ['group-pickup-as-ringer', nil, 'false'] + @new_settings << ['group-pickup-alert-type', nil, '0' ] + #@new_settings << ['default-profile', nil, '' ] + @new_settings << ['count-medium-priority', nil, '5'] + @new_settings << ['timer-medium-priority', nil, '60'] # 1 - 999 + @new_settings << ['timer-high-priority', nil, '5'] # 0 - 999 + @new_settings << ['dss-sip-detect-timer', nil, '10'] + @new_settings << ['dss-sip-deflect', nil, 'false' ] + @new_settings << ['dss-sip-refuse', nil, 'false' ] + @new_settings << ['feature-availability', nil, 'false'] + @new_settings << ['feature-availability', nil, 'true' ] + @new_settings << ['feature-availability', nil, 'true' ] + @new_settings << ['local-control-feature-availability', nil, 'false' ] + @new_settings << ['trace-level', nil, '0' ] # Off + #@new_settings << ['default-locked-function-keys', nil, 'true' ]# "unknown item" + @new_settings << ['blf-code', nil, 'f-ia-'] # pickup prefix for softkey function 59 (BLF) + @new_settings << ['stimulus-feature-code', nil, true] + @new_settings << ['stimulus-led-control-uri', nil, true] + + @new_settings << ['min-user-passw-length', nil, '6' ]# 6 - 24 + #OPTIMIZE language + @new_settings << ['country-iso', nil, country ] + @new_settings << ['language-iso', nil, language] + @new_settings << ['date-format', nil, '0' ] # DD.MM.YYYY + #OPTIMIZE ringtones + @new_settings << ['ringer-melody', nil, '1'] + + @new_settings << ['ringer-melody', nil, '2'] + @new_settings << ['ringer-tone-sequence', nil, '2'] + + 1.upto(8) do |index| + @new_settings << ['alert', index, "Ringer#{index}^#{index}^2^60"] + end + @new_settings << ['alert', 9, "Ringer9^1^1^60"] + @new_settings << ['alert', 10, "Ringer10^1^3^60"] + @new_settings << ['alert', 11, "Ringer0^0^2^60"] + + #Applications + @new_settings << ['XML-app-name', 1, 'call_history'] + @new_settings << ['XML-app-control-key', 1, '3'] + @new_settings << ['XML-app-action', 1, 'update'] + @new_settings << ['XML-app-display-name', 1, 'Call History'] + @new_settings << ['XML-app-program-name', 1, "config_siemens/#{@phone.id}/call_history.xml"] + @new_settings << ['XML-app-special-instance', 1, '3'] + @new_settings << ['XML-app-server-addr', 1, request.host] + @new_settings << ['XML-app-server-port', 1, '80'] + @new_settings << ['XML-app-transport', 1, '0'] + @new_settings << ['XML-app-proxy-enabled', 1, 'false'] + @new_settings << ['XML-app-remote-debug', 1, 'false'] + @new_settings << ['XML-app-debug-prog-name', 1, ''] + @new_settings << ['XML-app-num-tabs', 1, '3'] + @new_settings << ['XML-app-restart', 1, 'true'] + @new_settings << ['XML-app-auto-start', 1, 'true'] + @new_settings << ['XML-app-tab1-display-name', 1, 'Missed'] + @new_settings << ['XML-app-tab1-name', 1, 'call_history'] + @new_settings << ['XML-app-tab2-display-name', 1, 'Received'] + @new_settings << ['XML-app-tab2-name', 1, 'call_history_received'] + @new_settings << ['XML-app-tab3-display-name', 1, 'Dialed'] + @new_settings << ['XML-app-tab3-name', 1, 'call_history_dialed'] + + @new_settings << ['XML-app-name', 2, 'menu'] + @new_settings << ['XML-app-control-key', 2, '6'] + @new_settings << ['XML-app-action', 2, 'update'] + @new_settings << ['XML-app-display-name', 2, 'Menu'] + @new_settings << ['XML-app-program-name', 2, "config_siemens/#{@phone.id}/menu.xml"] + @new_settings << ['XML-app-special-instance', 2, '0'] + @new_settings << ['XML-app-server-addr', 2, request.host] + @new_settings << ['XML-app-server-port', 2, '80'] + @new_settings << ['XML-app-transport', 2, '0'] + @new_settings << ['XML-app-proxy-enabled', 2, 'false'] + @new_settings << ['XML-app-remote-debug', 2, 'false'] + @new_settings << ['XML-app-debug-prog-name', 2, ''] + @new_settings << ['XML-app-num-tabs', 2, '3'] + @new_settings << ['XML-app-restart', 2, 'true'] + @new_settings << ['XML-app-tab1-display-name', 2, "Gemeinschaft #{GEMEINSCHAFT_VERSION}"] + @new_settings << ['XML-app-tab1-name', 2, 'menu'] + @new_settings << ['XML-app-tab2-display-name', 2, 'Status'] + @new_settings << ['XML-app-tab2-name', 2, 'menu_status'] + @new_settings << ['XML-app-tab3-display-name', 2, 'Help'] + @new_settings << ['XML-app-tab3-name', 2, 'menu_help'] + + + @new_settings << ['clear-calllog', nil, 'true'] + @new_settings << ['server-based-features', nil, 'true'] + + + if ! @sip_account.call_forwards.blank? + call_forwarding_object = @sip_account.call_forwards.where(:call_forward_case_id => CallForwardCase.where(:value => 'always').first).first + if call_forwarding_object + @new_settings << ['key-functionality', 4002, '1'] + @new_settings << ['function-key-def', 4002, '63'] + @new_settings << ['stimulus-led-control-uri', 4002, "f-cftg-#{call_forwarding_object.id}" ] + @new_settings << ['send-url-address', 4002, request.host] + @new_settings << ['send-url-protocol', 4002, 3] # 0=https, 3=http + @new_settings << ['send-url-port', 4002, '80'] + @new_settings << ['send-url-path', 4002, "/config_siemens/#{@phone.id}/#{@sip_account.id}/call_forwarding.xml"] + @new_settings << ['send-url-query', 4002, "id=#{call_forwarding_object.id}&function=toggle"] + @new_settings << ['send-url-method', 4002, '0'] # 0=get, 1=post + else + @new_settings << ['key-functionality', 4002, '0'] + end + else + @new_settings << ['key-functionality', 4002, '0'] + end + + @new_settings << ['function-key-def', 4003, '10'] # Hold + + @new_settings << ['feature-availability', 2, 'false' ] # call forwarding + @new_settings << ['feature-availability', 11, 'false' ] # DND + @new_settings << ['feature-availability', 30, 'false'] # DSS + @new_settings << ['feature-availability', 31, 'false'] # feature toggle + @new_settings << ['feature-availability', 33, 'true'] # line overview + @new_settings << ['feature-availability', 33, 'false'] # phone lock + + + @soft_keys = Array.new + # Fill softkeys with keys dependent on limit of phone + @sip_account.softkeys.order(:position).each do |sk| + @soft_keys << sk + end + # Delete unset softkeys + # OPTIMIZE 40 should be enough for 2 modules, but for some reason array is empty o early + max_keys = max_keys + 50 + while @soft_keys.length <= max_keys + @soft_keys << Softkey.new + end + + @key_pos=1 + + #@soft_keys.each do |sk| + + while @key_pos < shift_key_position + + (1..shift_key_position-1).each do |idx| + first_level_keys(idx) + end + end + if @key_pos == shift_key_position + @new_settings << ['function-key-def', shift_key_position, '18'] + @new_settings << ['key-label', shift_key_position, 'Shift'] + @new_settings << ['key-label-unicode', shift_key_position, 'Shift'] + @key_pos = @key_pos+1 + end + + (1001..1000+shift_key_position-1).each do |idx| + second_level_keys(idx) + end + # First key-module first level + (301..311).each do |idx| + first_level_keys(idx) + end + # First key-module shift level + (1301..1311).each do |idx| + second_level_keys(idx) + end + # Second key-module first level + (401..411).each do |idx| + first_level_keys(idx) + end + # Second key-module shift level + (1401..1411).each do |idx| + second_level_keys(idx) + end + [312, 412].each do |idx| + @new_settings << ['function-key-def', idx, '18'] + @new_settings << ['key-label', idx, 'Shift'] + @new_settings << ['key-label-unicode', idx, 'Shift'] + end + #end + logger.debug(@new_settings) + end + + if (@phone.nil? || @sip_account.blank?) && fragment != "final" + @new_settings = Array.new + @my_nonce = params[:WorkpointMessage][:Message][:nonce] + @new_settings << ['e164', nil, 'NULL'] + @new_settings << ['sip-user-id', nil, ""] + @new_settings << ['sip-pwd', nil, ""] + @new_settings << ['sip-name', nil, ""] + @new_settings << ['display-id-unicode', nil, ""] + @new_settings << ['reg-addr', nil, ""] + @new_settings << ['registrar-addr', nil, "NULL"] + @new_settings << ['outbound-proxy-user', nil, ""] + @new_settings << ['sgnl-gateway-addr-user', nil, ""] + @new_settings << ['realm', nil, ""] + @new_settings << ['pickup-group-uri', nil, ""] + logger.debug(@new_settings) + + respond_to { |format| + format.xml { render :action => "write" } + } + elsif contact_reason == 'local-changes' + respond_to { |format| + format.xml { render :action => "clean-up" } + } + elsif (reply_status == 'accepted' && contact_reason == 'reply-to' && reply_action == 'ReadAllItems') + respond_to { |format| + format.xml { render :action => "write" } + } + + elsif ["reply-to"].include? contact_reason + respond_to { |format| + format.xml { render :action => "clean-up" } + } + + else + respond_to { |format| + format.xml { render :action => "index" } + } + end + + end + + def first_level_keys(key_idx) + sk = @soft_keys.shift + if sk.softkey_function + softkey_function = sk.softkey_function.name + end + case softkey_function + when 'blf' + @new_settings << ['function-key-def', key_idx, '59'] + @new_settings << ['stimulus-led-control-uri', key_idx, sk.number ] + @new_settings << ['stimulus-DTMF-sequence', key_idx, sk.number ] + @new_settings << ['blf-popup', key_idx, 'true'] + when 'log_out' + @new_settings << ['function-key-def', key_idx, '1'] + @new_settings << ['select-dial', key_idx, 'f-lo' ] + @new_settings << ['stimulus-led-control-uri', key_idx, '' ] + @new_settings << ['stimulus-DTMF-sequence', key_idx, '' ] + when 'log_in' + @new_settings << ['function-key-def', key_idx, '1'] + @new_settings << ['select-dial', key_idx, "f-li-#{sk.number}" ] + @new_settings << ['stimulus-led-control-uri', key_idx, '' ] + @new_settings << ['stimulus-DTMF-sequence', key_idx, '' ] + when 'hold' + @new_settings << ['function-key-def', key_idx, '10'] + @new_settings << ['select-dial', key_idx, '' ] + @new_settings << ['stimulus-led-control-uri', key_idx, '' ] + @new_settings << ['stimulus-DTMF-sequence', key_idx, '' ] + when 'dtmf' + @new_settings << ['function-key-def', key_idx, '54'] + @new_settings << ['stimulus-DTMF-sequence', key_idx, sk.number ] + @new_settings << ['stimulus-led-control-uri', key_idx, '' ] + @new_settings << ['stimulus-DTMF-sequence', key_idx, '' ] + when 'call_forwarding' + @new_settings << ['function-key-def', key_idx, '63'] + @new_settings << ['stimulus-led-control-uri', key_idx, "f-cftg-#{sk.call_forward_id}" ] + @new_settings << ['send-url-address', key_idx, request.host] + @new_settings << ['send-url-protocol', key_idx, 3] + @new_settings << ['send-url-port', key_idx, '80'] + @new_settings << ['send-url-path', key_idx, "/config_siemens/#{@phone.id}/#{@sip_account.id}/call_forwarding.xml"] + @new_settings << ['send-url-query', key_idx, "id=#{sk.call_forward_id}&function=toggle"] + @new_settings << ['send-url-method', key_idx, '0'] + @new_settings << ['blf-popup', key_idx, 'false'] + when 'call_forwarding_always' + phone_number = PhoneNumber.where(:number => sk.number, :phone_numberable_type => 'SipAccount').first + if phone_number + account_param = (phone_number.phone_numberable_id != @sip_account.id ? "&account=#{phone_number.phone_numberable_id}" : '') + else + phone_number = @sip_account.phone_numbers.first + account_param = '' + end + + if phone_number + @new_settings << ['function-key-def', key_idx, '63'] + @new_settings << ['stimulus-led-control-uri', key_idx, "f-cfutg-#{phone_number.id}" ] + @new_settings << ['send-url-address', key_idx, request.host] + @new_settings << ['send-url-protocol', key_idx, 3] # 0=https, 3=http + @new_settings << ['send-url-port', key_idx, '80'] + @new_settings << ['send-url-path', key_idx, "/config_siemens/#{@phone.id}/#{@sip_account.id}/call_forwarding.xml"] + @new_settings << ['send-url-query', key_idx, "type=always&function=toggle#{account_param}"] + @new_settings << ['send-url-method', key_idx, '0'] # 0=get, 1=post + # @new_settings << ['send-url-user-id', key_idx, 'user'] + # @new_settings << ['send-url-passwd', key_idx, 'secret'] + @new_settings << ['blf-popup', key_idx, 'false'] + end + when 'call_forwarding_assistant' + phone_number = PhoneNumber.where(:number => sk.number, :phone_numberable_type => 'SipAccount').first + if phone_number + account_param = (phone_number.phone_numberable_id != @sip_account.id ? "&account=#{phone_number.phone_numberable_id}" : '') + else + phone_number = @sip_account.phone_numbers.first + account_param = '' + end + + if phone_number + @new_settings << ['function-key-def', key_idx, '63'] + @new_settings << ['stimulus-led-control-uri', key_idx, "f-cfatg-#{phone_number.id}" ] + @new_settings << ['send-url-address', key_idx, request.host] + @new_settings << ['send-url-protocol', key_idx, 3] # 0=https, 3=http + @new_settings << ['send-url-port', key_idx, '80'] + @new_settings << ['send-url-path', key_idx, "/config_siemens/#{@phone.id}/#{@sip_account.id}/call_forwarding.xml"] + @new_settings << ['send-url-query', key_idx, "type=assistant&function=toggle#{account_param}"] + @new_settings << ['send-url-method', key_idx, '0'] # 0=get, 1=post + @new_settings << ['blf-popup', key_idx, 'false'] + end + when 'hunt_group_membership' + phone_number = PhoneNumber.where(:number => sk.number, :phone_numberable_type => 'HuntGroup').first + if phone_number + hunt_group = HuntGroup.where(:id => phone_number.phone_numberable_id).first + end + + sip_account_phone_numbers = Array.new() + @sip_account.phone_numbers.each do |pn| + sip_account_phone_numbers.push(pn.number) + end + + hunt_group_member_numbers = PhoneNumber.where(:number => sip_account_phone_numbers, :phone_numberable_type => 'HuntGroupMember') + + hunt_group_member = nil + if hunt_group and hunt_group_member_numbers + hunt_group_member_numbers.each do |hunt_group_member_number| + hunt_group_member = hunt_group.hunt_group_members.where(:id => hunt_group_member_number.phone_numberable_id).first + if hunt_group_member + break + end + end + end + + if hunt_group_member + @new_settings << ['function-key-def', key_idx, '63'] + @new_settings << ['stimulus-led-control-uri', key_idx, "f-hgmtg-#{hunt_group_member.id}" ] + @new_settings << ['send-url-address', key_idx, request.host] + @new_settings << ['send-url-protocol', key_idx, 3] # 0=https, 3=http + @new_settings << ['send-url-port', key_idx, '80'] + @new_settings << ['send-url-path', key_idx, "/config_siemens/#{@phone.id}/#{@sip_account.id}/hunt_group.xml"] + @new_settings << ['send-url-query', key_idx, "group=#{hunt_group.id}&account=#{hunt_group_member.id}&function=toggle"] + @new_settings << ['send-url-method', key_idx, '0'] # 0=get, 1=post + @new_settings << ['blf-popup', key_idx, 'false'] + end + when nil + @new_settings << ['function-key-def', key_idx, '0'] + @new_settings << ['stimulus-led-control-uri', key_idx, '' ] + @new_settings << ['stimulus-DTMF-sequence', key_idx, '' ] + else + @new_settings << ['function-key-def', key_idx, '1'] + @new_settings << ['select-dial', key_idx, sk.number ] + @new_settings << ['stimulus-led-control-uri', key_idx, '' ] + @new_settings << ['stimulus-DTMF-sequence', key_idx, '' ] + end + @new_settings << ['key-label', key_idx, sk.label ] + @new_settings << ['key-label-unicode', key_idx, sk.label ] + @key_pos = @key_pos+1 + end + + def second_level_keys(key_idx) + sk = @soft_keys.shift + softkey_function = nil + if sk.softkey_function + softkey_function = sk.softkey_function.name + end + case softkey_function + when 'log_out' + @new_settings << ['function-key-def', key_idx, '1'] + @new_settings << ['select-dial', key_idx, 'f-lo' ] + when 'log_in' + @new_settings << ['function-key-def', key_idx, '1'] + @new_settings << ['select-dial', key_idx, "f-li-#{sk.number}" ] + when 'dtmf' + @new_settings << ['function-key-def', key_idx, '54'] + @new_settings << ['stimulus-DTMF-sequence', key_idx, sk.number ] + when nil + @new_settings << ['function-key-def', key_idx, '0'] + else + @new_settings << ['function-key-def', key_idx, '1'] + @new_settings << ['select-dial', key_idx, sk.number ] + end + @new_settings << ['key-label', key_idx, sk.label ] + @new_settings << ['key-label-unicode', key_idx, sk.label ] + @key_pos = @key_pos+1 + end + + def call_history + if ! params[:number].blank? + number = params[:number] + end + + if ! params[:function].blank? + function = params[:function].to_s.downcase + end + + if ! params[:sip_account].blank? + @sip_account = SipAccount.where({ :id => params[:sip_account].to_i }).first + end + + if ! @sip_account and ! params[:phonenumber].blank? + @sip_account = SipAccount.where(:auth_name => params[:phonenumber]).first + end + + if ! params[:type].blank? + @type = params[:type] + elsif ! params[:tab].blank? + @type = params[:tab].rpartition("_")[2] + end + + if ! ['dialed', 'missed', 'received'].include? @type + @type = 'missed' + end + + if ! @sip_account + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- SipAccount not found -->", + ) + return + end + + base_url = "#{request.protocol}#{request.host_with_port}#{request.fullpath.split("?")[0]}" + + @phone_xml_object = { + :name => "menu_list", + :columns => 1, + :url => base_url, + :make_call => (function.to_s == 'dial' ? number.to_s : nil), + :hidden => {:sip_account => @sip_account.id, :type => @type}, + :commands => [{ + :type => 'SELECT', + :label => 'Dial', + :display_on => 'LISTITEM', + :key => 'function', + :value => 'dial', + }], + :entries => [], + } + + if function.to_s == 'clear_notification' + @sip_account.call_histories.update_all({:read_flag => true}) + end + + last_missed_call = @sip_account.call_histories.where(:entry_type => 'missed').order('start_stamp DESC').first + if last_missed_call and !last_missed_call.read_flag + @phone_xml_object[:led] = true + else + @phone_xml_object[:led] = false + end + + calls = @sip_account.call_histories.where(:entry_type => @type).order('start_stamp DESC').limit(MAX_DIRECTORY_ENTRIES) + + if @type == 'missed' && @phone_xml_object[:led] == true + @phone_xml_object[:commands].push({ + :type => 'SELECT', + :label => 'Clear Notification', + :key => 'function', + :value => 'clear_notification', + }) + end + + auto_reload_time = 60 + + SIEMENS_HISTORY_RELOAD_TIMES.each_pair do |time_range, reload_value| + if time_range === Time.now.localtime.hour + auto_reload_time = reload_value + end + end + + @phone_xml_object[:commands].push({ + :type => 'UPDATE', + :auto => auto_reload_time, + :label => 'Update', + }) + + calls.each do |call| + display_name = call.display_name + phone_number = call.display_number + phone_book_entry = call.phone_book_entry_by_number(phone_number) + if display_name.blank? + display_name = phone_book_entry.to_s + end + + @phone_xml_object[:entries].push({ + :selected => false, + :key => 'number', + :value => phone_number, + :text => "#{call_date_compact(call.start_stamp)} #{display_name} #{phone_number}", + }) + end + + respond_to { |format| + format.any { + self.formats = [ :xml ] + render :action => "_#{@phone_xml_object[:name]}" + } + } + end + + def call_forwarding + if ! params[:type].blank? + @type = params[:type] + end + + if ! params[:function].blank? + @function = params[:function] + end + + if ! params[:id].blank? + @call_forwarding_id = params[:id].to_i + end + + if ! params[:sip_account].blank? + @sip_account = SipAccount.where({ :id => params[:sip_account].to_i }).first + end + + if ! params[:account].blank? + @sip_account = SipAccount.where({ :id => params[:account].to_i }).first + end + + if ! @sip_account + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- SipAccount not found -->", + ) + return + end + + if @function == 'toggle' + if @call_forwarding_id + call_forwarding = @sip_account.call_forwards.where(:id => @call_forwarding_id).first + + if !call_forwarding and @sip_account.softkeys.where(:call_forward_id => @call_forwarding_id).count > 0 + call_forwarding = CallForward.where(:id => @call_forwarding_id).first + end + + if call_forwarding + call_forwarding.toggle + end + elsif @type + call_forwarding = @sip_account.call_forwarding_toggle(@type) + end + if !call_forwarding + render( + :status => 500, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- Call forwarding not set: #{@sip_account.errors.messages.inspect} -->", + ) + return + end + + if !call_forwarding.errors.blank? + error_messages = Array.new() + call_forwarding.errors.messages.each_pair do |key, message| + error_messages.push(message.join(';')) + end + render( + :status => 500, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- ERROR #{error_messages.join(',')} #{call_forwarding.to_s}) -->", + ) + elsif call_forwarding.active + render( + :status => 200, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- ON #{call_forwarding.to_s} -->", + ) + else + render( + :status => 200, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- OFF #{call_forwarding.to_s} -->", + ) + end + return + end + + base_url = "#{request.protocol}#{request.host_with_port}#{request.fullpath.split("?")[0]}" + + @phone_xml_object = { + :name => "number_list", + :columns => 1, + :url => base_url, + :hidden => {:sip_account => @sip_account.id, :type => @type}, + :entries => [] + } + + respond_to { |format| + format.any { + self.formats = [ :xml ] + render + } + } + end + + + def hunt_group + if ! params[:goto].blank? + redirect_to params[:goto] + return; + end + + if ! params[:function].blank? + @function = params[:function] + end + + if ! params[:group].blank? + @hunt_group = HuntGroup.where({ :id => params[:group].to_i }).first + end + + if ! @sip_account + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- SipAccount not found -->", + ) + return + end + + if ! params[:account].blank? + hunt_group_member = @hunt_group.hunt_group_members.where({ :id => params[:account].to_i }).first + end + + base_url = "#{request.protocol}#{request.host_with_port}#{request.fullpath.split("?")[0]}" + + if @function == 'toggle' + if ! hunt_group_member + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- HuntGroupMember not found -->", + ) + return + end + + if hunt_group_member.can_switch_status_itself == true + if hunt_group_member.active + hunt_group_member.active = false + else + hunt_group_member.active = true + end + + if ! hunt_group_member.save + render( + :status => 500, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- #{hunt_group_member.errors.inspect} -->", + ) + else + render( + :status => 200, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- Member #{hunt_group_member.id} toggled -->", + ) + end + return + end + elsif @function == 'members' + commands = [{ + :type => 'UPDATE', + :auto => 20, + :label => 'Update', + },{ + :type => 'BACK', + :label => 'Back', + :display_on => 'OPTIONS', + },{ + :type => 'EXIT', + :label => 'Exit', + :display_on => 'OPTIONS', + },{ + :type => 'SELECT', + :label => 'Show', + :display_on => 'LISTITEM', + }] + + @phone_xml_object = { + :name => "menu_list", + :columns => 1, + :url => base_url, + :hidden => {:function => @function, :group => @hunt_group.id}, + :entries => [], + :commands => commands, + } + + @hunt_group.hunt_group_members.where(:active => true).each do |member| + @phone_xml_object[:entries].push({ + :selected => false, + :value => member.id, + :text => member.name, + }) + end + else + hunt_groups = Array.new() + phone_numbers = Array.new() + @sip_account.phone_numbers.each do |phone_number| + phone_numbers.push(phone_number.number) + assistant_call_forwardings = phone_number.call_forwards.where(:call_forward_case_id => CallForwardCase.where(:value => 'assistant').first.id) + assistant_call_forwardings.each do |assistant_call_forwarding| + if assistant_call_forwarding.call_forwardable_type == 'HuntGroup' && assistant_call_forwarding.call_forwardable_id.to_i > 0 + hunt_groups.push(assistant_call_forwarding.call_forwardable_id.to_i) + end + end + end + + hunt_group_members = Array.new() + if phone_numbers.length > 0 + hunt_group_members = PhoneNumber.where(:phone_numberable_type => 'HuntGroupMember', :number => phone_numbers) + end + + hunt_group_members.each do |hunt_group| + hunt_groups.push(hunt_group.phone_numberable.hunt_group_id) + end + + hunt_groups = HuntGroup.where(:id => hunt_groups) + + @phone_xml_object = { + :name => "menu_list", + :columns => 1, + :url => base_url, + :hidden => {:function => 'members'}, + :entries => [], + :commands => [{ + :type => 'EXIT', + :label => 'Exit', + :display_on => 'OPTIONS', + },{ + :type => 'BACK', + :label => 'Back', + :display_on => 'OPTIONS', + },{ + :type => 'SELECT', + :label => 'Select', + :display_on => 'LISTITEM', + }], + } + + hunt_groups.each do |hunt_group| + @phone_xml_object[:entries].push({ + :selected => false, + :key => 'group', + :value => hunt_group.id, + :text => hunt_group.name, + }) + end + + end + + respond_to { |format| + format.any { + self.formats = [ :xml ] + render :action => "_#{@phone_xml_object[:name]}" + } + } + end + + + def menu + if ! @phone or ! @sip_account + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- Phone or SipAccount not found -->", + ) + return + end + + type = 'menu' + if ! params[:type].blank? + type = params[:type] + elsif ! params[:tab].blank? + tab = params[:tab].rpartition("_") + if tab[1] != '' + type = tab[2] + end + end + + if ! params[:item].blank? + item = params[:item] + end + + menu_url = "#{request.protocol}#{request.host_with_port}#{request.fullpath.split("?")[0]}" + base_url = "#{request.protocol}#{request.host_with_port}/config_siemens/#{@phone.id}" + + @phone_xml_object = { + :name => "menu_list", + :columns => 1, + :url => menu_url, + :hidden => {:type => type}, + :entries => [] + } + + case type + when 'menu' + items = [ + { + :value => 'phone_directory', + :text => "Directory", + :url => "#{menu_url}?type=#{type}", + },{ + :value => 'call_history', + :text => "Call History", + :url => "#{menu_url}?type=call_history", + }, + ] + when 'status' + items = [ + { + :value => 'hunt_group', + :text => "Hunt Group", + :url => "#{base_url}/hunt_group.xml" + }, + ] + + commands = [ + { + :type => 'UPDATE', + :auto => 10, + :label => 'Update', + } + ] + when 'help' + items = [ + { + :key => 'item', + :value => 'help', + :text => "Help", + :url => "#{menu_url}?type=#{type}", + }, + ] + when 'call_history' + items = [ + { + :value => 'missed', + :text => "Missed", + :url => "#{base_url}/call_history.xml?type=missed", + },{ + :value => 'dialed', + :text => "Dialed", + :url => "#{base_url}/call_history.xml?type=dialed", + },{ + :value => 'received', + :text => "Received", + :url => "#{base_url}/call_history.xml?type=received", + }, + ] + end + + if item + items.each do |entry| + if entry[:value] == item + redirect_to entry[:url] + return; + end + end + end + + @phone_xml_object[:entries] = items + @phone_xml_object[:commands] = commands + + respond_to { |format| + format.any { + self.formats = [ :xml ] + render :action => "_#{@phone_xml_object[:name]}" + } + } + end + + def call_date_compact(date) + if date.strftime('%Y%m%d') == DateTime::now.strftime('%Y%m%d') + return date.strftime('%H:%M') + end + return date.strftime('%d.%m %H:%M') + end +end diff --git a/app/controllers/config_siemens_sort_controller.rb b/app/controllers/config_siemens_sort_controller.rb new file mode 100644 index 0000000..c0739e5 --- /dev/null +++ b/app/controllers/config_siemens_sort_controller.rb @@ -0,0 +1,371 @@ +require 'nokogiri' +#doc.search('Message/ItemList').each do |a| puts a.children end +class ConfigSiemensController < ApplicationController +#TODO Authentication + # No access for admins though as this contains personal data. + + # We can't use load_and_authorize_resource() here because + # ConfigSiemensController isn't a resource. + # We can try client certificates + + skip_authorization_check + + + def index + os40_keys=7 + os60_keys=8 + os80_keys=9 + doc = Nokogiri::XML(request.body.read) + #logger.debug("#{params[:WorkpointMessage].to_xml}") + #logger.debug("#{params[:WorkpointMessage][:Message][:ItemList].to_xml}") + @phone_items=Hash.new + contact_reason = params[:WorkpointMessage][:Message][:ReasonForContact] + reply_status = doc.search('Message/ReasonForContact').first[:status] + reply_action = doc.search('Message/ReasonForContact').first[:action] + + doc.search('Message/ItemList/Item').each do |post_item| + @phone_items[post_item[:name]]=post_item.children.to_s + end + + mac_address = @phone_items['mac-addr'] + phone_type = @phone_items['device-type'] + if phone_type == "OpenStage 40" + max_keys = (os40_keys) * 2 + elsif phone_type == "OpenStage 60" + max_keys = (os60_keys) * 2 + elsif phone_type == "OpenStage 80" + max_keys = (os80_keys) * 2 + else + max_keys = 0 + end + + blf_keys_max = max_keys / 2 + shift_key_position = blf_keys_max - 1 + + #logger.debug(request.body.read) + @phone = Phone.find_by_mac_address(mac_address.gsub(':','').upcase) + if ! @phone.nil? + @phone.update_attributes(:ip_address => request.remote_ip) + sip_account = SipAccount.where(:sip_accountable_type == @phone.phoneable_type, + :sip_accountable_id == @phone.phoneable_id).first + end + + if ! @phone.nil? && ! sip_account.nil? + #logger.debug(@phone_items) + @my_nonce = params[:WorkpointMessage][:Message][:nonce] + @new_settings = Array.new + + @new_settings << ['dhcp', nil, 'true'] + @new_settings << ['hostname', nil, mac_address.gsub(':', '') ] + @new_settings << ['e164-hostname', nil, 'false'] + @new_settings << ['mobility-enabled', nil, 'false'] + @new_settings << ['mobility-password-on-logoff', nil, 'false'] + @new_settings << ['e164', nil, sip_account.try(:phone_numbers).first.number] + @new_settings << ['sip-user-id', nil, sip_account.auth_name] + @new_settings << ['reg-id', nil, sip_account.auth_name] + @new_settings << ['reg-number', nil, sip_account.auth_name] + @new_settings << ['fully-qualified-phone-no', nil, sip_account.auth_name] + @new_settings << ['sip-pwd', nil, sip_account.password] + @new_settings << ['sip-name', nil, sip_account.caller_name] + @new_settings << ['register-by-name', nil, 'false'] + #OPTIMIZE Display ID ? + @new_settings << ['display-id', nil, sip_account.try(:phone_numbers).first.number] + @new_settings << ['display-id-unicode', nil, sip_account.caller_name] + @new_settings << ['use-display-id', nil, 'true'] + @new_settings << ['reg-addr', nil, sip_account.sip_domain.host] + @new_settings << ['reg-port', nil, '5060'] + @new_settings << ['registrar-addr', nil, sip_account.sip_domain.host] + @new_settings << ['registrar-port', nil, '5060'] + @new_settings << ['outbound-proxy', nil, sip_account.sip_domain.host] + @new_settings << ['outbound-proxy-user', nil, sip_account.sip_domain.host] + @new_settings << ['sgnl-gateway-addr', nil, sip_account.sip_domain.host] + @new_settings << ['sgnl-gateway-addr-user', nil, sip_account.sip_domain.host] + @new_settings << ['sgnl-gateway-port', nil, '5060' ] + @new_settings << ['sgnl-gateway-port-user', nil, '5060'] + @new_settings << ['sgnl-route', nil, '0' ] + @new_settings << ['mwi-e164', nil, '' ] + @new_settings << ['rtp-base-port', nil, '5004'] + @new_settings << ['default-domain', nil, '' ] + @new_settings << ['sip-transport', nil, '0' ] + @new_settings << ['sip-transport-user', nil, '0' ] + @new_settings << ['server-type', nil, '0' ] + @new_settings << ['session-timer', nil, 'true'] + @new_settings << ['session-duration', nil, '3600' ] + @new_settings << ['reg-ttl', nil, '3600' ] + @new_settings << ['realm', nil, sip_account.sip_domain.realm] + @new_settings << ['emergency-e164', nil, '0110' ] + @new_settings << ['voice-mail-e164', nil, 'voicemail'] + @new_settings << ['auto-answer', nil, 'false'] + @new_settings << ['beep-on-auto-answer', nil, 'true'] + @new_settings << ['auto-reconnect', nil, 'false' ] + @new_settings << ['beep-on-auto-reconnect', nil, 'true'] + @new_settings << ['permit-decline-call', nil, 'true'] + @new_settings << ['transfer-on-ring', nil, 'false' ] + @new_settings << ['join-allowed-in-conference', nil, 'true'] + @new_settings << ['pickup-group-uri', nil, '*8*'] + @new_settings << ['pickup-group-uri', nil, '' ] + @new_settings << ['hot-line-warm-line-digits', nil, '' ] + @new_settings << ['initial-digit-timer', nil, '30' ] + @new_settings << ['conference-factory-uri', nil, ''] + @new_settings << ['callback-busy-allow', nil, 'false'] + @new_settings << ['callback-busy-code', nil, '' ] + @new_settings << ['callback-ring-allow', nil, 'false'] + @new_settings << ['callback-ring-code', nil, ''] + @new_settings << ['callback-cancel-code', nil, ''] + @new_settings << ['park-server', nil, ''] + #OPTIMIZE Callwaiting + @new_settings << ['call-waiting-enabled', nil, 'true'] + @new_settings << ['qos-layer2', nil, 'true'] + @new_settings << ['l2qos-voice', nil, '5' ] + @new_settings << ['l2qos-signalling', nil, '3' ] + @new_settings << ['l2qos-default', nil, '0'] + @new_settings << ['qos-layer3', nil, 'true'] + @new_settings << ['l3qos-voice', nil, '46'] + @new_settings << ['l3qos-signalling', nil, '26'] + @new_settings << ['vlan-method', nil, '1'] + #OPTIMIZE Timezone + @new_settings << ['sntp-tz-offset', nil, ''] + @new_settings << ['daylight-save', nil, ''] + @new_settings << ['daylight-save-minutes', nil, ''] + #OPTIMIZE Use SNMP? + @new_settings << ['snmp-trap-addr', nil, ''] + @new_settings << ['snmp-trap-port', nil, ''] + @new_settings << ['snmp-trap-pwd', nil, 'snmp' ] + @new_settings << ['snmp-traps-active', nil, 'false' ] + @new_settings << ['diagnostic-trap-addr', nil, ''] + @new_settings << ['diagnostic-trap-port', nil, ''] + @new_settings << ['diagnostic-trap-pwd', nil, 'snmp' ] + @new_settings << ['diagnostic-traps-active', nil, 'false' ] + @new_settings << ['diagnostic-snmp-active', nil, 'false'] + @new_settings << ['qdc-collection-unit-addr', nil, ''] + @new_settings << ['qdc-collection-unit-port', nil, '12010'] + + @new_settings << ['qdc-trap-pwd', nil, 'QOSDC'] + @new_settings << ['qdc-snmp-active', nil, 'false'] + @new_settings << ['qdc-qcu-active', nil, 'false'] + @new_settings << ['snmp-queries-allowed', nil, 'false'] + @new_settings << ['snmp-pwd', nil, ''] + @new_settings << ['disable-microphone', nil, 'false'] + @new_settings << ['loudspeech-enabled', nil, 'true'] + @new_settings << ['audio-silence-suppression', nil, 'false'] + + @new_settings << ['port1', nil, '0' ] # 0=Automatic (speed) + @new_settings << ['port2', nil, '0' ] + @new_settings << ['port2-mode', nil, '1' ] + @new_settings << ['port2-auto-mdix-enabled', nil, 'true' ] + @new_settings << ['originating-line-preference', nil, '0'] + @new_settings << ['terminating-line-preference', nil, '0'] + @new_settings << ['line-key-operating-mode', nil, '0'] + @new_settings << ['line-rollover-type', nil, '2'] + @new_settings << ['line-rollover-volume', nil, '5' ]# 1-5 + @new_settings << ['line-registration-leds', nil, 'true'] + @new_settings << ['keyset-use-focus', nil, 'true' ] + @new_settings << ['keyset-remote-forward-ind', nil, 'true'] + @new_settings << ['keyset-reservation-timer', nil, '60' ] # 0-300 + @new_settings << ['dial-plan-enabled', nil, '' ] + @new_settings << ['Canonical-dialing-international-prefix', nil, ''] + @new_settings << ['Canonical-dialing-local-country-code', nil, ''] + @new_settings << ['Canonical-dialing-national-prefix', nil, ''] + @new_settings << ['Canonical-dialing-local-area-code', nil, ''] + @new_settings << ['Canonical-dialing-local-node', nil, ''] + @new_settings << ['Canonical-dialing-external-access', nil, '0'] + @new_settings << ['Canonical-dialing-operator-code', nil, ''] + @new_settings << ['Canonical-dialing-emergency-number', nil, ''] + @new_settings << ['Canonical-dialing-dial-needs-access-code', nil, '0'] + @new_settings << ['Canonical-dialing-dial-needs-intGWcode', nil, '0'] + @new_settings << ['Canonical-dialing-min-local-number-length', nil, '10'] + @new_settings << ['Canonical-dialing-extension-initial-digits', nil, ''] + @new_settings << ['Canonical-dialing-dial-internal-form', nil, '0' ] + @new_settings << ['Canonical-dialing-dial-external-form', nil, '0' ] + @new_settings << ['Canonical-lookup-local-code', nil, '' ] + @new_settings << ['Canonical-lookup-international-code', nil, ''] + @new_settings << ['hot-keypad-dialing', nil, ''] + @new_settings << ['ldap-transport', nil, '0'] + @new_settings << ['ldap-server-address', nil, '' ] + @new_settings << ['ldap-server-port', nil, '389' ] + @new_settings << ['ldap-authentication', nil, '1'] + @new_settings << ['ldap-user', nil, '' ] + @new_settings << ['ldap-pwd', nil, '' ] + @new_settings << ['ldap-max-responses', nil, '25'] + @new_settings << ['backup-addr', nil, ''] + @new_settings << ['backup-registration', nil, 'false'] + @new_settings << ['qdc-qcu-active', nil, 'false' ] + @new_settings << ['min-admin-passw-length', nil, '6' ] + @new_settings << ['default-locked-config-menus', nil, 'true' ] + @new_settings << ['locked-config-menus', nil, 'true' ] + @new_settings << ['default-locked-local-function-menus', nil, 'true' ] + @new_settings << ['locked-local-function-menus', nil, 'true' ] + @new_settings << ['dls-mode-secure', nil, '0' ] + @new_settings << ['dls-chunk-size', nil, '9492'] + @new_settings << ['default-passw-policy', nil, 'false'] + @new_settings << ['deflect-destination', nil, ''] + @new_settings << ['display-skin', nil, ''] + @new_settings << ['enable-bluetooth-interface', nil, 'true'] + @new_settings << ['usb-access-enabled', nil, 'false' ] + @new_settings << ['usb-backup-enabled', nil, 'false' ] + @new_settings << ['line-button-mode', nil, '0' ] + @new_settings << ['lock-forwarding', nil, '' ] + @new_settings << ['loudspeaker-function-mode', nil, '0' ] + @new_settings << ['max-pin-retries', nil, '' ] + @new_settings << ['inactivity-timeout', nil, '30' ] + @new_settings << ['not-used-timeout', nil, '2' ] + @new_settings << ['passw-char-set', nil, '0' ] + @new_settings << ['refuse-call', nil, 'true' ] + @new_settings << ['restart-password', nil, ''] + #OPTIMIZE clock format + @new_settings << ['time-format', nil, '0' ]# 1=12 h + @new_settings << ['uaCSTA-enabled', nil, 'false' ] + @new_settings << ['enable-test-interface', nil, 'false'] + @new_settings << ['enable-WBM', nil, 'true'] + @new_settings << ['pixelsaver-timeout', nil, '2' ]# 2 hours? + @new_settings << ['voice-message-dial-tone', nil, '' ] + @new_settings << ['call-pickup-allowed', nil, 'true' ] + @new_settings << ['group-pickup-tone-allowed', nil, 'true'] + @new_settings << ['group-pickup-as-ringer', nil, 'false'] + @new_settings << ['group-pickup-alert-type', nil, '0' ] + @new_settings << ['default-profile', nil, '' ] + @new_settings << ['count-medium-priority', nil, '5'] + @new_settings << ['timer-medium-priority', nil, '60'] # 1 - 999 + @new_settings << ['timer-high-priority', nil, '5'] # 0 - 999 + @new_settings << ['dss-sip-detect-timer', nil, '10'] + @new_settings << ['dss-sip-deflect', nil, 'false' ] + @new_settings << ['dss-sip-refuse', nil, 'false' ] + @new_settings << ['feature-availability', nil, 'false'] + @new_settings << ['feature-availability', nil, 'true' ] + @new_settings << ['feature-availability', nil, 'true' ] + @new_settings << ['local-control-feature-availability', nil, 'false' ] + @new_settings << ['trace-level', nil, '0' ] # Off + @new_settings << ['default-locked-function-keys', nil, 'true' ]# "unknown item" + #OPTIMIZE Put pickup prefix into database/global constant? + @new_settings << ['blf-code', nil, 'f_ia_'] # pickup prefix for softkey function 59 (BLF) + @new_settings << ['stimulus-feature-code', nil, true] + @new_settings << ['stimulus-led-control-uri', nil, true] + + + @new_settings << ['min-user-passw-length', nil, '6' ]# 6 - 24 + #OPTIMIZE language + @new_settings << ['country-iso', nil, 'DE' ] + @new_settings << ['language-iso', nil, 'de'] + @new_settings << ['date-format', nil, '0' ] # DD.MM.YYYY + #OPTIMIZE ringtones + @new_settings << ['ringer-melody', nil, '1'] + + @new_settings << ['ringer-melody', nil, '2'] + @new_settings << ['ringer-tone-sequence', nil, '2'] + + soft_keys = Array.new + # Getting BLF keys only for the first level + blf_keys = sip_account.softkeys.find( + :all, + :conditions => {:function => ['blf', 'conference']}, + :limit => blf_keys_max) + #Getting other keys + non_blf_keys = sip_account.softkeys.find( + :all, + :conditions => {:function => ['speed_dial']}) + + # Fill softkey array with BLF keys up to shift key + blf_keys.each do |k| + soft_keys << k + end + # Fill sofkey with other keys up to end + non_blf_keys.each do |k| + if soft_keys.length < max_keys + soft_keys << k + end + end + # Delete unset softkeys + while soft_keys.length < max_keys + soft_keys << Softkey.new + end + + key_pos=1 + + #soft_keys.each do |sk| + + while key_pos < shift_key_position + + (1..shift_key_position-1).each do |key_idx| + sk = soft_keys.shift + logger.debug(sk.function, key_idx) + if sk.function == "blf" + @new_settings << ['function-key-def', key_idx, '59'] + @new_settings << ['select-dial', key_idx, sk.number ] + elsif sk.function == "log_out" + @new_settings << ['function-key-def', key_idx, '1'] + @new_settings << ['select-dial', key_idx, 'f_lo' ] + elsif sk.function == "log_in" + @new_settings << ['function-key-def', key_idx, '1'] + @new_settings << ['select-dial', key_idx, "f_li_#{sk.number}" ] + elsif sk.function == "dtmf" + @new_settings << ['function-key-def', key_idx, '54'] + @new_settings << ['stimulus-DTMF-sequence', key_idx, sk.number ] + elsif sk.function.nil? + @new_settings << ['function-key-def', key_idx, '0'] + else + @new_settings << ['function-key-def', key_idx, '1'] + @new_settings << ['select-dial', key_idx, sk.number ] + end + @new_settings << ['key-label', key_idx, sk.label ] + @new_settings << ['key-label-unicode', key_idx, sk.label ] + key_pos = key_pos+1 + + end + end + if key_pos == shift_key_position + @new_settings << ['function-key-def', shift_key_position, '18'] + @new_settings << ['key-label', shift_key_position, 'Shift'] + @new_settings << ['key-label-unicode', shift_key_position, 'Shift'] + key_pos = key_pos+1 + end + + (1001..1000+shift_key_position-1).each do |key_idx| + sk = soft_keys.shift + if sk.function == "log_out" + @new_settings << ['function-key-def', key_idx, '1'] + @new_settings << ['select-dial', key_idx, 'f_lo' ] + elsif sk.function == "log_in" + @new_settings << ['function-key-def', key_idx, '1'] + @new_settings << ['select-dial', key_idx, "f_li_#{sk.number}" ] + elsif sk.function == "dtmf" + @new_settings << ['function-key-def', key_idx, '54'] + @new_settings << ['stimulus-DTMF-sequence', key_idx, sk.number ] + elsif sk.function.nil? + @new_settings << ['function-key-def', key_idx, '0'] + else + @new_settings << ['function-key-def', key_idx, '1'] + @new_settings << ['select-dial', key_idx, sk.number ] + end + @new_settings << ['key-label', key_idx, sk.label ] + @new_settings << ['key-label-unicode', key_idx, sk.label ] + key_pos = key_pos+1 + + end + + #end + logger.debug(@new_settings) + end + + if @phone.nil? || sip_account.nil? + respond_to { |format| + format.xml { render :action => "clean-up" } + } + + elsif (reply_status == 'accepted' && contact_reason == 'reply-to' && reply_action == 'ReadAllItems') + respond_to { |format| + format.xml { render :action => "write" } + } + + elsif ["reply-to"].include? contact_reason + respond_to { |format| + format.xml { render :action => "clean-up" } + } + + else + respond_to { |format| + format.xml { render :action => "index" } + } + end + + end +end diff --git a/app/controllers/config_snom_controller.rb b/app/controllers/config_snom_controller.rb new file mode 100644 index 0000000..40f0c4e --- /dev/null +++ b/app/controllers/config_snom_controller.rb @@ -0,0 +1,1169 @@ +class ConfigSnomController < ApplicationController + MAX_SIP_ACCOUNTS_COUNT = 11 + MAX_SOFTKEYS_COUNT = 12 + (42 * 3) - 1 + MAX_DIRECTORY_ENTRIES = 20 + KEYPAD_TO_CHAR = { + '0' => [' ','-','.',',','0'], + '1' => [' ','-','.',',','1'], + '2' => ['a','b','c','2'], + '3' => ['d','e','f','3'], + '4' => ['g','h','i','4'], + '5' => ['j','k','l','5'], + '6' => ['m','n','o','6'], + '7' => ['p','q','r','s','7'], + '8' => ['t','u','v','8'], + '9' => ['w','x','y','z','9'], + } + + skip_authorization_check + + before_filter { |controller| + @mac_address = params[:mac_address].to_s.upcase.gsub(/[^0-9A-F]/,'') + @provisioning_authenticated = false + + if !params[:provisioning_key].blank? + @phone = Phone.where({ :provisioning_key => params[:provisioning_key] }).first + if @phone + @provisioning_authenticated = true + @mac_address = @phone.mac_address + end + end + + if ! @mac_address.blank? then + if !@phone + @phone = Phone.where({ :mac_address => @mac_address }).first + end + + if ! @phone && PROVISIONING_AUTO_ADD_PHONE + tenant = Tenant.where(:id => PROVISIONING_AUTO_TENANT_ID).first + if ! tenant + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- Tenant not found -->", + ) + return + end + + @phone = tenant.phones.build + @phone.mac_address = @mac_address + @phone.hot_deskable = true + + mac_address_to_model = { + '00041325' => 'Snom 300', + '00041328' => 'Snom 300', + '0004132D' => 'Snom 300', + '0004132F' => 'Snom 300', + '00041334' => 'Snom 300', + '00041350' => 'Snom 300', + '0004133B' => 'Snom 300', + '00041337' => 'Snom 300', + '00041324' => 'Snom 320', + '00041327' => 'Snom 320', + '0004132C' => 'Snom 320', + '00041331' => 'Snom 320', + '00041335' => 'Snom 320', + '00041338' => 'Snom 320', + '00041351' => 'Snom 320', + '00041323' => 'Snom 360', + '00041329' => 'Snom 360', + '0004132B' => 'Snom 360', + '00041339' => 'Snom 360', + '00041390' => 'Snom 360', + '00041326' => 'Snom 370', + '0004132E' => 'Snom 370', + '0004133A' => 'Snom 370', + '00041352' => 'Snom 370', + '00041340' => 'Snom 820', + '00041345' => 'Snom 821', + '00041348' => 'Snom 821', + '00041341' => 'Snom 870', + } + + @phone.phone_model = PhoneModel.where(:name => mac_address_to_model[@mac_address[0, 8]]).first + if ! @phone.save + render( + :status => 500, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- #{@phone.errors.messages.inspect} -->", + ) + return + end + + if ! PROVISIONING_AUTO_ADD_SIP_ACCOUNT + return + end + + caller_name_index = 0 + sip_account_last = tenant.sip_accounts.where('caller_name LIKE ?', "#{PROVISIONING_AUTO_SIP_ACCOUNT_CALLER_PREFIX}%").sort { |item1, item2| + item1.caller_name.gsub(/[^0-9]/, '').to_i <=> item2.caller_name.gsub(/[^0-9]/, '').to_i + }.last + + if sip_account_last + caller_name_index = sip_account_last.caller_name.gsub(/[^0-9]/, '').to_i + end + caller_name_index = caller_name_index + 1 + + @sip_account = tenant.sip_accounts.build + @sip_account.caller_name = "#{PROVISIONING_AUTO_SIP_ACCOUNT_CALLER_PREFIX}#{caller_name_index}" + @sip_account.call_waiting = CALL_WAITING + @sip_account.clir = DEFAULT_CLIR_SETTING + @sip_account.clip = DEFAULT_CLIP_SETTING + @sip_account.voicemail_pin = random_pin + @sip_account.callforward_rules_act_per_sip_account = CALLFORWARD_RULES_ACT_PER_SIP_ACCOUNT_DEFAULT + @sip_account.hotdeskable = false + loop do + @sip_account.auth_name = SecureRandom.hex(DEFAULT_LENGTH_SIP_AUTH_NAME) + break unless SipAccount.exists?(:auth_name => @sip_account.auth_name) + end + @sip_account.password = SecureRandom.hex(DEFAULT_LENGTH_SIP_PASSWORD) + + if ! @sip_account.save + render( + :status => 500, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- #{@sip_account.errors.messages.inspect} -->", + ) + return + end + + phone_sip_account = PhoneSipAccount.new() + phone_sip_account.phone_id = @phone.id + phone_sip_account.sip_account_id = @sip_account.id + + if ! phone_sip_account.save + render( + :status => 500, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- #{phone_sip_account.errors.messages.inspect} -->", + ) + return + end + + end + elsif ! params[:phone].blank? then + @phone = Phone.where({ :id => params[:phone].to_i }).first + end + + if ! @phone + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- Phone not found -->", + ) + end + + if ! params[:sip_account].blank? + @sip_account = @phone.sip_accounts.where({ :id => params[:sip_account].to_i }).first + if ! @sip_account + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- SipAccount not found -->", + ) + end + end + + if ! params[:type].blank? + @type = params[:type].to_s.strip.downcase + end + + if ! params[:keys].blank? + @dialpad_keys = params[:keys].to_s.strip + end + } + + + def show + send_sensitve = @provisioning_authenticated || !@phone.provisioning_key_active + @phone_settings = Hash.new() + + if defined?(PROVISIONING_KEY_LENGTH) && PROVISIONING_KEY_LENGTH > 0 + if @phone.provisioning_key.blank? + @phone.update_attributes({ :provisioning_key => SecureRandom.hex(PROVISIONING_KEY_LENGTH), :provisioning_key_active => false }) + elsif @provisioning_authenticated + @phone.update_attributes({ :provisioning_key_active => true }) + end + + if send_sensitve + if defined?(PROVISIONING_PROTOCOL) && PROVISIONING_PROTOCOL + provisioning_protocol = PROVISIONING_PROTOCOL + else + provisioning_protocol = request.protocol + end + @phone_settings[:setting_server] = "#{provisioning_protocol}#{request.host_with_port}/snom-#{@phone.provisioning_key}.xml" + end + end + + if defined?(PROVISIONING_SET_HTTP_USER) && @phone.http_user.blank? + if PROVISIONING_SET_HTTP_USER.class == Fixnum + @phone.update_attributes({ :http_user => SecureRandom.hex(PROVISIONING_SET_HTTP_USER) }) + elsif PROVISIONING_SET_HTTP_USER.class == String + @phone.update_attributes({ :http_user => PROVISIONING_SET_HTTP_USER }) + end + end + + if defined?(PROVISIONING_SET_HTTP_PASSWORD) && @phone.http_password.blank? + if PROVISIONING_SET_HTTP_PASSWORD.class == Fixnum + @phone.update_attributes({ :http_password => SecureRandom.hex(PROVISIONING_SET_HTTP_PASSWORD) }) + elsif PROVISIONING_SET_HTTP_PASSWORD.class == String + @phone.update_attributes({ :http_password => PROVISIONING_SET_HTTP_PASSWORD }) + end + end + + if send_sensitve + @phone_settings[:http_user] = @phone.http_user + @phone_settings[:http_pass] = @phone.http_password + if defined?(PROVISIONING_ADMIN_PASSWORD) + if PROVISIONING_ADMIN_PASSWORD.class == TrueClass + @phone_settings[:admin_mode_password] = @phone.http_password + elsif PROVISIONING_ADMIN_PASSWORD.class == String + @phone_settings[:admin_mode_password] = PROVISIONING_ADMIN_PASSWORD + end + end + end + + if ! request.env['HTTP_USER_AGENT'].index('snom') + Rails.logger.info "---> User-Agent indicates not a Snom phone (#{request.env['HTTP_USER_AGENT'].inspect})" + else + Rails.logger.info "---> Phone #{@mac_address.inspect}, IP address #{request.remote_ip.inspect}" + @phone.update_attributes({ :ip_address => request.remote_ip }) + end + + @softkeys = Array.new() + @sip_accounts = Array.new() + + if send_sensitve + @phone.sip_accounts.each do |sip_account| + if (sip_account.sip_accountable_type == @phone.phoneable_type) and (sip_account.sip_accountable_id == @phone.phoneable_id) + snom_sip_account = { + :id => sip_account.id, + :active => 'on', + :pname => sip_account.auth_name, + :pass => sip_account.password, + :host => sip_account.host, + :outbound => sip_account.host, + :name => sip_account.auth_name, + :realname => 'Call', + :idle_text => sip_account.caller_name, + :mailbox => "<sip:#{sip_account.auth_name}@#{sip_account.host}>" + } + @sip_accounts.push(snom_sip_account) + sip_account_index = @sip_accounts.length + sip_account.softkeys.order(:position).each do |softkey| + if softkey.softkey_function + softkey_function = softkey.softkey_function.name + end + case softkey_function + when 'blf' + @softkeys.push({:context => sip_account_index, :label => softkey.label, :data => "blf <sip:#{softkey.number}@#{sip_account.host}>|f-ia-"}) + when 'speed_dial' + @softkeys.push({:context => sip_account_index, :label => softkey.label, :data => "speed #{softkey.number}"}) + when 'dtmf' + @softkeys.push({:context => sip_account_index, :label => softkey.label, :data => "dtmf #{softkey.number}"}) + when 'log_out' + @softkeys.push({:context => sip_account_index, :label => softkey.label, :data => "speed f-lo"}) + when 'log_in' + @softkeys.push({:context => sip_account_index, :label => softkey.label, :data => "speed f-li-#{softkey.number}"}) + when 'conference' + @softkeys.push({:context => sip_account_index, :label => softkey.label, :data => "blf <sip:#{softkey.number}@#{sip_account.host}>|f-ta-"}) + when 'call_forwarding' + @softkeys.push({ + :context => sip_account_index, + :function => softkey.function, + :label => softkey.label, + :softkey => softkey, + :general_type => t("softkeys.functions.#{softkey.softkey_function.name}"), + :subscription => { + :to => "f-cftg-#{softkey.call_forward_id}@#{sip_account.host}", + :for => "#{sip_account.auth_name}@#{sip_account.host}" + }, + :actions => [{ + :type => :url, + :target => "#{request.protocol}#{request.host_with_port}/config_snom/#{@phone.id}/#{snom_sip_account[:id]}/call_forwarding.xml?id=#{softkey.call_forward_id}&function=toggle", + :when => 'on press', + }], + }) + when 'call_forwarding_always' + phone_number = PhoneNumber.where(:number => softkey.number, :phone_numberable_type => 'SipAccount').first + if phone_number + account_param = (phone_number.phone_numberable_id != snom_sip_account[:id] ? "&account=#{phone_number.phone_numberable_id}" : '') + else + phone_number = sip_account.phone_numbers.first + account_param = '' + end + + @softkeys.push({ + :context => sip_account_index, + :function => softkey.function, + :label => softkey.label, + :softkey => softkey, + :general_type => t("softkeys.functions.#{softkey.softkey_function.name}"), + :subscription => { + :to => "f-cfutg-#{phone_number.id}@#{sip_account.host}", + :for => "#{sip_account.auth_name}@#{sip_account.host}" + }, + :actions => [{ + :type => :url, + :target => "#{request.protocol}#{request.host_with_port}/config_snom/#{@phone.id}/#{snom_sip_account[:id]}/call_forwarding.xml?type=always&function=toggle#{account_param}", + :when => 'on press', + }], + }) + when 'call_forwarding_assistant' + phone_number = PhoneNumber.where(:number => softkey.number, :phone_numberable_type => 'SipAccount').first + if phone_number + account_param = (phone_number.phone_numberable_id != snom_sip_account[:id] ? "&account=#{phone_number.phone_numberable_id}" : '') + else + phone_number = sip_account.phone_numbers.first + account_param = '' + end + + @softkeys.push({ + :context => sip_account_index, + :function => softkey.function, + :label => softkey.label, + :softkey => softkey, + :general_type => t("softkeys.functions.#{softkey.softkey_function.name}"), + :subscription => { + :to => "f-cfatg-#{phone_number.id}@#{sip_account.host}", + :for => "#{sip_account.auth_name}@#{sip_account.host}" + }, + :actions => [{ + :type => :url, + :target => "#{request.protocol}#{request.host_with_port}/config_snom/#{@phone.id}/#{snom_sip_account[:id]}/call_forwarding.xml?type=assistant&function=toggle#{account_param}", + :when => 'on press', + }], + }) + when 'hunt_group_membership' + phone_number = PhoneNumber.where(:number => softkey.number, :phone_numberable_type => 'HuntGroup').first + if phone_number + hunt_group = HuntGroup.where(:id => phone_number.phone_numberable_id).first + end + + sip_account_phone_numbers = Array.new() + SipAccount.where(:id => @sip_accounts.first[:id]).first.phone_numbers.each do |phone_number| + sip_account_phone_numbers.push(phone_number.number) + end + + hunt_group_member_numbers = PhoneNumber.where(:number => sip_account_phone_numbers, :phone_numberable_type => 'HuntGroupMember') + + hunt_group_member = nil + if hunt_group and hunt_group_member_numbers + hunt_group_member_numbers.each do |hunt_group_member_number| + hunt_group_member = hunt_group.hunt_group_members.where(:id => hunt_group_member_number.phone_numberable_id).first + if hunt_group_member + break + end + end + end + + if hunt_group_member + @softkeys.push({ + :context => sip_account_index, + :function => softkey.function, + :label => softkey.label, + :softkey => softkey, + :general_type => t("softkeys.functions.#{softkey.softkey_function.name}"), + :subscription => { + :to => "f-hgmtg-#{hunt_group_member.id}@#{sip_account.host}", + :for => "#{sip_account.auth_name}@#{sip_account.host}" + }, + :actions => [{ + :type => :url, + :target => "#{request.protocol}#{request.host_with_port}/config_snom/#{@phone.id}/#{snom_sip_account[:id]}/hunt_group.xml?group=#{hunt_group.id}&account=#{hunt_group_member.id}&function=toggle", + :when => 'on press', + }], + }) + else + @softkeys.push({:context => sip_account_index, :label => softkey.label, :data => 'none'}) + end + when 'acd_membership' + acd_agent = nil + phone_number = PhoneNumber.where(:number => softkey.number, :phone_numberable_type => 'AutomaticCallDistributor').first + if phone_number + acd = AutomaticCallDistributor.where(:id => phone_number.phone_numberable_id).first + if acd + acd_agent = acd.acd_agents.where(:destination_type => 'SipAccount', :destination_id => sip_account.id).first + end + end + + if acd_agent + @softkeys.push({ + :context => sip_account_index, + :function => softkey.function, + :label => softkey.label, + :softkey => softkey, + :general_type => t("softkeys.functions.#{softkey.softkey_function.name}"), + :subscription => { + :to => "f-acdmtg-#{acd_agent.id}@#{sip_account.host}", + :for => "#{sip_account.auth_name}@#{sip_account.host}" + }, + :actions => [{ + :type => :url, + :target => "#{request.protocol}#{request.host_with_port}/config_snom/#{@phone.id}/#{snom_sip_account[:id]}/acd.xml?acd=#{acd.id}&agent=#{acd_agent.id}&function=toggle", + :when => 'on press', + }], + }) + else + @softkeys.push({:context => sip_account_index, :label => softkey.label, :data => 'none'}) + end + when 'hold' + @softkeys.push({:context => sip_account_index, :label => softkey.label, :data => "keyevent F_R"}) + else + @softkeys.push({:label => softkey.label, :data => 'none'}) + end + end + end + end + end + + languages_map = { + 'ca' => 'Catalan', + 'bs' => 'Bosanski', + 'da' => 'Dansk', + 'de' => 'Deutsch', + 'cs' => 'Cestina', + 'en' => 'English', + 'es' => 'Espanol', + 'fi' => 'Suomi', + 'et' => 'Estonian', + 'fr' => 'Francais', + 'he' => 'Hebrew', + 'hu' => 'Hungarian', + 'it' => 'Italiano', + 'nl' => 'Dutch', + 'no' => 'Norsk', + 'pl' => 'Polski', + 'pt' => 'Portugues', + 'si' => 'Slovenian', + 'sk' => 'Slovencina', + 'ru' => 'Russian', + 'sv' => 'Svenska', + 'tr' => 'Turkce', + } + + tone_schemes_map = { + '1' => 'USA', # United States + '61' => 'AUS', # Australia + '43' => 'AUT', # Austria + '86' => 'CHN', # China + '45' => 'DNK', # Denmark + '33' => 'FRA', # France + '49' => 'GER', # Germany + '44' => 'GBR', # Great Britain + '91' => 'IND', # India + '39' => 'ITA', # Italy + '81' => 'JPN', # Japan + '52' => 'MEX', # Mexico + '31' => 'NLD', # Netherlands + '47' => 'NOR', # Norway + '64' => 'NZL', # New Zealand + '34' => 'ESP', # Spain + '46' => 'SWE', # Sweden + '41' => 'SWI', # Switzerland + } + + if @phone.phoneable + if @phone.phoneable_type == 'Tenant' + tenant = @phone.phoneable + language = tenant.language.code + elsif @phone.phoneable_type == 'User' + tenant = @phone.phoneable.current_tenant + language = @phone.phoneable.language.code + end + end + + if tenant && tenant.country + tone_scheme = tenant.country.country_code + end + + @phone_settings[:tone_scheme] = tone_schemes_map.include?(tone_scheme.to_s) ? tone_schemes_map[tone_scheme.to_s] : 'USA' + @phone_settings[:language] = languages_map.include?(language.to_s) ? languages_map[language.to_s] : 'English' + + xml_applications_url = "#{request.protocol}#{request.host_with_port}/config_snom/#{@phone.id}/#{(@sip_accounts.blank? ? '0' : @sip_accounts.first[:id])}" + @dkeys = { + :menu => 'keyevent F_SETTINGS', + :retrieve => 'speed f-vmcheck', + :conf => 'keyevent F_CONFERENCE', + :redial => "url #{xml_applications_url}/call_history.xml?type=dialed", + :directory => "url #{xml_applications_url}/phone_book.xml", + :idle_ok => "url #{xml_applications_url}/call_history.xml?type=dialed", + :idle_cancel => "keyevent F_CANCEL", + :idle_up => "keyevent F_PREV_ID", + :idle_down => "keyevent F_NEXT_ID", + :idle_left => "url #{xml_applications_url}/call_history.xml?type=received", + :idle_right => "url #{xml_applications_url}/call_history.xml?type=missed", + } + + # Remap conference key to first conference if found + #conference = Conference.where(:conferenceable_type => @phone.phoneable_type, :conferenceable_id => @phone.phoneable_id).first + #if conference and conference.phone_numbers + # @dkeys[:conf] = "speed f_ta_#{conference.phone_numbers.first.number}" + #end + + @sip_accounts.length().upto(MAX_SIP_ACCOUNTS_COUNT) do |index| + snom_sip_account = { + :id => index, + :active => 'off', + :pname => '', + :pass => '', + :host => '', + :outbound => '', + :name => '', + :realname => '', + :idle_text => '', + } + @sip_accounts.push(snom_sip_account) + end + + @softkeys.length().upto(MAX_SOFTKEYS_COUNT) do |index| + @softkeys.push({:label => "", :data => "none"}) + end + + @state_settings_url = "#{request.protocol}#{request.host_with_port}/config_snom/#{@phone.id}/state_settings.xml" + + respond_to { |format| + format.any { + self.formats = [ :xml ] + render + } + } + end + + def idle_screen + + snom_360_bg = 'Qk0+BAAAAAAAAD4AAAAoAAAAgAAAAEAAAAABAAEAAAAAAAAEAAATCwAAEwsAAAIAAAACAAAA//// +AAAAAAAAAAAAAAAAAAAAAbbZxzbbAAAAAAAAAAAAAAG222222wAAAAAAAAAAAAAB9ttttt8AAAAA +AAAAAAAAAbbbbbbbAAAAAAAAAAAAAAG222222wAAAAAAAAAAAAABttttttsAAAAAAAAAAAAAAOPx +xx+OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkkkkkkkkkkkkkkAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAJJJJJJJJJJJJJJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAACSSSSSSSSSSSSSQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAA' + + @phone_xml_object = { + :image => { + :data => snom_360_bg, + :location_x => 0, + :location_y => 0, + :invert => 0 + }, + :clock => { + :location_x => 128, + :location_y => 0, + }, + :date => { + :location_x => 100, + :location_y => 40, + }, + :line => { + :location_x => 0, + :location_y => 0, + }, + } + + respond_to { |format| + format.any { + self.formats = [ :xml ] + render + } + } + end + + def log_in + + base_url = "#{request.protocol}#{request.host_with_port}#{request.fullpath.split("?")[0]}" + exit_url = "#{request.protocol}#{request.host_with_port}#{request.fullpath.rpartition("/")[0]}/exit.xml" + + log_in_number = params[:log_in].to_s.gsub(/[^0-9]/,'') + pin = params[:pin].to_s.gsub(/[^0-9]/,'') + + if ! params[:user].blank? + user = User.where(:id => params[:user].to_i).first + phone_number = PhoneNumber.where(:number => log_in_number, :phone_numberable_type => 'SipAccount').first + if phone_number + sip_account = phone_number.phone_numberable + end + elsif ! params[:log_in].blank? + phone_number = PhoneNumber.where(:number => log_in_number, :phone_numberable_type => 'SipAccount').first + if phone_number && phone_number.phone_numberable && phone_number.phone_numberable.sip_accountable && phone_number.phone_numberable.sip_accountable_type == 'User' + user = phone_number.phone_numberable.sip_accountable + end + end + + @phone_xml_object = { + :name => "snom_phone_text", + :title => "Error", + :prompt => "Log in", + :text => 'Log in failed!', + :fetch_url => base_url, + :fetch_mil => '2000', + } + + if ! user + @phone_xml_object = { + :name => "snom_phone_input", + :title => "Log In", + :prompt => "Log In", + :url => base_url, + :display_name => "Log In", + :query_string_param => "log_in", + :default_value => log_in_number, + :input_flags => "n", + :softkeys => [ + {:name => "F1", :label => "Exit", :url => exit_url} + ] + } + elsif pin.blank? + @phone_xml_object = { + :name => "snom_phone_input", + :title => "PIN", + :prompt => "PIN", + :url => base_url, + :display_name => "PIN", + :query_string_param => "user=#{user.id}&log_in=#{log_in_number}&pin", + :default_value => "", + :input_flags => "pn", + :softkeys => [ + {:name => "F1", :label => "Exit", :url => exit_url} + ] + } + elsif user.authenticate_by_pin?(pin) + if @phone.user_login(user, sip_account) + @phone_xml_object = { + :name => "snom_phone_text", + :title => "Log in successful", + :prompt => "Log in", + :text => "#{user.to_s} logged in", + :fetch_url => exit_url, + :fetch_mil => '1000', + } + end + end + + respond_to { |format| + format.any { + self.formats = [ :xml ] + render :action => "_#{@phone_xml_object[:name]}" + } + } + end + + def log_out + if ! @phone + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- Phone not found -->", + ) + return + end + + exit_url = "#{request.protocol}#{request.host_with_port}#{request.fullpath.rpartition("/")[0]}/exit.xml" + + if @phone.user_logout() + @phone_xml_object = { + :name => "snom_phone_text", + :title => "Log out successful", + :prompt => "Log out", + :text => 'Log out successful', + :fetch_url => exit_url, + :fetch_mil => '1000', + } + else + @phone_xml_object = { + :name => "snom_phone_text", + :title => "Error", + :prompt => "Log out", + :text => 'Log out failed!', + :fetch_url => exit_url, + :fetch_mil => '2000', + } + end + + respond_to { |format| + format.any { + self.formats = [ :xml ] + render :action => "_#{@phone_xml_object[:name]}" + } + } + end + + def phone_book + + if ! @sip_account + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- SipAccount not found -->", + ) + return + end + + @phone_xml_object = { + :name => 'snom_phone_directory', + :title => "$(lang:menu100_phone_book) #{@dialpad_keys}".strip, + :entries => [], + :softkeys => [], + } + + phone_books = Array.new() + phone_books = phone_books + @sip_account.sip_accountable.try(:phone_books).all + if @sip_account.sip_accountable.class == User + phone_books = phone_books + @sip_account.sip_accountable.try(:current_tenant).try(:phone_books).all + end + + phone_book_ids = Array.new() + phone_books.each do |phone_book| + phone_book_ids << phone_book.id + end + + PhoneBookEntry.where(:phone_book_id => phone_book_ids).order(:last_name).order(:first_name).limit(MAX_DIRECTORY_ENTRIES).each do |phone_book_entry| + if phone_book_entry.phone_numbers.count > 1 + @phone_xml_object[:entries] << { :text => phone_book_entry.to_s, :number => phone_book_entry.phone_numbers.first } + end + phone_book_entry.phone_numbers.each do |phone_number| + if phone_book_entry.phone_numbers.count > 1 + entry_name = " #{phone_number.name} #{phone_number.number}" + else + entry_name = "#{phone_book_entry.to_s} #{phone_number.number}" + end + + @phone_xml_object[:entries] << { :text => entry_name, :number => phone_number.number } + end + end + + base_url = "#{request.protocol}#{request.host_with_port}#{request.fullpath.split("?")[0]}" + phone_book_url = "#{base_url}?type=#{@type.to_s}" + for key_id in (0..9) + @phone_xml_object[:softkeys] << {:name => key_id, :url => "#{phone_book_url}&keys=#{@dialpad_keys.to_s}#{key_id}" } + end + @phone_xml_object[:softkeys] << {:name => '*', :url => "#{phone_book_url}&keys=#{@dialpad_keys.to_s[0..-2]}" } + @phone_xml_object[:softkeys] << {:name => '#', :url => "#{phone_book_url}&keys=" } + + respond_to { |format| + format.any { + self.formats = [ :xml ] + render :action => "_#{@phone_xml_object[:name]}" + } + } + + end + + def call_history + + if ! @sip_account + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- SipAccount not found -->", + ) + return + end + + if ['dialed', 'missed', 'received'].include? @type + @phone_xml_object = { + :name => "snom_phone_directory", + :title => "$(lang:menu100_call_lists) - #{@type.to_s.camelize}", + :entries => [] + } + + if @type == 'missed' + hunt_group_member_ids = PhoneNumber.where(:phone_numberable_type => 'HuntGroupMember', :number => @sip_account.phone_numbers.map {|a| a.number}).map {|a| a.phone_numberable_id} + hunt_group_ids = HuntGroupMember.where(:id => hunt_group_member_ids, :active => true).map {|a| a.hunt_group_id} + calls = CallHistory.where('entry_type = ? AND ((call_historyable_type = "SipAccount" AND call_historyable_id = ?) OR (call_historyable_type = "HuntGroup" AND call_historyable_id IN (?)))', @type, @sip_account.id, hunt_group_ids).order('start_stamp DESC').limit(MAX_DIRECTORY_ENTRIES) + else + calls = @sip_account.call_histories.where(:entry_type => @type).order('start_stamp DESC').limit(MAX_DIRECTORY_ENTRIES) + end + + calls.each do |call| + display_name = call.display_name + phone_number = call.display_number + phone_book_entry = call.phone_book_entry_by_number(phone_number) + if display_name.blank? + display_name = phone_book_entry.to_s + end + + @phone_xml_object[:entries].push({ + :selected => false, + :number => phone_number, + :text => "#{call_date_compact(call.start_stamp)} #{display_name} #{call.display_number}", + }) + end + else + base_url = "#{request.protocol}#{request.host_with_port}#{request.fullpath.split("?")[0]}" + @phone_xml_object = { + :name => 'snom_phone_menu', + :title => '$(lang:menu100_call_lists)', + :entries => [ + {:text => '$(lang:list_missed)', :url => "#{base_url}?&type=missed", :selected => false}, + {:text => '$(lang:list_taken)', :url => "#{base_url}?&type=received", :selected => false}, + {:text => '$(lang:list_dialed)', :url => "#{base_url}?&type=dialed", :selected => false}, + ] + } + end + + respond_to { |format| + format.any { + self.formats = [ :xml ] + render :action => "_#{@phone_xml_object[:name]}" + } + } + + end + + def state_settings + @base_url = "#{request.protocol}#{request.host_with_port}#{request.fullpath.rpartition("/")[0]}" + + @sip_account_ids = Array.new() + @phone.sip_accounts.each do |sip_account| + if (sip_account.sip_accountable_type == @phone.phoneable_type) and (sip_account.sip_accountable_id == @phone.phoneable_id) + @sip_account_ids.push(sip_account.id) + end + end + + if @phone.hot_deskable + @enable_login = true + if @phone.phoneable_type != 'Tenant' + @enable_logout = true + end + end + + respond_to { |format| + format.any { + self.formats = [ :xml ] + render + } + } + end + + def call_forwarding + if ! params[:type].blank? + @type = params[:type] + end + + if ! params[:function].blank? + @function = params[:function] + end + + if ! params[:id].blank? + @call_forwarding_id = params[:id].to_i + end + + if ! params[:sip_account].blank? + @sip_account = SipAccount.where({ :id => params[:sip_account].to_i }).first + end + + if ! params[:account].blank? + @sip_account = SipAccount.where({ :id => params[:account].to_i }).first + end + + + if ! @sip_account + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- SipAccount not found -->", + ) + return + end + + exit_url = "#{request.protocol}#{request.host_with_port}#{request.fullpath.rpartition("/")[0]}/exit.xml" + + if @function == 'toggle' + if @call_forwarding_id + call_forwarding = @sip_account.call_forwards.where(:id => @call_forwarding_id).first + + if !call_forwarding and @sip_account.softkeys.where(:call_forward_id => @call_forwarding_id).count > 0 + call_forwarding = CallForward.where(:id => @call_forwarding_id).first + end + + if call_forwarding + call_forwarding.toggle + end + elsif @type + call_forwarding = @sip_account.call_forwarding_toggle(@type) + end + if !call_forwarding + render( + :status => 500, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- Call forwarding not set: #{@sip_account.errors.messages.inspect} -->", + ) + return + end + + if !call_forwarding.errors.blank? + error_messages = Array.new() + call_forwarding.errors.messages.each_pair do |key, message| + error_messages.push(message.join(';')) + end + @phone_xml_object = { + :name => 'snom_phone_text', + :title => t("call_forwards.name"), + :prompt => t("call_forwards.name"), + :text => "ERROR #{error_messages.join(',')} #{call_forwarding.to_s})", + :fetch_url => exit_url, + :fetch_mil => '1000', + } + elsif call_forwarding.active + @phone_xml_object = { + :name => 'snom_phone_text', + :title => t("call_forwards.name"), + :prompt => t("call_forwards.name"), + :text => "ON #{call_forwarding.to_s})", + :fetch_url => exit_url, + :fetch_mil => '1000', + } + else + @phone_xml_object = { + :name => 'snom_phone_text', + :title => t("call_forwards.name"), + :prompt => t("call_forwards.name"), + :text => "OFF #{call_forwarding.to_s}", + :fetch_url => exit_url, + :fetch_mil => '1000', + } + end + end + + respond_to { |format| + format.any { + self.formats = [ :xml ] + render :action => "_#{@phone_xml_object[:name]}" + } + } + end + + def hunt_group + if ! params[:function].blank? + @function = params[:function] + end + + if ! params[:sip_account].blank? + @sip_account = SipAccount.where({ :id => params[:sip_account].to_i }).first + end + + if ! params[:group].blank? + @hunt_group = HuntGroup.where({ :id => params[:group].to_i }).first + end + + if ! @sip_account + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- SipAccount not found -->", + ) + return + end + + if ! @hunt_group + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- HuntGroup not found -->", + ) + return + end + + if ! params[:account].blank? + hunt_group_member = @hunt_group.hunt_group_members.where({ :id => params[:account].to_i }).first + end + + if ! hunt_group_member + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- HuntGroupMember not found -->", + ) + return + end + + exit_url = "#{request.protocol}#{request.host_with_port}#{request.fullpath.rpartition("/")[0]}/exit.xml" + + if @function == 'toggle' + if hunt_group_member.can_switch_status_itself == true + if hunt_group_member.active + hunt_group_member.active = false + else + hunt_group_member.active = true + end + + if ! hunt_group_member.save + render( + :status => 500, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- #{hunt_group_member.errors.inspect} -->", + ) + return + end + end + + if hunt_group_member.active + @phone_xml_object = { + :name => 'snom_phone_text', + :title => 'Hunt Group', + :prompt => 'Hunt Group', + :text => "#{@hunt_group.name} on", + :fetch_url => exit_url, + :fetch_mil => '1000', + } + else + @phone_xml_object = { + :name => 'snom_phone_text', + :title => 'Hunt Group', + :prompt => 'Hunt Group', + :text => "#{@hunt_group.name} off", + :fetch_url => exit_url, + :fetch_mil => '1000', + } + end + + respond_to { |format| + format.any { + self.formats = [ :xml ] + render :action => "_#{@phone_xml_object[:name]}" + } + } + end + end + + def acd + if ! params[:function].blank? + @function = params[:function] + end + + if ! params[:sip_account].blank? + @sip_account = SipAccount.where({ :id => params[:sip_account].to_i }).first + end + + if ! params[:acd].blank? + @acd = AutomaticCallDistributor.where({ :id => params[:acd].to_i }).first + end + + if ! @sip_account + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- SipAccount not found -->", + ) + return + end + + if ! @acd + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- AutomaticCallDistributor not found -->", + ) + return + end + + if ! params[:agent].blank? + acd_agent = @acd.acd_agents.where({ :id => params[:agent].to_i }).first + end + + if ! acd_agent + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- ACD Agent not found -->", + ) + return + end + + exit_url = "#{request.protocol}#{request.host_with_port}#{request.fullpath.rpartition("/")[0]}/exit.xml" + + if @function == 'toggle' + if acd_agent.status == 'active' + acd_agent.status = 'inactive' + else + acd_agent.status = 'active' + end + + if ! acd_agent.save + render( + :status => 500, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- #{acd_agent.errors.inspect} -->", + ) + return + end + + if acd_agent.status == 'active' + @phone_xml_object = { + :name => 'snom_phone_text', + :title => 'ACD', + :prompt => 'ACD', + :text => "#{@acd.name} on", + :fetch_url => exit_url, + :fetch_mil => '1000', + } + else + @phone_xml_object = { + :name => 'snom_phone_text', + :title => 'ACD', + :prompt => 'ACD', + :text => "#{@acd.name} off", + :fetch_url => exit_url, + :fetch_mil => '1000', + } + end + + respond_to { |format| + format.any { + self.formats = [ :xml ] + render :action => "_#{@phone_xml_object[:name]}" + } + } + end + end + + def exit + render( + :status => 200, + :layout => false, + :content_type => 'text/xml', + :text => "<exit />", + ) + end + + def call_date_compact(date) + if date.strftime('%Y%m%d') == DateTime::now.strftime('%Y%m%d') + return date.strftime('%H:%M') + end + return date.strftime('%d.%m %H:%M') + end + +end diff --git a/app/controllers/fax_accounts_controller.rb b/app/controllers/fax_accounts_controller.rb new file mode 100644 index 0000000..ce03bc5 --- /dev/null +++ b/app/controllers/fax_accounts_controller.rb @@ -0,0 +1,82 @@ +class FaxAccountsController < ApplicationController + load_resource :user + load_resource :user_group + load_and_authorize_resource :fax_account, :through => [:user, :user_group] + + before_filter :set_and_authorize_parent + before_filter :spread_breadcrumbs + + def index + end + + def show + end + + def new + @fax_account = @parent.fax_accounts.build + @fax_account.name = generate_a_new_name(@parent, @fax_account) + @fax_account.days_till_auto_delete = DAYS_TILL_AUTO_DELETE + @fax_account.retries = DEFAULT_NUMBER_OF_RETRIES + @fax_account.station_id = @parent.to_s + @fax_account.phone_numbers.build + if @parent.class == User && !@parent.email.blank? + @fax_account.email = @parent.email + end + end + + def create + @fax_account = @parent.fax_accounts.build(params[:fax_account]) + if @fax_account.save + m = method( :"#{@parent.class.name.underscore}_fax_account_path" ) + redirect_to m.( @parent, @fax_account ), :notice => t('fax_accounts.controller.successfuly_created') + else + render :new + end + end + + def edit + end + + def update + if @fax_account.update_attributes(params[:fax_account]) + m = method( :"#{@parent.class.name.underscore}_fax_account_path" ) + redirect_to m.( @parent, @fax_account ), :notice => t('fax_accounts.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @fax_account.destroy + m = method( :"#{@parent.class.name.underscore}_fax_accounts_url" ) + redirect_to m.( @parent ), :notice => t('fax_accounts.controller.successfuly_destroyed') + end + + private + def set_and_authorize_parent + @parent = @user || @user_group + authorize! :read, @parent + end + + def spread_breadcrumbs + if @parent && @parent.class == User + add_breadcrumb t("users.index.page_title"), tenant_users_path(@user.current_tenant) + add_breadcrumb @user, tenant_user_path(@user.current_tenant, @user) + add_breadcrumb t("fax_accounts.index.page_title"), user_fax_accounts_path(@user) + if @fax_account && !@fax_account.new_record? + add_breadcrumb @fax_account, user_fax_account_path(@user, @fax_account) + end + end + + if @parent && @parent.class == UserGroup + @user_group = @parent + add_breadcrumb t("user_groups.index.page_title"), tenant_user_groups_path(@user_group.tenant) + add_breadcrumb @user_group, tenant_user_group_path(@user_group.tenant, @user_group) + add_breadcrumb t("fax_accounts.index.page_title"), user_group_fax_accounts_path(@user_group) + if @fax_account && !@fax_account.new_record? + add_breadcrumb @fax_account, user_group_fax_account_path(@user_group, @fax_account) + end + end + end + +end diff --git a/app/controllers/fax_documents_controller.rb b/app/controllers/fax_documents_controller.rb new file mode 100644 index 0000000..eff9604 --- /dev/null +++ b/app/controllers/fax_documents_controller.rb @@ -0,0 +1,82 @@ +class FaxDocumentsController < ApplicationController + load_and_authorize_resource :fax_account + load_and_authorize_resource :fax_document, :through => [:fax_account] + + before_filter :spread_breadcrumbs + + def index + @fax_documents = @fax_documents.order(:created_at).reverse_order + end + + def show + respond_to do |format| + @fax_document = FaxDocument.find(params[:id]) + format.html + format.xml { render :xml => @fax_document } + format.pdf { + caller_number = @fax_document.caller_id_number.to_s.gsub(/[^0-9]/, '') + if caller_number.blank? + caller_number = 'anonymous' + end + + if @fax_document.document.path + send_file @fax_document.document.path, :type => "application/pdf", + :filename => "#{@fax_document.created_at.strftime('%Y%m%d-%H%M%S')}-#{caller_number}.pdf" + else + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- Document not found -->", + ) + end + } + end + end + + def new + @fax_document = @fax_account.fax_documents.build + @phone_number = @fax_document.build_destination_phone_number + end + + def create + @fax_document = @fax_account.fax_documents.build(params[:fax_document]) + @fax_document.retry_counter = @fax_account.retries + if @fax_document.save + @fax_document.queue_for_sending! + redirect_to fax_account_fax_document_path(@fax_document.fax_account, @fax_document), :notice => t('fax_documents.controller.successfuly_created') + else + render :new + end + end + + def destroy + @fax_account = FaxAccount.find(params[:fax_account_id]) + @fax_document = @fax_account.fax_documents.find(params[:id]) + @fax_document.destroy + redirect_to fax_account_fax_documents_url, :notice => t('fax_documents.controller.successfuly_destroyed') + end + + private + def spread_breadcrumbs + breadcrumbs = [] + breadcrumbs = case @fax_account.fax_accountable.class.to_s + when 'User' ; [ + [@fax_account.fax_accountable.to_s, user_path(@fax_account.fax_accountable)], + [t('fax_accounts.name').pluralize, user_fax_accounts_path(@fax_account.fax_accountable)], + [t('fax_documents.name').pluralize, fax_account_fax_documents_path(@fax_account)], + ] + when 'UserGroup' ; [ + [@fax_account.fax_accountable, user_group_path(@fax_account.fax_accountable)], + [t('fax_accounts.name').pluralize, user_group_fax_accounts_path(@fax_account.fax_accountable)], + [t('fax_documents.name').pluralize, fax_account_fax_documents_path(@fax_account)], + ] + end + if !breadcrumbs.blank? + breadcrumbs.each do |breadcrumb| + add_breadcrumb breadcrumb[0], breadcrumb[1] + end + end + end + +end diff --git a/app/controllers/freeswitch_voicemail_msgs_controller.rb b/app/controllers/freeswitch_voicemail_msgs_controller.rb new file mode 100644 index 0000000..085db3d --- /dev/null +++ b/app/controllers/freeswitch_voicemail_msgs_controller.rb @@ -0,0 +1,7 @@ +class FreeswitchVoicemailMsgsController < ApplicationController + load_and_authorize_resource :sip_account + load_and_authorize_resource :freeswitch_voicemail_msg, :through => [:sip_account] + + def index + end +end diff --git a/app/controllers/gemeinschaft_setups_controller.rb b/app/controllers/gemeinschaft_setups_controller.rb new file mode 100644 index 0000000..cafb8a3 --- /dev/null +++ b/app/controllers/gemeinschaft_setups_controller.rb @@ -0,0 +1,61 @@ +class GemeinschaftSetupsController < ApplicationController + load_and_authorize_resource :gemeinschaft_setup + + skip_before_filter :go_to_setup_if_new_installation + # before_filter :redirect_if_not_a_fresh_installation + + def new + @user = @gemeinschaft_setup.build_user( + :user_name => t('gemeinschaft_setups.initial_setup.admin_name'), + :male => true, + :email => 'admin@localhost', + ) + @sip_domain = @gemeinschaft_setup.build_sip_domain( + :host => guess_local_host(), + :realm => guess_local_host(), + ) + @gemeinschaft_setup.country = Country.find_by_name('Germany') + @gemeinschaft_setup.language = Language.find_by_name('Deutsch') + end + + def create + if @gemeinschaft_setup.save + super_tenant = Tenant.create( + :name => SUPER_TENANT_NAME, + :country_id => @gemeinschaft_setup.country.id, + :language_id => @gemeinschaft_setup.language_id, + :description => t('gemeinschaft_setups.initial_setup.super_tenant_description'), + ) + + # Admin + user = @gemeinschaft_setup.user + super_tenant.tenant_memberships.create(:user_id => user.id) + user.update_attributes(:current_tenant_id => super_tenant.id) + + # Create the Super-Tenant's group: + super_tenant_super_admin_group = super_tenant.user_groups.create(:name => t('gemeinschaft_setups.initial_setup.super_admin_group_name')) + super_tenant_super_admin_group.user_group_memberships.create(:user_id => user.id) + + # Auto-Login: + session[:user_id] = user.id + + # Redirect to the user + redirect_to new_tenant_url, :notice => t('gemeinschaft_setups.initial_setup.successful_setup') + else + render :new + end + end + + private + + def redirect_if_not_a_fresh_installation + if GemeinschaftSetup.all.count > 0 + if current_user + redirect_to root_url , :alert => t('gemeinschaft_setups.initial_setup.access_denied_only_available_on_a_new_system') + else + redirect_to log_in_path , :alert => t('gemeinschaft_setups.initial_setup.access_denied_only_available_on_a_new_system') + end + end + end + +end diff --git a/app/controllers/gs_cluster_sync_log_entries_controller.rb b/app/controllers/gs_cluster_sync_log_entries_controller.rb new file mode 100644 index 0000000..3e65037 --- /dev/null +++ b/app/controllers/gs_cluster_sync_log_entries_controller.rb @@ -0,0 +1,25 @@ +class GsClusterSyncLogEntriesController < ApplicationController + + # GET /gs_cluster_sync_log_entries/new.json + def new + @gs_cluster_sync_log_entry = GsClusterSyncLogEntry.new + + respond_to do |format| + format.json { render json: @gs_cluster_sync_log_entry } + end + end + + # POST /gs_cluster_sync_log_entries.json + def create + @gs_cluster_sync_log_entry = GsClusterSyncLogEntry.new(params[:gs_cluster_sync_log_entry]) + + respond_to do |format| + if @gs_cluster_sync_log_entry.save + format.json { render json: @gs_cluster_sync_log_entry, status: :created, location: @gs_cluster_sync_log_entry } + else + format.json { render json: @gs_cluster_sync_log_entry.errors, status: :unprocessable_entity } + end + end + end + +end diff --git a/app/controllers/gs_nodes_controller.rb b/app/controllers/gs_nodes_controller.rb new file mode 100644 index 0000000..3667775 --- /dev/null +++ b/app/controllers/gs_nodes_controller.rb @@ -0,0 +1,170 @@ +class GsNodesController < ApplicationController + + load_and_authorize_resource :gs_node, :only => [:index, :show, :new, :create, :edit, :update, :destroy] + + before_filter :spread_breadcrumbs + + def index + + end + + def show + + end + + def new + @gs_node = GsNode.new + @gs_node.push_updates_to = true + @gs_node.accepts_updates_from = true + @gs_node.element_name = 'gs_cluster_sync_log_entry' + end + + def create + @gs_node = GsNode.new(params[:gs_node]) + if @gs_node.save + redirect_to @gs_node, :notice => t('gs_nodes.controller.successfuly_created') + else + render :new + end + end + + def edit + @gs_node = GsNode.find(params[:id]) + end + + def update + @gs_node = GsNode.find(params[:id]) + if @gs_node.update_attributes(params[:gs_node]) + redirect_to @gs_node, :notice => t('gs_nodes.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @gs_node = GsNode.find(params[:id]) + @gs_node.destroy + redirect_to gs_nodes_url, :notice => t('gs_nodes.controller.successfuly_destroyed') + end + + def sync + if !GsNode.where(:ip_address => request.remote_ip).first + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- Node not found -->", + ) + return + end + + if ! params[:newer].blank? + @newer_as = Time.at(params[:newer].to_i) + else + @newer_as = Time.at(0) + end + + if ! params[:class].blank? + @request_class = params[:class].to_s + else + @request_class = ''; + end + + @node = GsNode.where(:ip_address => HOMEBASE_IP_ADDRESS).first + + if @request_class.blank? || @request_class == "tenants" + @tenants = Tenant.where('updated_at > ?', @newer_as) + end + + if @request_class.blank? || @request_class == "user_groups" + @user_groups = UserGroup.where('updated_at > ?', @newer_as) + end + + if @request_class.blank? || @request_class == "users" + @users = User.where('updated_at > ?', @newer_as) + end + + if @request_class.blank? || @request_class == "user_group_memberships" + @user_group_memberships = UserGroupMembership.where('updated_at > ?', @newer_as) + end + + if @request_class.blank? || @request_class == "sip_accounts" + @sip_accounts = SipAccount.where('updated_at > ?',@newer_as) + end + + if @request_class.blank? || @request_class == "conferences" + @conferences = Conference.where('updated_at > ?', @newer_as) + end + + if @request_class.blank? || @request_class == "fax_accounts" + @fax_accounts = FaxAccount.where('updated_at > ?', @newer_as) + end + + if @request_class.blank? || @request_class == "phone_books" + @phone_books = PhoneBook.where('updated_at > ?',@newer_as) + end + + if @request_class.blank? || @request_class == "phone_book_entries" + @phone_book_entries = PhoneBookEntry.where('updated_at > ?',@newer_as) + end + + if @request_class.blank? || @request_class == "phone_numbers" + @phone_numbers = PhoneNumber.where('updated_at > ?', @newer_as) + end + + if @request_class.blank? || @request_class == "call_forwards" + @call_forwards = CallForward.where('updated_at > ?', @newer_as) + end + + if @request_class.blank? || @request_class == "softkeys" + @softkeys = Softkey.where('updated_at > ?', @newer_as) + end + + if @request_class.blank? || @request_class == "ringtones" + @ringtones = Ringtone.where('updated_at > ?', @newer_as) + end + + if @request_class.blank? || @request_class == "conference_invitees" + @conference_invitees = ConferenceInvitee.where('updated_at > ?', @newer_as) + end + + if @request_class == "fax_documents" + @fax_documents = FaxDocument.where('updated_at > ?', @newer_as) + end + + if @request_class == "call_histories" + @call_histories = CallHistory.where('updated_at > ?', @newer_as) + end + + if @request_class.blank? || @request_class == "deleted_items" + @deleted_items = Array.new + deleted_items_log = GsClusterSyncLogEntry.where('action = "destroy" AND updated_at > ?', @newer_as) + deleted_items_log.each do |deleted_item_log_entry| + content = JSON(deleted_item_log_entry.content) + content['class_name'] = deleted_item_log_entry.class_name + if content['uuid'] + @deleted_items << content + end + end + end + + if params[:image].to_s == 'false' + @image_include = false + else + @image_include = true + end + + end + + private + + def spread_breadcrumbs + if @gs_node + add_breadcrumb t("gs_nodes.index.page_title"), gs_nodes_path + + if @gs_node && !@gs_node.new_record? + add_breadcrumb @gs_node, gs_node_path(@gs_node) + end + end + end +end diff --git a/app/controllers/gui_functions_controller.rb b/app/controllers/gui_functions_controller.rb new file mode 100644 index 0000000..2ab2c5e --- /dev/null +++ b/app/controllers/gui_functions_controller.rb @@ -0,0 +1,73 @@ +class GuiFunctionsController < ApplicationController + before_filter :load_user_groups + before_filter :spread_breadcrumbs + + def index + @gui_functions = GuiFunction.order(:category, :name) + end + + def show + @gui_function = GuiFunction.find(params[:id]) + end + + def new + @gui_function = GuiFunction.new + + @user_groups.each do |user_group| + if @gui_function.user_groups.where(:id => user_group.id).count == 0 + @gui_function.gui_function_memberships.build(:user_group_id => user_group.id, :activated => true) + end + end + end + + def create + @gui_function = GuiFunction.new(params[:gui_function]) + + if @gui_function.save + redirect_to @gui_function, :notice => t('gui_functions.controller.successfuly_created') + else + render :new + end + end + + def edit + @gui_function = GuiFunction.find(params[:id]) + @user_groups.each do |user_group| + if @gui_function.user_groups.where(:id => user_group.id).count == 0 + @gui_function.gui_function_memberships.build(:user_group_id => user_group.id, :activated => true) + end + end + end + + def update + @gui_function = GuiFunction.find(params[:id]) + if @gui_function.update_attributes(params[:gui_function]) + redirect_to @gui_function, :notice => t('gui_functions.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @gui_function = GuiFunction.find(params[:id]) + @gui_function.destroy + redirect_to gui_functions_url, :notice => t('gui_functions.controller.successfuly_destroyed') + end + + private + def load_user_groups + @user_groups = Tenant.find(@current_user.current_tenant).user_groups.order(:position) + end + + def spread_breadcrumbs + if @tenant + add_breadcrumb t("user_groups.index.page_title"), tenant_user_groups_path(@tenant) + if @user_group && !@user_group.new_record? + add_breadcrumb @user_group, tenant_user_group_path(@tenant, @user_group) + end + end + + add_breadcrumb t("gui_functions.index.page_title"), gui_functions_path + end + +end diff --git a/app/controllers/hunt_group_members_controller.rb b/app/controllers/hunt_group_members_controller.rb new file mode 100644 index 0000000..90206ee --- /dev/null +++ b/app/controllers/hunt_group_members_controller.rb @@ -0,0 +1,67 @@ +class HuntGroupMembersController < ApplicationController + load_and_authorize_resource :hunt_group + load_and_authorize_resource :hunt_group_member, :through => [:hunt_group] + + before_filter :spread_breadcrumbs + + def index + if params[:active] + if params[:active].downcase == 'true' + @hunt_group_members = @hunt_group_members.where(:active => true) + elsif params[:active].downcase == 'false' + @hunt_group_members = @hunt_group_members.where(:active => false) + end + end + end + + def show + end + + def new + @hunt_group_member = @hunt_group.hunt_group_members.build + + i = @hunt_group.hunt_group_members.count + loop do + i += 1 + break unless @hunt_group.hunt_group_members.where(:name => "#{t('hunt_group_members.name')} #{i}").count > 0 + end + @hunt_group_member.name = "#{t('hunt_group_members.name')} #{i}" + @hunt_group_member.active = true + @hunt_group_member.can_switch_status_itself = true + end + + def create + @hunt_group_member = @hunt_group.hunt_group_members.build(params[:hunt_group_member]) + if @hunt_group_member.save + redirect_to hunt_group_hunt_group_member_path(@hunt_group, @hunt_group_member), :notice => t('hunt_group_members.controller.successfuly_created') + else + render :new + end + end + + def edit + end + + def update + if @hunt_group_member.update_attributes(params[:hunt_group_member]) + redirect_to hunt_group_hunt_group_member_path(@hunt_group, @hunt_group_member), :notice => t('hunt_group_members.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @hunt_group_member.destroy + redirect_to hunt_group_hunt_group_members_path(@hunt_group), :notice => t('hunt_group_members.controller.successfuly_destroyed') + end + + def spread_breadcrumbs + add_breadcrumb t("hunt_groups.index.page_title"), tenant_hunt_groups_path(@hunt_group.tenant) + add_breadcrumb @hunt_group, tenant_hunt_group_path(@hunt_group.tenant, @hunt_group) + add_breadcrumb t("hunt_group_members.index.page_title"), hunt_group_hunt_group_members_path(@hunt_group) + if @hunt_group_member && !@hunt_group_member.new_record? + add_breadcrumb @hunt_group_member, hunt_group_hunt_group_member_path(@hunt_group, @hunt_group_member) + end + end + +end diff --git a/app/controllers/hunt_groups_controller.rb b/app/controllers/hunt_groups_controller.rb new file mode 100644 index 0000000..13a556a --- /dev/null +++ b/app/controllers/hunt_groups_controller.rb @@ -0,0 +1,55 @@ +class HuntGroupsController < ApplicationController + load_and_authorize_resource :tenant + load_and_authorize_resource :hunt_group, :through => [:tenant] + + before_filter :spread_breadcrumbs + + def index + end + + def show + end + + def new + i = @tenant.hunt_groups.count + loop do + i += 1 + break unless @tenant.hunt_groups.where(:name => "#{t('hunt_groups.name')} #{i}").count > 0 + end + @hunt_group = @tenant.hunt_groups.build(:name => "#{t('hunt_groups.name')} #{i}") + end + + def create + @hunt_group = @tenant.hunt_groups.build(params[:hunt_group]) + if @hunt_group.save + redirect_to tenant_hunt_group_path(@tenant, @hunt_group), :notice => t('hunt_groups.controller.successfuly_created') + else + render :new + end + end + + def edit + end + + def update + @hunt_group = HuntGroup.find(params[:id]) + if @hunt_group.update_attributes(params[:hunt_group]) + redirect_to tenant_hunt_group_path(@tenant, @hunt_group), :notice => t('hunt_groups.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @hunt_group.destroy + redirect_to tenant_hunt_groups_path(@tenant), :notice => t('hunt_groups.controller.successfuly_destroyed') + end + + private + def spread_breadcrumbs + add_breadcrumb t("hunt_groups.index.page_title"), tenant_hunt_groups_path(@tenant) + if @hunt_group && !@hunt_group.new_record? + add_breadcrumb @hunt_group, tenant_hunt_group_path(@tenant, @hunt_group) + end + end +end diff --git a/app/controllers/manufacturers_controller.rb b/app/controllers/manufacturers_controller.rb new file mode 100644 index 0000000..1bcf9de --- /dev/null +++ b/app/controllers/manufacturers_controller.rb @@ -0,0 +1,49 @@ +class ManufacturersController < ApplicationController + load_and_authorize_resource :manufacturer + + before_filter :spread_breadcrumbs + + def index + end + + def show + end + + def new + end + + def create + @manufacturer = Manufacturer.new(params[:manufacturer]) + if @manufacturer.save + redirect_to @manufacturer, :notice => t('manufacturers.controller.successfuly_created') + else + render :new + end + end + + def edit + end + + def update + if @manufacturer.update_attributes(params[:manufacturer]) + redirect_to @manufacturer, :notice => t('manufacturers.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @manufacturer.destroy + redirect_to manufacturers_url, :notice => t('manufacturers.controller.successfuly_destroyed') + end + + private + + def spread_breadcrumbs + add_breadcrumb t("manufacturers.index.page_title"), manufacturers_path + if @manufacturer && !@manufacturer.new_record? + add_breadcrumb @manufacturer, manufacturer_path(@manufacturer) + end + end + +end diff --git a/app/controllers/page_controller.rb b/app/controllers/page_controller.rb new file mode 100644 index 0000000..1f37449 --- /dev/null +++ b/app/controllers/page_controller.rb @@ -0,0 +1,24 @@ +class PageController < ApplicationController + # load_and_authorize_resource :class => false + # CanCan doesn't work here really good because Page is not a resource. + + before_filter :if_fresh_system_then_go_to_wizard + skip_before_filter :home_breadcrumb, :only => [:index] + + def index;end + def conference;end + + private + def if_fresh_system_then_go_to_wizard + if Tenant.count == 0 && User.count == 0 + # This is a brand new system. We need to run a setup first. + redirect_to wizards_new_initial_setup_path + else + if current_user.nil? + # You need to login first. + redirect_to log_in_path, :alert => I18n.t('pages.controller.access_denied_login_first') + end + end + end + +end diff --git a/app/controllers/phone_book_entries_controller.rb b/app/controllers/phone_book_entries_controller.rb new file mode 100644 index 0000000..823d50e --- /dev/null +++ b/app/controllers/phone_book_entries_controller.rb @@ -0,0 +1,135 @@ +class PhoneBookEntriesController < ApplicationController + load_and_authorize_resource :phone_book + load_and_authorize_resource :phone_book_entry, :through => :phone_book, :shallow => true + + before_filter :spread_breadcrumbs + + def index + # In case this is a search params[:q] or params[:name] will contain the query. + # + @query = params[:q] + @query ||= params[:name] + @query = @query.strip if @query + + if !@query.blank? + if @query.match(/^\+?\d+$/) != nil + # Find by phone number + phone_book_entries_ids = @phone_book_entries.map{|entry| entry.id} + @found_phone_numbers = PhoneNumber. + where(:phone_numberable_type => 'PhoneBookEntry', :phone_numberable_id => phone_book_entries_ids). + where('number LIKE ?', "#{@query}%") + @search_result = @phone_book_entries.where(:id => @found_phone_numbers.map{|entry| entry.phone_numberable_id}) + elsif @query.match(/^[\"\'](.*)[\"\']$/) != nil + # The User searched for =>'example'<= so he wants an EXACT search for that. + # This is the fasted and most accurate way of searching. + # The order to search is: last_name, first_name and organization. + # It stops searching as soon as it finds results. + # + @query = $1 + @search_result = @phone_book_entries.where(:last_name => @query) + @search_result = @phone_book_entries.where(:first_name => @query) if @search_result.count == 0 + @search_result = @phone_book_entries.where(:organization => @query) if @search_result.count == 0 + + @exact_search = true + else + # Search with SQL LIKE + # + @search_result = @phone_book_entries. + where( '( ( last_name LIKE ? ) OR ( first_name LIKE ? ) OR ( organization LIKE ? ) )', + "#{@query}%", "#{@query}%", "#{@query}%" ) + + @exact_search = false + end + + # Let's have a run with our phonetic search. + # + phonetic_query = PhoneBookEntry.koelner_phonetik(@query) + @phonetic_search_result = @phone_book_entries.where(:last_name_phonetic => phonetic_query) + @phonetic_search_result = @phone_book_entries.where(:first_name_phonetic => phonetic_query) if @phonetic_search_result.count == 0 + @phonetic_search_result = @phone_book_entries.where(:organization_phonetic => phonetic_query) if @phonetic_search_result.count == 0 + + if @phonetic_search_result.count == 0 + # Let's try the search with SQL LIKE. Just in case. + # + @phonetic_search_result = @phone_book_entries.where( 'last_name_phonetic LIKE ?', "#{phonetic_query}%" ) + @phonetic_search_result = @phone_book_entries.where( 'first_name_phonetic LIKE ?', "#{phonetic_query}%" ) if @phonetic_search_result.count == 0 + @phonetic_search_result = @phone_book_entries.where( 'organization_phonetic LIKE ?', "#{phonetic_query}%" ) if @phonetic_search_result.count == 0 + end + + @phonetic_search = true if @phonetic_search_result.count > 0 + + @phone_book_entries = @search_result + + if @phone_book_entries.count == 0 && @exact_search == false && @phonetic_search + @phone_book_entries = @phonetic_search_result + end + end + + # Let's sort the results and do pagination. + # + @phone_book_entries = @phone_book_entries. + order([ :last_name, :first_name, :organization ]). + paginate( + :page => @pagination_page_number, + :per_page => DEFAULT_PAGINATION_ENTRIES_PER_PAGE + ) + end + + def show + end + + def new + @phone_book_entry = @phone_book.phone_book_entries.build + @phone_book_entry.is_male = true + end + + def create + @phone_book_entry = @phone_book.phone_book_entries.build( params[:phone_book_entry] ) + if @phone_book_entry.save + redirect_to phone_book_phone_book_entry_path( @phone_book, @phone_book_entry ), :notice => t('phone_book_entries.controller.successfuly_created', :resource => @phone_book_entry) + else + render :new + end + end + + def edit + end + + def update + if @phone_book_entry.update_attributes(params[:phone_book_entry]) + redirect_to @phone_book_entry, :notice => t('phone_book_entries.controller.successfuly_updated', :resource => @phone_book_entry) + else + render :edit + end + end + + def destroy + @phone_book_entry.destroy + redirect_to phone_book_entries_url, :notice => t('phone_book_entries.controller.successfuly_destroyed') + end + + private + + def spread_breadcrumbs + if @phone_book + if @phone_book.phone_bookable.class == Tenant + add_breadcrumb t("phone_books.index.page_title"), tenant_phone_books_path(@phone_book.phone_bookable) + add_breadcrumb @phone_book, tenant_phone_book_path(@phone_book.phone_bookable, @phone_book) + add_breadcrumb t("phone_book_entries.index.page_title"), phone_book_phone_book_entries_path(@phone_book) + end + + if @phone_book.phone_bookable.class == User + add_breadcrumb t("users.index.page_title"), tenant_users_path(@phone_book.phone_bookable.current_tenant) + add_breadcrumb @phone_book.phone_bookable, tenant_user_path(@phone_book.phone_bookable.current_tenant, @phone_book.phone_bookable) + add_breadcrumb t("phone_books.index.page_title"), user_phone_books_path(@phone_book.phone_bookable) + add_breadcrumb @phone_book, user_phone_book_path(@phone_book.phone_bookable, @phone_book) + add_breadcrumb t("phone_book_entries.index.page_title"), phone_book_phone_book_entries_path(@phone_book) + end + + if @phone_book_entry && !@phone_book_entry.new_record? + add_breadcrumb @phone_book_entry, phone_book_phone_book_entry_path(@phone_book, @phone_book_entry) + end + end + end + +end diff --git a/app/controllers/phone_books_controller.rb b/app/controllers/phone_books_controller.rb new file mode 100644 index 0000000..54e7889 --- /dev/null +++ b/app/controllers/phone_books_controller.rb @@ -0,0 +1,105 @@ +class PhoneBooksController < ApplicationController + load_resource :user + load_resource :user_group + load_resource :tenant + load_and_authorize_resource :phone_book, :through => [:user, :user_group, :tenant] + + before_filter :set_and_authorize_parent + before_filter :spread_breadcrumbs + + def index + end + + def show + @by_name = params[:name] + + @pagination_page_number = params[:page].to_i + @pagination_page_number = 1 if @pagination_page_number < 1 + + if @by_name.blank? + @phone_book_entries = @phone_book. + phone_book_entries. + order([ :last_name, :first_name ]). + paginate( + :page => @pagination_page_number, + :per_page => DEFAULT_PAGINATION_ENTRIES_PER_PAGE + ) + else + # search by name + @by_name = @by_name. + gsub( /[^A-Za-z0-9#]/, '' ). + gsub('*','?'). + gsub('%','_'). + gsub(/^#/,''). + upcase + + @phone_book_entries = @phone_book. + phone_book_entries. + where( '( ( last_name LIKE ? ) OR ( first_name LIKE ? ) )', "#{@by_name}%", "#{@by_name}%" ). + order([ :last_name, :first_name ]). + paginate( + :page => @pagination_page_number, + :per_page => DEFAULT_PAGINATION_ENTRIES_PER_PAGE + ) + end + end + + def new + @phone_book = @parent.phone_books.build + @phone_book.name = generate_a_new_name(@parent, @phone_book) + end + + def create + @phone_book = @parent.phone_books.build( params[:phone_book] ) + if @phone_book.save + m = method( :"#{@parent.class.name.underscore}_phone_book_path" ) + redirect_to m.( @parent, @phone_book ), :notice => t('phone_books.controller.successfuly_created') + else + render :new + end + end + + def edit + end + + def update + if @phone_book.update_attributes(params[:phone_book]) + m = method( :"#{@parent.class.name.underscore}_phone_book_path" ) + redirect_to m.( @parent, @phone_book ), :notice => t('phone_books.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @phone_book.destroy + m = method( :"#{@parent.class.name.underscore}_phone_books_url" ) + redirect_to m.( @parent ), :notice => t('phone_books.controller.successfuly_destroyed') + end + + private + def set_and_authorize_parent + @parent = @user || @user_group || @tenant + authorize! :read, @parent + end + + def spread_breadcrumbs + if @parent.class == Tenant + add_breadcrumb t("phone_books.index.page_title"), tenant_phone_books_path(@tenant) + if @phone_book && !@phone_book.new_record? + add_breadcrumb @phone_book, tenant_phone_book_path(@tenant, @phone_book) + end + end + + if @parent.class == User + add_breadcrumb t("users.index.page_title"), tenant_users_path(@user.current_tenant) + add_breadcrumb @user, tenant_user_path(@user.current_tenant, @user) + add_breadcrumb t("phone_books.index.page_title"), user_phone_books_path(@user) + if @phone_book && !@phone_book.new_record? + add_breadcrumb @phone_book, user_phone_book_path(@user, @phone_book) + end + end + + end + +end diff --git a/app/controllers/phone_models_controller.rb b/app/controllers/phone_models_controller.rb new file mode 100644 index 0000000..59facdf --- /dev/null +++ b/app/controllers/phone_models_controller.rb @@ -0,0 +1,52 @@ +class PhoneModelsController < ApplicationController + load_and_authorize_resource :manufacturer + load_and_authorize_resource :phone_model, :through => [:manufacturer] + + before_filter :spread_breadcrumbs + + def index + end + + def show + end + + def new + @phone_model = @manufacturer.phone_models.build + end + + def create + @phone_model = @manufacturer.phone_models.build.new(params[:phone_model]) + if @phone_model.save + redirect_to manufacturer_phone_model_path( @manufacturer, @phone_model ), :notice => t('phone_models.controller.successfuly_created') + else + render :new + end + end + + def edit + end + + def update + if @phone_model.update_attributes(params[:phone_model]) + redirect_to manufacturer_phone_model_path( @manufacturer, @phone_model ), :notice => t('phone_models.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @phone_model.destroy + redirect_to manufacturer_phone_models_url( @manufacturer ), :notice => t('phone_models.controller.successfuly_destroyed') + end + + private + + def spread_breadcrumbs + add_breadcrumb t("manufacturers.index.page_title"), manufacturers_path + add_breadcrumb @manufacturer, manufacturer_path(@manufacturer) + add_breadcrumb t("phone_models.index.page_title"), manufacturer_phone_models_path(@manufacturer) + if @phone_model && !@phone_model.new_record? + add_breadcrumb @phone_model, manufacturer_phone_model_path(@manufacturer, @phone_model) + end + end +end diff --git a/app/controllers/phone_number_ranges_controller.rb b/app/controllers/phone_number_ranges_controller.rb new file mode 100644 index 0000000..a4e7238 --- /dev/null +++ b/app/controllers/phone_number_ranges_controller.rb @@ -0,0 +1,56 @@ +class PhoneNumberRangesController < ApplicationController + load_and_authorize_resource :tenant + load_and_authorize_resource :phone_number_range, :through => [:tenant] + + before_filter :set_parent + before_filter :spread_breadcrumbs + + def index + end + + def show + end + + def new + @phone_number_range = @parent.phone_number_ranges.build + end + + def create + @phone_number_range = @parent.phone_number_ranges.build(params[:phone_number_range]) + if @phone_number_range.save + redirect_to @phone_number_range, :notice => t('phone_number_ranges.controller.successfuly_created') + else + render :new + end + end + + def edit + end + + def update + if @phone_number_range.update_attributes(params[:phone_number_range]) + redirect_to @phone_number_range, :notice => t('phone_number_ranges.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @phone_number_range.destroy + redirect_to phone_number_ranges_url, :notice => t('phone_number_ranges.controller.successfuly_destroyed') + end + + private + + def set_parent + @parent = @tenant + end + + def spread_breadcrumbs + add_breadcrumb t("phone_number_ranges.index.page_title"), tenant_phone_number_ranges_path(@tenant) + if @phone_number_range && !@phone_number_range.new_record? + add_breadcrumb t("phone_number_ranges.ranges.#{@phone_number_range}.label"), tenant_phone_number_range_path(@tenant, @phone_number_range) + end + end + +end diff --git a/app/controllers/phone_numbers_controller.rb b/app/controllers/phone_numbers_controller.rb new file mode 100644 index 0000000..065934c --- /dev/null +++ b/app/controllers/phone_numbers_controller.rb @@ -0,0 +1,226 @@ +class PhoneNumbersController < ApplicationController + load_resource :phone_book_entry + load_resource :sip_account + load_resource :conference + load_resource :fax_account + load_resource :phone_number_range + load_resource :callthrough + load_resource :whitelist + load_resource :access_authorization + load_resource :hunt_group + load_resource :hunt_group_member + load_resource :automatic_call_distributor + load_and_authorize_resource :phone_number, :through => [:phone_book_entry, :sip_account, :conference, + :fax_account, :phone_number_range, :callthrough, + :whitelist, :access_authorization, :hunt_group, + :hunt_group_member, :automatic_call_distributor] + + before_filter :set_and_authorize_parent + before_filter :spread_breadcrumbs + + def index + end + + def show + @ringtoneable_classes = { + 'SipAccount' => true, + 'HuntGroup' => true, + 'AutomaticCallDistributor' => true, + 'PhoneBookEntry' => true, + } + @forwardable_classes = { + 'SipAccount' => true, + 'HuntGroup' => true, + 'AutomaticCallDistributor' => true, + } + end + + def new + @phone_number = @parent.phone_numbers.build() + end + + def create + @phone_number = @parent.phone_numbers.new( params[:phone_number] ) + if @phone_number.save + m = method( :"#{@parent.class.name.underscore}_phone_number_path" ) + redirect_to m.( @parent, @phone_number ), :notice => t('phone_numbers.controller.successfuly_created') + else + render :new + end + end + + def edit + end + + def update + if @phone_number.update_attributes(params[:phone_number]) + m = method( :"#{@parent.class.name.underscore}_phone_number_path" ) + redirect_to m.( @parent, @phone_number ), :notice => t('phone_numbers.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @phone_number.destroy + m = method( :"#{@parent.class.name.underscore}_phone_numbers_url" ) + redirect_to m.(), :notice => t('phone_numbers.controller.successfuly_destroyed') + end + + def move_higher + @phone_number.move_higher + redirect_to :back + end + + def move_lower + @phone_number.move_lower + redirect_to :back + end + + private + def set_and_authorize_parent + @parent = @phone_book_entry || @sip_account || @conference || @fax_account || + @phone_number_range || @callthrough || @whitelist || @access_authorization || + @hunt_group || @hunt_group_member || @automatic_call_distributor + + authorize! :read, @parent + + @show_path_method = method( :"#{@parent.class.name.underscore}_phone_number_path" ) + @index_path_method = method( :"#{@parent.class.name.underscore}_phone_numbers_path" ) + @new_path_method = method( :"new_#{@parent.class.name.underscore}_phone_number_path" ) + @edit_path_method = method( :"edit_#{@parent.class.name.underscore}_phone_number_path" ) + end + + def spread_breadcrumbs + if @parent.class == Callthrough + add_breadcrumb t("#{@parent.class.name.underscore.pluralize}.index.page_title"), tenant_callthroughs_path(@parent.tenant) + add_breadcrumb @callthrough, tenant_callthrough_path(@parent.tenant, @callthrough) + add_breadcrumb t("phone_numbers.index.page_title"), callthrough_phone_numbers_path(@parent) + if @phone_number && !@phone_number.new_record? + add_breadcrumb @phone_number, callthrough_phone_number_path(@callthrough, @phone_number) + end + end + + if @parent.class == SipAccount + if @sip_account.sip_accountable.class == User + add_breadcrumb t("#{@sip_account.sip_accountable.class.name.underscore.pluralize}.index.page_title"), method( :"tenant_#{@sip_account.sip_accountable.class.name.underscore.pluralize}_path" ).(@sip_account.tenant) + add_breadcrumb @sip_account.sip_accountable, method( :"tenant_#{@sip_account.sip_accountable.class.name.underscore}_path" ).(@sip_account.tenant, @sip_account.sip_accountable) + end + add_breadcrumb t("sip_accounts.index.page_title"), method( :"#{@sip_account.sip_accountable.class.name.underscore}_sip_accounts_path" ).(@sip_account.sip_accountable) + add_breadcrumb @sip_account, method( :"#{@sip_account.sip_accountable.class.name.underscore}_sip_account_path" ).(@sip_account.sip_accountable, @sip_account) + add_breadcrumb t("phone_numbers.index.page_title"), sip_account_phone_numbers_path(@sip_account) + if @phone_number && !@phone_number.new_record? + add_breadcrumb @phone_number, sip_account_phone_number_path(@sip_account, @phone_number) + end + end + + if @parent.class == Conference + @conference = @parent + conference_parent = @conference.conferenceable + if conference_parent && conference_parent.class == User + @user = conference_parent + add_breadcrumb t("users.index.page_title"), tenant_users_path(@user.current_tenant) + add_breadcrumb @user, tenant_user_path(@user.current_tenant, @user) + add_breadcrumb t("conferences.index.page_title"), user_conferences_path(@user) + add_breadcrumb @conference, user_conference_path(@user, @conference) + end + if conference_parent && conference_parent.class == Tenant + @tenant = conference_parent + add_breadcrumb t("conferences.index.page_title"), tenant_conferences_path(@tenant) + add_breadcrumb @conference, tenant_conference_path(@tenant, @conference) + end + add_breadcrumb t("phone_numbers.index.page_title"), conference_phone_numbers_path(@conference) + if @phone_number && !@phone_number.new_record? + add_breadcrumb @phone_number, conference_phone_number_path(@conference, @phone_number) + end + end + + if @parent.class == HuntGroup + add_breadcrumb t("#{@parent.class.name.underscore.pluralize}.index.page_title"), tenant_hunt_groups_path(@parent.tenant) + add_breadcrumb @hunt_group, tenant_hunt_group_path(@parent.tenant, @hunt_group) + add_breadcrumb t("phone_numbers.index.page_title"), hunt_group_phone_numbers_path(@parent) + end + + if @parent.class == HuntGroupMember + add_breadcrumb t("hunt_groups.index.page_title"), tenant_hunt_groups_path(@parent.hunt_group.tenant) + add_breadcrumb @parent.hunt_group, tenant_hunt_group_path(@parent.hunt_group.tenant, @parent.hunt_group) + add_breadcrumb t("hunt_group_members.index.page_title"), hunt_group_hunt_group_members_path(@parent.hunt_group) + add_breadcrumb @parent, hunt_group_hunt_group_member_path(@parent.hunt_group, @parent) + add_breadcrumb t("phone_numbers.index.page_title"), hunt_group_member_phone_numbers_path(@parent) + if @phone_number && !@phone_number.new_record? + add_breadcrumb @phone_number, hunt_group_member_phone_number_path(@parent, @phone_number) + end + end + + if @parent.class == AccessAuthorization + if @parent.access_authorizationable.class == Callthrough + callthrough = @parent.access_authorizationable + tenant = callthrough.tenant + add_breadcrumb t("callthroughs.index.page_title"), tenant_callthroughs_path(tenant) + add_breadcrumb callthrough, tenant_callthrough_path(tenant, callthrough) + add_breadcrumb t("access_authorizations.index.page_title"), callthrough_access_authorizations_path(callthrough) + add_breadcrumb @parent, callthrough_access_authorization_path(callthrough, @parent) + add_breadcrumb t("phone_numbers.index.page_title"), access_authorization_phone_numbers_path(@parent) + if @phone_number && !@phone_number.new_record? + add_breadcrumb @phone_number, access_authorization_phone_number_path(@parent, @phone_number) + end + end + end + + if @parent.class == PhoneBookEntry + @phone_book = @parent.phone_book + if @parent.phone_book.phone_bookable.class == Tenant + @tenant = @parent.phone_book.phone_bookable + add_breadcrumb t("phone_books.index.page_title"), tenant_phone_books_path(@tenant) + add_breadcrumb @phone_book, tenant_phone_book_path(@tenant, @phone_book) + add_breadcrumb @phone_book_entry, phone_book_phone_book_entry_path(@phone_book, @phone_book_entry) + + if @phone_number && !@phone_number.new_record? + add_breadcrumb @phone_number, phone_book_entry_phone_number_path(@phone_book_entry, @phone_number) + end + end + + if @parent.phone_book.phone_bookable.class == User + @user = @parent.phone_book.phone_bookable + add_breadcrumb t("users.index.page_title"), tenant_users_path(@user.current_tenant) + add_breadcrumb @user, tenant_user_path(@user.current_tenant, @user) + add_breadcrumb t("phone_books.index.page_title"), user_phone_books_path(@user) + add_breadcrumb @phone_book, user_phone_book_path(@user, @phone_book) + add_breadcrumb @phone_book_entry, phone_book_phone_book_entry_path(@phone_book, @phone_book_entry) + + if @phone_number && !@phone_number.new_record? + add_breadcrumb @phone_number, phone_book_entry_phone_number_path(@phone_book_entry, @phone_number) + end + end + end + + if @parent.class == Whitelist + @tenant = @parent.whitelistable.tenant + @callthrough = @parent.whitelistable + @whitelist = @parent + add_breadcrumb t("callthroughs.name").pluralize, tenant_callthroughs_path(@tenant) + add_breadcrumb @callthrough, tenant_callthrough_path(@tenant, @callthrough) + add_breadcrumb t("whitelists.index.page_title"), callthrough_whitelists_path(@callthrough) + add_breadcrumb @whitelist, callthrough_whitelist_path(@callthrough, @whitelist) + add_breadcrumb t("phone_numbers.index.page_title"), whitelist_phone_numbers_path(@whitelist) + if @phone_number && !@phone_number.new_record? + add_breadcrumb @phone_number, whitelist_phone_number_path(@whitelist, @phone_number) + end + end + + if @parent.class == AutomaticCallDistributor + if @automatic_call_distributor.automatic_call_distributorable.class == User + add_breadcrumb t("#{@automatic_call_distributor.automatic_call_distributorable.class.name.underscore.pluralize}.index.page_title"), method( :"tenant_#{@automatic_call_distributor.automatic_call_distributorable.class.name.underscore.pluralize}_path" ).(@automatic_call_distributor.tenant) + add_breadcrumb @automatic_call_distributor.automatic_call_distributorable, method( :"tenant_#{@automatic_call_distributor.automatic_call_distributorable.class.name.underscore}_path" ).(@automatic_call_distributor.tenant, @automatic_call_distributor.automatic_call_distributorable) + end + add_breadcrumb t("automatic_call_distributors.index.page_title"), method( :"#{@automatic_call_distributor.automatic_call_distributorable.class.name.underscore}_automatic_call_distributors_path" ).(@automatic_call_distributor.automatic_call_distributorable) + add_breadcrumb @automatic_call_distributor, method( :"#{@automatic_call_distributor.automatic_call_distributorable.class.name.underscore}_automatic_call_distributor_path" ).(@automatic_call_distributor.automatic_call_distributorable, @automatic_call_distributor) + add_breadcrumb t("phone_numbers.index.page_title"), automatic_call_distributor_phone_numbers_path(@automatic_call_distributor) + if @phone_number && !@phone_number.new_record? + add_breadcrumb @phone_number, automatic_call_distributor_phone_number_path(@automatic_call_distributor, @phone_number) + end + end + + end + +end diff --git a/app/controllers/phone_sip_accounts_controller.rb b/app/controllers/phone_sip_accounts_controller.rb new file mode 100644 index 0000000..8558c55 --- /dev/null +++ b/app/controllers/phone_sip_accounts_controller.rb @@ -0,0 +1,60 @@ +class PhoneSipAccountsController < ApplicationController + load_and_authorize_resource :phone + load_and_authorize_resource :phone_sip_account, :through => [:phone] + + before_filter :spread_breadcrumbs + + def index + end + + def show + end + + def new + @available_sip_accounts = @phone.phoneable.sip_accounts + + # Ensure a SipAccount is used on a single phone only. + # + @available_sip_accounts = @available_sip_accounts.delete_if { |x| x.phone_sip_account_ids.count > 0 } + + if @available_sip_accounts.count == 0 + redirect_to method( :"new_#{@phone.phoneable.class.name.underscore}_sip_account_path" ).(@phone.phoneable), :alert => t('phone_sip_accounts.controller.no_existing_sip_accounts_warning') + else + @phone_sip_account = @phone.phone_sip_accounts.build(:sip_account_id => @available_sip_accounts.first.try(:id)) + end + end + + def create + @phone_sip_account = @phone.phone_sip_accounts.build(params[:phone_sip_account]) + if @phone_sip_account.save + redirect_to method( :"#{@phone_sip_account.phone.phoneable.class.name.underscore}_phone_path" ).(@phone_sip_account.phone.phoneable, @phone_sip_account.phone), :notice => t('phone_sip_accounts.controller.successfuly_created') + else + render :new + end + end + + def destroy + @phone_sip_account.destroy + redirect_to method( :"#{@phone_sip_account.phone.phoneable.class.name.underscore}_phone_path" ).(@phone_sip_account.phone.phoneable, @phone_sip_account.phone), :notice => t('phone_sip_accounts.controller.successfuly_destroyed') + end + + private + + def spread_breadcrumbs + if @phone.phoneable.class == User + user = @phone.phoneable + add_breadcrumb t('users.index.page_title'), tenant_users_path(user.current_tenant) + add_breadcrumb user, tenant_user_path(user.current_tenant, user) + add_breadcrumb t('phones.index.page_title'), user_phones_path(user) + elsif @phone.phoneable.class == Tenant + tenant = @phone.phoneable + add_breadcrumb t('phones.index.page_title'), tenant_phones_path(tenant) + end + add_breadcrumb @phone, method( :"#{@phone.phoneable.class.name.underscore}_phone_path" ).(@phone.phoneable, @phone) + add_breadcrumb t('phone_sip_accounts.index.page_title'), phone_phone_sip_accounts_path(@phone) + if @phone_sip_account && !@phone_sip_account.new_record? + add_breadcrumb @phone_sip_account, phone_phone_sip_account_path(@phone, @phone_sip_account) + end + end + +end diff --git a/app/controllers/phones_controller.rb b/app/controllers/phones_controller.rb new file mode 100644 index 0000000..d46bf86 --- /dev/null +++ b/app/controllers/phones_controller.rb @@ -0,0 +1,72 @@ +class PhonesController < ApplicationController + load_resource :tenant + load_resource :user + load_and_authorize_resource :phone, :through => [:tenant, :user] + + before_filter :set_and_authorize_parent + before_filter :spread_breadcrumbs + + def index + end + + def show + end + + def new + @phone = @phoneable.phones.build() + + # Use the last phone.phone_model as the default. + # + @phone.phone_model_id = Phone.last.try(:phone_model).try(:id) + end + + def create + @phone = @phoneable.phones.build(params[:phone]) + if @phone.save + m = method( :"#{@phoneable.class.name.underscore}_phone_path" ) + redirect_to m.( @phoneable, @phone ), :notice => t('phones.controller.successfuly_created') + else + render :new + end + end + + def edit + end + + def update + if @phone.update_attributes(params[:phone]) + m = method( :"#{@phoneable.class.name.underscore}_phone_path" ) + redirect_to m.( @phoneable, @phone ), :notice => t('phones.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @phone.destroy + m = method( :"#{@phoneable.class.name.underscore}_phones_url" ) + redirect_to m.( @phoneable ), :notice => t('phones.controller.successfuly_destroyed') + end + + private + def set_and_authorize_parent + @phoneable = (@user || @tenant) + @parent = @phoneable + authorize! :read, @parent + @nesting_prefix = @phoneable ? "#{@phoneable.class.name.underscore}_" : '' + end + + def spread_breadcrumbs + if @user + add_breadcrumb t('users.index.page_title'), tenant_users_path(@user.current_tenant) + add_breadcrumb @user, tenant_user_path(@user.current_tenant, @user) + add_breadcrumb t('phones.index.page_title'), user_phones_path(@user) + elsif @tenant + add_breadcrumb t('phones.index.page_title'), tenant_phones_path(@tenant) + end + if @phone && !@phone.new_record? + add_breadcrumb @phone, method( :"#{@phone.phoneable.class.name.underscore}_phone_path" ).(@phone.phoneable, @phone) + end + end + +end diff --git a/app/controllers/ringtones_controller.rb b/app/controllers/ringtones_controller.rb new file mode 100644 index 0000000..7ffe30e --- /dev/null +++ b/app/controllers/ringtones_controller.rb @@ -0,0 +1,67 @@ +class RingtonesController < ApplicationController + load_resource :phone_number + load_resource :boss_assistant_cooperation + load_and_authorize_resource :ringtone, :through => [:phone_number, :boss_assistant_cooperation] + + before_filter :set_parent + before_filter :spread_breadcrumbs + + def index + end + + def show + end + + def new + @ringtone = @parent.ringtones.build + end + + def create + @ringtone = @parent.ringtones.build(params[:ringtone]) + if @ringtone.save + redirect_to phone_number_ringtone_path(@parent, @ringtone), :notice => t('ringtones.controller.successfuly_created') + else + render :new + end + end + + def edit + end + + def update + if @ringtone.update_attributes(params[:ringtone]) + redirect_to method( :"#{@parent.class.name.underscore}_ringtone_path" ).(@ringtone.ringtoneable, @ringtone), :notice => t('ringtones.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @ringtone.destroy + redirect_to phone_number_ringtones_path(@parent), :notice => t('ringtones.controller.successfuly_destroyed') + end + + private + def set_parent + @parent = @phone_number || @boss_assistant_cooperation + end + + def spread_breadcrumbs + if @parent.class == PhoneNumber && @parent.phone_numberable.class == SipAccount + @sip_account = @parent.phone_numberable + if @sip_account.sip_accountable.class == User + add_breadcrumb t("#{@sip_account.sip_accountable.class.name.underscore.pluralize}.index.page_title"), method( :"tenant_#{@sip_account.sip_accountable.class.name.underscore.pluralize}_path" ).(@sip_account.tenant) + add_breadcrumb @sip_account.sip_accountable, method( :"tenant_#{@sip_account.sip_accountable.class.name.underscore}_path" ).(@sip_account.tenant, @sip_account.sip_accountable) + end + add_breadcrumb t("sip_accounts.index.page_title"), method( :"#{@sip_account.sip_accountable.class.name.underscore}_sip_accounts_path" ).(@sip_account.sip_accountable) + add_breadcrumb @sip_account, method( :"#{@sip_account.sip_accountable.class.name.underscore}_sip_account_path" ).(@sip_account.sip_accountable, @sip_account) + add_breadcrumb t("phone_numbers.index.page_title"), sip_account_phone_numbers_path(@sip_account) + add_breadcrumb @phone_number, sip_account_phone_number_path(@sip_account, @phone_number) + add_breadcrumb t("ringtones.index.page_title"), phone_number_ringtones_path(@phone_number) + if @ringtone && !@ringtone.new_record? + add_breadcrumb @ringtone, phone_number_ringtone_path(@phone_number, @ringtone) + end + end + end + +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 0000000..f92ae1c --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,44 @@ +class SessionsController < ApplicationController + + before_filter :redirect_to_https + skip_before_filter :home_breadcrumb + + def new + end + + def create + user = User.find_by_email(params[:sessions][:login_data].downcase.strip) + if user.nil? + user = User.find_by_user_name(params[:sessions][:login_data].downcase.strip) + end + if user && user.authenticate(params[:sessions][:password]) + session[:user_id] = user.id + redirect_to tenant_user_path(user.current_tenant, user), :notice => t('sessions.controller.successfully_created', :resource => user) + elsif user && !user.email.blank? && params[:sessions][:reset_password] =~ (/(1|t|y|yes|true)$/i) + password = SecureRandom.base64(8)[0..7] + if user.update_attributes(:password => password) + Notifications.new_password(user, password).deliver + flash.now.notice = t('sessions.flash_messages.password_recovery_successful', :resource => user) + else + flash.now.alert = t('sessions.flash_messages.password_recovery_failed', :resource => user) + end + render "new" + else + flash.now.alert = t('sessions.flash_messages.invalid_email_or_password', :resource => user) + render "new" + end + end + + def destroy + session[:user_id] = nil + redirect_to root_url, :notice => t('sessions.controller.successfully_destroyed') + end + + private + def redirect_to_https + if GUI_REDIRECT_HTTPS and ! request.ssl? + redirect_to :protocol => "https://" + end + end + +end diff --git a/app/controllers/sip_accounts_controller.rb b/app/controllers/sip_accounts_controller.rb new file mode 100644 index 0000000..1f3166e --- /dev/null +++ b/app/controllers/sip_accounts_controller.rb @@ -0,0 +1,98 @@ +class SipAccountsController < ApplicationController + load_resource :user + load_resource :tenant + load_and_authorize_resource :sip_account, :through => [:user, :tenant ] + + before_filter :set_and_authorize_parent + before_filter :spread_breadcrumbs + + def index + end + + def show + end + + def new + @sip_account = @parent.sip_accounts.build + @sip_account.caller_name = @parent + @sip_account.call_waiting = CALL_WAITING + @sip_account.clir = DEFAULT_CLIR_SETTING + @sip_account.clip = DEFAULT_CLIP_SETTING + @sip_account.voicemail_pin = random_pin + @sip_account.callforward_rules_act_per_sip_account = CALLFORWARD_RULES_ACT_PER_SIP_ACCOUNT_DEFAULT + @sip_account.hotdeskable = true + + # Make sure that we don't use an already taken auth_name + # + loop do + @sip_account.auth_name = SecureRandom.hex(DEFAULT_LENGTH_SIP_AUTH_NAME) + + break unless SipAccount.exists?(:auth_name => @sip_account.auth_name) + end + @sip_account.password = SecureRandom.hex(DEFAULT_LENGTH_SIP_PASSWORD) + end + + def create + @sip_account = @parent.sip_accounts.build(params[:sip_account]) + + if @sip_account.auth_name.blank? + loop do + @sip_account.auth_name = SecureRandom.hex(DEFAULT_LENGTH_SIP_AUTH_NAME) + + break unless SipAccount.exists?(:auth_name => @sip_account.auth_name) + end + end + if @sip_account.password.blank? + @sip_account.password = SecureRandom.hex(DEFAULT_LENGTH_SIP_PASSWORD) + end + + if @sip_account.save + m = method( :"#{@parent.class.name.underscore}_sip_account_path" ) + redirect_to m.( @parent, @sip_account ), :notice => t('sip_accounts.controller.successfuly_created', :resource => @parent) + else + render :new + end + end + + def edit + end + + def update + if @sip_account.update_attributes(params[:sip_account]) + m = method( :"#{@parent.class.name.underscore}_sip_account_path" ) + redirect_to m.( @parent, @sip_account ), :notice => t('sip_accounts.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @sip_account.destroy + m = method( :"#{@parent.class.name.underscore}_sip_accounts_url" ) + redirect_to m.( @parent ), :notice => t('sip_accounts.controller.successfuly_destroyed') + end + + private + def set_and_authorize_parent + @parent = @user || @tenant + authorize! :read, @parent + end + + def spread_breadcrumbs + if @user + add_breadcrumb t("users.index.page_title"), tenant_users_path(@user.current_tenant) + add_breadcrumb @user, tenant_user_path(@user.current_tenant, @user) + add_breadcrumb t("sip_accounts.index.page_title"), user_sip_accounts_path(@user) + if @sip_account && !@sip_account.new_record? + add_breadcrumb @sip_account, user_sip_account_path(@user, @sip_account) + end + end + if @tenant + add_breadcrumb t("sip_accounts.index.page_title"), tenant_sip_accounts_path(@tenant) + if @sip_account && !@sip_account.new_record? + add_breadcrumb @sip_account, tenant_sip_account_path(@tenant, @sip_account) + end + end + end + +end diff --git a/app/controllers/sip_domains_controller.rb b/app/controllers/sip_domains_controller.rb new file mode 100644 index 0000000..7301192 --- /dev/null +++ b/app/controllers/sip_domains_controller.rb @@ -0,0 +1,41 @@ +class SipDomainsController < ApplicationController + def index + @sip_domains = SipDomain.all + end + + def show + @sip_domain = SipDomain.find(params[:id]) + end + + def new + @sip_domain = SipDomain.new + end + + def create + @sip_domain = SipDomain.new(params[:sip_domain]) + if @sip_domain.save + redirect_to @sip_domain, :notice => t('sip_domains.controller.successfuly_created') + else + render :new + end + end + + def edit + @sip_domain = SipDomain.find(params[:id]) + end + + def update + @sip_domain = SipDomain.find(params[:id]) + if @sip_domain.update_attributes(params[:sip_domain]) + redirect_to @sip_domain, :notice => t('sip_domains.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @sip_domain = SipDomain.find(params[:id]) + @sip_domain.destroy + redirect_to sip_domains_url, :notice => t('sip_domains.controller.successfuly_destroyed') + end +end diff --git a/app/controllers/softkeys_controller.rb b/app/controllers/softkeys_controller.rb new file mode 100644 index 0000000..d2a2bb9 --- /dev/null +++ b/app/controllers/softkeys_controller.rb @@ -0,0 +1,91 @@ +class SoftkeysController < ApplicationController + load_and_authorize_resource :sip_account + load_and_authorize_resource :softkey, :through => [:sip_account] + + before_filter :set_available_call_forwards_and_softkey_functions, :only => [ :new, :edit, :update ] + before_filter :spread_breadcrumbs + + def index + end + + def show + end + + def new + @softkey = @sip_account.softkeys.build + + delete_call_forward_softkey_if_no_callforward_is_available + end + + def create + @softkey = @sip_account.softkeys.build(params[:softkey]) + if @softkey.save + redirect_to sip_account_softkey_path(@softkey.sip_account, @softkey), :notice => t('softkeys.controller.successfuly_created') + else + render :new + end + end + + def edit + delete_call_forward_softkey_if_no_callforward_is_available + end + + def update + if @softkey.update_attributes(params[:softkey]) + redirect_to sip_account_softkey_path(@softkey.sip_account, @softkey), :notice => t('softkeys.controller.successfuly_updated') + else + delete_call_forward_softkey_if_no_callforward_is_available + render :edit + end + end + + def destroy + @softkey.destroy + redirect_to sip_account_softkeys_path(@softkey.sip_account), :notice => t('softkeys.controller.successfuly_destroyed') + end + + def move_higher + @softkey.move_higher + redirect_to :back + end + + def move_lower + @softkey.move_lower + redirect_to :back + end + + private + + def set_available_call_forwards_and_softkey_functions + @available_call_forwards = @softkey.possible_blf_call_forwards + + @softkey_functions = [] + SoftkeyFunction.accessible_by(current_ability, :read).each do |softkey_function| + if GuiFunction.display?("softkey_function_#{softkey_function.name.downcase}_field_in_softkey_form", @current_user) + @softkey_functions << softkey_function + end + end + end + + def spread_breadcrumbs + if @sip_account.sip_accountable.class == User + add_breadcrumb t('users.name'), tenant_users_path(@sip_account.sip_accountable.current_tenant) + add_breadcrumb @sip_account.sip_accountable, tenant_user_path(@sip_account.sip_accountable.current_tenant, @sip_account.sip_accountable) + add_breadcrumb t('sip_accounts.index.page_title'), user_sip_accounts_path(@sip_account.sip_accountable) + add_breadcrumb @sip_account, user_sip_account_path(@sip_account.sip_accountable, @sip_account) + add_breadcrumb t('softkeys.index.page_title'), sip_account_softkeys_path(@sip_account) + elsif @sip_account.sip_accountable.class == Tenant + add_breadcrumb t('sip_accounts.index.page_title'), tenant_sip_accounts_path(@sip_account.sip_accountable) + add_breadcrumb @sip_account, tenant_sip_account_path(@sip_account.sip_accountable, @sip_account) + add_breadcrumb t('softkeys.index.page_title'), sip_account_softkeys_path(@sip_account) + end + end + + def delete_call_forward_softkey_if_no_callforward_is_available + # Don't display the call_forward option if there aren't any call_forwards to choose from. + # + if @softkey.sip_account.phone_numbers.map{|phone_number| phone_number.call_forwards}.flatten.count == 0 + @softkey_functions.delete_if { |softkey_function| softkey_function == SoftkeyFunction.find_by_name('call_forwarding') } + end + end +end diff --git a/app/controllers/system_messages_controller.rb b/app/controllers/system_messages_controller.rb new file mode 100644 index 0000000..d7fe515 --- /dev/null +++ b/app/controllers/system_messages_controller.rb @@ -0,0 +1,30 @@ +class SystemMessagesController < ApplicationController + load_and_authorize_resource :user + load_and_authorize_resource :system_message, :through => [:user] + + def index + @system_messages = @system_messages.where(:created_at => Time.now - 6.hours .. Time.now) + end + + def show + end + + def new + @system_message = @user.system_messages.build + end + + def create + @system_message = @user.system_messages.build(params[:system_message]) + if @system_message.save + # Push the new message via AJAX to the browser. + # + # PrivatePub.publish_to("/users/#{@system_message.user.id}/system_messages", + # "$('#system_message').empty();$('#system_message').append('<span class=\"created_at\">#{(I18n.l @system_message.created_at, :format => :short )}</span> #{@system_message.content}');$('#system_message_display').fadeIn();" + # ) + + redirect_to user_system_message_path(@user, @system_message), :notice => t('system_messages.controller.successfuly_created') + else + render :new + end + end +end diff --git a/app/controllers/tenants_controller.rb b/app/controllers/tenants_controller.rb new file mode 100644 index 0000000..724d179 --- /dev/null +++ b/app/controllers/tenants_controller.rb @@ -0,0 +1,91 @@ +class TenantsController < ApplicationController + load_and_authorize_resource :tenant + + def index + end + + def show + end + + def new + @tenant.name = generate_a_new_name(@tenant) + @tenant.sip_domain = SipDomain.last + @tenant.country = GemeinschaftSetup.first.country + @tenant.language = GemeinschaftSetup.first.language + @tenant.internal_extension_ranges = '10-99' + @tenant.from_field_voicemail_email = 'admin@localhost' + @tenant.from_field_pin_change_email = 'admin@localhost' + end + + def create + if @tenant.save + # Become a member of this tenant. + # + @tenant.tenant_memberships.create(:user_id => @current_user.id) + + # Groups + # + admin_group = @tenant.user_groups.create(:name => t('gemeinschaft_setups.initial_setup.admin_group_name')) + admin_group.users << @current_user + + user_group = @tenant.user_groups.create(:name => t('gemeinschaft_setups.initial_setup.user_group_name')) + user_group.users << @current_user + + @current_user.update_attributes!(:current_tenant_id => @tenant.id) + + # Generate the internal_extensions + # + if !@tenant.internal_extension_ranges.blank? + if @tenant.array_of_internal_extension_numbers.count < 105 + # This can be done more or less quick. + @tenant.generate_internal_extensions + else + # Better be on the save side and start a delayed job for this. + @tenant.delay.generate_internal_extensions + end + end + + # Generate the external numbers (DIDs) + # + if !@tenant.did_list.blank? + if @tenant.array_of_dids.count < 105 + # This can be done more or less quick. + @tenant.generate_dids + else + # Better be on the save side and start a delayed job for this. + @tenant.delay.generate_dids + end + end + + if Delayed::Job.count > 0 + redirect_to @tenant, :notice => t('tenants.controller.successfuly_created_plus_delayed_jobs', + :resource => @tenant, + :amount_of_numbers => @tenant.array_of_internal_extension_numbers.count + @tenant.array_of_dids.count + ) + else + redirect_to @tenant, :notice => t('tenants.controller.successfuly_created', + :resource => @tenant + ) + end + else + render :new + end + end + + def edit + end + + def update + if @tenant.update_attributes(params[:tenant]) + redirect_to @tenant, :notice => t('tenants.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @tenant.destroy + redirect_to tenants_url, :notice => t('tenants.controller.successfuly_destroyed') + end + +end diff --git a/app/controllers/user_group_memberships_controller.rb b/app/controllers/user_group_memberships_controller.rb new file mode 100644 index 0000000..1cbbd48 --- /dev/null +++ b/app/controllers/user_group_memberships_controller.rb @@ -0,0 +1,48 @@ +class UserGroupMembershipsController < ApplicationController + load_and_authorize_resource :user_group + load_and_authorize_resource :user_group_membership, :through => [:user_group] + + before_filter :spread_breadcrumbs + + def index + @potential_users_count = @user_group.tenant.users.count - @user_group.users.count + end + + def show + end + + def new + @user_group_membership = @user_group.user_group_memberships.build + @potential_users = (@user_group.tenant.users.order(:last_name) - @user_group.users) + if @potential_users.count == 0 + redirect_to user_group_user_group_memberships_path(@user_group), :alert => t('user_group_memberships.controller.no_more_user_to_add') + end + end + + def create + @user_group_membership = @user_group.user_group_memberships.build(params[:user_group_membership]) + if @user_group_membership.save + redirect_to user_group_user_group_membership_path(@user_group, @user_group_membership), :notice => t('user_group_memberships.controller.successfuly_created') + else + render :new + end + end + + def destroy + @user_group_membership.destroy + redirect_to user_group_user_group_memberships_path(@user_group), :notice => t('user_group_memberships.controller.successfuly_destroyed') + end + + private + + def spread_breadcrumbs + add_breadcrumb t("user_groups.index.page_title"), tenant_user_groups_path(@user_group.tenant) + add_breadcrumb @user_group, tenant_user_group_path(@user_group.tenant, @user_group) + add_breadcrumb t("user_group_memberships.index.page_title"), user_group_user_group_memberships_path(@user_group) + + if @user_group_membership && !@user_group_membership.new_record? + add_breadcrumb @user_group_membership, user_group_user_group_membership_path(@user_group, @user_group_membership) + end + end + +end diff --git a/app/controllers/user_groups_controller.rb b/app/controllers/user_groups_controller.rb new file mode 100644 index 0000000..158abaa --- /dev/null +++ b/app/controllers/user_groups_controller.rb @@ -0,0 +1,69 @@ +class UserGroupsController < ApplicationController + load_resource :tenant + load_resource :user + load_and_authorize_resource :user_group, :through => [:tenant, :user] + + before_filter :set_and_authorize_parent + before_filter :spread_breadcrumbs + + def index + end + + def show + end + + def new + @user_group = @parent.user_groups.build + end + + def create + @user_group = @parent.user_groups.build(params[:user_group]) + if @user_group.save + redirect_to @user_group, :notice => t('user_groups.controller.successfuly_created') + else + render :new + end + end + + def edit + end + + def update + if @user_group.update_attributes(params[:user_group]) + redirect_to @user_group, :notice => t('user_groups.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @user_group.destroy + redirect_to user_groups_url, :notice => t('user_groups.controller.successfuly_destroyed') + end + + private + + def set_and_authorize_parent + @parent = @user || @tenant + authorize! :read, @parent + end + + def spread_breadcrumbs + if @tenant + add_breadcrumb t("user_groups.index.page_title"), tenant_user_groups_path(@tenant) + if @user_group && !@user_group.new_record? + add_breadcrumb @user_group, tenant_user_group_path(@tenant, @user_group) + end + end + + if @user + add_breadcrumb t("users.index.page_title"), tenant_users_path(@parent) + add_breadcrumb @user, tenant_user_path(@user.current_tenant, @user) + add_breadcrumb t("user_groups.index.page_title"), user_user_groups_path(@user) + if @user_group && !@user_group.new_record? + add_breadcrumb @user_group, user_user_group_path(@user, @user_group) + end + end + end + +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb new file mode 100644 index 0000000..454c26b --- /dev/null +++ b/app/controllers/users_controller.rb @@ -0,0 +1,85 @@ +class UsersController < ApplicationController + load_resource :tenant + load_resource :user_group + load_and_authorize_resource :user, :through => [:tenant, :user_group] + + before_filter :set_and_authorize_parent + before_filter :spread_breadcrumbs + + def index + end + + def show + @phone_books = PhoneBook.accessible_by( Ability.new( @user ) ).all + end + + def new + @user = @parent.users.build(params[:user]) + @user.male = true + @user.send_voicemail_as_email_attachment = true + end + + def create + @user = @parent.users.build(params[:user]) + if @user.save + if @parent.class == Tenant + @parent.tenant_memberships.create(:user => @user) + if @parent.user_groups.exists?(:name => 'Users') + @parent.user_groups.where(:name => 'Users').first.user_group_memberships.create(:user => @user) + end + redirect_to tenant_user_url( @parent, @user), :notice => t('users.controller.successfuly_created', :resource => @user) + else + redirect_to tenant_user_path(@user.current_tenant, @user), :notice => t('users.controller.successfuly_created_and_login', :resource => @user) + end + else + render :new + end + end + + def edit + end + + def update + if @user.update_attributes(params[:user]) + # Make sure that the flash notice gets rendered in the correct language. + I18n.locale = @user.language.code.downcase + + redirect_to tenant_user_path(@user.current_tenant, @user), :notice => t('users.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @user.destroy + redirect_to @parent, :notice => t('users.controller.successfuly_destroyed') + end + + def destroy_avatar + user = User.find(params[:user_id]) + user.remove_image = true # https://github.com/jnicklas/carrierwave/issues/360 + user.remove_image! + user.save + user.reload + user.image.remove! + user.save + redirect_to @parent, :notice => t('users.controller.avatar_destroyed') + end + + private + def set_and_authorize_parent + @parent = @tenant || @user_group + authorize! :read, @parent + end + + def spread_breadcrumbs + if @tenant + add_breadcrumb t("users.index.page_title"), tenant_users_path(@tenant) + + if @user && !@user.new_record? + add_breadcrumb @user, tenant_user_path(@tenant, @user) + end + end + end + +end diff --git a/app/controllers/voicemail_messages_controller.rb b/app/controllers/voicemail_messages_controller.rb new file mode 100644 index 0000000..58f5265 --- /dev/null +++ b/app/controllers/voicemail_messages_controller.rb @@ -0,0 +1,140 @@ +class VoicemailMessagesController < ApplicationController + + load_resource :sip_account + load_and_authorize_resource :voicemail_message, :through => [:sip_account] + + before_filter :set_and_authorize_parent + before_filter :spread_breadcrumbs + + before_filter { |controller| + if ! params[:type].blank? then + @type = params[:type].to_s + end + + if ! params[:page].blank? then + @pagination_page_number = params[:page].to_i + end + } + + def index + @messages_count = @sip_account.voicemail_messages.count + @messages_unread_count = @sip_account.voicemail_messages.where(:read_epoch => 0).count + @messages_read_count = @messages_count - @messages_unread_count + + if @type == 'read' + @voicemail_messages = @sip_account.voicemail_messages.where('read_epoch > 0').order('created_epoch DESC').paginate( + :page => @pagination_page_number, + :per_page => DEFAULT_PAGINATION_ENTRIES_PER_PAGE + ) + elsif @type == 'unread' + @voicemail_messages = @sip_account.voicemail_messages.where(:read_epoch => 0).order('created_epoch DESC').paginate( + :page => @pagination_page_number, + :per_page => DEFAULT_PAGINATION_ENTRIES_PER_PAGE + ) + else + @voicemail_messages = @sip_account.voicemail_messages.order('created_epoch DESC').paginate( + :page => @pagination_page_number, + :per_page => DEFAULT_PAGINATION_ENTRIES_PER_PAGE + ) + end + end + + def show + respond_to do |format| + format.wav { + if @voicemail_message.file_path + send_file @voicemail_message.file_path, :type => "audio/x-wav", + :filename => "#{Time.at(@voicemail_message.created_epoch).strftime('%Y%m%d-%H%M%S')}-#{@voicemail_message.cid_number}.wav" + else + render( + :status => 404, + :layout => false, + :content_type => 'text/plain', + :text => "<!-- Message not found -->", + ) + end + } + end + end + + def new + end + + def create + end + + def edit + end + + def update + end + + def destroy + @voicemail_message.destroy + m = method( :"#{@parent.class.name.underscore}_voicemail_messages_url" ) + redirect_to m.(), :notice => t('voicemail_messages.controller.successfuly_destroyed') + end + + def destroy_multiple + result = false + if ! params[:selected_uuids].blank? then + voicemail_messages = @sip_account.voicemail_messages.where(:uuid => params[:selected_uuids]) + voicemail_messages.each do |voicemail_message| + result = voicemail_message.destroy + end + end + + m = method( :"#{@parent.class.name.underscore}_voicemail_messages_url" ) + if result + redirect_to m.(), :notice => t('voicemail_messages.controller.successfuly_destroyed') + else + redirect_to m.() + end + end + + def call + phone_number = @voicemail_message.cid_number + if ! phone_number.blank? && @sip_account.registration + @sip_account.call(phone_number) + end + redirect_to(:back) + end + + def mark_read + @voicemail_message.mark_read + redirect_to(:back) + end + + def mark_unread + @voicemail_message.mark_read(false) + redirect_to(:back) + end + + private + def set_and_authorize_parent + @parent = @sip_account + + authorize! :read, @parent + + @show_path_method = method( :"#{@parent.class.name.underscore}_voicemail_message_path" ) + @index_path_method = method( :"#{@parent.class.name.underscore}_voicemail_messages_path" ) + @new_path_method = method( :"new_#{@parent.class.name.underscore}_voicemail_message_path" ) + @edit_path_method = method( :"edit_#{@parent.class.name.underscore}_voicemail_message_path" ) + end + + def spread_breadcrumbs + if @parent.class == SipAccount + if @sip_account.sip_accountable.class == User + add_breadcrumb t("#{@sip_account.sip_accountable.class.name.underscore.pluralize}.index.page_title"), method( :"tenant_#{@sip_account.sip_accountable.class.name.underscore.pluralize}_path" ).(@sip_account.tenant) + add_breadcrumb @sip_account.sip_accountable, method( :"tenant_#{@sip_account.sip_accountable.class.name.underscore}_path" ).(@sip_account.tenant, @sip_account.sip_accountable) + end + add_breadcrumb t("sip_accounts.index.page_title"), method( :"#{@sip_account.sip_accountable.class.name.underscore}_sip_accounts_path" ).(@sip_account.sip_accountable) + add_breadcrumb @sip_account, method( :"#{@sip_account.sip_accountable.class.name.underscore}_sip_account_path" ).(@sip_account.sip_accountable, @sip_account) + add_breadcrumb t("voicemail_messages.index.page_title"), sip_account_voicemail_messages_path(@sip_account) + if @voicemail_message && !@voicemail_message.new_record? + add_breadcrumb @voicemail_message, sip_account_voicemail_message_path(@sip_account, @voicemail_message) + end + end + end + +end diff --git a/app/controllers/voicemail_settings_controller.rb b/app/controllers/voicemail_settings_controller.rb new file mode 100644 index 0000000..d31de8f --- /dev/null +++ b/app/controllers/voicemail_settings_controller.rb @@ -0,0 +1,91 @@ +class VoicemailSettingsController < ApplicationController + load_resource :sip_account + load_and_authorize_resource :voicemail_setting, :through => :sip_account, :singleton => true + + before_filter :set_and_authorize_parent + before_filter :spread_breadcrumbs + before_filter :voicemail_defaults, :only => [:index, :show, :new, :create, :edit] + + def index + render :edit + end + + def show + render :edit + end + + def new + render :edit + end + + def create + @sip_account = SipAccount.where(:id => params[:sip_account_id]).first + params[:voicemail_setting][:username] = @sip_account.auth_name + params[:voicemail_setting][:domain] = @sip_account.sip_domain.try(:host) + @voicemail_setting = VoicemailSetting.new(params[:voicemail_setting]) + if @voicemail_setting.save + redirect_to sip_account_voicemail_settings_path(@sip_account), :notice => t('voicemail_settings.controller.successfuly_created') + else + render :action => 'edit' + end + end + + def edit + + end + + def update + if @voicemail_setting.update_attributes(params[:voicemail_setting]) + redirect_to sip_account_voicemail_settings_path(@sip_account), :notice => t('voicemail_settings.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + + end + + private + def set_and_authorize_parent + @parent = @sip_account + + authorize! :read, @parent + + @show_path_method = method( :"#{@parent.class.name.underscore}_voicemail_setting_path" ) + @index_path_method = method( :"#{@parent.class.name.underscore}_voicemail_settings_path" ) + @new_path_method = method( :"new_#{@parent.class.name.underscore}_voicemail_setting_path" ) + @edit_path_method = method( :"edit_#{@parent.class.name.underscore}_voicemail_setting_path" ) + end + + def spread_breadcrumbs + if @parent.class == SipAccount + if @sip_account.sip_accountable.class == User + add_breadcrumb t("#{@sip_account.sip_accountable.class.name.underscore.pluralize}.index.page_title"), method( :"tenant_#{@sip_account.sip_accountable.class.name.underscore.pluralize}_path" ).(@sip_account.tenant) + add_breadcrumb @sip_account.sip_accountable, method( :"tenant_#{@sip_account.sip_accountable.class.name.underscore}_path" ).(@sip_account.tenant, @sip_account.sip_accountable) + end + add_breadcrumb t("sip_accounts.index.page_title"), method( :"#{@sip_account.sip_accountable.class.name.underscore}_sip_accounts_path" ).(@sip_account.sip_accountable) + add_breadcrumb @sip_account, method( :"#{@sip_account.sip_accountable.class.name.underscore}_sip_account_path" ).(@sip_account.sip_accountable, @sip_account) + add_breadcrumb t("voicemail_settings.index.page_title"), sip_account_voicemail_settings_path(@sip_account) + end + end + + def voicemail_defaults + path = "/opt/freeswitch/storage/voicemail/default/#{@sip_account.sip_domain.host}/#{@sip_account.auth_name}/" + @greeting_files = Dir.glob("#{path}*greeting*.wav").collect {|r| [ File.basename(r), File.expand_path(r) ] } + @name_files = Dir.glob("#{path}*name*.wav").collect {|r| [ File.basename(r), File.expand_path(r) ] } + + if @voicemail_setting.blank? then + @voicemail_setting = @sip_account.voicemail_setting + end + + if @voicemail_setting.blank? + @voicemail_setting = VoicemailSetting.new + @voicemail_setting.notify = true + @voicemail_setting.attachment = true + @voicemail_setting.mark_read = true + @voicemail_setting.purge = false + end + end + +end diff --git a/app/controllers/whitelists_controller.rb b/app/controllers/whitelists_controller.rb new file mode 100644 index 0000000..0526844 --- /dev/null +++ b/app/controllers/whitelists_controller.rb @@ -0,0 +1,61 @@ +class WhitelistsController < ApplicationController + load_and_authorize_resource :callthrough + load_and_authorize_resource :whitelist, :through => [:callthrough] + + before_filter :set_parent_and_path_methods + before_filter :spread_breadcrumbs + + def index + end + + def show + end + + def new + @whitelist.phone_numbers.build + end + + def create + @whitelist = @parent.whitelists.build(params[:whitelist]) + if @whitelist.save + redirect_to @show_path_method.(@parent, @whitelist), :notice => t('whitelists.controller.successfuly_created') + else + render :new + end + end + + def edit + end + + def update + if @whitelist.update_attributes(params[:whitelist]) + redirect_to @show_path_method.(@parent, @whitelist), :notice => t('whitelists.controller.successfuly_updated') + else + render :edit + end + end + + def destroy + @whitelist.destroy + redirect_to @index_path_method.(@parent), :notice => t('whitelists.controller.successfuly_destroyed') + end + + private + + def set_parent_and_path_methods + @parent = @callthrough + @show_path_method = method( :"#{@parent.class.name.underscore}_whitelist_path" ) + @index_path_method = method( :"#{@parent.class.name.underscore}_whitelists_path" ) + @new_path_method = method( :"new_#{@parent.class.name.underscore}_whitelist_path" ) + @edit_path_method = method( :"edit_#{@parent.class.name.underscore}_whitelist_path" ) + end + + def spread_breadcrumbs + if @parent && @parent.class == Callthrough + add_breadcrumb t("#{@parent.class.name.underscore.pluralize}.name").pluralize, tenant_callthroughs_path(@parent.tenant) + add_breadcrumb @callthrough, tenant_callthrough_path(@parent.tenant, @callthrough) + add_breadcrumb t("whitelists.index.page_title"), callthrough_whitelists_path(@parent) + end + end + +end diff --git a/app/helpers/access_authorizations_helper.rb b/app/helpers/access_authorizations_helper.rb new file mode 100644 index 0000000..d16f5c6 --- /dev/null +++ b/app/helpers/access_authorizations_helper.rb @@ -0,0 +1,2 @@ +module AccessAuthorizationsHelper +end diff --git a/app/helpers/acd_agents_helper.rb b/app/helpers/acd_agents_helper.rb new file mode 100644 index 0000000..5be92a8 --- /dev/null +++ b/app/helpers/acd_agents_helper.rb @@ -0,0 +1,2 @@ +module AcdAgentsHelper +end diff --git a/app/helpers/acd_callers_helper.rb b/app/helpers/acd_callers_helper.rb new file mode 100644 index 0000000..534b99c --- /dev/null +++ b/app/helpers/acd_callers_helper.rb @@ -0,0 +1,2 @@ +module AcdCallersHelper +end diff --git a/app/helpers/addresses_helper.rb b/app/helpers/addresses_helper.rb new file mode 100644 index 0000000..5f4dc13 --- /dev/null +++ b/app/helpers/addresses_helper.rb @@ -0,0 +1,2 @@ +module AddressesHelper +end diff --git a/app/helpers/api/rows_helper.rb b/app/helpers/api/rows_helper.rb new file mode 100644 index 0000000..a18dab4 --- /dev/null +++ b/app/helpers/api/rows_helper.rb @@ -0,0 +1,2 @@ +module Api::RowsHelper +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb new file mode 100644 index 0000000..de6be79 --- /dev/null +++ b/app/helpers/application_helper.rb @@ -0,0 +1,2 @@ +module ApplicationHelper +end diff --git a/app/helpers/automatic_call_distributors_helper.rb b/app/helpers/automatic_call_distributors_helper.rb new file mode 100644 index 0000000..19a9828 --- /dev/null +++ b/app/helpers/automatic_call_distributors_helper.rb @@ -0,0 +1,2 @@ +module AutomaticCallDistributorsHelper +end diff --git a/app/helpers/call_forward_cases_helper.rb b/app/helpers/call_forward_cases_helper.rb new file mode 100644 index 0000000..63a4939 --- /dev/null +++ b/app/helpers/call_forward_cases_helper.rb @@ -0,0 +1,2 @@ +module CallForwardCasesHelper +end diff --git a/app/helpers/call_forwards_helper.rb b/app/helpers/call_forwards_helper.rb new file mode 100644 index 0000000..ffb6977 --- /dev/null +++ b/app/helpers/call_forwards_helper.rb @@ -0,0 +1,2 @@ +module CallForwardsHelper +end diff --git a/app/helpers/callthroughs_helper.rb b/app/helpers/callthroughs_helper.rb new file mode 100644 index 0000000..2ee0b9b --- /dev/null +++ b/app/helpers/callthroughs_helper.rb @@ -0,0 +1,2 @@ +module CallthroughsHelper +end diff --git a/app/helpers/conference_invitees_helper.rb b/app/helpers/conference_invitees_helper.rb new file mode 100644 index 0000000..dab6843 --- /dev/null +++ b/app/helpers/conference_invitees_helper.rb @@ -0,0 +1,2 @@ +module ConferenceInviteesHelper +end diff --git a/app/helpers/conferences_helper.rb b/app/helpers/conferences_helper.rb new file mode 100644 index 0000000..edfcfdd --- /dev/null +++ b/app/helpers/conferences_helper.rb @@ -0,0 +1,2 @@ +module ConferencesHelper +end diff --git a/app/helpers/config_siemens_helper.rb b/app/helpers/config_siemens_helper.rb new file mode 100644 index 0000000..7ff8c5b --- /dev/null +++ b/app/helpers/config_siemens_helper.rb @@ -0,0 +1,2 @@ +module ConfigSiemensHelper +end diff --git a/app/helpers/error_messages_helper.rb b/app/helpers/error_messages_helper.rb new file mode 100644 index 0000000..8e9c4d3 --- /dev/null +++ b/app/helpers/error_messages_helper.rb @@ -0,0 +1,23 @@ +module ErrorMessagesHelper + # Render error messages for the given objects. The :message and :header_message options are allowed. + def error_messages_for(*objects) + options = objects.extract_options! + options[:header_message] ||= I18n.t(:"activerecord.errors.header", :default => "Invalid Fields") + options[:message] ||= I18n.t(:"activerecord.errors.message", :default => "Correct the following errors and try again.") + messages = objects.compact.map { |o| o.errors.full_messages }.flatten + unless messages.empty? + content_tag(:div, :class => "error_messages") do + list_items = messages.map { |msg| content_tag(:li, msg.html_safe) } + content_tag(:h2, options[:header_message].html_safe) + content_tag(:p, options[:message].html_safe) + content_tag(:ul, list_items.join.html_safe) + end + end + end + + module FormBuilderAdditions + def error_messages(options = {}) + @template.error_messages_for(@object, options) + end + end +end + +ActionView::Helpers::FormBuilder.send(:include, ErrorMessagesHelper::FormBuilderAdditions) diff --git a/app/helpers/fax_accounts_helper.rb b/app/helpers/fax_accounts_helper.rb new file mode 100644 index 0000000..529c4fb --- /dev/null +++ b/app/helpers/fax_accounts_helper.rb @@ -0,0 +1,2 @@ +module FaxAccountsHelper +end diff --git a/app/helpers/fax_documents_helper.rb b/app/helpers/fax_documents_helper.rb new file mode 100644 index 0000000..c168948 --- /dev/null +++ b/app/helpers/fax_documents_helper.rb @@ -0,0 +1,2 @@ +module FaxDocumentsHelper +end diff --git a/app/helpers/gemeinschaft_setups_helper.rb b/app/helpers/gemeinschaft_setups_helper.rb new file mode 100644 index 0000000..f241900 --- /dev/null +++ b/app/helpers/gemeinschaft_setups_helper.rb @@ -0,0 +1,2 @@ +module GemeinschaftSetupsHelper +end diff --git a/app/helpers/gs_cluster_sync_log_entries_helper.rb b/app/helpers/gs_cluster_sync_log_entries_helper.rb new file mode 100644 index 0000000..9eef5de --- /dev/null +++ b/app/helpers/gs_cluster_sync_log_entries_helper.rb @@ -0,0 +1,2 @@ +module GsClusterSyncLogEntriesHelper +end diff --git a/app/helpers/gs_nodes_helper.rb b/app/helpers/gs_nodes_helper.rb new file mode 100644 index 0000000..9ba2a39 --- /dev/null +++ b/app/helpers/gs_nodes_helper.rb @@ -0,0 +1,2 @@ +module GsNodesHelper +end diff --git a/app/helpers/gui_functions_helper.rb b/app/helpers/gui_functions_helper.rb new file mode 100644 index 0000000..35324cd --- /dev/null +++ b/app/helpers/gui_functions_helper.rb @@ -0,0 +1,2 @@ +module GuiFunctionsHelper +end diff --git a/app/helpers/hunt_group_members_helper.rb b/app/helpers/hunt_group_members_helper.rb new file mode 100644 index 0000000..e198542 --- /dev/null +++ b/app/helpers/hunt_group_members_helper.rb @@ -0,0 +1,2 @@ +module HuntGroupMembersHelper +end diff --git a/app/helpers/hunt_groups_helper.rb b/app/helpers/hunt_groups_helper.rb new file mode 100644 index 0000000..d1b3b05 --- /dev/null +++ b/app/helpers/hunt_groups_helper.rb @@ -0,0 +1,2 @@ +module HuntGroupsHelper +end diff --git a/app/helpers/layout_helper.rb b/app/helpers/layout_helper.rb new file mode 100644 index 0000000..1dad619 --- /dev/null +++ b/app/helpers/layout_helper.rb @@ -0,0 +1,70 @@ +# These helper methods can be called in your template to set +# variables to be used in the layout. +# This module should be included in all views globally, +# to do so you may need to add this line to your +# ApplicationController +# helper :layout +# +module LayoutHelper + + def title( page_title, show_title = true ) + content_for(:title) { strip_tags(page_title.to_s) } + @show_title = show_title + end + + def show_title? + @show_title + end + + def stylesheet( *args ) + content_for(:head) { stylesheet_link_tag( *args ) } + end + + def javascript( *args ) + content_for(:head) { javascript_include_tag( *args ) } + end + + def translation_missing?( output ) + (output =~ /span/ or output.empty?) + end + + def conditional_hint( translation_key ) + output = t( translation_key ) + return output unless translation_missing?( output ) + false + end + + def conditional_t( translation_key ) + output = t( translation_key ) + strip_tags( output ) + end + + def resolve_flash_sign( type ) + return case type.to_s + when 'alert' ; '!' + when 'warning' ; '!' + else ; 'i' + end + end + + # Returns navigation as an array. + # + def navigation_items + unless @io + @io = [] + + if can?( :index, PhoneBookEntry ) + @io << { :url => phone_book_entries_path , :title => t('phone_book_entries.index.page_title' ) } + end + + # This could be a link to VoiceMails. + # + # if can?( :index, Object ) + # @io << { :url => "#" , :title => t('voice_mail') } + # end + + end + @io + end + +end diff --git a/app/helpers/manufacturers_helper.rb b/app/helpers/manufacturers_helper.rb new file mode 100644 index 0000000..3f9e083 --- /dev/null +++ b/app/helpers/manufacturers_helper.rb @@ -0,0 +1,2 @@ +module ManufacturersHelper +end diff --git a/app/helpers/page_helper.rb b/app/helpers/page_helper.rb new file mode 100644 index 0000000..625cfe4 --- /dev/null +++ b/app/helpers/page_helper.rb @@ -0,0 +1,2 @@ +module PageHelper +end diff --git a/app/helpers/phone_book_entries_helper.rb b/app/helpers/phone_book_entries_helper.rb new file mode 100644 index 0000000..db24cae --- /dev/null +++ b/app/helpers/phone_book_entries_helper.rb @@ -0,0 +1,2 @@ +module PhoneBookEntriesHelper +end diff --git a/app/helpers/phone_books_helper.rb b/app/helpers/phone_books_helper.rb new file mode 100644 index 0000000..55ebf19 --- /dev/null +++ b/app/helpers/phone_books_helper.rb @@ -0,0 +1,2 @@ +module PhoneBooksHelper +end diff --git a/app/helpers/phone_models_helper.rb b/app/helpers/phone_models_helper.rb new file mode 100644 index 0000000..2cc6545 --- /dev/null +++ b/app/helpers/phone_models_helper.rb @@ -0,0 +1,2 @@ +module PhoneModelsHelper +end diff --git a/app/helpers/phone_number_ranges_helper.rb b/app/helpers/phone_number_ranges_helper.rb new file mode 100644 index 0000000..f4d5897 --- /dev/null +++ b/app/helpers/phone_number_ranges_helper.rb @@ -0,0 +1,2 @@ +module PhoneNumberRangesHelper +end diff --git a/app/helpers/phone_numbers_helper.rb b/app/helpers/phone_numbers_helper.rb new file mode 100644 index 0000000..cb4f200 --- /dev/null +++ b/app/helpers/phone_numbers_helper.rb @@ -0,0 +1,2 @@ +module PhoneNumbersHelper +end diff --git a/app/helpers/phone_sip_accounts_helper.rb b/app/helpers/phone_sip_accounts_helper.rb new file mode 100644 index 0000000..f834d2c --- /dev/null +++ b/app/helpers/phone_sip_accounts_helper.rb @@ -0,0 +1,2 @@ +module PhoneSipAccountsHelper +end diff --git a/app/helpers/phones_helper.rb b/app/helpers/phones_helper.rb new file mode 100644 index 0000000..69ebd13 --- /dev/null +++ b/app/helpers/phones_helper.rb @@ -0,0 +1,2 @@ +module PhonesHelper +end diff --git a/app/helpers/phones_sip_accounts_helper.rb b/app/helpers/phones_sip_accounts_helper.rb new file mode 100644 index 0000000..03e3fd2 --- /dev/null +++ b/app/helpers/phones_sip_accounts_helper.rb @@ -0,0 +1,2 @@ +module PhonesSipAccountsHelper +end diff --git a/app/helpers/ringtones_helper.rb b/app/helpers/ringtones_helper.rb new file mode 100644 index 0000000..33deac9 --- /dev/null +++ b/app/helpers/ringtones_helper.rb @@ -0,0 +1,2 @@ +module RingtonesHelper +end diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb new file mode 100644 index 0000000..309f8b2 --- /dev/null +++ b/app/helpers/sessions_helper.rb @@ -0,0 +1,2 @@ +module SessionsHelper +end diff --git a/app/helpers/sip_accounts_helper.rb b/app/helpers/sip_accounts_helper.rb new file mode 100644 index 0000000..1e666fd --- /dev/null +++ b/app/helpers/sip_accounts_helper.rb @@ -0,0 +1,2 @@ +module SipAccountsHelper +end diff --git a/app/helpers/sip_domains_helper.rb b/app/helpers/sip_domains_helper.rb new file mode 100644 index 0000000..c1d85ee --- /dev/null +++ b/app/helpers/sip_domains_helper.rb @@ -0,0 +1,2 @@ +module SipDomainsHelper +end diff --git a/app/helpers/softkeys_helper.rb b/app/helpers/softkeys_helper.rb new file mode 100644 index 0000000..e551779 --- /dev/null +++ b/app/helpers/softkeys_helper.rb @@ -0,0 +1,2 @@ +module SoftkeysHelper +end diff --git a/app/helpers/system_messages_helper.rb b/app/helpers/system_messages_helper.rb new file mode 100644 index 0000000..fef2386 --- /dev/null +++ b/app/helpers/system_messages_helper.rb @@ -0,0 +1,2 @@ +module SystemMessagesHelper +end diff --git a/app/helpers/tenants_helper.rb b/app/helpers/tenants_helper.rb new file mode 100644 index 0000000..b7bb45d --- /dev/null +++ b/app/helpers/tenants_helper.rb @@ -0,0 +1,2 @@ +module TenantsHelper +end diff --git a/app/helpers/user_groups_helper.rb b/app/helpers/user_groups_helper.rb new file mode 100644 index 0000000..83cd8f3 --- /dev/null +++ b/app/helpers/user_groups_helper.rb @@ -0,0 +1,2 @@ +module UserGroupsHelper +end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb new file mode 100644 index 0000000..2310a24 --- /dev/null +++ b/app/helpers/users_helper.rb @@ -0,0 +1,2 @@ +module UsersHelper +end diff --git a/app/helpers/whitelists_helper.rb b/app/helpers/whitelists_helper.rb new file mode 100644 index 0000000..083be60 --- /dev/null +++ b/app/helpers/whitelists_helper.rb @@ -0,0 +1,2 @@ +module WhitelistsHelper +end diff --git a/app/mailers/.gitkeep b/app/mailers/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/mailers/.gitkeep diff --git a/app/mailers/notifications.rb b/app/mailers/notifications.rb new file mode 100644 index 0000000..2c7f2ce --- /dev/null +++ b/app/mailers/notifications.rb @@ -0,0 +1,110 @@ +class Notifications < ActionMailer::Base + default from: "admin@example.com" + + # Subject can be set in your I18n file at config/locales/en.yml + # with the following lookup: + # + # en.notifications.new_pin.subject + # + def new_pin(conference) + @conference = conference + + @pin = Hash.new() + if conference.conferenceable_type == 'User' + user = conference.conferenceable + + if ! user.first_name.blank? + @pin[:greeting] = user.first_name + else + @pin[:greeting] = user.user_name + end + else + @pin[:greeting] = conference.conferenceable.to_s + end + + @pin[:conference] = conference.to_s + @pin[:pin] = conference.pin + @pin[:phone_numbers] = conference.phone_numbers.join(', ') + + mail(from: Tenant.find(DEFAULT_API_TENANT_ID).from_field_pin_change_email,to: "#{conference.conferenceable.email}", :subject => "Conference PIN changed: #{@pin[:conference]}") + end + + def new_password(user, password) + @password = password + + @message = Hash.new() + if ! user.first_name.blank? + @message[:greeting] = user.first_name + else + @message[:greeting] = user.user_name + end + + mail(from: Tenant.find(DEFAULT_API_TENANT_ID).from_field_pin_change_email, to: "#{user.email}", :subject => "Password recovery") + end + + def new_voicemail(freeswitch_voicemail_msg, attach_file = false) + sip_account = SipAccount.find_by_auth_name(freeswitch_voicemail_msg.username) + user = sip_account.sip_accountable + + @voicemail = Hash.new() + if ! user.first_name.blank? + @voicemail[:greeting] = user.first_name + else + @voicemail[:greeting] = user.user_name + end + + @voicemail[:destination] = freeswitch_voicemail_msg.in_folder + @voicemail[:from] = "#{freeswitch_voicemail_msg.cid_number} #{freeswitch_voicemail_msg.cid_name}" + @voicemail[:to] = sip_account.to_s + @voicemail[:date] = Time.at(freeswitch_voicemail_msg.created_epoch).getlocal.to_s + @voicemail[:duration] = Time.at(freeswitch_voicemail_msg.message_len).utc.strftime('%T') + + if attach_file + caller_number = freeswitch_voicemail_msg.cid_number.gsub(/[^0-9]/, '') + if caller_number.blank? + caller_number = 'anonymous' + end + attachments["#{Time.at(freeswitch_voicemail_msg.created_epoch).getlocal.strftime('%Y%m%d-%H%M%S')}-#{caller_number}.wav"] = File.read(freeswitch_voicemail_msg.file_path) + end + + mail(from: Tenant.find(DEFAULT_API_TENANT_ID).from_field_voicemail_email, to: "#{user.email}", :subject => "New Voicemail from #{@voicemail[:from]}, received #{Time.at(freeswitch_voicemail_msg.created_epoch).getlocal.to_s}") + end + + def new_fax(fax_document) + fax_account = fax_document.fax_account + + if !fax_account || fax_account.email.blank? + return false + end + + caller_number = fax_document.caller_id_number.gsub(/[^0-9]/, '') + if caller_number.blank? + caller_number = 'anonymous' + end + + @fax = { + :greeting => '', + :account_name => fax_account.name, + :from => "#{caller_number} #{fax_document.caller_id_name}", + :remote_station_id => fax_document.remote_station_id, + :local_station_id => fax_document.local_station_id, + :date => fax_document.created_at, + } + + if fax_account.fax_accountable + if fax_account.fax_accountable_type == 'User' + user = fax_account.fax_accountable + if ! user.first_name.blank? + @fax[:greeting] = user.first_name + else + @fax[:greeting] = user.user_name + end + elsif fax_account.fax_accountable_type == 'Tenant' + @fax[:greeting] = fax_account.fax_accountable.name + end + end + attachments["#{fax_document.created_at.strftime('%Y%m%d-%H%M%S')}-#{caller_number}.pdf"] = File.read(fax_document.document.path) + mail(from: Tenant.find(DEFAULT_API_TENANT_ID).from_field_voicemail_email, to: "#{fax_account.email}", :subject => "New Fax Document from #{@fax[:from]}, received #{fax_document.created_at}") + end + +end diff --git a/app/models/.gitkeep b/app/models/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/models/.gitkeep diff --git a/app/models/ability.rb b/app/models/ability.rb new file mode 100644 index 0000000..d9ec74a --- /dev/null +++ b/app/models/ability.rb @@ -0,0 +1,170 @@ +class Ability + include CanCan::Ability + + def initialize( user ) + # See the wiki for details: https://github.com/ryanb/cancan/wiki/Defining-Abilities + if user && user.current_tenant != nil + if GemeinschaftSetup.count == 1 && Tenant.count == 1 && User.count == 1 && UserGroup.count == 1 + # This is a new installation with a Master-Tenant and a Super-Admin. + # + can [:read, :create], Tenant + else + tenant = user.current_tenant + + if user.current_tenant.user_groups.where(:name => 'Admins').first \ + && user.current_tenant.user_groups.where(:name => 'Admins').first.users.include?(user) + # ADMIN ABILITIES + # With great power comes great responsibility! + # + can :manage, :all + + # Manufacturers and PhoneModels can not be changed + # + cannot [:create, :destroy, :edit, :update], Manufacturer + cannot [:create, :destroy, :edit, :update], PhoneModel + + # Super-Tenant can not be destroyed or edited + # + cannot [:create, :destroy, :edit, :update], Tenant, :id => 1 + + cannot :manage, PhoneBook + + # Phonebooks and PhoneBookEntries + # + can :manage, PhoneBook, :phone_bookable_type => 'Tenant', :phone_bookable_id => tenant.id + can :manage, PhoneBookEntry, :phone_book => { :phone_bookable_type => 'Tenant', :phone_bookable_id => tenant.id } + + can :manage, PhoneBook, :phone_bookable_type => 'UserGroup', :phone_bookable_id => tenant.user_group_ids + tenant.user_groups.each do |user_group| + can :manage, PhoneBookEntry, :phone_book => { :id => user_group.phone_book_ids } + end + + # Personal Phonebooks and PhoneBookEntries + # + can :manage, PhoneBook, :phone_bookable_type => 'User', :phone_bookable_id => user.id + can :manage, PhoneBookEntry, :phone_book => { :phone_bookable_type => 'User', :phone_bookable_id => user.id } + + can :read, PhoneBook, :phone_bookable_type => 'Tenant', :phone_bookable_id => tenant.id + can :read, PhoneBookEntry, :phone_book => { :phone_bookable_type => 'Tenant', :phone_bookable_id => tenant.id } + + can :read, PhoneBook, :phone_bookable_type => 'UserGroup', :phone_bookable_id => user.user_group_ids + user.user_groups.each do |user_group| + can :read, PhoneBookEntry, :phone_book => { :id => user_group.phone_book_ids } + end + + # SystemMessages + # + cannot [:destroy, :edit, :update], SystemMessage + + # A FacDocument can't be changed + # + cannot [:edit, :update], FaxDocument + + # Can manage GsNodes + # + can :manage, GsNode + + # Can't phones/1/phone_sip_accounts/1/edit + # + cannot :edit, PhoneSipAccount + + # Dirty hack to disable PhoneNumberRange in the GUI + # + if STRICT_INTERNAL_EXTENSION_HANDLING == false + cannot :manage, PhoneNumberRange + end + else + # Any user can do the following stuff. + # + + # Own Tenant and own User + # + can :read, Tenant, :id => user.current_tenant.id + can [ :read, :edit, :update ], User, :id => user.id + + # Destroy his own avatar + # + can :destroy_avatar, User, :id => user.id + + # Phonebooks and PhoneBookEntries + # + cannot :manage, PhoneBook + + can :manage, PhoneBook, :phone_bookable_type => 'User', :phone_bookable_id => user.id + can :manage, PhoneBookEntry, :phone_book => { :phone_bookable_type => 'User', :phone_bookable_id => user.id } + can :manage, PhoneNumber, :phone_numberable_type => 'PhoneBookEntry', :phone_numberable_id => user.phone_books.map{ |phone_book| phone_book.phone_book_entry_ids}.flatten + + can :read, PhoneBook, :phone_bookable_type => 'Tenant', :phone_bookable_id => tenant.id + can :read, PhoneBookEntry, :phone_book => { :phone_bookable_type => 'Tenant', :phone_bookable_id => tenant.id } + + can :read, PhoneBook, :phone_bookable_type => 'UserGroup', :phone_bookable_id => user.user_group_ids + user.user_groups.each do |user_group| + can :read, PhoneBookEntry, :phone_book => { :id => user_group.phone_book_ids } + end + + # UserGroups + # + can :read, UserGroupMembership, :user_id => user.id + can :read, UserGroup, :users => { :user_group_memberships => { :user_id => user.id }} + + # SipAccounts and Phones + # + can :read, SipAccount, :sip_accountable_type => 'User', :sip_accountable_id => user.id + user.sip_accounts.each do |sip_account| + can :read, PhoneNumber, :id => sip_account.phone_number_ids + can :manage, CallForward, :phone_number_id => sip_account.phone_number_ids + can :manage, Ringtone, :ringtoneable_type => 'PhoneNumber', :ringtoneable_id => sip_account.phone_number_ids + can [:read, :destroy, :call] , CallHistory, :id => sip_account.call_history_ids + end + can :read, Phone, :phoneable_type => 'User', :phoneable_id => user.id + + # Softkeys + # + can :manage, Softkey, :sip_account => { :id => user.sip_account_ids } + + # Fax + # + can :read, FaxAccount, :fax_accountable_type => 'User', :fax_accountable_id => user.id + user.fax_accounts.each do |fax_account| + can :read, PhoneNumber, :id => fax_account.phone_number_ids + can [:read, :create, :delete], FaxDocument, :fax_account_id => fax_account.id + end + + # Conferences + # + can [ :read, :edit, :update, :destroy ], Conference, :id => user.conference_ids + user.conferences.each do |conference| + can :read, PhoneNumber, :id => conference.phone_number_ids + can :manage, ConferenceInvitee, :conference_id => conference.id + end + + # User can manage CallForwards of the PhoneNumbers of his + # own SipAccounts: + # + can :manage, CallForward, :phone_number_id => user.phone_number_ids + + # SystemMessages + # + can :read, SystemMessage, :user_id => user.id + + # SoftkeyFunctions + # + can :read, SoftkeyFunction + + # Voicemail + # + can :manage, VoicemailMessage + can :manage, VoicemailSetting + end + end + else + if GemeinschaftSetup.count == 0 && Tenant.count == 0 && User.count == 0 + # This is a fresh system. + # + can :create, GemeinschaftSetup + can :manage, SipDomain + end + end + + end +end diff --git a/app/models/access_authorization.rb b/app/models/access_authorization.rb new file mode 100644 index 0000000..ef33115 --- /dev/null +++ b/app/models/access_authorization.rb @@ -0,0 +1,41 @@ +class AccessAuthorization < ActiveRecord::Base + attr_accessible :name, :login, :pin, :phone_numbers_attributes, :sip_account_id + + belongs_to :access_authorizationable, :polymorphic => true + + validates_uniqueness_of :name, :scope => [ :access_authorizationable_type, :access_authorizationable_id ], + :allow_nil => true, :allow_blank => true + + # The login is optional. But if set has to be done with digits only. + # + validates_format_of :login, :with => /\A([0-9]+)\Z/, + :allow_nil => true, :allow_blank => true, + :message => "must be numeric." + + # The PIN is optional. But when set it has to be a proper PIN. + # + validates_format_of :pin, :with => /\A([0-9]+)\Z/, + :allow_nil => true, :allow_blank => true, + :message => "must be numeric." + + validates_length_of :pin, :minimum => MINIMUM_PIN_LENGTH, :maximum => MAXIMUM_PIN_LENGTH, + :allow_nil => true, :allow_blank => true + + has_many :phone_numbers, :as => :phone_numberable, :dependent => :destroy + accepts_nested_attributes_for :phone_numbers, + :reject_if => lambda { |phone_number| phone_number[:number].blank? }, + :allow_destroy => true + + # Optional SIP account. + # + belongs_to :sip_account + + validates_presence_of :sip_account, :if => Proc.new{ |access_authorization| !access_authorization.sip_account_id.blank? }, + :message => 'Given SIP account does not exist.' + + acts_as_list :scope => [ :access_authorizationable_type, :access_authorizationable_id ] + + def to_s + self.name || I18n.t('access_authorizations.name') + ' ID ' + self.id.to_s + end +end diff --git a/app/models/acd_agent.rb b/app/models/acd_agent.rb new file mode 100644 index 0000000..a00ac4b --- /dev/null +++ b/app/models/acd_agent.rb @@ -0,0 +1,39 @@ +class AcdAgent < ActiveRecord::Base + DESTINATION_TYPES = ['SipAccount'] + STATUSES = ['active', 'inactive'] + + attr_accessible :uuid, :name, :status, :automatic_call_distributor_id, :last_call, :calls_answered, :destination_type, :destination_id + + belongs_to :automatic_call_distributor + + belongs_to :destination, :polymorphic => true + + after_save :set_presence + + def to_s + self.name || I18n.t('acd_agents.name') + ' ID ' + self.id.to_s + end + + private + def set_presence + dialplan_function = nil + + state = 'early' + if self.status == 'active' + state = 'confirmed' + elsif self.status == 'inactive' + state = 'terminated' + end + + require 'freeswitch_event' + event = FreeswitchEvent.new("PRESENCE_IN") + event.add_header("proto", "sip") + event.add_header("from", "f-acdmtg-#{self.id}@#{SipDomain.first.host}") + event.add_header("event_type", "presence") + event.add_header("alt_event_type", "dialog") + event.add_header("presence-call-direction", "outbound") + event.add_header("answer-state", state) + event.add_header("unique-id", "acd_agent_#{self.id}") + return event.fire() + end +end diff --git a/app/models/acd_caller.rb b/app/models/acd_caller.rb new file mode 100644 index 0000000..1be48b9 --- /dev/null +++ b/app/models/acd_caller.rb @@ -0,0 +1,6 @@ +class AcdCaller < ActiveRecord::Base + attr_accessible :channel_uuid, :automatic_call_distributor_id, :status, :enter_time, :agent_answer_time, :callback_number, :callback_attempts + + has_one :channel, :class_name => 'FreeswitchChannel', :foreign_key => 'uuid', :primary_key => 'channel_uuid' + belongs_to :automatic_call_distributor +end diff --git a/app/models/address.rb b/app/models/address.rb new file mode 100644 index 0000000..bcf3474 --- /dev/null +++ b/app/models/address.rb @@ -0,0 +1,8 @@ +class Address < ActiveRecord::Base + attr_accessible :phone_book_entry_id, :line1, :line2, :street, :zip_code, :city, :country_id, :position, :uuid + + belongs_to :country + + validates_presence_of :uuid + validates_uniqueness_of :uuid +end diff --git a/app/models/api.rb b/app/models/api.rb new file mode 100644 index 0000000..557d875 --- /dev/null +++ b/app/models/api.rb @@ -0,0 +1,5 @@ +module Api + def self.table_name_prefix + 'api_' + end +end diff --git a/app/models/api/row.rb b/app/models/api/row.rb new file mode 100644 index 0000000..ac35516 --- /dev/null +++ b/app/models/api/row.rb @@ -0,0 +1,152 @@ +class Api::Row < ActiveRecord::Base + + # This is the place to do some basic mapping. + # + alias_attribute :UserName, :user_name + alias_attribute :LastName, :last_name + alias_attribute :FirstName, :first_name + alias_attribute :PhoneOffice, :office_phone_number + alias_attribute :VoipNr, :internal_extension + alias_attribute :CellPhone, :mobile_phone_number + alias_attribute :Fax, :fax_phone_number + alias_attribute :Email, :email + alias_attribute :PIN, :pin + alias_attribute :PIN_LastUpdate, :pin_updated_at + alias_attribute :Photo, :photo_file_name + + belongs_to :user + + # Validations + # + validates_presence_of :user_name + validates_uniqueness_of :user_name + + after_destroy :destroy_user + + def to_s + self.user_name + end + + def create_a_new_gemeinschaft_user + tenant = Tenant.find(DEFAULT_API_TENANT_ID) + + # Find or create the user + # + if tenant.users.where(:user_name => self.user_name).count > 0 + user = tenant.users.where(:user_name => self.user_name).first + else + user = tenant.users.create( + :user_name => self.user_name, + :last_name => self.last_name, + :first_name => self.first_name, + :middle_name => self.middle_name, + :email => self.email, + :new_pin => self.pin, + :new_pin_confirmation => self.pin, + :password => self.pin, + :password_confirmation => self.pin, + :language_id => tenant.language_id, + ) + end + + self.update_attributes({:user_id => user.id}) + + # Find or create a sip_account + # + if user.sip_accounts.count > 0 + sip_account = user.sip_accounts.first + else + sip_account = user.sip_accounts.create( + :caller_name => self.user.to_s, + :voicemail_pin => self.pin, + ) + end + + # Create phone_numbers to this sip_account (BTW: phone_numbers are unqiue) + # + sip_account.phone_numbers.create(:number => self.internal_extension) + sip_account.phone_numbers.create(:number => self.office_phone_number) + + + # Find or create a fax account + # + if user.fax_accounts.count > 0 + fax_account = user.fax_accounts.first + else + fax_account = user.fax_accounts.create( + :name => 'Default Fax', + :station_id => user.to_s, + :email => self.email, + :days_till_auto_delete => 90, + :retries => 3, + ) + end + + # Create phone_numbers to this fax_account + # + fax_account.phone_numbers.create(:number => self.fax_phone_number) + + end + + def destroy_user + self.user.destroy + end + + def update_user_data + user = self.user + user.update_attributes( + :user_name => self.user_name, + :last_name => self.last_name, + :first_name => self.first_name, + :middle_name => self.middle_name, + :email => self.email, + :new_pin => self.pin, + :new_pin_confirmation => self.pin, + :password => self.pin, + :password_confirmation => self.pin, + ) + + # Find or create a sip_account + # + if user.sip_accounts.count > 0 + sip_account = user.sip_accounts.first + else + sip_account = user.sip_accounts.create( + :caller_name => self.user.to_s, + :voicemail_pin => self.pin, + ) + end + + # Delete old phone_numbers + # + sip_account.phone_numbers.destroy_all + + # Create phone_numbers to this sip_account (BTW: phone_numbers are unqiue) + # + sip_account.phone_numbers.create(:number => self.internal_extension) + sip_account.phone_numbers.create(:number => self.office_phone_number) + + # Find or create a fax account + # + if user.fax_accounts.count > 0 + fax_account = user.fax_accounts.first + else + fax_account = user.fax_accounts.create( + :name => 'Default Fax', + :station_id => user.to_s, + :email => self.email, + :days_till_auto_delete => 90, + :retries => 3, + ) + end + + # Delete old phone_number + # + fax_account.phone_numbers.destroy_all + + # Create phone_numbers to this fax_account + # + fax_account.phone_numbers.create(:number => self.fax_phone_number) + end + +end diff --git a/app/models/area_code.rb b/app/models/area_code.rb new file mode 100644 index 0000000..6a9d946 --- /dev/null +++ b/app/models/area_code.rb @@ -0,0 +1,22 @@ +class AreaCode < ActiveRecord::Base + + # Associations: + # + belongs_to :country + + # Validations: + # + validates_presence_of :country + validates_presence_of :name + validates_presence_of :area_code + + validates_uniqueness_of :area_code, :scope => [ :country_id, :central_office_code ] + + + def to_s + "#{self.name} (#{self.area_code}" + + (self.central_office_code.blank? ? '' : "-#{self.central_office_code}") + + ')' + end + +end diff --git a/app/models/automatic_call_distributor.rb b/app/models/automatic_call_distributor.rb new file mode 100644 index 0000000..678e0eb --- /dev/null +++ b/app/models/automatic_call_distributor.rb @@ -0,0 +1,21 @@ +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 + + has_many :acd_agents, :dependent => :destroy + has_many :phone_numbers, :as => :phone_numberable, :dependent => :destroy + accepts_nested_attributes_for :phone_numbers, + :reject_if => lambda { |phone_number| phone_number[:number].blank? }, + :allow_destroy => true + + validates_presence_of :strategy + + STRATEGIES = ['ring_all', 'round_robin'] + JOIN_ON = ['agents_available', 'agents_active', 'always'] + LEAVE_ON = ['no_agents_available_timeout', 'no_agents_active_timeout', 'no_agents_available', 'no_agents_active', 'timeout', 'never'] + + def to_s + self.name + end +end diff --git a/app/models/call.rb b/app/models/call.rb new file mode 100644 index 0000000..57961ec --- /dev/null +++ b/app/models/call.rb @@ -0,0 +1,36 @@ +class Call < ActiveRecord::Base + self.table_name = 'channels' + self.primary_key = 'uuid' + + # Makes sure that this is a readonly model. + def readonly? + return true + end + + # Prevent objects from being destroyed + def before_destroy + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def self.delete_all + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def delete + raise ActiveRecord::ReadOnlyRecord + end + + def sip_account + auth_name = self.name.match('^.+[/:](.+)@.+$') + if auth_name && ! auth_name[1].blank? + return SipAccount.where(:auth_name => auth_name[1]).first + end + end + + def kill + require 'freeswitch_event' + return FreeswitchAPI.execute('uuid_kill', self.uuid, true); + end +end diff --git a/app/models/call_forward.rb b/app/models/call_forward.rb new file mode 100644 index 0000000..0018cfb --- /dev/null +++ b/app/models/call_forward.rb @@ -0,0 +1,262 @@ +class CallForward < ActiveRecord::Base + + attr_accessor :to_voicemail, :hunt_group_id + + attr_accessible :phone_number_id, :call_forward_case_id, :timeout, + :destination, :source, :depth, :active, :to_voicemail, + :hunt_group_id, + :call_forwardable_type, :call_forwardable_id, + :call_forwarding_destination, :position, :uuid + + belongs_to :phone_number + belongs_to :call_forwardable, :polymorphic => true + has_many :softkeys + + acts_as_list :scope => [ :phone_number_id, :call_forward_case_id ] + + validates_presence_of :phone_number + validates_presence_of :call_forward_case_id + validates_presence_of :destination, :if => Proc.new { |cf| cf.call_forwardable_type.to_s.downcase == 'phonenumber' || cf.call_forwardable_type.blank? } + + validates_inclusion_of :destination, + :in => [ nil, '' ], + :if => Proc.new { |cf| cf.to_voicemail == true } + + belongs_to :call_forward_case + + validates_presence_of :depth + validates_numericality_of :depth, + :only_integer => true, + :greater_than_or_equal_to => 1, + :less_than_or_equal_to => MAX_CALL_FORWARD_DEPTH + + before_validation { + self.timeout = nil if self.call_forward_case_id != 3 + } + + validates_numericality_of :timeout, + :if => Proc.new { |cf| cf.call_forward_case_id == 3 }, + :only_integer => true, + :greater_than_or_equal_to => 1, + :less_than_or_equal_to => 120 + + validates_inclusion_of :timeout, + :in => [ nil ], + :if => Proc.new { |cf| cf.call_forward_case_id != 3 } + + validate :validate_empty_hunt_group, :if => Proc.new { |cf| cf.active == true && cf.call_forwardable_type == 'HuntGroup' && cf.call_forward_case.value == 'assistant' } + + validates_presence_of :uuid + validates_uniqueness_of :uuid + + # Make sure the call forward's parent can't be changed: + before_validation { |cfwd| + if cfwd.id \ + && cfwd.phone_number_id != cfwd.phone_number_id_was + errors.add( :phone_number_id, "cannot be changed." ) + end + } + + #before_validation :set_call_forwardable + before_save :split_and_format_destination_numbers + after_save :set_presence + after_save :work_through_callforward_rules_act_per_sip_account + after_save :deactivate_concurring_entries, :if => Proc.new { |cf| cf.active == true } + before_destroy :check_if_other_callforward_rules_have_to_be_destroyed + before_destroy :deactivate_connected_softkeys + + def case_string + return self.call_forward_case ? self.call_forward_case.value : nil + end + + def to_s + if self.call_forwardable_type.blank? + self.call_forwardable_type = '' + else + call_forwardable_type = " #{self.call_forwardable_type}" + end + if self.call_forwardable + destination = "#{self.call_forwardable}#{call_forwardable_type}" + else + destination = "#{self.destination}#{call_forwardable_type}" + end + "#{self.phone_number} (#{I18n.t("call_forward_cases.#{self.call_forward_case}")}) -> #{destination}" + end + + def set_this_callforward_rule_to_all_phone_numbers_of_the_parent_sip_account + # This is to make sure that no recursion kicks in. + # + if ! self.phone_number.phone_numberable.respond_to? :callforward_rules_act_per_sip_account + return false + end + + old_value_of_callforward_rules_act_per_sip_account = self.phone_number.phone_numberable.callforward_rules_act_per_sip_account + self.phone_number.phone_numberable.update_attributes({:callforward_rules_act_per_sip_account => false}) + + attributes_of_this_call_forward = self.attributes.delete_if {|key, value| ['id','updated_at','created_at','phone_number_id','call_forward_case_id', 'uuid'].include?(key)} + phone_numbers = self.phone_number.phone_numberable.phone_numbers.where('id != ?', self.phone_number.id) + + phone_numbers.each do |phone_number| + # Problem + call_forward = phone_number.call_forwards.find_or_create_by_call_forward_case_id_and_position(self.call_forward_case_id, self.position, attributes_of_this_call_forward) + call_forward.update_attributes(attributes_of_this_call_forward) + end + + self.phone_number.phone_numberable.update_attributes({:callforward_rules_act_per_sip_account => old_value_of_callforward_rules_act_per_sip_account}) + end + + def destroy_all_similar_callforward_rules_of_the_parent_sip_account + # This is to make sure that no recursion kicks in. + # + if ! self.phone_number.phone_numberable.respond_to? :callforward_rules_act_per_sip_account + return false + end + + old_value_of_callforward_rules_act_per_sip_account = self.phone_number.phone_numberable.callforward_rules_act_per_sip_account + self.phone_number.phone_numberable.update_attributes({:callforward_rules_act_per_sip_account => false}) + + phone_numbers_of_parent_sip_account = self.phone_number.phone_numberable.phone_numbers.where('id != ?', self.phone_number.id) + + phone_numbers_of_parent_sip_account.each do |phone_number| + if self.call_forwardable_type != 'Voicemail' + phone_number.call_forwards.where(:call_forward_case_id => self.call_forward_case_id, :destination => self.destination).destroy_all + else + phone_number.call_forwards.where(:call_forward_case_id => self.call_forward_case_id, :call_forwardable_type => self.call_forwardable_type).destroy_all + end + end + + self.phone_number.phone_numberable.update_attributes({:callforward_rules_act_per_sip_account => old_value_of_callforward_rules_act_per_sip_account}) + end + + def call_forwarding_destination + "#{self.call_forwardable_id}:#{self.call_forwardable_type}" + end + + def call_forwarding_destination=(destination_record) + self.call_forwardable_id, delimeter, self.call_forwardable_type = destination_record.to_s.partition(':') + end + + def toggle + self.active = ! self.active + return self.save + end + + def deactivate_connected_softkeys + softkey_function_deactivated = SoftkeyFunction.find_by_name('deactivated') + self.softkeys.each do |softkey| + if softkey.softkey_function_id != softkey_function_deactivated.id + softkey.update_attributes(:call_forward_id => nil, :softkey_function_id => softkey_function_deactivated.id) + end + end + end + + private + def split_and_format_destination_numbers + if !self.destination.blank? + destinations = self.destination.gsub(/[^+0-9\,]/,'').gsub(/[\,]+/,',').split(/\,/).delete_if{|x| x.blank?} + self.destination = nil + if destinations.count > 0 + destinations.each do |single_destination| + self.destination = self.destination.to_s + ", #{PhoneNumber.parse_and_format(single_destination)}" + end + end + self.destination = self.destination.to_s.gsub(/[^+0-9\,]/,'').gsub(/[\,]+/,',').split(/\,/).sort.delete_if{|x| x.blank?}.join(', ') + end + end + + def set_presence + state = 'terminated' + + if self.active + if self.call_forwardable_type and self.call_forwardable_type.downcase() == 'voicemail' + state = 'early' + else + state = 'confirmed' + end + end + + return send_presence_event(state) + + #if self.call_forward_case_id_changed? + # call_forwarding_service = CallForwardCase.where(:id => self.call_forward_case_id_was).first + # if call_forwarding_service + # send_presence_event(call_forwarding_service.value, state) + # end + #end + + #return send_presence_event(self.call_forward_case.value, state) + end + + def set_call_forwardable + if @hunt_group_id && HuntGroup.where(:id => @hunt_group_id.to_i).count > 0 + self.call_forwardable = HuntGroup.where(:id => @hunt_group_id.to_i).first + end + + if @to_voicemail && @to_voicemail.first.downcase == 'true' + self.call_forwardable_type = 'Voicemail' + self.call_forwardable_id = nil + end + end + + def work_through_callforward_rules_act_per_sip_account + if ! self.phone_number.phone_numberable.respond_to? :callforward_rules_act_per_sip_account + return false + end + + if self.phone_number.phone_numberable.callforward_rules_act_per_sip_account == true + self.set_this_callforward_rule_to_all_phone_numbers_of_the_parent_sip_account + end + end + + def check_if_other_callforward_rules_have_to_be_destroyed + if ! self.phone_number.phone_numberable.respond_to? :callforward_rules_act_per_sip_account + return false + end + + if self.phone_number.phone_numberable.callforward_rules_act_per_sip_account == true + self.destroy_all_similar_callforward_rules_of_the_parent_sip_account + end + end + + def send_presence_event(state, call_forwarding_service = nil) + dialplan_function = "cftg-#{self.id}" + unique_id = "call_forwarding_#{self.id}" + + if call_forwarding_service == 'always' + dialplan_function = "cfutg-#{self.phone_number.id}" + unique_id = "call_forwarding_number_#{self.phone_number.id}" + elsif call_forwarding_service == 'assistant' + dialplan_function = "cfatg-#{self.phone_number.id}" + unique_id = "call_forwarding_number_#{self.phone_number.id}" + end + + if dialplan_function + require 'freeswitch_event' + event = FreeswitchEvent.new("PRESENCE_IN") + event.add_header("proto", "sip") + event.add_header("from", "f-#{dialplan_function}@#{SipDomain.first.host}") + event.add_header("event_type", "presence") + event.add_header("alt_event_type", "dialog") + event.add_header("presence-call-direction", "outbound") + event.add_header("answer-state", state) + event.add_header("unique-id", unique_id) + return event.fire() + end + end + + def deactivate_concurring_entries + CallForward.where(:phone_number_id => self.phone_number_id, :call_forward_case_id => self.call_forward_case_id, :active => true).each do |call_forwarding_entry| + if call_forwarding_entry.id != self.id + call_forwarding_entry.update_attributes(:active => false) + end + end + end + + def validate_empty_hunt_group + hunt_group = self.call_forwardable + if hunt_group && hunt_group.hunt_group_members.where(:active => true).count == 0 + errors.add(:call_forwarding_destination, 'HuntGroup has no active members') + end + end + +end diff --git a/app/models/call_forward_case.rb b/app/models/call_forward_case.rb new file mode 100644 index 0000000..a0b872b --- /dev/null +++ b/app/models/call_forward_case.rb @@ -0,0 +1,13 @@ +class CallForwardCase < ActiveRecord::Base + + attr_accessible :value + + has_many :call_forwards + + validates_presence_of :value + + def to_s + self.value + end + +end diff --git a/app/models/call_history.rb b/app/models/call_history.rb new file mode 100644 index 0000000..4db056a --- /dev/null +++ b/app/models/call_history.rb @@ -0,0 +1,199 @@ +class CallHistory < ActiveRecord::Base + belongs_to :call_historyable, :polymorphic => true + belongs_to :caller_account, :polymorphic => true + belongs_to :callee_account, :polymorphic => true + belongs_to :auth_account, :polymorphic => true + + def display_number + if self.entry_type == 'dialed' + return self.destination_number.to_s + else + return self.caller_id_number.to_s + end + end + + def display_name + if self.entry_type == 'dialed' + begin + account = self.callee_account + rescue + account = nil + end + name_str = self.callee_id_name + else + begin + account = self.caller_account + rescue + account = nil + end + name_str = self.caller_id_name + end + + if name_str.blank? + if account.class == SipAccount + return account.caller_name.to_s + elsif account + return account.to_s + end + else + return name_str.to_s + end + end + + def display_auth_account_name + begin + account = self.auth_account + rescue + return nil + end + + if account.class == SipAccount + return account.caller_name.to_s + elsif account + return account.to_s + end + end + + def display_image(image_size = :mini, phone_book_entry) + if phone_book_entry + image = phone_book_entry.image_url(image_size) + if ! image.blank? + return image + end + end + + begin + if self.entry_type == 'dialed' + account = self.callee_account + else + account = self.caller_account + end + rescue + return nil + end + + if account.class == SipAccount && account.sip_accountable.class == User + return account.sip_accountable.image_url(image_size).to_s + end + end + + def display_call_date(date_format, date_today_format) + if self.start_stamp.strftime('%Y%m%d') == DateTime::now.strftime('%Y%m%d') + return self.start_stamp.strftime(date_today_format) + end + return self.start_stamp.strftime(date_format) + end + + def display_duration + if self.duration.to_i > 0 + minutes = (self.duration / 1.minutes).to_i + seconds = self.duration - minutes.minutes.seconds + return '%i:%02i' % [minutes, seconds] + end + end + + def phone_book_entry_by_number(number) + begin + call_historyable = self.call_historyable + rescue + return nil + end + + if ! call_historyable + return nil + end + + if call_historyable.class == SipAccount + owner = call_historyable.sip_accountable + end + + if owner.class == User + phone_books = owner.phone_books.all + phone_books.concat(owner.current_tenant.phone_books.all) + elsif owner.class == Tenant + phone_books = owner.phone_books.all + end + + if ! phone_books + return nil + end + + phone_books.each do |phone_book| + phone_book_entry = phone_book.find_entry_by_number(number) + if phone_book_entry + return phone_book_entry + end + end + + return nil + + end + + def voicemail_message + begin + return self.call_historyable.voicemail_messages.where(:forwarded_by => self.caller_channel_uuid).first + rescue + return nil + end + end + + def call_historyable_uuid + begin + return self.call_historyable.uuid + rescue + return nil + end + end + + def call_historyable_uuid=(uuid) + begin + return self.call_historyable_id = self.call_historyable_type.constantize.where(:uuid => uuid).first.id + rescue + end + end + + def caller_account_uuid + begin + return self.caller_account.uuid + rescue + return nil + end + end + + def caller_account_uuid=(uuid) + begin + return self.caller_account_id = self.caller_account_type.constantize.where(:uuid => uuid).first.id + rescue + end + end + + def callee_account_uuid + begin + return self.callee_account.uuid + rescue + return nil + end + end + + def callee_account_uuid=(uuid) + begin + return self.callee_account_id = self.callee_account_type.constantize.where(:uuid => uuid).first.id + rescue + end + end + + def auth_account_uuid + begin + return self.auth_account.uuid + rescue + return nil + end + end + + def auth_account_uuid=(uuid) + begin + return self.auth_account_id = self.auth_account_type.constantize.where(:uuid => uuid).first.id + rescue + end + end +end diff --git a/app/models/callthrough.rb b/app/models/callthrough.rb new file mode 100644 index 0000000..c057fa6 --- /dev/null +++ b/app/models/callthrough.rb @@ -0,0 +1,60 @@ +class Callthrough < ActiveRecord::Base + attr_accessible :name, :clip_no_screening, + :phone_numbers_attributes, :access_authorizations_attributes, + :whitelists_attributes + + # Validations and Associations + # + belongs_to :tenant + + validates_presence_of :tenant_id + validates_presence_of :tenant + + # These are the phone_numbers for this callthrough. + # One has to dial this number to access the callthrough. + # + has_many :phone_numbers, :as => :phone_numberable, :dependent => :destroy + + accepts_nested_attributes_for :phone_numbers, + :reject_if => lambda { |phone_number| phone_number[:number].blank? }, + :allow_destroy => true + + validate :requires_at_least_one_phone_number + + # These are the access authorizations for this callthrough. + # One has to be known by his phone number or by a login/pin or even both. + # + has_many :access_authorizations, :as => :access_authorizationable, :dependent => :destroy + + accepts_nested_attributes_for :access_authorizations, + :reject_if => lambda { |access_authorization| access_authorization[:login].blank? && access_authorization[:pin].blank? && access_authorization[:phone_numbers_attributes]['0'][:number].blank? }, + :allow_destroy => true + + has_many :access_authorization_phone_numbers, :source => :phone_numbers, + :through => :access_authorizations, :readonly => true + + # These are the whitelists of the phone numbers which can be called through this callthrough. + # + has_many :whitelists, :as => :whitelistable, :dependent => :destroy + + accepts_nested_attributes_for :whitelists, + :reject_if => lambda { |whitelist| whitelist[:phone_numbers_attributes]['0']['number'].blank? }, + :allow_destroy => true + + has_many :whitelisted_phone_numbers, :source => :phone_numbers, + :through => :whitelists, :readonly => true + + # Delegations: + # + delegate :sip_domain, :to => :tenant, :allow_nil => true + + def to_s + self.name || I18n.t('callthroughs.name') + ' ID ' + self.id + end + + + private + def requires_at_least_one_phone_number + errors.add(:base, "You must provide at least one phone number") if !self.phone_numbers.map{|phone_number| phone_number.valid?}.include?(true) + end +end diff --git a/app/models/conference.rb b/app/models/conference.rb new file mode 100644 index 0000000..8be9f21 --- /dev/null +++ b/app/models/conference.rb @@ -0,0 +1,63 @@ +class Conference < ActiveRecord::Base + attr_accessible :name, :start, :end, :description, :pin, + :open_for_anybody, :max_members, :announce_new_member_by_name, + :announce_left_member_by_name + + belongs_to :conferenceable, :polymorphic => true + has_many :conference_invitees, :dependent => :destroy + has_many :phone_numbers, :as => :phone_numberable, :dependent => :destroy + + validates_presence_of :conferenceable_type, :conferenceable_id + validates_presence_of :conferenceable + validates_presence_of :name + validates_presence_of :start, :if => Proc.new { |conference| !conference.end.blank? } + validates_presence_of :end, :if => Proc.new { |conference| !conference.start.blank? } + 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), + :allow_nil => false, + :allow_blank => false + + validates_inclusion_of :open_for_anybody, :in => [true, false] + + validates_numericality_of :pin, :only_integer => true, + :greater_than => 0, + :allow_nil => true, + :allow_blank => true + validates_length_of :pin, :minimum => MINIMUM_PIN_LENGTH, + :allow_nil => true, + :allow_blank => true + + validate :start_and_end_dates_must_make_sense, :if => Proc.new { |conference| !conference.start.blank? && !conference.end.blank? } + + after_save :send_pin_email_when_pin_has_changed + + default_scope where(:state => 'active').order(:start) + + # State Machine stuff + state_machine :initial => :active do + end + + def sip_domain + self.conferenceable.try(:sip_domain) + end + + def to_s + name + end + + private + + def start_and_end_dates_must_make_sense + errors.add(:start, 'must be in the future') if self.start < Time.now - 10.minutes + errors.add(:end, 'must be later than the start') if self.end < self.start + end + + def send_pin_email_when_pin_has_changed + if self.conferenceable.class == User && self.pin_changed? + Notifications.new_pin(self).deliver + end + end + +end diff --git a/app/models/conference_invitee.rb b/app/models/conference_invitee.rb new file mode 100644 index 0000000..7de20de --- /dev/null +++ b/app/models/conference_invitee.rb @@ -0,0 +1,39 @@ +class ConferenceInvitee < ActiveRecord::Base + attr_accessible :pin, :speaker, :moderator, :phone_number, :phone_number_attributes + + belongs_to :conference + belongs_to :phone_book_entry + has_one :phone_number, :as => :phone_numberable, :dependent => :destroy + accepts_nested_attributes_for :phone_number + + validates_presence_of :conference_id + validates_presence_of :conference + validates_presence_of :phone_number + validates_numericality_of :pin, :only_integer => true, + :greater_than => 0, + :allow_nil => true, + :allow_blank => true + validates_length_of :pin, :minimum => MINIMUM_PIN_LENGTH, + :allow_nil => true, + :allow_blank => true + + validates_inclusion_of :speaker, :in => [true, false] + validates_inclusion_of :moderator, :in => [true, false] + + validate :uniqueness_of_phone_number_in_the_parent_conference + validates_uniqueness_of :phone_book_entry_id, :scope => :conference_id, :allow_nil => true + + def to_s + "ID #{self.id}" + end + + private + + def uniqueness_of_phone_number_in_the_parent_conference + if self.conference.conference_invitees.where('id != ?', self.id).count > 0 && + self.conference.conference_invitees.where('id != ?', self.id).map{|x| x.phone_number.number}. + include?(self.phone_number.number) + errors.add(:base, 'Phone number is not unique within the conference.') + end + end +end diff --git a/app/models/country.rb b/app/models/country.rb new file mode 100644 index 0000000..018e348 --- /dev/null +++ b/app/models/country.rb @@ -0,0 +1,21 @@ +class Country < ActiveRecord::Base + + has_many :area_codes, :dependent => :destroy + has_many :tenants + has_many :phone_number_ranges, :as => :phone_number_rangeable, :dependent => :destroy + + validates_presence_of :name + validates_presence_of :country_code + validates_presence_of :international_call_prefix + + validates_numericality_of :country_code, + :only_integer => true + + validates_uniqueness_of :name, :scope => [ :country_code ], + :case_sensitive => false + + def to_s + self.name + end + +end diff --git a/app/models/dial_in_number_store.rb b/app/models/dial_in_number_store.rb new file mode 100644 index 0000000..17c2202 --- /dev/null +++ b/app/models/dial_in_number_store.rb @@ -0,0 +1,16 @@ +class DialInNumberStore < ActiveRecord::Base + # Associations and Validations + # + validates_presence_of :dial_in_number_storeable_type + validates_presence_of :dial_in_number_storeable_id + + belongs_to :dial_in_number_storeable, :polymorphic => true + + validates_presence_of :dial_in_number_storeable + + has_many :phone_numbers, :as => :phone_numberable, :dependent => :destroy + + # Delegations: + # + delegate :tenant, :to => :dial_in_number_storeable, :allow_nil => true +end diff --git a/app/models/fax_account.rb b/app/models/fax_account.rb new file mode 100644 index 0000000..683447a --- /dev/null +++ b/app/models/fax_account.rb @@ -0,0 +1,77 @@ +# encoding: UTF-8 + +class FaxAccount < ActiveRecord::Base + attr_accessible :name, :email, :station_id, :days_till_auto_delete, :phone_numbers_attributes, :retries + + # Validations: + # + validates_presence_of :fax_accountable_type, :fax_accountable_id + validates_presence_of :fax_accountable + validates_presence_of :name + validates_presence_of :tenant_id + validates_presence_of :tenant + + validates_numericality_of :days_till_auto_delete, :allow_nil => true + validates_numericality_of :retries, :only_integer => true, :greater_than_or_equal_to => 0 + + validates_uniqueness_of :name, :scope => [:fax_accountable_type, :fax_accountable_id] + + # Associations: + # + has_many :phone_numbers, :as => :phone_numberable, :dependent => :destroy + has_many :fax_documents, :dependent => :destroy + + belongs_to :fax_accountable, :polymorphic => true + belongs_to :tenant + + accepts_nested_attributes_for :phone_numbers + + # Hooks + # + before_validation :find_and_set_tenant_id + before_validation :convert_umlauts + + def to_s + name + end + + private + def require_at_least_one_phone_number + if self.phone_numbers.count < 1 + errors.add(:base, 'needs at least one valid phone number') + end + end + + def find_and_set_tenant_id + if self.new_record? and self.tenant_id != nil + return + else + tenant = case self.fax_accountable_type + when 'UserGroup' ; fax_accountable.tenant + when 'User' ; fax_accountable.current_tenant || fax_accountable.tenants.last + else nil + end + self.tenant_id = tenant.id if tenant != nil + end + end + + def convert_umlauts + self.name = self.name.sub(/ä/,'ae'). + sub(/Ä/,'Ae'). + sub(/ü/,'ue'). + sub(/Ü/,'Ue'). + sub(/ö/,'oe'). + sub(/Ö/,'Oe'). + sub(/ß/,'ss') + self.name = self.name.gsub(/[^a-zA-Z0-9\-\,\:\.\+ ]/,'_') + self.station_id = self.station_id.sub(/ä/,'ae'). + sub(/Ä/,'Ae'). + sub(/ü/,'ue'). + sub(/Ü/,'Ue'). + sub(/ö/,'oe'). + sub(/Ö/,'Oe'). + sub(/ß/,'ss') + self.station_id = self.station_id.gsub(/[^a-zA-Z0-9\-\,\:\.\+ ]/,'_') + end + +end diff --git a/app/models/fax_document.rb b/app/models/fax_document.rb new file mode 100644 index 0000000..67bdea9 --- /dev/null +++ b/app/models/fax_document.rb @@ -0,0 +1,82 @@ +class FaxDocument < ActiveRecord::Base +# attr_accessible :inbound, :transmission_time, :sent_at, :document_total_pages, :document_transferred_pages, :ecm_requested, :ecm_used, :image_resolution, :image_size, :local_station_id, :result_code, :result_text, :remote_station_id, :success, :transfer_rate, :t38_gateway_format, :t38_peer, :document + + mount_uploader :document, DocumentUploader + mount_uploader :tiff, TiffUploader + + validates_presence_of :document + validates_numericality_of :retry_counter, :only_integer => true, :greater_than_or_equal_to => 0 + + belongs_to :fax_account + belongs_to :fax_resolution + + validates_presence_of :fax_resolution_id + validates_presence_of :fax_resolution + + has_one :destination_phone_number, :class_name => 'PhoneNumber', :as => :phone_numberable, :dependent => :destroy + accepts_nested_attributes_for :destination_phone_number + + has_many :fax_thumbnails, :order => :position, :dependent => :destroy + + after_create :render_thumbnails + after_create :convert_pdf_to_tiff + + # Scopes + scope :inbound, where(:state => 'inbound') + scope :outbound, where(:state => ['queued_for_sending','sending','successful','unsuccessful']) + + # State Machine stuff + state_machine :initial => :new do + event :queue_for_sending do + transition [:new] => :queued_for_sending + end + + event :send_now do + transition [:queued_for_sending] => :sending + end + + event :cancel do + transition [:sending, :queued_for_sending] => :unsuccessful + end + + event :successful_sent do + transition [:sending, :queued_for_sending] => :successful + end + + event :mark_as_inbound do + transition [:new] => :inbound + end + end + + def to_s + name + end + + private + def render_thumbnails + directory = "/tmp/GS-#{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 + (0..(number_of_thumbnails-1)).each do |i| + fax_thumbnail = self.fax_thumbnails.build + fax_thumbnail.thumbnail = File.open("#{directory}/fax_page-#{i}.png") + fax_thumbnail.save! + end + system("rm -rf #{directory}") + self.update_attributes(:document_total_pages => number_of_thumbnails) if self.document_total_pages.nil? + end + + 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}" + 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}\"" + self.tiff = File.open("#{directory}/#{tiff_file_name}") + self.save + system("rm -rf #{directory}") + end + +end diff --git a/app/models/fax_resolution.rb b/app/models/fax_resolution.rb new file mode 100644 index 0000000..c9093fb --- /dev/null +++ b/app/models/fax_resolution.rb @@ -0,0 +1,15 @@ +class FaxResolution < ActiveRecord::Base + validates_presence_of :name + validates_presence_of :resolution_value + + validates_uniqueness_of :name + validates_uniqueness_of :resolution_value + + has_many :fax_documents, :dependent => :destroy + + acts_as_list + + def to_s + self.name + end +end diff --git a/app/models/fax_thumbnail.rb b/app/models/fax_thumbnail.rb new file mode 100644 index 0000000..a29c9ad --- /dev/null +++ b/app/models/fax_thumbnail.rb @@ -0,0 +1,8 @@ +class FaxThumbnail < ActiveRecord::Base + mount_uploader :thumbnail, ThumbnailUploader + validates_presence_of :thumbnail + + belongs_to :fax_document + + acts_as_list :scope => :fax_document +end diff --git a/app/models/freeswitch_alias.rb b/app/models/freeswitch_alias.rb new file mode 100644 index 0000000..9953edb --- /dev/null +++ b/app/models/freeswitch_alias.rb @@ -0,0 +1,23 @@ +class FreeswitchAlias < ActiveRecord::Base + self.table_name = 'aliases' + + # Makes sure that this is a readonly model. + def readonly? + return true + end + + # Prevent objects from being destroyed + def before_destroy + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def self.delete_all + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def delete + raise ActiveRecord::ReadOnlyRecord + end +end diff --git a/app/models/freeswitch_call.rb b/app/models/freeswitch_call.rb new file mode 100644 index 0000000..95b2cdd --- /dev/null +++ b/app/models/freeswitch_call.rb @@ -0,0 +1,24 @@ +class FreeswitchCall < ActiveRecord::Base + self.table_name = 'calls' + self.primary_key = 'call_uuid' + + # Makes sure that this is a readonly model. + def readonly? + return true + end + + # Prevent objects from being destroyed + def before_destroy + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def self.delete_all + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def delete + raise ActiveRecord::ReadOnlyRecord + end +end diff --git a/app/models/freeswitch_cdr.rb b/app/models/freeswitch_cdr.rb new file mode 100644 index 0000000..fd0eb75 --- /dev/null +++ b/app/models/freeswitch_cdr.rb @@ -0,0 +1,4 @@ +class FreeswitchCdr < ActiveRecord::Base + self.table_name = 'cdrs' + self.primary_key = 'uuid' +end diff --git a/app/models/freeswitch_channel.rb b/app/models/freeswitch_channel.rb new file mode 100644 index 0000000..489e17d --- /dev/null +++ b/app/models/freeswitch_channel.rb @@ -0,0 +1,24 @@ +class FreeswitchChannel < ActiveRecord::Base + self.table_name = 'channels' + self.primary_key = 'uuid' + + # Makes sure that this is a readonly model. + def readonly? + return true + end + + # Prevent objects from being destroyed + def before_destroy + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def self.delete_all + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def delete + raise ActiveRecord::ReadOnlyRecord + end +end diff --git a/app/models/freeswitch_complete.rb b/app/models/freeswitch_complete.rb new file mode 100644 index 0000000..e7ff465 --- /dev/null +++ b/app/models/freeswitch_complete.rb @@ -0,0 +1,23 @@ +class FreeswitchComplete < ActiveRecord::Base + self.table_name = 'complete' + + # Makes sure that this is a readonly model. + def readonly? + return true + end + + # Prevent objects from being destroyed + def before_destroy + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def self.delete_all + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def delete + raise ActiveRecord::ReadOnlyRecord + end +end diff --git a/app/models/freeswitch_fifo_bridge.rb b/app/models/freeswitch_fifo_bridge.rb new file mode 100644 index 0000000..06167f3 --- /dev/null +++ b/app/models/freeswitch_fifo_bridge.rb @@ -0,0 +1,23 @@ +class FreeswitchFifoBridge < ActiveRecord::Base + self.table_name = 'fifo_bridge' + + # Makes sure that this is a readonly model. + def readonly? + return true + end + + # Prevent objects from being destroyed + def before_destroy + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def self.delete_all + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def delete + raise ActiveRecord::ReadOnlyRecord + end +end diff --git a/app/models/freeswitch_fifo_caller.rb b/app/models/freeswitch_fifo_caller.rb new file mode 100644 index 0000000..50c1fb5 --- /dev/null +++ b/app/models/freeswitch_fifo_caller.rb @@ -0,0 +1,23 @@ +class FreeswitchFifoCaller < ActiveRecord::Base + self.table_name = 'fifo_callers' + + # Makes sure that this is a readonly model. + def readonly? + return true + end + + # Prevent objects from being destroyed + def before_destroy + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def self.delete_all + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def delete + raise ActiveRecord::ReadOnlyRecord + end +end diff --git a/app/models/freeswitch_fifo_outbound.rb b/app/models/freeswitch_fifo_outbound.rb new file mode 100644 index 0000000..029c21d --- /dev/null +++ b/app/models/freeswitch_fifo_outbound.rb @@ -0,0 +1,23 @@ +class FreeswitchFifoOutbound < ActiveRecord::Base + self.table_name = 'fifo_outbound' + + # Makes sure that this is a readonly model. + def readonly? + return true + end + + # Prevent objects from being destroyed + def before_destroy + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def self.delete_all + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def delete + raise ActiveRecord::ReadOnlyRecord + end +end diff --git a/app/models/freeswitch_interface.rb b/app/models/freeswitch_interface.rb new file mode 100644 index 0000000..1602d62 --- /dev/null +++ b/app/models/freeswitch_interface.rb @@ -0,0 +1,23 @@ +class FreeswitchInterface < ActiveRecord::Base + self.table_name = 'interfaces' + + # Makes sure that this is a readonly model. + def readonly? + return true + end + + # Prevent objects from being destroyed + def before_destroy + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def self.delete_all + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def delete + raise ActiveRecord::ReadOnlyRecord + end +end diff --git a/app/models/freeswitch_nat.rb b/app/models/freeswitch_nat.rb new file mode 100644 index 0000000..8baf2bf --- /dev/null +++ b/app/models/freeswitch_nat.rb @@ -0,0 +1,23 @@ +class FreeswitchNat < ActiveRecord::Base + self.table_name = 'nat' + + # Makes sure that this is a readonly model. + def readonly? + return true + end + + # Prevent objects from being destroyed + def before_destroy + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def self.delete_all + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def delete + raise ActiveRecord::ReadOnlyRecord + end +end diff --git a/app/models/freeswitch_registration.rb b/app/models/freeswitch_registration.rb new file mode 100644 index 0000000..7e80815 --- /dev/null +++ b/app/models/freeswitch_registration.rb @@ -0,0 +1,23 @@ +class FreeswitchRegistration < ActiveRecord::Base + self.table_name = 'registrations' + + # Makes sure that this is a readonly model. + def readonly? + return true + end + + # Prevent objects from being destroyed + def before_destroy + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def self.delete_all + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def delete + raise ActiveRecord::ReadOnlyRecord + end +end diff --git a/app/models/freeswitch_task.rb b/app/models/freeswitch_task.rb new file mode 100644 index 0000000..6e964d2 --- /dev/null +++ b/app/models/freeswitch_task.rb @@ -0,0 +1,23 @@ +class FreeswitchTask < ActiveRecord::Base + self.table_name = 'tasks' + + # Makes sure that this is a readonly model. + def readonly? + return true + end + + # Prevent objects from being destroyed + def before_destroy + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def self.delete_all + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def delete + raise ActiveRecord::ReadOnlyRecord + end +end diff --git a/app/models/freeswitch_voicemail_pref.rb b/app/models/freeswitch_voicemail_pref.rb new file mode 100644 index 0000000..b2400e8 --- /dev/null +++ b/app/models/freeswitch_voicemail_pref.rb @@ -0,0 +1,23 @@ +class FreeswitchVoicemailPref < ActiveRecord::Base + self.table_name = 'voicemail_prefs' + + # Makes sure that this is a readonly model. + def readonly? + return true + end + + # Prevent objects from being destroyed + def before_destroy + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def self.delete_all + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def delete + raise ActiveRecord::ReadOnlyRecord + end +end diff --git a/app/models/gemeinschaft_setup.rb b/app/models/gemeinschaft_setup.rb new file mode 100644 index 0000000..b445b21 --- /dev/null +++ b/app/models/gemeinschaft_setup.rb @@ -0,0 +1,8 @@ +class GemeinschaftSetup < ActiveRecord::Base + belongs_to :user + accepts_nested_attributes_for :user + belongs_to :sip_domain + accepts_nested_attributes_for :sip_domain + belongs_to :country + belongs_to :language +end diff --git a/app/models/gs_cluster_sync_log_entry.rb b/app/models/gs_cluster_sync_log_entry.rb new file mode 100644 index 0000000..063ff23 --- /dev/null +++ b/app/models/gs_cluster_sync_log_entry.rb @@ -0,0 +1,99 @@ +class GsClusterSyncLogEntry < ActiveRecord::Base + attr_accessible :gs_node_id, :class_name, :action, :content, :status, :history, + :homebase_ip_address, :waiting_to_be_synced, :association_method, + :association_uuid + + validates :class_name, + :presence => true + + validates :action, + :presence => true + + validates :content, + :presence => true + + after_create :apply_to_local_database + + def apply_to_local_database + if self.homebase_ip_address != HOMEBASE_IP_ADDRESS + if self.class_name.constantize.new.attribute_names.include?('is_native') + case self.action + when 'create' + new_local_copy = self.class_name.constantize.new( + JSON(self.content). + delete_if{|key, value| ['id','updated_at','created_at']. + include?(key) }, + :without_protection => true) + new_local_copy.is_native = false + find_and_connect_to_an_association(new_local_copy) + if new_local_copy.save(:validate => false) + logger.info "Created local copy of #{self.class_name} with the ID #{new_local_copy.id}. #{new_local_copy.to_s}" + else + logger.error "Couldn't create a local copy of #{self.class_name} with the ID #{new_local_copy.id}. #{new_local_copy.errors.to_yaml}" + end + + when 'update' + local_copy = find_local_copy + if local_copy + # Only update an object if the update it self is newer than the local object. + # + if local_copy.updated_at < JSON(self.content)['updated_at'].to_time + local_copy.update_attributes(JSON(self.content).delete_if{|key, value| ['id','updated_at','created_at'].include?(key) }, :without_protection => true) + find_and_connect_to_an_association(local_copy) + if local_copy.save(:validate => false) + logger.info "Updated local copy of #{self.class_name} with the ID #{local_copy.id}. #{local_copy.to_s}" + else + logger.error "Couldn't update local copy of #{self.class_name} with the ID #{local_copy.id}. #{local_copy.errors.to_yaml}" + end + else + logger.error "Didn't update local copy of #{self.class_name} with the ID #{local_copy.id} because of a race condition (the local version was newer than the update). Please check GsClusterSyncLogEntry ID #{self.id}." + end + else + logger.error "Couldn't find local copy of #{self.class_name}. #{self.content}" + end + + when 'destroy' + local_copy = find_local_copy + if local_copy + local_copy.destroy + logger.info "Destroyed local copy of #{self.class_name} with the ID #{local_copy.id}. #{local_copy.to_s}" + else + logger.error "Couldn't find local copy of #{self.class_name}. #{self.content}" + end + end + else + logger.error "The class #{self.class_name} doesn't offer the attribute is_native. Can't synchronize without." + end + end + end + + def find_local_copy + self.class_name.constantize.find_by_uuid(JSON(self.content)['uuid']) + end + + # Connect to the association (e.g. User to a SipAccount) + # + def find_and_connect_to_an_association(local_copy) + if !(self.association_method.blank? || self.association_uuid.blank?) && (self.association_method_changed? || self.association_uuid_changed?) + name_of_the_association_type = local_copy.attribute_names.delete_if{|x| !x.include?('_type')}.first + association = local_copy.send(name_of_the_association_type).constantize.where(:uuid => self.association_uuid).first + if association + local_copy.send "#{association_method}=", association + end + end + end + + def populate_other_cluster_nodes + if self.homebase_ip_address == 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 + remote_enty = RemoteGsNode::GsClusterSyncLogEntry.create(self.attributes.delete_if{|key, value| ['id','updated_at','created_at'].include?(key) }) + self.update_attributes(:waiting_to_be_synced => false) + self.save + end + end + end + end + +end diff --git a/app/models/gs_node.rb b/app/models/gs_node.rb new file mode 100644 index 0000000..229ceb2 --- /dev/null +++ b/app/models/gs_node.rb @@ -0,0 +1,30 @@ +class GsNode < ActiveRecord::Base + attr_accessible :name, :ip_address, :site, :element_name, :push_updates_to, :accepts_updates_from + + has_many :phone_numbers, :foreign_key => :gs_node_id, :dependent => :destroy + has_many :users, :foreign_key => :gs_node_id, :dependent => :destroy + has_many :sip_accounts, :foreign_key => :gs_node_id, :dependent => :destroy + has_many :hunt_groups, :foreign_key => :gs_node_id, :dependent => :destroy + + validates :name, + :presence => true + + validates :ip_address, + :presence => true + + validates :site, + :presence => true + + validates :element_name, + :presence => true + + def to_s + name + end + + def synced + self.last_sync = Time.now + return self.save + end + +end diff --git a/app/models/gui_function.rb b/app/models/gui_function.rb new file mode 100644 index 0000000..e27a8d2 --- /dev/null +++ b/app/models/gui_function.rb @@ -0,0 +1,40 @@ +class GuiFunction < ActiveRecord::Base + attr_accessible :category, :name, :description, :gui_function_memberships_attributes + + has_many :gui_function_memberships, :dependent => :destroy + has_many :user_groups, :through => :gui_function_memberships + + accepts_nested_attributes_for :gui_function_memberships + + validates :name, :presence => true, + :format => { :with => /\A[a-z_0-9]+\z/, :message => "Only lower case letters allowed" }, + :length => { :in => 3..255 }, + :uniqueness => true + + def to_s + self.name + end + + def self.display?(function_name = nil, user) + if function_name.blank? || GemeinschaftSetup.count == 0 + true + else + if !user || user.class != User || function_name.class != String + false + else + function_name = function_name.downcase + + activated_gui_function_names = GuiFunctionMembership.where(:user_group_id => user.user_group_ids, :activated => true).map{|gui_function_membership| gui_function_membership.gui_function.name}.uniq + deactivated_gui_function_names = GuiFunctionMembership.where(:user_group_id => user.user_group_ids, :activated => false).map{|gui_function_membership| gui_function_membership.gui_function.name}.uniq + + deactivated_gui_function_names = deactivated_gui_function_names - activated_gui_function_names + + if deactivated_gui_function_names.include?(function_name) + false + else + true + end + end + end + end +end diff --git a/app/models/gui_function_membership.rb b/app/models/gui_function_membership.rb new file mode 100644 index 0000000..d2bc7cd --- /dev/null +++ b/app/models/gui_function_membership.rb @@ -0,0 +1,7 @@ +class GuiFunctionMembership < ActiveRecord::Base + belongs_to :gui_function + belongs_to :user_group + + validates_associated :gui_function + validates_associated :user_group +end diff --git a/app/models/hunt_group.rb b/app/models/hunt_group.rb new file mode 100644 index 0000000..276ae53 --- /dev/null +++ b/app/models/hunt_group.rb @@ -0,0 +1,43 @@ +class HuntGroup < ActiveRecord::Base + attr_accessible :name, :strategy, :seconds_between_jumps, :phone_numbers_attributes + + belongs_to :tenant + 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_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, + :if => Proc.new{ |hunt_group| hunt_group.strategy != 'ring_all' } + validates_inclusion_of :seconds_between_jumps, + :in => VALID_SECONDS_BETWEEN_JUMPS_VALUES, + :if => Proc.new{ |hunt_group| hunt_group.strategy != 'ring_all' } + validates_inclusion_of :seconds_between_jumps, + :in => [nil], + :if => Proc.new{ |hunt_group| hunt_group.strategy == 'ring_all' } + + validates_presence_of :uuid + validates_uniqueness_of :uuid + + has_many :hunt_group_members, :dependent => :destroy, :order => :position + + has_many :phone_numbers, :as => :phone_numberable, :dependent => :destroy + accepts_nested_attributes_for :phone_numbers, + :reject_if => lambda { |phone_number| phone_number[:number].blank? }, + :allow_destroy => true + + has_many :hunt_group_members, :dependent => :destroy + + def to_s + self.name || I18n.t('hunt_groups.name') + ' ID ' + self.id.to_s + end + +end diff --git a/app/models/hunt_group_member.rb b/app/models/hunt_group_member.rb new file mode 100644 index 0000000..7d9d3e0 --- /dev/null +++ b/app/models/hunt_group_member.rb @@ -0,0 +1,67 @@ +class HuntGroupMember < ActiveRecord::Base + attr_accessible :name, :active, :can_switch_status_itself, :phone_numbers_attributes + + belongs_to :hunt_group + validates_presence_of :hunt_group + + has_many :phone_numbers, :as => :phone_numberable, :dependent => :destroy + accepts_nested_attributes_for :phone_numbers, + :reject_if => lambda { |phone_number| phone_number[:number].blank? }, + :allow_destroy => true + + acts_as_list :scope => :hunt_group + + after_save :set_presence + after_save :trigger_connected_call_forward_if_necessary + + def to_s + self.name || I18n.t('hunt_group_members.name') + ' ID ' + self.id.to_s + end + + private + def set_presence + dialplan_function = nil + state = 'terminated' + + if self.active + state = 'confirmed' + end + + require 'freeswitch_event' + event = FreeswitchEvent.new("PRESENCE_IN") + event.add_header("proto", "sip") + event.add_header("from", "f-hgmtg-#{self.id}@#{SipDomain.first.host}") + event.add_header("event_type", "presence") + event.add_header("alt_event_type", "dialog") + event.add_header("presence-call-direction", "outbound") + event.add_header("answer-state", state) + event.add_header("unique-id", "hunt_group_member_#{self.id}") + return event.fire() + end + + # Turn on/off a connected CallForward. + # The last member who leaves the hunt_group deactivates the CallForward and the + # first member actives it. + # + def trigger_connected_call_forward_if_necessary + if self.active_changed? && self.hunt_group.hunt_group_members.count > 0 + # deactive CallForward + # + if self.hunt_group.hunt_group_members.where(:active => false).count == self.hunt_group.hunt_group_members.count + self.hunt_group.call_forwards.where(:active => true).each do |x| + x.update_attributes({:active => false}) + end + end + + # active CallForward + # + if self.hunt_group.hunt_group_members.where(:active => true).count > 0 + self.hunt_group.call_forwards.where(:active => false).each do |x| + x.update_attributes({:active => true}) + end + end + end + end + + +end diff --git a/app/models/language.rb b/app/models/language.rb new file mode 100644 index 0000000..1b9c2c0 --- /dev/null +++ b/app/models/language.rb @@ -0,0 +1,11 @@ +class Language < ActiveRecord::Base + has_many :tenants + has_many :users + + validates_presence_of :name + validates_presence_of :code + + def to_s + name + end +end diff --git a/app/models/manufacturer.rb b/app/models/manufacturer.rb new file mode 100644 index 0000000..03d2bb7 --- /dev/null +++ b/app/models/manufacturer.rb @@ -0,0 +1,46 @@ +class Manufacturer < ActiveRecord::Base + attr_accessible :name, :ieee_name, :homepage_url + + # Associations: + # + has_many :ouis, :dependent => :destroy + has_many :phone_models, :order => :name, :dependent => :destroy + + + # Validations: + # + validates_presence_of :name + validates_presence_of :ieee_name + + validates_uniqueness_of :name, :case_sensitive => false + + validate :validate_homepage_url + + # State Machine stuff + default_scope where(:state => 'active').order(:name) + state_machine :initial => :active do + + event :deactivate do + transition [:active] => :deactivated + end + + event :activate do + transition [:deactivated] => :active + end + end + + def to_s + self.name + end + + private + + def validate_homepage_url + if ! self.homepage_url.blank? + if ! CustomValidators.validate_url( self.homepage_url ) + errors.add( :homepage_url, "is invalid." ) + end + end + end + +end diff --git a/app/models/oui.rb b/app/models/oui.rb new file mode 100644 index 0000000..9a5bb1f --- /dev/null +++ b/app/models/oui.rb @@ -0,0 +1,17 @@ +class Oui < ActiveRecord::Base + attr_accessible :value + + validates_presence_of :manufacturer + validates_presence_of :value + + belongs_to :manufacturer + + # State Machine stuff + default_scope where(:state => 'active') + state_machine :initial => :active do + end + + def to_s + value + end +end diff --git a/app/models/phone.rb b/app/models/phone.rb new file mode 100644 index 0000000..89371eb --- /dev/null +++ b/app/models/phone.rb @@ -0,0 +1,240 @@ +require 'scanf' + +class Phone < ActiveRecord::Base + + attr_accessible :mac_address, :ip_address, :http_user, :http_password, + :phone_model_id, :hot_deskable, :nightly_reboot, + :provisioning_key, :provisioning_key_active + + # Associations + # + belongs_to :phone_model + belongs_to :phoneable, :polymorphic => true + + has_many :phone_sip_accounts, :dependent => :destroy, :uniq => true, :order => :position + has_many :sip_accounts, :through => :phone_sip_accounts + + # Validations + # + before_validation :sanitize_mac_address + + validates_presence_of :mac_address + validate_mac_address :mac_address + validates_uniqueness_of :mac_address + + validates_uniqueness_of :ip_address, + :if => Proc.new { |me| ! me.ip_address.blank? } + validate_ip_address :ip_address, + :if => Proc.new { |me| ! me.ip_address.blank? } + + validates_presence_of :phone_model + validates_presence_of :phoneable + + before_save :save_last_ip_address + before_save :destroy_phones_sip_accounts_if_phoneable_changed + before_save :remove_ip_address_when_mac_address_was_changed + + # State machine: + # + default_scope where(:state => 'active') + state_machine :initial => :active do + + event :deactivate do + transition [:active] => :deactivated + end + + event :activate do + transition [:deactivated] => :active + end + end + + def to_s + "%s %s %s" % [ + pretty_mac_address, + "(#{self.phone_model})", + self.ip_address ? "(#{self.ip_address})" : "", + ] + end + + def pretty_mac_address + return [].fill('%02X', 0, 6).join(':') % self.mac_address.scanf( '%2X' * 6 ) + end + + + def resync(reboot = false, sip_account = nil) + if ! self.phone_model || ! self.phone_model.manufacturer + return false + end + + if self.phone_model.manufacturer.ieee_name == 'SNOM Technology AG' + if !sip_account + self.sip_accounts.where(:sip_accountable_type => self.phoneable_type).each do |sip_account_associated| + if sip_account_associated.registration + sip_account = sip_account_associated + break + end + end + end + + if ! sip_account or ! sip_account.registration + require 'open-uri' + begin + if open("http://#{self.ip_address}/advanced_update.htm?reboot=Reboot", :http_basic_authentication=>[self.http_user, self.http_password], :proxy => nil) + return true + end + rescue + return false + end + end + + require 'freeswitch_event' + event = FreeswitchEvent.new("NOTIFY") + event.add_header("profile", "gemeinschaft") + event.add_header("event-string", "check-sync;reboot=#{reboot.to_s}") + event.add_header("user", sip_account.auth_name) + event.add_header("host", sip_account.sip_domain.host) + event.add_header("content-type", "application/simple-message-summary") + return event.fire() + + elsif self.phone_model.manufacturer.ieee_name == 'Siemens Enterprise CommunicationsGmbH & Co. KG' + require 'open-uri' + begin + if open("http://#{self.ip_address}:8085/contact_dls.html/ContactDLS", :http_basic_authentication=>[self.http_user, self.http_password], :proxy => nil) + return true + end + rescue + return false + end + end + + return false + end + + + # OPTIMIZE i18n translations + def user_login(user, sip_account = nil) + if ! self.hot_deskable + errors.add(:hot_deskable, "Phone not hot-deskable") + return false + end + + phones_affected = Hash.new() + sip_accounts = Array.new(1, sip_account) + + if !sip_account + sip_accounts = user.sip_accounts.where(:hotdeskable => true).all + end + + if sip_accounts.blank? + errors.add(:sip_accounts, "No hot-deskable Sip Accounts available") + return false + end + + sip_account_resync = self.sip_accounts.where(:sip_accountable_type => self.phoneable_type).first + + phones_affected.each_pair do |id,phone| + if phone.id != self.id + phone.user_logout() + end + end + + self.phoneable = user + sip_accounts.each do |sip_account| + if ! self.sip_accounts.where(:id => sip_account.id).first + self.sip_accounts.push(sip_account) + end + end + + @not_destroy_phones_sip_accounts = true + if ! self.save + return false + end + + sleep(0.5) + + if ! self.resync(true, sip_account_resync) + errors.add(:resync, "Resync failed") + return false + end + + return true + end + + + # OPTIMIZE i18n translations + def user_logout + if ! self.hot_deskable or self.phoneable_type == 'Tenant' + errors.add(:hot_deskable, "Phone not hot-deskable") + return false + end + + sip_account = self.sip_accounts.where(:sip_accountable_type => self.phoneable_type).first + + tenant_sip_account = self.sip_accounts.where(:sip_accountable_type => 'Tenant').first + if tenant_sip_account + tenant = tenant_sip_account.sip_accountable + end + + sip_account_ids = Array.new() + self.sip_accounts.where(:sip_accountable_type => 'User', :hotdeskable => true).each do |sip_account| + sip_account_ids.push(sip_account.id) + end + + if tenant + self.phoneable = tenant + @not_destroy_phones_sip_accounts = true + if ! self.save + errors.add(:phoneable, "Could not change owner") + return false + end + end + + if ! PhoneSipAccount.destroy_all(:sip_account_id => sip_account_ids) + errors.add(:sip_accounts, "Could not delete sip_accounts") + return false + end + + sleep(0.5) + + if ! self.resync(true, sip_account) + errors.add(:resync, "Resync failed") + return false + end + + return true + end + + private + + # Sanitize MAC address. + # + def sanitize_mac_address + self.mac_address = self.mac_address.to_s.upcase.gsub( /[^A-F0-9]/, '' ) + end + + # Saves the last IP address. + # + def save_last_ip_address + if self.ip_address_changed? \ + && self.ip_address != self.ip_address_was + self.last_ip_address = self.ip_address_was + end + end + + # When ever the parent of a phone changes all the SIP accounts associations + # are destroyed unless this is a user logout operation + # + def destroy_phones_sip_accounts_if_phoneable_changed + if (self.phoneable_type_changed? || self.phoneable_id_changed?) && ! @not_destroy_phones_sip_accounts + self.phone_sip_accounts.destroy_all + end + end + + def remove_ip_address_when_mac_address_was_changed + if self.mac_address_changed? + self.ip_address = nil + self.last_ip_address = nil + end + end + +end diff --git a/app/models/phone_book.rb b/app/models/phone_book.rb new file mode 100644 index 0000000..3603eae --- /dev/null +++ b/app/models/phone_book.rb @@ -0,0 +1,33 @@ +class PhoneBook < ActiveRecord::Base + attr_accessible :name, :description, :uuid + + belongs_to :phone_bookable, :polymorphic => true + has_many :phone_book_entries, :dependent => :destroy + + validates_presence_of :name + validates_uniqueness_of :name, :scope => [ :phone_bookable_type, :phone_bookable_id ] + + validates_length_of :name, :within => 1..50 + + validates_presence_of :uuid + validates_uniqueness_of :uuid + + # State Machine stuff + default_scope where(:state => 'active') + state_machine :initial => :active do + end + + def to_s + name + end + + def find_entry_by_number(number) + phone_book_entries_ids = self.phone_book_entries.map{|phone_book_entry| phone_book_entry.id} + + phone_number = PhoneNumber.where(:phone_numberable_id => phone_book_entries_ids, :phone_numberable_type => 'PhoneBookEntry', :number => number).first + + if phone_number + return phone_number.phone_numberable + end + end +end diff --git a/app/models/phone_book_entry.rb b/app/models/phone_book_entry.rb new file mode 100644 index 0000000..db2b44b --- /dev/null +++ b/app/models/phone_book_entry.rb @@ -0,0 +1,109 @@ +# encoding: UTF-8 + +class PhoneBookEntry < ActiveRecord::Base + before_save :run_phonetic_algorithm + before_save :save_value_of_to_s + + 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 + has_many :conference_invitees, :dependent => :destroy + + acts_as_list :scope => :phone_book + + validates_presence_of :phone_book + + validates_presence_of :last_name, + :unless => Proc.new { |entry| entry.is_organization } + + validates_presence_of :organization, + :if => Proc.new { |entry| entry.is_organization } + + validates_inclusion_of :is_male, :in => [true, false, 1, '1', 'on'], + :unless => Proc.new { |entry| entry.is_organization } + + has_many :phone_numbers, :as => :phone_numberable, :dependent => :destroy + + has_many :addresses, :dependent => :destroy + + # Avatar like photo + mount_uploader :image, ImageUploader + + # TODO Validate homepage URLs and social media accounts. + + + default_scope where(:state => 'active') + + # State Machine stuff + state_machine :initial => :active do + end + + def to_s + if self.is_organization + "#{self.organization}".strip + else + [self.last_name.strip, self.first_name.strip].join(', ') + end + end + + def self.koelner_phonetik(input) + if input.blank? + nil + else + # TODO: koelner_phonetik() needs to be tested. + + # Umwandeln in Grossbuchstaben + phonetik = input.upcase.gsub(/[^A-ZÜüÖöÄäß]/,'').strip + + # Umwandeln anhand der Tabelle auf + # http://de.wikipedia.org/wiki/K%C3%B6lner_Verfahren + phonetik = phonetik.gsub(/([XKQ])X/, '\1'+'8') + phonetik = phonetik.gsub(/[DT]([CSZ])/, '8'+'\1') + phonetik = phonetik.gsub(/C([^AHKOQUX])/, '8'+'\1') + phonetik = phonetik.gsub(/^C([^AHKLOQRUX])/, '8'+'\1') + phonetik = phonetik.gsub(/([SZ])C/, '\1'+'8') + phonetik = phonetik.gsub(/[SZß]/, '8') + phonetik = phonetik.gsub(/R/, '7') + phonetik = phonetik.gsub(/[MN]/, '6') + phonetik = phonetik.gsub(/L/, '5') + phonetik = phonetik.gsub(/X/, '48') + phonetik = phonetik.gsub(/([^SZ])C([AHKOQUX])/, '\1'+'4'+'\2' ) + phonetik = phonetik.gsub(/^C([AHKLOQRUX])/, '4'+'\1') + phonetik = phonetik.gsub(/[GKQ]/, '4') + phonetik = phonetik.gsub(/PH/, '3H') + phonetik = phonetik.gsub(/[FVW]/, '3') + phonetik = phonetik.gsub(/[DT]([^CSZ])/, '2'+'\1') + phonetik = phonetik.gsub(/[BP]/, '1') + phonetik = phonetik.gsub(/H/, '') + phonetik = phonetik.gsub(/[AEIJOUYÜüÖöÄä]/, '0') + + # Regeln für Buchstaben am Ende des Wortes + phonetik = phonetik.gsub(/P/, '1') + phonetik = phonetik.gsub(/[DT]/, '2') + phonetik = phonetik.gsub(/C/, '8') + + # Entfernen aller doppelten + phonetik = phonetik.gsub(/([0-9])\1+/, '\1') + + # Entfernen aller Codes "0" außer am Anfang. + phonetik = phonetik.gsub(/^0/, 'X') + phonetik = phonetik.gsub(/0/, '') + phonetik = phonetik.gsub(/^X/, '0') + + phonetik + end + end + + private + + def run_phonetic_algorithm + self.first_name_phonetic = PhoneBookEntry.koelner_phonetik(self.first_name) if self.first_name_changed? + self.last_name_phonetic = PhoneBookEntry.koelner_phonetik(self.last_name) if self.last_name_changed? + self.organization_phonetic = PhoneBookEntry.koelner_phonetik(self.organization) if self.organization_changed? + end + + def save_value_of_to_s + self.value_of_to_s = self.to_s + end + +end diff --git a/app/models/phone_model.rb b/app/models/phone_model.rb new file mode 100644 index 0000000..e00e0e3 --- /dev/null +++ b/app/models/phone_model.rb @@ -0,0 +1,56 @@ +class PhoneModel < ActiveRecord::Base + attr_accessible :name, :product_manual_homepage_url, :product_homepage_url, :uuid + + # Associations + # + belongs_to :manufacturer + + has_many :phones, :dependent => :destroy + + # Validations + # + validates_presence_of :name + validate :validate_product_manual_homepage_url + validate :validate_product_homepage_url + + validates_presence_of :uuid + validates_uniqueness_of :uuid + + def to_s + self.name + end + + # State machine: + # + default_scope where(:state => 'active') + state_machine :initial => :active do + + event :deactivate do + transition [:active] => :deactivated + end + + event :activate do + transition [:deactivated] => :active + end + end + + + private + + def validate_product_manual_homepage_url + if ! self.product_manual_homepage_url.blank? + if ! CustomValidators.validate_url( self.product_manual_homepage_url ) + errors.add( :product_manual_homepage_url, "is invalid." ) + end + end + end + + def validate_product_homepage_url + if ! self.product_homepage_url.blank? + if ! CustomValidators.validate_url( self.product_homepage_url ) + errors.add( :product_homepage_url, "is invalid." ) + end + end + end + +end diff --git a/app/models/phone_number.rb b/app/models/phone_number.rb new file mode 100644 index 0000000..4c0cf46 --- /dev/null +++ b/app/models/phone_number.rb @@ -0,0 +1,304 @@ +class PhoneNumber < ActiveRecord::Base + NUMBER_TYPES_INBOUND = ['SipAccount', 'Conference', 'FaxAccount', 'Callthrough', 'HuntGroup'] + + attr_accessible :name, :number, :gs_node_id, :access_authorization_user_id + + has_many :call_forwards, :dependent => :destroy + + has_many :ringtones, :as => :ringtoneable, :dependent => :destroy + + belongs_to :phone_numberable, :polymorphic => true + + belongs_to :gs_node + + validates_uniqueness_of :number, :scope => [:phone_numberable_type, :phone_numberable_id] + + validate :validate_inbound_uniqueness + + 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 } + + acts_as_list :scope => [:phone_numberable_id, :phone_numberable_type] + + # Sync other nodes when this is a cluster. + # + validates_presence_of :uuid + validates_uniqueness_of :uuid + after_create { self.create_on_other_gs_nodes('phone_numberable', self.phone_numberable.try(:uuid)) } + after_destroy :destroy_on_other_gs_nodes + after_update { self.update_on_other_gs_nodes('phone_numberable', self.phone_numberable.try(:uuid)) } + + # State machine: + # + default_scope where(:state => 'active') + state_machine :initial => :active do + + event :deactivate do + transition [:active] => :deactivated + end + + event :activate do + transition [:deactivated] => :active + end + end + + + def to_s + parts = [] + parts << "+#{self.country_code}" if self.country_code + parts << self.area_code if self.area_code + parts << self.central_office_code if self.central_office_code + parts << self.subscriber_number if self.subscriber_number + + if parts.empty? + return self.number + end + return parts.join("-") + end + + # Parse a number in a tenant's context (respect the tenant's country) + # + def self.parse( number, tenant=nil ) + number = number.to_s.gsub( /[^0-9+]/, '' ) + + if tenant.class.name == 'Tenant' + country = tenant.country + else + tenant = nil + country = GemeinschaftSetup.first.try(:country) + country ||= Country.where(:name => "Germany").first + end + + parts = { + :country_code => nil, + :area_code => nil, + :central_office_code => nil, + :subscriber_number => nil, + :extension => nil, + } + + if country + if ! country.international_call_prefix.blank? + number = number.gsub( /^#{Regexp.escape( country.international_call_prefix )}/, '+' ) + end + if ! country.trunk_prefix.blank? + number = number.gsub( /^#{Regexp.escape( country.trunk_prefix )}/, "+#{country.country_code}" ) + end + end + + if number.match( /^[+]/ ) + parts = self.parse_international_number( number.gsub(/[^0-9]/,'') ) + return nil if parts.nil? + else + # Check if the number is an internal extension. + if tenant + internal_extension_range = tenant.phone_number_ranges.where(:name => INTERNAL_EXTENSIONS).first + if internal_extension_range + if internal_extension_range.phone_numbers.where(:number => number).length > 0 + parts[:extension] = number + end + end + end + + # Otherwise assume the number is a special number such as an emergency number. + if ! parts[:extension] + parts[:subscriber_number] = number + end + end + + # return nil if all parts are blank: + return nil if ( + parts[:country_code].blank? && + parts[:area_code].blank? && + parts[:central_office_code].blank? && + parts[:subscriber_number].blank? && + parts[:extension].blank? + ) + parts # return value + end + + def self.parse_and_format( number, tenant=nil ) + attributes = PhoneNumber.parse(number, tenant) + if attributes + formated_number = attributes.map{|key,value| value}.delete_if{|x| x.nil?}.join('-') + formated_number = "+#{formated_number}" if attributes[:country_code] + return formated_number + end + return number + end + + # Parse an international number. + # Assumed format for +number+ is e.g. "49261200000" + # + def self.parse_international_number( number ) + number = number.to_s.gsub( /[^0-9]/, '' ) + + parts = { + :country_code => nil, + :area_code => nil, + :central_office_code => nil, + :subscriber_number => nil, + :extension => nil, + } + + # Find country by country code: + country = Country.where( :country_code => number[0, 3]).first + country ||= Country.where( :country_code => number[0, 2]).first + country ||= Country.where( :country_code => number[0, 1]).first + + return nil if ! country # invalid number format + + parts[:country_code] = country.country_code + remainder = number[ parts[:country_code].length, 999 ] # e.g. "261200000" + + case parts[:country_code] + + when '1' + # Assure an NANP number + return nil if ! remainder.match(/[2-9]{1}[0-9]{2}[2-9]{1}[0-9]{2}[0-9]{4}/) + + # Shortcut for NANPA closed dialplan: + parts[:area_code ] = remainder[ 0, 3] + parts[:central_office_code ] = remainder[ 3, 3] + parts[:subscriber_number ] = remainder[ 6, 4] + else + # variable-length dialplan, e.g. Germany + + # Find longest area_code for the country: + longest_area_code = country.area_codes.order( "LENGTH(area_code) DESC" ).first + + # Find a matching area_code: + if longest_area_code + longest_area_code.area_code.length.downto(1) do |area_code_length| + area_code = country.area_codes.where( :area_code => remainder[ 0, area_code_length ] ).first + if area_code + parts[:area_code] = area_code.area_code + break + end + end + + return nil if ! parts[:area_code] # No matching area_code for the country. + + remainder = remainder.gsub( /^#{parts[:area_code]}/, '' ) + #remainder = number[ parts[:area_code].length, 999 ] # e.g. "200000" + end + parts[:subscriber_number] = remainder + end + + parts # return value + 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. + # + self.country_code = nil + self.area_code = nil + self.subscriber_number = nil + self.central_office_code = nil + 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.country_code = nil + self.area_code = nil + self.subscriber_number = nil + self.central_office_code = nil + self.extension = self.number.to_s.strip + else + parsed_number = PhoneNumber.parse( self.number ) + if parsed_number + self.country_code = parsed_number[:country_code] + self.area_code = parsed_number[:area_code] + self.subscriber_number = parsed_number[:subscriber_number] + self.extension = parsed_number[:extension] + self.central_office_code = parsed_number[:central_office_code] + + self.number = self.to_s.gsub( /[^\+0-9]/, '' ) + end + end + end + end + + # Find the (grand-)parent tenant of this phone number: + # + def tenant + #OPTIMIZE Add a tenant_id to SipAccount + case self.phone_numberable + when SipAccount + self.phone_numberable.tenant + when Conference + case self.phone_numberable.conferenceable + when Tenant + self.phone_numberable.conferenceable + when User + self.phone_numberable.conferenceable.current_tenant #OPTIMIZE + when UserGroup + self.phone_numberable.conferenceable.tenant + end + end + end + + def move_up? + return self.position.to_i > PhoneNumber.where(:phone_numberable_id => self.phone_numberable_id, :phone_numberable_type => self.phone_numberable_type ).order(:position).first.position.to_i + end + + def move_down? + return self.position.to_i < PhoneNumber.where(:phone_numberable_id => self.phone_numberable_id, :phone_numberable_type => self.phone_numberable_type ).order(:position).last.position.to_i + end + + private + + def validate_number + if ! PhoneNumber.parse( self.number ) + errors.add( :number, "is invalid." ) + end + end + + def check_if_number_is_available + if self.phone_numberable_type != 'PhoneBookEntry' && self.tenant + + phone_number_ranges = self.tenant.phone_number_ranges.where( + :name => [INTERNAL_EXTENSIONS, DIRECT_INWARD_DIALING_NUMBERS] + ) + if !phone_number_ranges.empty? + if !PhoneNumber.where(:phone_numberable_type => 'PhoneNumberRange'). + where(:phone_numberable_id => phone_number_ranges). + exists?(:number => self.number) + errors.add(:number, "isn't defined as an extenation or DID for the tenant '#{self.tenant}'. #{phone_number_ranges.inspect}") + end + end + end + end + + def validate_inbound_uniqueness + if NUMBER_TYPES_INBOUND.include?(self.phone_numberable_type) + numbering_scope = PhoneNumber.where(:state => 'active', :number => self.number, :phone_numberable_type => NUMBER_TYPES_INBOUND) + if numbering_scope.where(:id => self.id).count == 0 && numbering_scope.count > 0 + errors.add(:number, 'not unique') + end + end + end + + def save_value_of_to_s + self.value_of_to_s = self.to_s + end + + def copy_existing_call_forwards_if_necessary + if self.phone_numberable.class == SipAccount && self.phone_numberable.callforward_rules_act_per_sip_account == true + sip_account = SipAccount.find(self.phone_numberable) + if sip_account.phone_numbers.where('id != ?', self.id).count > 0 + if sip_account.phone_numbers.where('id != ?', self.id).order(:created_at).first.call_forwards.count > 0 + sip_account.phone_numbers.where('id != ?', self.id).first.call_forwards.each do |call_forward| + call_forward.set_this_callforward_rule_to_all_phone_numbers_of_the_parent_sip_account + end + end + end + end + end + +end diff --git a/app/models/phone_number_range.rb b/app/models/phone_number_range.rb new file mode 100644 index 0000000..2fdd9b6 --- /dev/null +++ b/app/models/phone_number_range.rb @@ -0,0 +1,16 @@ +class PhoneNumberRange < ActiveRecord::Base + attr_accessible :name, :description + + has_many :phone_numbers, :as => :phone_numberable, :dependent => :destroy + belongs_to :phone_number_rangeable, :polymorphic => true + + 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_presence_of :phone_number_rangeable_id + validates_presence_of :phone_number_rangeable + + def to_s + name + end +end diff --git a/app/models/phone_sip_account.rb b/app/models/phone_sip_account.rb new file mode 100644 index 0000000..449ba39 --- /dev/null +++ b/app/models/phone_sip_account.rb @@ -0,0 +1,17 @@ +class PhoneSipAccount < ActiveRecord::Base + attr_accessible :sip_account_id + + belongs_to :phone + belongs_to :sip_account + + validates_presence_of :phone + validates_presence_of :sip_account + + validates_uniqueness_of :sip_account_id, :scope => :phone_id + + acts_as_list :scope => :phone + + def to_s + "Position #{self.position}" + end +end diff --git a/app/models/remote_gs_node/gs_cluster_sync_log_entry.rb b/app/models/remote_gs_node/gs_cluster_sync_log_entry.rb new file mode 100644 index 0000000..843494e --- /dev/null +++ b/app/models/remote_gs_node/gs_cluster_sync_log_entry.rb @@ -0,0 +1,9 @@ +# Find docu about ActiveResource at +# http://ofps.oreilly.com/titles/9780596521424/activeresource_id59243.html +# test = RemoteGSNode::GcLogEntry.first.attributes.delete_if{|key, value| ['id','updated_at','created_at'].include?(key) }) + +module RemoteGsNode + class GsClusterSyncLogEntry < ActiveResource::Base + self.site = 'http://0.0.0.0:3000' + end +end
\ No newline at end of file diff --git a/app/models/ringtone.rb b/app/models/ringtone.rb new file mode 100644 index 0000000..36053c0 --- /dev/null +++ b/app/models/ringtone.rb @@ -0,0 +1,15 @@ +class Ringtone < ActiveRecord::Base + attr_accessible :audio, :bellcore_id + + mount_uploader :audio, AudioUploader + validates_presence_of :audio, :if => Proc.new{ |ringtone| ringtone.bellcore_id.blank? } + validates_presence_of :ringtoneable_type + validates_presence_of :ringtoneable_id + validates_presence_of :ringtoneable + + belongs_to :ringtoneable, :polymorphic => true + + def to_s + self.bellcore_id.to_s + end +end diff --git a/app/models/sip_account.rb b/app/models/sip_account.rb new file mode 100644 index 0000000..8459265 --- /dev/null +++ b/app/models/sip_account.rb @@ -0,0 +1,221 @@ +# encoding: UTF-8 + +class SipAccount < ActiveRecord::Base + include ActionView::Helpers::TextHelper + + attr_accessible :auth_name, :caller_name, :password, :voicemail_pin, + :tenant_id, :call_waiting, :clir, :clip_no_screening, + :clip, :description, :callforward_rules_act_per_sip_account, + :hotdeskable, :gs_node_id + + # Associations: + # + belongs_to :sip_accountable, :polymorphic => true + + has_many :phone_sip_accounts, :uniq => true + has_many :phones, :through => :phone_sip_accounts + + has_many :phone_numbers, :as => :phone_numberable, :dependent => :destroy + has_many :call_forwards, :through => :phone_numbers + + belongs_to :tenant + belongs_to :sip_domain + + has_many :softkeys, :dependent => :destroy + + has_many :voicemail_messages, :foreign_key => 'username', :primary_key => 'auth_name' + + has_many :call_histories, :as => :call_historyable, :dependent => :destroy + + has_one :voicemail_setting, :class_name => "VoicemailSetting", :primary_key => 'auth_name', :foreign_key => 'username', :dependent => :destroy + + belongs_to :gs_node + + # Delegations: + # + delegate :host, :to => :sip_domain, :allow_nil => true + delegate :realm, :to => :sip_domain, :allow_nil => true + + # Validations: + # + validates_presence_of :caller_name + validates_presence_of :sip_accountable + validates_presence_of :tenant + validates_presence_of :sip_domain + + validate_sip_password :password + + validates_format_of :voicemail_pin, :with => /[0-9]+/, + :allow_nil => true, :allow_blank => true + + validates_uniqueness_of :auth_name, :scope => :sip_domain_id + + # Before and after hooks: + # + before_save :save_value_of_to_s + after_save :create_voicemail_setting, :if => :'voicemail_setting == nil' + before_validation :find_and_set_tenant_id + before_validation :set_sip_domain_id + before_validation :convert_umlauts_in_caller_name + before_destroy :remove_sip_accounts_or_logout_phones + + # Sync other nodes when this is a cluster. + # + validates_presence_of :uuid + validates_uniqueness_of :uuid + + after_create { self.create_on_other_gs_nodes('sip_accountable', self.sip_accountable.try(:uuid)) } + after_destroy :destroy_on_other_gs_nodes + after_update { self.update_on_other_gs_nodes('sip_accountable', self.sip_accountable.try(:uuid)) } + + 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 })" + end + + def call_forwarding_toggle( call_forwarding_service, to_voicemail = nil ) + if ! self.phone_numbers.first + errors.add(:base, "You must provide at least one phone number") + end + + service_id = CallForwardCase.where(:value => call_forwarding_service).first.id + + call_forwarding_master = self.phone_numbers.first.call_forwards.where(:call_forward_case_id => service_id).order(:active).all(:conditions => 'source IS NULL OR source = ""').first + if ! call_forwarding_master + errors.add(:base, "No call forwarding entries found that could be toggled") + return false + end + + if call_forwarding_master.active + call_forwarding_master.active = false + else + if call_forwarding_service = 'assistant' && call_forwarding_master.call_forwardable_type == 'HuntGroup' && call_forwarding_master.call_forwardable + if call_forwarding_master.call_forwardable.hunt_group_members.where(:active => true).count > 0 + call_forwarding_master.active = true + else + call_forwarding_master.active = false + end + end + end + + self.phone_numbers.each do |phone_number| + call_forwarding = phone_number.call_forwards.where(:call_forward_case_id => service_id).order(:active).all(:conditions => 'source IS NULL OR source = ""').first + if ! call_forwarding + call_forwarding = CallForward.new() + call_forwarding.phone_number_id = phone_number.id + end + + if to_voicemail == nil + to_voicemail = call_forwarding_master.to_voicemail + end + + call_forwarding.call_forward_case_id = call_forwarding_master.call_forward_case_id + call_forwarding.timeout = call_forwarding_master.timeout + call_forwarding.destination = call_forwarding_master.destination + call_forwarding.source = call_forwarding_master.source + call_forwarding.depth = call_forwarding_master.depth + call_forwarding.active = call_forwarding_master.active + call_forwarding.to_voicemail = to_voicemail + + if ! call_forwarding.save + call_forwarding.errors.messages.each_with_index do |(error_key, error_message), index| + errors.add(error_key, "number: #{phone_number}: #{error_message}") + end + end + end + + if errors.empty? + return call_forwarding_master + end + + return false + end + + def registration + return FreeswitchRegistration.where(:reg_user => self.auth_name).first + end + + def call( phone_number ) + require 'freeswitch_event' + return FreeswitchAPI.execute( + 'originate', + "{origination_uuid=#{UUID.new.generate},origination_caller_id_number='#{phone_number}',origination_caller_id_name='Call'}user/#{self.auth_name} #{phone_number}", + true + ); + end + + + private + + def save_value_of_to_s + self.value_of_to_s = self.to_s + end + + def find_and_set_tenant_id + if self.new_record? and self.tenant_id != nil + return + else + tenant = case self.sip_accountable_type + when 'Tenant' ; sip_accountable + when 'UserGroup' ; sip_accountable.tenant + when 'User' ; sip_accountable.try(:current_tenant) || sip_accountable.try(:tenants).try(:last) + else nil + end + self.tenant_id = tenant.id if tenant != nil + end + end + + def set_sip_domain_id + self.sip_domain_id = self.tenant.try(:sip_domain_id) + end + + def convert_umlauts_in_caller_name + if !self.caller_name.blank? + self.caller_name = self.caller_name.sub(/ä/,'ae'). + sub(/Ä/,'Ae'). + sub(/ü/,'ue'). + sub(/Ü/,'Ue'). + sub(/ö/,'oe'). + sub(/Ö/,'Oe'). + sub(/ß/,'ss') + + self.caller_name = self.caller_name.gsub(/[^a-zA-Z0-9\-\,\:\. ]/,'_') + end + end + + # Make sure that a tenant phone goes back to the tenant and doesn't + # get deleted with this user. + # + def remove_sip_accounts_or_logout_phones + self.phones.each do |phone| + if phone.sip_accounts.where(:sip_accountable_type => 'Tenant').count > 0 + phone.user_logout + else + PhoneSipAccount.delete_all(:sip_account_id => self.id) + end + end + self.reload + end + + # 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 + self.phones.each do |phone| + phone.user_logout; + end + end + end + + def create_voicemail_setting + voicemail_setting = VoicemailSetting.new() + voicemail_setting.username = self.auth_name + voicemail_setting.domain = self.sip_domain.try(:host) + voicemail_setting.password = self.voicemail_pin + voicemail_setting.notify = true + voicemail_setting.attachment = true + voicemail_setting.mark_read = true + voicemail_setting.purge = false + voicemail_setting.save + end +end diff --git a/app/models/sip_domain.rb b/app/models/sip_domain.rb new file mode 100644 index 0000000..252fe4a --- /dev/null +++ b/app/models/sip_domain.rb @@ -0,0 +1,16 @@ +class SipDomain < ActiveRecord::Base + attr_accessible :host, :realm + + has_many :tenants, :dependent => :restrict + has_many :sip_accounts, :dependent => :restrict + + validates_presence_of :host + validates_uniqueness_of :host, :case_sensitive => false + + validates_presence_of :realm + validates_uniqueness_of :realm + + def to_s + self.host + end +end diff --git a/app/models/softkey.rb b/app/models/softkey.rb new file mode 100644 index 0000000..a709036 --- /dev/null +++ b/app/models/softkey.rb @@ -0,0 +1,100 @@ +class Softkey < ActiveRecord::Base + attr_accessible :softkey_function_id, :number, :label, :call_forward_id, :uuid + + belongs_to :sip_account + belongs_to :softkey_function + belongs_to :call_forward + + # Any CallForward BLF must have a valid softkey_call_forward_id. + # + validates_presence_of :call_forward_id, :if => Proc.new{ |softkey| self.softkey_function_id != nil && + self.softkey_function_id == SoftkeyFunction.find_by_name('call_forwarding').try(:id) } + + # These functions need a number to act. + # + validates_presence_of :number, :if => Proc.new{ |softkey| self.softkey_function_id != nil && + ['blf','speed_dial','dtmf','conference'].include?(softkey.softkey_function.name) } + + validates_presence_of :uuid + validates_uniqueness_of :uuid + + acts_as_list :scope => :sip_account + + before_validation :clean_up_and_leave_only_values_which_make_sense_for_the_current_softkey_function_id + after_validation :save_function_name_in_function, :if => Proc.new{ |softkey| self.call_forward_id.blank? } + after_save :resync_phone + after_destroy :resync_phone + + def possible_blf_call_forwards + if self.sip_account.phone_numbers.count == 0 + nil + else + if self.sip_account.callforward_rules_act_per_sip_account == true + # We pick one phone_number and display the rules of it. + # + phone_number = self.sip_account.phone_numbers.order(:number).first + call_forwards = self.sip_account.call_forwards.where(:phone_number_id => phone_number.id) + else + call_forwards = self.sip_account.call_forwards + end + + phone_numbers_ids = self.sip_account.phone_number_ids + phone_numbers = PhoneNumber.where(:id => phone_numbers_ids).pluck(:number) + + hunt_group_ids = PhoneNumber.where(:phone_numberable_type => 'HuntGroupMember', :number => phone_numbers). + map{ |phone_number| phone_number.phone_numberable.hunt_group.id }. + uniq + + call_forwards + CallForward.where(:call_forwardable_type => 'HuntGroup', :call_forwardable_id => hunt_group_ids). + where('phone_number_id NOT IN (?)', phone_numbers_ids) + end + end + + def to_s + if (['call_forwarding'].include?(self.softkey_function.name)) + "#{self.call_forward}" + else + if ['log_out', 'log_in'].include?(self.softkey_function.name) + I18n.t("softkeys.functions.#{self.softkey_function.name}") + else + "#{self.softkey_function.name} : #{self.number.to_s}" + end + end + end + + def resync_phone + phone_sip_account = PhoneSipAccount.find_by_sip_account_id(self.sip_account_id) + if phone_sip_account && phone_sip_account.phone + phone_sip_account.phone.resync() + end + end + + def move_up? + return self.position.to_i > Softkey.where(:sip_account_id => self.sip_account_id ).order(:position).first.position.to_i + end + + def move_down? + return self.position.to_i < Softkey.where(:sip_account_id => self.sip_account_id ).order(:position).last.position.to_i + end + + private + + def save_function_name_in_function + self.function = self.softkey_function.name + end + + # Make sure that no number is set when there is no need for one. + # And make sure that there is no CallForward connected when not needed. + # + def clean_up_and_leave_only_values_which_make_sense_for_the_current_softkey_function_id + if self.softkey_function_id != nil + if ['blf','speed_dial','dtmf','conference'].include?(self.softkey_function.name) + self.call_forward_id = nil + end + if ['call_forwarding'].include?(self.softkey_function.name) + self.number = nil + end + end + end + +end diff --git a/app/models/softkey_function.rb b/app/models/softkey_function.rb new file mode 100644 index 0000000..976827d --- /dev/null +++ b/app/models/softkey_function.rb @@ -0,0 +1,13 @@ +class SoftkeyFunction < ActiveRecord::Base + validates_presence_of :name + + validates_uniqueness_of :name + + acts_as_list + + default_scope order(:position) + + def to_s + self.name + end +end diff --git a/app/models/system_message.rb b/app/models/system_message.rb new file mode 100644 index 0000000..0d9e862 --- /dev/null +++ b/app/models/system_message.rb @@ -0,0 +1,7 @@ +class SystemMessage < ActiveRecord::Base + attr_accessible :content + + belongs_to :user + + validates_presence_of :content +end diff --git a/app/models/tenant.rb b/app/models/tenant.rb new file mode 100644 index 0000000..6f98603 --- /dev/null +++ b/app/models/tenant.rb @@ -0,0 +1,243 @@ +# 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 + + # Associations: + # + has_many :tenant_memberships, :dependent => :destroy + has_many :users, :through => :tenant_memberships, :validate => true + + has_many :user_groups, :dependent => :destroy + + has_many :phone_books, :as => :phone_bookable, :dependent => :destroy + has_many :phone_book_entries, :through => :phone_books + + has_many :phone_number_ranges, :as => :phone_number_rangeable, :dependent => :destroy + + has_many :phones, :as => :phoneable, :dependent => :destroy + has_many :users_phones, :through => :users, :source => :phones, :readonly => true + + has_many :callthroughs, :dependent => :destroy + + has_many :fax_accounts, :dependent => :destroy # A tenant can't have a FaxAccount by itself! + + belongs_to :country + belongs_to :language + + belongs_to :sip_domain + + has_many :sip_accounts, :as => :sip_accountable, :dependent => :destroy + has_many :users_sip_accounts, :through => :users, :source => :sip_accounts, :readonly => true + + has_many :conferences, :as => :conferenceable, :dependent => :destroy + + has_many :hunt_groups, :dependent => :destroy + has_many :hunt_group_members, :through => :hunt_groups + + has_many :automatic_call_distributors, :as => :automatic_call_distributorable, :dependent => :destroy + has_many :acd_agents, :through => :automatic_call_distributors + + # Phone numbers of the tenant. + # + has_many :phone_number_ranges_phone_numbers, :through => :phone_number_ranges, :source => :phone_numbers, :readonly => true + has_many :phone_numbers, :through => :sip_accounts + has_many :conferences_phone_numbers, :through => :conferences, :source => :phone_numbers, :readonly => true + has_many :callthroughs_phone_numbers, :through => :conferences, :source => :phone_numbers, :readonly => true + has_many :huntgroups_phone_numbers, :through => :conferences, :source => :phone_numbers, :readonly => true + has_many :fax_accounts_phone_numbers, :through => :fax_accounts, :source => :phone_numbers, :readonly => true + + # Phone numbers of users of the tenant. + # + has_many :users_phone_numbers, :through => :users, :source => :phone_numbers, :readonly => true + has_many :user_groups_phone_numbers, :through => :users, :source => :phone_numbers, :readonly => true + has_many :users_conferences, :through => :users, :source => :conferences, :readonly => true + has_many :users_conferences_phone_numbers, :through => :users_conferences, :source => :phone_numbers, :readonly => true + has_many :users_fax_accounts, :through => :users, :source => :fax_accounts, :readonly => true + has_many :users_fax_accounts_phone_numbers, :through => :users_fax_accounts, :source => :phone_numbers, :readonly => true + + # Validations: + # + validates_presence_of :name, :state, :country, :language + validates_length_of :name, :within => 1..255 + validates_uniqueness_of :name + + validates_length_of :name, :within => 1..100 + + # Before and after hooks: + # + after_create :create_a_default_phone_book + + # State machine: + default_scope where(:state => 'active') + state_machine :initial => :active do + + event :deactivate do + transition [:active] => :deactivated + end + + event :activate do + transition [:deactivated] => :active + end + end + + def to_s + name + end + + if STRICT_INTERNAL_EXTENSION_HANDLING == true + def array_of_internal_extension_numbers + ranges = self.internal_extension_ranges.gsub(/[^0-9\-,]/,'').gsub(/[\-]+/,'-').gsub(/[,]+/,',').split(/,/) + output = [] + ranges.each do |range| + mini_range = range.split(/-/).map{|x| x.to_i}.sort + if mini_range.size == 1 + output << mini_range[0] + else + output = output + (mini_range[0]..mini_range[1]).to_a + end + output = output.try(:flatten) + end + output.try(:sort).try(:uniq).map{|number| number.to_s } + end + + # 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.') + + phone_number_list = Array.new + + if self.array_of_internal_extension_numbers.size > 0 + if self.country.phone_number_ranges.first.try(:phone_numbers) == nil + phone_number_list = self.array_of_internal_extension_numbers + 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}) + end + end + + phone_number_list.each do |number| + internal_extensions.phone_numbers.find_or_create_by_name_and_number('Extension', number) + end + end + + end + + if 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 = [] + numbers.each do |number| + if number.include?('x') + self.array_of_internal_extension_numbers.each do |internal_extension| + array_of_all_external_numbers << number.gsub(/x/, "-#{internal_extension.to_s}") + end + else + array_of_all_external_numbers << number + end + end + array_of_all_external_numbers.try(:sort).try(:uniq).map{|number| number.to_s } + end + + # 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.') + self.array_of_dids_generated_from_did_list.each do |number| + dids.phone_numbers.find_or_create_by_name_and_number('DID', number) + end + end + + end + + + # All phone_numbers which can be used + # + def internal_extensions_and_dids + @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]). + map{|pnr| pnr.id }) + end + + def array_of_internal_extensions + @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). + map{|pnr| pnr.id }). + map{|phone_number| phone_number.number }. + sort.uniq + end + + 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 }). + map{|phone_number| phone_number.to_s }. + sort.uniq + end + + def array_of_assigned_phone_numbers + (self.phone_numbers + self.conferences_phone_numbers + + self.callthroughs_phone_numbers + self.huntgroups_phone_numbers + + self.fax_accounts_phone_numbers + self.users_phone_numbers + + self.user_groups_phone_numbers + self.users_conferences_phone_numbers + + self.users_fax_accounts_phone_numbers). + map{|phone_number| phone_number.number }. + sort.uniq + end + + def array_of_available_internal_extensions + (self.array_of_internal_extensions - self.array_of_assigned_phone_numbers).sort.uniq + end + + def array_of_available_dids + (self.array_of_dids - self.array_of_assigned_phone_numbers).sort.uniq + end + + def array_of_available_internal_extensions_and_dids + self.array_of_available_internal_extensions + self.array_of_available_dids + end + + private + + # Create a public phone book for this tenant + def create_a_default_phone_book + if self.name != 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) + ) + amooma = general_phone_book.phone_book_entries.create( + :organization => 'AMOOMA GmbH', + :is_organization => true, + :description => "Hersteller von Gemeinschaft. Rufen Sie uns an, falls Sie kommerziellen Support oder Consulting für Gemeinschaft benötigen.", + :homepage_organization => 'http://www.amooma.de', + :twitter_account => 'amooma_de', + :facebook_account => 'https://www.facebook.com/AMOOMA.GmbH', + ) + amooma.phone_numbers.create( + :name => 'Office', + :number => '+492622706480' + ) + amooma.addresses.create( + :street => 'Bachstr. 124', + :zip_code => '56566', + :city => 'Neuwied', + :country_id => Country.where(:country_code => 49).first.try(:id), + ) + end + end +end diff --git a/app/models/tenant_membership.rb b/app/models/tenant_membership.rb new file mode 100644 index 0000000..122f702 --- /dev/null +++ b/app/models/tenant_membership.rb @@ -0,0 +1,25 @@ +class TenantMembership < ActiveRecord::Base + belongs_to :tenant + belongs_to :user + + validates_presence_of :tenant + validates_presence_of :user + + after_create :set_current_tenant_if_necessary + + # State Machine stuff + default_scope where(:state => 'active') + state_machine :initial => :active do + end + + private + # The first TenantMembership becomes the current_tenant by default. + # + def set_current_tenant_if_necessary + if !self.user.current_tenant + self.user.current_tenant = self.tenant + self.user.save + end + end + +end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..2d0256f --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,208 @@ +require 'digest/sha2' + +class User < ActiveRecord::Base + after_create :create_a_default_phone_book, :if => :'is_native != false' + + # Sync other nodes when this is a cluster. + # + after_create :create_on_other_gs_nodes + after_destroy :destroy_on_other_gs_nodes + after_update :update_on_other_gs_nodes + + attr_accessible :user_name, :email, :password, :password_confirmation, + :first_name, :middle_name, :last_name, :male, + :image, :current_tenant_id, :language_id, + :new_pin, :new_pin_confirmation, :send_voicemail_as_email_attachment, + :importer_checksum, :gs_node_id + + attr_accessor :new_pin, :new_pin_confirmation + + before_validation { + # If the PIN and PIN confirmation are left blank in the GUI + # then the user/admin does not want to change the PIN. + if self.new_pin.blank? && self.new_pin_confirmation.blank? + self.new_pin = nil + self.new_pin_confirmation = nil + end + } + + validates_length_of [:new_pin, :new_pin_confirmation], + :minimum => MINIMUM_PIN_LENGTH, :maximum => MAXIMUM_PIN_LENGTH, + :allow_blank => true, :allow_nil => true + validates_format_of [:new_pin, :new_pin_confirmation], + :with => /^[0-9]+$/, + :allow_blank => true, :allow_nil => true, + :message => "must be numeric." + + validates_confirmation_of :new_pin, :if => :'pin_changed?' + before_save :hash_new_pin, :if => :'pin_changed?' + + has_secure_password + + validates_presence_of :password, :password_confirmation, :on => :create, :if => :'password_digest.blank?' + validates_presence_of :email + validates_presence_of :last_name + validates_presence_of :first_name + validates_presence_of :user_name + + validates_uniqueness_of :user_name, :case_sensitive => false + validates_uniqueness_of :email, :allow_nil => true, :case_sensitive => false + + validates_length_of :user_name, :within => 0..50 + + validates_presence_of :uuid + validates_uniqueness_of :uuid + + # Associations: + # + has_many :tenant_memberships, :dependent => :destroy + has_many :tenants, :through => :tenant_memberships + + has_many :user_group_memberships, :dependent => :destroy, :uniq => true + has_many :user_groups, :through => :user_group_memberships + + has_many :phone_books, :as => :phone_bookable, :dependent => :destroy + has_many :phone_book_entries, :through => :phone_books + + has_many :phones, :as => :phoneable + has_many :sip_accounts, :as => :sip_accountable, :dependent => :destroy + has_many :phone_numbers, :through => :sip_accounts + + has_many :conferences, :as => :conferenceable, :dependent => :destroy + + has_many :fax_accounts, :as => :fax_accountable, :dependent => :destroy + + has_many :system_messages, :dependent => :destroy + + has_many :auto_destroy_access_authorization_phone_numbers, :class_name => 'PhoneNumber', :foreign_key => 'access_authorization_user_id', :dependent => :destroy + + belongs_to :current_tenant, :class_name => 'Tenant' + validates_presence_of :current_tenant, :if => Proc.new{ |user| user.current_tenant_id } + + belongs_to :language + validates_presence_of :language_id + validates_presence_of :language + + validate :current_tenant_is_included_in_tenants, :if => Proc.new{ |user| user.current_tenant_id } + + belongs_to :gs_node + + # Avatar like photo + mount_uploader :image, ImageUploader + + before_save :format_email_and_user_name + + before_destroy :destroy_or_logout_phones + + def destroy + clean_whitelist_entries + super + end + + def pin_changed? + ! @new_pin.blank? + end + + def sip_domain + if self.current_tenant + return self.current_tenant.sip_domain + end + return nil + end + + def to_s + max_first_name_length = 10 + max_last_name_length = 20 + if self.first_name.blank? + self.last_name.strip + else + "#{self.first_name.strip} #{self.last_name.strip}" + end + end + + def self.find_user_by_phone_number( number, tenant ) + tenant = Tenant.where( :id => tenant.id ).first + if tenant + if tenant.sip_domain + user = tenant.sip_domain.sip_accounts. + joins(:phone_numbers). + where(:phone_numbers => { :number => number }). + first. + try(:sip_accountable) + if user.class.name == 'User' + return user + end + end + end + return nil + end + + def authenticate_by_pin?( entered_pin ) + self.pin_hash == Digest::SHA2.hexdigest( "#{self.pin_salt}#{entered_pin}" ) + end + + + private + + def hash_new_pin + if @new_pin \ + && @new_pin_confirmation \ + && @new_pin_confirmation == @new_pin + self.pin_salt = SecureRandom.base64(8) + self.pin_hash = Digest::SHA2.hexdigest(self.pin_salt + @new_pin) + end + end + + def format_email_and_user_name + self.email = self.email.downcase.strip if !self.email.blank? + self.user_name = self.user_name.downcase.strip if !self.user_name.blank? + end + + # Create a personal phone book for this user: + def create_a_default_phone_book + private_phone_book = self.phone_books.find_or_create_by_name_and_description( + I18n.t('phone_books.private_phone_book.name', :resource => self.to_s), + I18n.t('phone_books.private_phone_book.description') + ) + end + + # Check if a current_tenant_id is possible tenant_membership wise. + def current_tenant_is_included_in_tenants + if !self.tenants.include?(Tenant.find(self.current_tenant_id)) + errors.add(:current_tenant_id, "is not possible (no TenantMembership)") + end + end + + # Make sure that there are no whitelist entries with phone_numbers of + # a just destroyed user. + # + def clean_whitelist_entries + phone_numbers = PhoneNumber.where( :phone_numberable_type => 'Whitelist'). + where( :number => self.phone_numbers.map{ |x| x.number } ) + phone_numbers.each do |phone_number| + if phone_number.phone_numberable.whitelistable.class == Callthrough + whitelist = Whitelist.find(phone_number.phone_numberable) + phone_number.destroy + if whitelist.phone_numbers.count == 0 + # Very lickly that this Whitelist doesn't make sense any more. + # + whitelist.destroy + end + end + end + end + + # Make sure that a tenant phone goes back to the tenant and doesn't + # get deleted with this user. + # + def destroy_or_logout_phones + self.phones.each do |phone| + if phone.sip_accounts.where(:sip_accountable_type => 'Tenant').count > 0 + phone.user_logout + else + phone.destroy + end + end + end + +end diff --git a/app/models/user_group.rb b/app/models/user_group.rb new file mode 100644 index 0000000..44f2fd8 --- /dev/null +++ b/app/models/user_group.rb @@ -0,0 +1,33 @@ +class UserGroup < ActiveRecord::Base + attr_accessible :name, :description + + belongs_to :tenant + + validates_presence_of :name + validates_uniqueness_of :name, :scope => :tenant_id + + validates_presence_of :tenant + + validates_length_of :name, :within => 1..50 + + has_many :user_group_memberships, :dependent => :destroy, :uniq => true + has_many :users, :through => :user_group_memberships + + has_many :gui_function_memberships, :dependent => :destroy + has_many :gui_functions, :through => :gui_function_memberships + + has_many :phone_books, :as => :phone_bookable, :dependent => :destroy + has_many :phone_book_entries, :through => :phone_books + + has_many :sip_accounts, :as => :sip_accountable, :dependent => :destroy + + has_many :conferences, :as => :conferenceable, :dependent => :destroy + + has_many :fax_accounts, :as => :fax_accountable, :dependent => :destroy + + acts_as_list :scope => :tenant_id + + def to_s + name + end +end diff --git a/app/models/user_group_membership.rb b/app/models/user_group_membership.rb new file mode 100644 index 0000000..18a8d48 --- /dev/null +++ b/app/models/user_group_membership.rb @@ -0,0 +1,26 @@ +class UserGroupMembership < ActiveRecord::Base + belongs_to :user + belongs_to :user_group + + validates_uniqueness_of :user_id, :scope => :user_group_id + validates_presence_of :user + validates_presence_of :user_group + + validate :user_belongs_to_the_tenant_of_the_user_group + + # State Machine stuff + default_scope where(:state => 'active') + state_machine :initial => :active do + end + + def to_s + "#{self.user} / #{self.user_group}" + end + + private + def user_belongs_to_the_tenant_of_the_user_group + if !self.user_group.tenant.users.include?(self.user) + errors.add(:user_id, "not a member of the tenant which this group belongs to") + end + end +end diff --git a/app/models/voicemail_message.rb b/app/models/voicemail_message.rb new file mode 100644 index 0000000..91ba457 --- /dev/null +++ b/app/models/voicemail_message.rb @@ -0,0 +1,52 @@ +class VoicemailMessage < ActiveRecord::Base + self.table_name = 'voicemail_msgs' + self.primary_key = 'uuid' + +# belongs_to :sip_account, :foreign_key => 'username', :primary_key => 'auth_name', :readonly => true + # Prevent objects from being destroyed + def before_destroy + raise ActiveRecord::ReadOnlyRecord + end + + # Prevent objects from being deleted + def self.delete_all + raise ActiveRecord::ReadOnlyRecord + end + + # Delete Message on FreeSWITCH over EventAPI + def delete + require 'freeswitch_event' + result = FreeswitchAPI.execute('vm_delete', "#{self.username}@#{self.domain} #{self.uuid}"); + end + + # Alias for delete + def destroy + self.delete + end + + # Mark Message read + def mark_read(mark_read_or_unread = true) + read_status = mark_read_or_unread ? 'read' : 'unread' + require 'freeswitch_event' + result = FreeswitchAPI.execute('vm_read', "#{self.username}@#{self.domain} #{read_status} #{self.uuid}"); + end + + def format_date(epoch, date_format = '%m/%d/%Y %H:%M', date_today_format = '%H:%M') + if epoch && epoch > 0 + time = Time.at(epoch) + if time.strftime('%Y%m%d') == Time.now.strftime('%Y%m%d') + return time.in_time_zone.strftime(date_today_format) + end + return time.in_time_zone.strftime(date_format) + end + end + + def display_duration + if self.message_len.to_i > 0 + minutes = (self.message_len / 1.minutes).to_i + seconds = self.message_len - minutes.minutes.seconds + return '%i:%02i' % [minutes, seconds] + end + end + +end diff --git a/app/models/voicemail_setting.rb b/app/models/voicemail_setting.rb new file mode 100644 index 0000000..a8bb304 --- /dev/null +++ b/app/models/voicemail_setting.rb @@ -0,0 +1,12 @@ +class VoicemailSetting < ActiveRecord::Base + self.table_name = 'voicemail_prefs' + self.primary_key = 'username' + + attr_accessible :username, :domain, :name_path, :greeting_path, :password, :notify, :attachment, :mark_read, :purge, :sip_account + + has_one :sip_account, :foreign_key => 'auth_name' + + validates_presence_of :username + validates_presence_of :domain + validates :username, :uniqueness => {:scope => :domain} +end diff --git a/app/models/whitelist.rb b/app/models/whitelist.rb new file mode 100644 index 0000000..8303728 --- /dev/null +++ b/app/models/whitelist.rb @@ -0,0 +1,23 @@ +class Whitelist < ActiveRecord::Base + attr_accessible :name, :phone_numbers_attributes, :uuid + + belongs_to :whitelistable, :polymorphic => true + + # These are the phone_numbers for this whitelist. + # + has_many :phone_numbers, :as => :phone_numberable, :dependent => :destroy + + accepts_nested_attributes_for :phone_numbers, + :reject_if => lambda { |phone_number| phone_number[:number].blank? }, + :allow_destroy => true + + acts_as_list :scope => [ :whitelistable_type, :whitelistable_id ] + + validates_presence_of :uuid + validates_uniqueness_of :uuid + + def to_s + self.name || I18n.t('whitelists.name') + ' ID ' + self.id.to_s + end + +end diff --git a/app/uploaders/audio_uploader.rb b/app/uploaders/audio_uploader.rb new file mode 100644 index 0000000..2ddbeac --- /dev/null +++ b/app/uploaders/audio_uploader.rb @@ -0,0 +1,49 @@ +# encoding: utf-8 + +class AudioUploader < CarrierWave::Uploader::Base + + # Include RMagick or ImageScience support: + # include CarrierWave::RMagick + # include CarrierWave::MiniMagick + # include CarrierWave::ImageScience + + # Choose what kind of storage to use for this uploader: + storage :file + # storage :fog + + # Override the directory where uploaded files will be stored. + # This is a sensible default for uploaders that are meant to be mounted: + def store_dir + "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" + end + + # Provide a default URL as a default if there hasn't been a file uploaded: + # def default_url + # "/images/fallback/" + [version_name, "default.png"].compact.join('_') + # end + + # Process files as they are uploaded: + # process :scale => [200, 300] + # + # def scale(width, height) + # # do something + # end + + # Create different versions of your uploaded files: + # version :thumb do + # process :scale => [50, 50] + # end + + # Add a white list of extensions which are allowed to be uploaded. + # For images you might use something like this: + # def extension_white_list + # %w(jpg jpeg gif png) + # end + + # Override the filename of the uploaded files: + # Avoid using model.id or version_name here, see uploader/store.rb for details. + # def filename + # "something.jpg" if original_filename + # end + +end diff --git a/app/uploaders/document_uploader.rb b/app/uploaders/document_uploader.rb new file mode 100644 index 0000000..05d70ca --- /dev/null +++ b/app/uploaders/document_uploader.rb @@ -0,0 +1,49 @@ +# encoding: utf-8 + +class DocumentUploader < CarrierWave::Uploader::Base + + # Include RMagick or ImageScience support: + # include CarrierWave::RMagick + include CarrierWave::MiniMagick + # include CarrierWave::ImageScience + + # Choose what kind of storage to use for this uploader: + storage :file + # storage :fog + + # Override the directory where uploaded files will be stored. + # This is a sensible default for uploaders that are meant to be mounted: + def store_dir + "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" + end + + # Provide a default URL as a default if there hasn't been a file uploaded: + # def default_url + # "/images/fallback/" + [version_name, "default.png"].compact.join('_') + # end + + # Process files as they are uploaded: + # process :scale => [200, 300] + # + # def scale(width, height) + # # do something + # end + + # Create different versions of your uploaded files: + # version :thumb do + # process :scale => [50, 50] + # end + + # Add a white list of extensions which are allowed to be uploaded. + # For images you might use something like this: + def extension_white_list + %w(pdf) + end + + # Override the filename of the uploaded files: + # Avoid using model.id or version_name here, see uploader/store.rb for details. + # def filename + # "something.jpg" if original_filename + # end + +end diff --git a/app/uploaders/image_uploader.rb b/app/uploaders/image_uploader.rb new file mode 100644 index 0000000..114ccc6 --- /dev/null +++ b/app/uploaders/image_uploader.rb @@ -0,0 +1,63 @@ +# encoding: utf-8 + +class ImageUploader < CarrierWave::Uploader::Base + + # Include RMagick or ImageScience support: + # include CarrierWave::RMagick + include CarrierWave::MiniMagick + # include CarrierWave::ImageScience + + # Choose what kind of storage to use for this uploader: + storage :file + # storage :fog + + # Override the directory where uploaded files will be stored. + # This is a sensible default for uploaders that are meant to be mounted: + def store_dir + "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" + end + + # Provide a default URL as a default if there hasn't been a file uploaded: + def default_url + "/images/fallback/" + [version_name, "default.jpg"].compact.join('_') + end + + # Process files as they are uploaded: + # process :scale => [200, 300] + # + # def scale(width, height) + # # do something + # end + + # Create different versions of your uploaded files: + version :mini do + process :resize_to_fill => [32, 32] + end + + version :small do + process :resize_to_fill => [72, 72] + end + + version :profile do + process :resize_to_fill => [200, 200] + end + + version :snom_caller_picture do + process :resize_to_fill => [100, 135] + end + + # Add a white list of extensions which are allowed to be uploaded. + # For images you might use something like this: + def extension_white_list + %w(jpg jpeg gif png) + end + + # Override the filename of the uploaded files: + # Avoid using model.id or version_name here, see uploader/store.rb for details. + # def filename + # "something.jpg" if original_filename + # end + + # TODO Secure filename + +end diff --git a/app/uploaders/thumbnail_uploader.rb b/app/uploaders/thumbnail_uploader.rb new file mode 100644 index 0000000..a401a91 --- /dev/null +++ b/app/uploaders/thumbnail_uploader.rb @@ -0,0 +1,62 @@ +# encoding: utf-8 + +class ThumbnailUploader < CarrierWave::Uploader::Base + + # Include RMagick or ImageScience support: + # include CarrierWave::RMagick + include CarrierWave::MiniMagick + # include CarrierWave::ImageScience + + # Choose what kind of storage to use for this uploader: + storage :file + # storage :fog + + # Override the directory where uploaded files will be stored. + # This is a sensible default for uploaders that are meant to be mounted: + def store_dir + "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" + end + + # Provide a default URL as a default if there hasn't been a file uploaded: + # def default_url + # "/images/fallback/" + [version_name, "default.png"].compact.join('_') + # end + + # Process files as they are uploaded: + # process :scale => [200, 300] + # + # def scale(width, height) + # # do something + # end + + # Create different versions of your uploaded files: + version :mini do + process :resize_to_limit => [75, 75] + end + + version :thumb do + process :resize_to_limit => [150, 150] + end + + version :medium do + process :resize_to_limit => [400, 400] + end + + version :big do + process :resize_to_limit => [800, 800] + end + + + # Add a white list of extensions which are allowed to be uploaded. + # For images you might use something like this: + def extension_white_list + %w(png) + end + + # Override the filename of the uploaded files: + # Avoid using model.id or version_name here, see uploader/store.rb for details. + # def filename + # "something.jpg" if original_filename + # end + +end diff --git a/app/uploaders/tiff_uploader.rb b/app/uploaders/tiff_uploader.rb new file mode 100644 index 0000000..d482aed --- /dev/null +++ b/app/uploaders/tiff_uploader.rb @@ -0,0 +1,49 @@ +# encoding: utf-8 + +class TiffUploader < CarrierWave::Uploader::Base + + # Include RMagick or ImageScience support: + # include CarrierWave::RMagick + # include CarrierWave::MiniMagick + # include CarrierWave::ImageScience + + # Choose what kind of storage to use for this uploader: + storage :file + # storage :fog + + # Override the directory where uploaded files will be stored. + # This is a sensible default for uploaders that are meant to be mounted: + def store_dir + "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" + end + + # Provide a default URL as a default if there hasn't been a file uploaded: + # def default_url + # "/images/fallback/" + [version_name, "default.png"].compact.join('_') + # end + + # Process files as they are uploaded: + # process :scale => [200, 300] + # + # def scale(width, height) + # # do something + # end + + # Create different versions of your uploaded files: + # version :thumb do + # process :scale => [50, 50] + # end + + # Add a white list of extensions which are allowed to be uploaded. + # For images you might use something like this: + # def extension_white_list + # %w(jpg jpeg gif png) + # end + + # Override the filename of the uploaded files: + # Avoid using model.id or version_name here, see uploader/store.rb for details. + # def filename + # "something.jpg" if original_filename + # end + +end diff --git a/app/views/access_authorizations/_form.html.haml b/app/views/access_authorizations/_form.html.haml new file mode 100644 index 0000000..fa417d9 --- /dev/null +++ b/app/views/access_authorizations/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([@parent, @access_authorization]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('access_authorizations.form.submit')
\ No newline at end of file diff --git a/app/views/access_authorizations/_form_core.html.haml b/app/views/access_authorizations/_form_core.html.haml new file mode 100644 index 0000000..10530d7 --- /dev/null +++ b/app/views/access_authorizations/_form_core.html.haml @@ -0,0 +1,11 @@ +.inputs + = f.input :name, :label => t('access_authorizations.form.name.label'), :hint => conditional_hint('access_authorizations.form.name.hint') + = f.input :login, :label => t('access_authorizations.form.login.label'), :hint => conditional_hint('access_authorizations.form.login.hint') + = f.input :pin, :label => t('access_authorizations.form.pin.label'), :hint => conditional_hint('access_authorizations.form.pin.hint') + - if SipAccount.count < 50 + = f.association :sip_account, :label => t('callthroughs.form.sip_account.label'), :hint => conditional_hint('callthroughs.form.sip_account.hint') + - else + = f.input :sip_account_id, :label => t('callthroughs.form.sip_account_id.label'), :hint => conditional_hint('callthroughs.form.sip_account_id.hint') + + = f.simple_fields_for :phone_numbers do |phone_number| + = render "phone_numbers/form_core", :f => phone_number
\ No newline at end of file diff --git a/app/views/access_authorizations/_index_core.html.haml b/app/views/access_authorizations/_index_core.html.haml new file mode 100644 index 0000000..083b16b --- /dev/null +++ b/app/views/access_authorizations/_index_core.html.haml @@ -0,0 +1,21 @@ +%table + %tr + %th= t('access_authorizations.index.name') + %th= t('access_authorizations.index.login') + %th= t('access_authorizations.index.pin') + %th= t('callthroughs.index.phone_numbers') + + - reset_cycle + - for access_authorization in access_authorizations + - show_path_method = method( :"#{access_authorization.access_authorizationable.class.name.underscore}_access_authorization_path" ) + - edit_path_method = method( :"edit_#{access_authorization.access_authorizationable.class.name.underscore}_access_authorization_path" ) + %tr{:class => cycle('odd', 'even')} + %td= access_authorization.name + %td= access_authorization.login + %td= access_authorization.pin + %td + =render 'phone_numbers/listing', :phone_numbers => access_authorization.phone_numbers + - if access_authorization.phone_numbers.count > 0 + %br + = link_to t('phone_numbers.index.actions.create'), new_access_authorization_phone_number_path(access_authorization) + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => access_authorization.access_authorizationable, :child => access_authorization}
\ No newline at end of file diff --git a/app/views/access_authorizations/edit.html.haml b/app/views/access_authorizations/edit.html.haml new file mode 100644 index 0000000..414f094 --- /dev/null +++ b/app/views/access_authorizations/edit.html.haml @@ -0,0 +1,3 @@ +- title t("access_authorizations.edit.page_title") + += render "form" diff --git a/app/views/access_authorizations/index.html.haml b/app/views/access_authorizations/index.html.haml new file mode 100644 index 0000000..05b27db --- /dev/null +++ b/app/views/access_authorizations/index.html.haml @@ -0,0 +1,6 @@ +- title t("access_authorizations.index.page_title") + +- if @access_authorizations.count > 0 + = render "index_core", :access_authorizations => @access_authorizations + += render :partial => 'shared/create_link', :locals => {:parent => @parent, :child_class => AccessAuthorization}
\ No newline at end of file diff --git a/app/views/access_authorizations/new.html.haml b/app/views/access_authorizations/new.html.haml new file mode 100644 index 0000000..0bbf16c --- /dev/null +++ b/app/views/access_authorizations/new.html.haml @@ -0,0 +1,3 @@ +- title t("access_authorizations.new.page_title") + += render "form" diff --git a/app/views/access_authorizations/show.html.haml b/app/views/access_authorizations/show.html.haml new file mode 100644 index 0000000..17d1d9b --- /dev/null +++ b/app/views/access_authorizations/show.html.haml @@ -0,0 +1,22 @@ +- title t("access_authorizations.show.page_title") + +%p + %strong= t('access_authorizations.show.name') + ":" + = @access_authorization.name +%p + %strong= t('access_authorizations.show.login') + ":" + = @access_authorization.login +%p + %strong= t('access_authorizations.show.pin') + ":" + = @access_authorization.pin +%p + %strong= t('access_authorizations.show.sip_account_id') + ":" + = @access_authorization.sip_account || t('access_authorizations.none') + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @parent, :child => @access_authorization } + +%h2= t('callthroughs.form.phone_numbers.label') +- if @access_authorization.phone_numbers.count > 0 + = render 'phone_numbers/index_core', :phone_numbers => @access_authorization.phone_numbers + %br += render :partial => 'shared/create_link', :locals => {:parent => @access_authorization, :child_class => PhoneNumber}
\ No newline at end of file diff --git a/app/views/acd_agents/_form.html.haml b/app/views/acd_agents/_form.html.haml new file mode 100644 index 0000000..3b78bac --- /dev/null +++ b/app/views/acd_agents/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([@automatic_call_distributor, @acd_agent]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('acd_agents.form.submit')
\ No newline at end of file diff --git a/app/views/acd_agents/_form_core.html.haml b/app/views/acd_agents/_form_core.html.haml new file mode 100644 index 0000000..ab4657c --- /dev/null +++ b/app/views/acd_agents/_form_core.html.haml @@ -0,0 +1,7 @@ +.inputs + = f.input :name, :label => t('acd_agents.form.name.label'), :hint => conditional_hint('acd_agents.form.name.hint') + = f.input :status, :label => t('acd_agents.form.status.label'), :hint => conditional_hint('acd_agents.form.status.hint'), :include_blank => false, :collection => AcdAgent::STATUSES + = f.input :last_call, :label => t('acd_agents.form.last_call.label'), :hint => conditional_hint('acd_agents.form.last_call.hint') + = f.input :calls_answered, :label => t('acd_agents.form.calls_answered.label'), :hint => conditional_hint('acd_agents.form.calls_answered.hint') + = f.input :destination_type, :label => t('acd_agents.form.destination_type.label'), :hint => conditional_hint('acd_agents.form.destination_type.hint'), :include_blank => false, :collection => AcdAgent::DESTINATION_TYPES + = f.input :destination_id, :label => t('acd_agents.form.destination_id.label'), :hint => conditional_hint('acd_agents.form.destination_id.hint') diff --git a/app/views/acd_agents/_index_core.html.haml b/app/views/acd_agents/_index_core.html.haml new file mode 100644 index 0000000..7cb1aae --- /dev/null +++ b/app/views/acd_agents/_index_core.html.haml @@ -0,0 +1,18 @@ +%table + %tr + %th= t('acd_agents.index.name') + %th= t('acd_agents.index.status') + %th= t('acd_agents.index.last_call') + %th= t('acd_agents.index.calls_answered') + %th= t('acd_agents.index.destination') + + - reset_cycle + - for acd_agent in acd_agents + %tr{:class => cycle('odd', 'even')} + %td= acd_agent.name + %td= acd_agent.status + %td= acd_agent.last_call + %td= acd_agent.calls_answered + %td= acd_agent.destination + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => acd_agent.automatic_call_distributor, :child => acd_agent} +
\ No newline at end of file diff --git a/app/views/acd_agents/_listing.html.haml b/app/views/acd_agents/_listing.html.haml new file mode 100644 index 0000000..0495ec2 --- /dev/null +++ b/app/views/acd_agents/_listing.html.haml @@ -0,0 +1,8 @@ +- amount_of_acd_agents = acd_agents.count +- if amount_of_acd_agents > 0 + - if amount_of_acd_agents < 30 + = acd_agents.map{|acd_agent| acd_agent}.join(', ') + - else + = acd_agents.limit(15).map{|acd_agent| acd_agent}.join(', ') + ', ' + = '[...]' + = acd_agents.offset(amount_of_acd_agents - 15).map{|acd_agent| acd_agent}.join(', ')
\ No newline at end of file diff --git a/app/views/acd_agents/edit.html.haml b/app/views/acd_agents/edit.html.haml new file mode 100644 index 0000000..8ab14b8 --- /dev/null +++ b/app/views/acd_agents/edit.html.haml @@ -0,0 +1,3 @@ +- title t("acd_agents.edit.page_title") + += render "form"
\ No newline at end of file diff --git a/app/views/acd_agents/index.html.haml b/app/views/acd_agents/index.html.haml new file mode 100644 index 0000000..d586dcf --- /dev/null +++ b/app/views/acd_agents/index.html.haml @@ -0,0 +1,6 @@ +- title t("acd_agents.index.page_title") + +- if @acd_agents && @acd_agents.count > 0 + = render "index_core", :acd_agents => @acd_agents + += render :partial => 'shared/create_link', :locals => {:parent => @automatic_call_distributor, :child_class => AcdAgent} diff --git a/app/views/acd_agents/new.html.haml b/app/views/acd_agents/new.html.haml new file mode 100644 index 0000000..546136b --- /dev/null +++ b/app/views/acd_agents/new.html.haml @@ -0,0 +1,3 @@ +- title t("acd_agents.new.page_title") + += render "form"
\ No newline at end of file diff --git a/app/views/acd_agents/show.html.haml b/app/views/acd_agents/show.html.haml new file mode 100644 index 0000000..97881f6 --- /dev/null +++ b/app/views/acd_agents/show.html.haml @@ -0,0 +1,28 @@ +- title t("acd_agents.show.page_title") + +%p + %strong= t('acd_agents.show.uuid') + ":" + = @acd_agent.uuid +%p + %strong= t('acd_agents.show.name') + ":" + = @acd_agent.name +%p + %strong= t('acd_agents.show.status') + ":" + = @acd_agent.status +%p + %strong= t('acd_agents.show.automatic_call_distributor_id') + ":" + = @acd_agent.automatic_call_distributor_id +%p + %strong= t('acd_agents.show.last_call') + ":" + = @acd_agent.last_call +%p + %strong= t('acd_agents.show.calls_answered') + ":" + = @acd_agent.calls_answered +%p + %strong= t('acd_agents.show.destination_type') + ":" + = @acd_agent.destination_type +%p + %strong= t('acd_agents.show.destination_id') + ":" + = @acd_agent.destination_id + += render :partial => 'shared/show_edit_destroy_part', :locals => {:parent => @automatic_call_distributor, :child => @acd_agent }
\ No newline at end of file diff --git a/app/views/acd_callers/_index_core.html.haml b/app/views/acd_callers/_index_core.html.haml new file mode 100644 index 0000000..958b3ff --- /dev/null +++ b/app/views/acd_callers/_index_core.html.haml @@ -0,0 +1,21 @@ +%table + %tr + %th= t('acd_callers.index.channel_uuid') + %th= t('acd_callers.index.automatic_call_distributor_id') + %th= t('acd_callers.index.status') + %th= t('acd_callers.index.enter_time') + %th= t('acd_callers.index.agent_answer_time') + %th= t('acd_callers.index.callback_number') + %th= t('acd_callers.index.callback_attempts') + + - reset_cycle + - for acd_caller in acd_callers + %tr{:class => cycle('odd', 'even')} + %td= acd_caller.channel_uuid + %td= acd_caller.automatic_call_distributor_id + %td= acd_caller.status + %td= acd_caller.enter_time + %td= acd_caller.agent_answer_time + %td= acd_caller.callback_number + %td= acd_caller.callback_attempts + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:child => acd_caller}
\ No newline at end of file diff --git a/app/views/acd_callers/index.html.haml b/app/views/acd_callers/index.html.haml new file mode 100644 index 0000000..70439ed --- /dev/null +++ b/app/views/acd_callers/index.html.haml @@ -0,0 +1,6 @@ +- title t("acd_callers.index.page_title") + +- if @acd_callers && @acd_callers.count > 0 + = render "index_core", :acd_callers => @acd_callers + += render :partial => 'shared/create_link', :locals => {:child_class => AcdCaller}
\ No newline at end of file diff --git a/app/views/acd_callers/show.html.haml b/app/views/acd_callers/show.html.haml new file mode 100644 index 0000000..0ce8345 --- /dev/null +++ b/app/views/acd_callers/show.html.haml @@ -0,0 +1,25 @@ +- title t("acd_callers.show.page_title") + +%p + %strong= t('acd_callers.show.channel_uuid') + ":" + = @acd_caller.channel_uuid +%p + %strong= t('acd_callers.show.automatic_call_distributor_id') + ":" + = @acd_caller.automatic_call_distributor_id +%p + %strong= t('acd_callers.show.status') + ":" + = @acd_caller.status +%p + %strong= t('acd_callers.show.enter_time') + ":" + = @acd_caller.enter_time +%p + %strong= t('acd_callers.show.agent_answer_time') + ":" + = @acd_caller.agent_answer_time +%p + %strong= t('acd_callers.show.callback_number') + ":" + = @acd_caller.callback_number +%p + %strong= t('acd_callers.show.callback_attempts') + ":" + = @acd_caller.callback_attempts + += render :partial => 'shared/show_edit_destroy_part', :locals => { :child => @acd_caller }
\ No newline at end of file diff --git a/app/views/addresses/_form.html.haml b/app/views/addresses/_form.html.haml new file mode 100644 index 0000000..eff9930 --- /dev/null +++ b/app/views/addresses/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for(@address) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('addresses.form.submit')
\ No newline at end of file diff --git a/app/views/addresses/_form_core.html.haml b/app/views/addresses/_form_core.html.haml new file mode 100644 index 0000000..f01ac1c --- /dev/null +++ b/app/views/addresses/_form_core.html.haml @@ -0,0 +1,9 @@ +.inputs + = f.input :phone_book_entry_id, :label => t('addresses.form.phone_book_entry_id.label'), :hint => conditional_hint('addresses.form.phone_book_entry_id.hint') + = f.input :line1, :label => t('addresses.form.line1.label'), :hint => conditional_hint('addresses.form.line1.hint') + = f.input :line2, :label => t('addresses.form.line2.label'), :hint => conditional_hint('addresses.form.line2.hint') + = f.input :street, :label => t('addresses.form.street.label'), :hint => conditional_hint('addresses.form.street.hint') + = f.input :zip_code, :label => t('addresses.form.zip_code.label'), :hint => conditional_hint('addresses.form.zip_code.hint') + = f.input :city, :label => t('addresses.form.city.label'), :hint => conditional_hint('addresses.form.city.hint') + = f.input :country_id, :label => t('addresses.form.country_id.label'), :hint => conditional_hint('addresses.form.country_id.hint') + = f.input :position, :label => t('addresses.form.position.label'), :hint => conditional_hint('addresses.form.position.hint') diff --git a/app/views/addresses/_index_core.html.haml b/app/views/addresses/_index_core.html.haml new file mode 100644 index 0000000..2050ded --- /dev/null +++ b/app/views/addresses/_index_core.html.haml @@ -0,0 +1,23 @@ +%table + %tr + %th= t('addresses.index.phone_book_entry_id') + %th= t('addresses.index.line1') + %th= t('addresses.index.line2') + %th= t('addresses.index.street') + %th= t('addresses.index.zip_code') + %th= t('addresses.index.city') + %th= t('addresses.index.country_id') + %th= t('addresses.index.position') + + - reset_cycle + - for address in addresses + %tr{:class => cycle('odd', 'even')} + %td= address.phone_book_entry_id + %td= address.line1 + %td= address.line2 + %td= address.street + %td= address.zip_code + %td= address.city + %td= address.country_id + %td= address.position + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => address.phone_book_entry, :child => address}
\ No newline at end of file diff --git a/app/views/addresses/edit.html.haml b/app/views/addresses/edit.html.haml new file mode 100644 index 0000000..3d85ae6 --- /dev/null +++ b/app/views/addresses/edit.html.haml @@ -0,0 +1,9 @@ +- title t("addresses.edit.page_title") + += render "form" + +%p + - if can? :edit, @address + = link_to t('addresses.edit.actions.edit'), @address + | + = link_to t('addresses.edit.actions.view_all'), addresses_path diff --git a/app/views/addresses/index.html.haml b/app/views/addresses/index.html.haml new file mode 100644 index 0000000..ecebc65 --- /dev/null +++ b/app/views/addresses/index.html.haml @@ -0,0 +1,6 @@ +- title t("addresses.index.page_title") + +- if @addresses.count > 0 + = render "index_core", :addresses => @addresses + += render :partial => 'shared/create_link', :locals => {:child_class => Address}
\ No newline at end of file diff --git a/app/views/addresses/new.html.haml b/app/views/addresses/new.html.haml new file mode 100644 index 0000000..280de55 --- /dev/null +++ b/app/views/addresses/new.html.haml @@ -0,0 +1,3 @@ +- title t("addresses.new.page_title") + += render "form" diff --git a/app/views/addresses/show.html.haml b/app/views/addresses/show.html.haml new file mode 100644 index 0000000..211d020 --- /dev/null +++ b/app/views/addresses/show.html.haml @@ -0,0 +1,28 @@ +- title t("addresses.show.page_title") + +%p + %strong= t('addresses.show.phone_book_entry_id') + ":" + = @address.phone_book_entry_id +%p + %strong= t('addresses.show.line1') + ":" + = @address.line1 +%p + %strong= t('addresses.show.line2') + ":" + = @address.line2 +%p + %strong= t('addresses.show.street') + ":" + = @address.street +%p + %strong= t('addresses.show.zip_code') + ":" + = @address.zip_code +%p + %strong= t('addresses.show.city') + ":" + = @address.city +%p + %strong= t('addresses.show.country_id') + ":" + = @address.country_id +%p + %strong= t('addresses.show.position') + ":" + = @address.position + += render :partial => 'shared/show_edit_destroy_part', :locals => { :child => @address }
\ No newline at end of file diff --git a/app/views/api/rows/_form.html.erb b/app/views/api/rows/_form.html.erb new file mode 100644 index 0000000..465b105 --- /dev/null +++ b/app/views/api/rows/_form.html.erb @@ -0,0 +1,21 @@ +<%= simple_form_for(@row) do |f| %> + <%= f.error_notification %> + + <div class="inputs"> + <%= f.input :user_name %> + <%= f.input :last_name %> + <%= f.input :middle_name %> + <%= f.input :first_name %> + <%= f.input :office_phone_number %> + <%= f.input :internal_extension %> + <%= f.input :mobile_phone_number %> + <%= f.input :fax_phone_number %> + <%= f.input :email %> + <%= f.input :pin %> + <%= f.input :photo_file_name %> + </div> + + <div class="actions"> + <%= f.button :submit %> + </div> +<% end %> diff --git a/app/views/api/rows/edit.html.erb b/app/views/api/rows/edit.html.erb new file mode 100644 index 0000000..bce5694 --- /dev/null +++ b/app/views/api/rows/edit.html.erb @@ -0,0 +1,3 @@ +<h1>Editing row</h1> + +<%= render 'form' %>
\ No newline at end of file diff --git a/app/views/api/rows/index.html.erb b/app/views/api/rows/index.html.erb new file mode 100644 index 0000000..d65e059 --- /dev/null +++ b/app/views/api/rows/index.html.erb @@ -0,0 +1,29 @@ +<h1>Listing rows</h1> + +<table> + <tr> + <th>ID</th> + <th>User name</th> + <th>Last name</th> + <th>First name</th> + <th>Internal extension</th> + <th>Office phone number</th> + <th>Fax phone number</th> + <th>Email</th> + <th>Pin</th> + </tr> + +<% @rows.each do |row| %> + <tr class='<%= cycle('odd', 'even') %>'> + <td><%= row.id %></td> + <td><%= link_to row.user_name, tenant_user_path(row.user.current_tenant, row.user) %></td> + <td><%= row.last_name %></td> + <td><%= row.first_name %></td> + <td><%= row.internal_extension %></td> + <td><%= row.office_phone_number %></td> + <td><%= row.fax_phone_number %></td> + <td><%= row.email %></td> + <td><%= row.pin %></td> + </tr> +<% end %> +</table>
\ No newline at end of file diff --git a/app/views/api/rows/new.html.erb b/app/views/api/rows/new.html.erb new file mode 100644 index 0000000..fccd964 --- /dev/null +++ b/app/views/api/rows/new.html.erb @@ -0,0 +1,3 @@ +<h1>New row</h1> + +<%= render 'form' %>
\ No newline at end of file diff --git a/app/views/api/rows/show.html.erb b/app/views/api/rows/show.html.erb new file mode 100644 index 0000000..aad60b5 --- /dev/null +++ b/app/views/api/rows/show.html.erb @@ -0,0 +1,56 @@ +<p id="notice"><%= notice %></p> + +<p> + <b>User name:</b> + <%= @row.user_name %> +</p> + +<p> + <b>Last name:</b> + <%= @row.last_name %> +</p> + +<p> + <b>Middle name:</b> + <%= @row.middle_name %> +</p> + +<p> + <b>First name:</b> + <%= @row.first_name %> +</p> + +<p> + <b>Office phone number:</b> + <%= @row.office_phone_number %> +</p> + +<p> + <b>Internal extension:</b> + <%= @row.internal_extension %> +</p> + +<p> + <b>Mobile phone number:</b> + <%= @row.mobile_phone_number %> +</p> + +<p> + <b>Fax phone number:</b> + <%= @row.fax_phone_number %> +</p> + +<p> + <b>Email:</b> + <%= @row.email %> +</p> + +<p> + <b>Pin:</b> + <%= @row.pin %> +</p> + +<p> + <b>Photo file name:</b> + <%= @row.photo_file_name %> +</p>
\ No newline at end of file diff --git a/app/views/automatic_call_distributors/_form.html.haml b/app/views/automatic_call_distributors/_form.html.haml new file mode 100644 index 0000000..fcf133c --- /dev/null +++ b/app/views/automatic_call_distributors/_form.html.haml @@ -0,0 +1,8 @@ += simple_form_for([@parent, @automatic_call_distributor]) do |f| + = f.error_notification + + = render "form_core", :f => f, :join_on => @join_on, :leave_on => @leave_on, :strategies => @strategies + + + .actions + = f.button :submit, conditional_t('automatic_call_distributors.form.submit')
\ No newline at end of file diff --git a/app/views/automatic_call_distributors/_form_core.html.haml b/app/views/automatic_call_distributors/_form_core.html.haml new file mode 100644 index 0000000..77a38a6 --- /dev/null +++ b/app/views/automatic_call_distributors/_form_core.html.haml @@ -0,0 +1,16 @@ +.inputs + = f.input :name, :label => t('automatic_call_distributors.form.name.label'), :hint => conditional_hint('automatic_call_distributors.form.name.hint') + = f.input :strategy, :label => t('automatic_call_distributors.form.strategy.label'), :hint => conditional_hint('automatic_call_distributors.form.strategy.hint'), :include_blank => false, :as => :select, :collection => strategies + = f.input :max_callers, :label => t('automatic_call_distributors.form.max_callers.label'), :hint => conditional_hint('automatic_call_distributors.form.max_callers.hint') + = f.input :agent_timeout, :label => t('automatic_call_distributors.form.agent_timeout.label'), :hint => conditional_hint('automatic_call_distributors.form.agent_timeout.hint') + = f.input :retry_timeout, :label => t('automatic_call_distributors.form.retry_timeout.label'), :hint => conditional_hint('automatic_call_distributors.form.retry_timeout.hint') + = f.input :join, :label => t('automatic_call_distributors.form.join.label'), :hint => conditional_hint('automatic_call_distributors.form.join.hint'), :include_blank => false, :as => :select, :collection => join_on + = f.input :leave, :label => t('automatic_call_distributors.form.leave.label'), :hint => conditional_hint('automatic_call_distributors.form.leave.hint'), :include_blank => false, :as => :select, :collection => leave_on + + = f.input :announce_position, :label => t('automatic_call_distributors.form.announce_position.label'), :hint => conditional_hint('automatic_call_distributors.announce_position.hint'), :collection => [[t('automatic_call_distributors.announce_position.on_change_only'), 0],[t('automatic_call_distributors.announce_position.never'), nil],[t('automatic_call_distributors.announce_position.every_x_seconds', :x_seconds => 30), 30],[t('automatic_call_distributors.announce_position.every_x_seconds', :x_seconds => 60), 60], [t('automatic_call_distributors.announce_position.every_x_seconds', :x_seconds => 120), 120], [t('automatic_call_distributors.announce_position.every_x_seconds', :x_seconds => 300), 300], [t('automatic_call_distributors.announce_position.every_x_seconds', :x_seconds => 600), 600]], :include_blank => false + + = f.input :announce_call_agents, :label => t('automatic_call_distributors.form.announce_call_agents.label'), :hint => conditional_hint('automatic_call_distributors.form.announce_call_agents.hint') + = f.input :greeting, :label => t('automatic_call_distributors.form.greeting.label'), :hint => conditional_hint('automatic_call_distributors.form.greeting.hint') + = f.input :goodbye, :label => t('automatic_call_distributors.form.goodbye.label'), :hint => conditional_hint('automatic_call_distributors.form.goodbye.hint') + = f.input :music, :label => t('automatic_call_distributors.form.music.label'), :hint => conditional_hint('automatic_call_distributors.form.music.hint') +
\ No newline at end of file diff --git a/app/views/automatic_call_distributors/_index_core.html.haml b/app/views/automatic_call_distributors/_index_core.html.haml new file mode 100644 index 0000000..c31a648 --- /dev/null +++ b/app/views/automatic_call_distributors/_index_core.html.haml @@ -0,0 +1,39 @@ +%table + %tr + %th= t('automatic_call_distributors.index.name') + %th= t('automatic_call_distributors.index.strategy') + %th= t('automatic_call_distributors.index.max_callers') + %th= t('automatic_call_distributors.index.agent_timeout') + %th= t('automatic_call_distributors.index.retry_timeout') + %th= t('automatic_call_distributors.index.join') + %th= t('automatic_call_distributors.index.leave') + %th= t('automatic_call_distributors.index.phone_numbers') + %th= t('automatic_call_distributors.index.acd_agents') + + - reset_cycle + - for automatic_call_distributor in automatic_call_distributors + %tr{:class => cycle('odd', 'even')} + %td= automatic_call_distributor.name + %td= t("automatic_call_distributors.strategies.#{automatic_call_distributor.strategy}") + %td= automatic_call_distributor.max_callers + %td= automatic_call_distributor.agent_timeout + %td= automatic_call_distributor.retry_timeout + %td= t("automatic_call_distributors.join_on.#{automatic_call_distributor.join}") + %td= t("automatic_call_distributors.leave_on.#{automatic_call_distributor.leave}") + + %td + - if automatic_call_distributor.phone_numbers.count > 0 + = render 'phone_numbers/listing', :phone_numbers => automatic_call_distributor.phone_numbers + %br + = render :partial => 'shared/create_link', :locals => {:parent => automatic_call_distributor, :child_class => PhoneNumber, :short_link => true} + + %td + - if automatic_call_distributor.acd_agents.count > 3 + = link_to automatic_call_distributor.acd_agents.count, automatic_call_distributor_acd_agents_path(automatic_call_distributor) + %br + - elsif automatic_call_distributor.acd_agents.count > 0 + = render 'acd_agents/listing', :acd_agents => automatic_call_distributor.acd_agents + %br + = render :partial => 'shared/create_link', :locals => {:parent => automatic_call_distributor, :child_class => AcdAgent, :short_link => true} + + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => automatic_call_distributor.automatic_call_distributorable, :child => automatic_call_distributor}
\ No newline at end of file diff --git a/app/views/automatic_call_distributors/edit.html.haml b/app/views/automatic_call_distributors/edit.html.haml new file mode 100644 index 0000000..28cba74 --- /dev/null +++ b/app/views/automatic_call_distributors/edit.html.haml @@ -0,0 +1,3 @@ +- title t("automatic_call_distributors.edit.page_title") + += render "form"
\ No newline at end of file diff --git a/app/views/automatic_call_distributors/index.html.haml b/app/views/automatic_call_distributors/index.html.haml new file mode 100644 index 0000000..f3f8b2b --- /dev/null +++ b/app/views/automatic_call_distributors/index.html.haml @@ -0,0 +1,6 @@ +- title t("automatic_call_distributors.index.page_title") + +- if @automatic_call_distributors && @automatic_call_distributors.count > 0 + = render "index_core", :automatic_call_distributors => @automatic_call_distributors + += render :partial => 'shared/create_link', :locals => {:parent => @parent, :child_class => AutomaticCallDistributor} diff --git a/app/views/automatic_call_distributors/new.html.haml b/app/views/automatic_call_distributors/new.html.haml new file mode 100644 index 0000000..96a2d93 --- /dev/null +++ b/app/views/automatic_call_distributors/new.html.haml @@ -0,0 +1,3 @@ +- title t("automatic_call_distributors.new.page_title") + += render "form"
\ No newline at end of file diff --git a/app/views/automatic_call_distributors/show.html.haml b/app/views/automatic_call_distributors/show.html.haml new file mode 100644 index 0000000..e5bf785 --- /dev/null +++ b/app/views/automatic_call_distributors/show.html.haml @@ -0,0 +1,59 @@ +- title t("automatic_call_distributors.show.page_title") + +%table + %tr + %th= t('automatic_call_distributors.show.uuid') + ":" + %td= @automatic_call_distributor.uuid + %tr + %th= t('automatic_call_distributors.show.name') + ":" + %td= @automatic_call_distributor.name + %tr + %th= t('automatic_call_distributors.show.strategy') + ":" + %td= t("automatic_call_distributors.strategies.#{@automatic_call_distributor.strategy}") + %tr + %th= t('automatic_call_distributors.show.max_callers') + ":" + %td= @automatic_call_distributor.max_callers + %tr + %th= t('automatic_call_distributors.show.agent_timeout') + ":" + %td= @automatic_call_distributor.agent_timeout + %tr + %th= t('automatic_call_distributors.show.retry_timeout') + ":" + %td= @automatic_call_distributor.retry_timeout + %tr + %th= t('automatic_call_distributors.show.join') + ":" + %td= t("automatic_call_distributors.join_on.#{@automatic_call_distributor.join}") + %tr + %th= t('automatic_call_distributors.show.leave') + ":" + %td= t("automatic_call_distributors.leave_on.#{@automatic_call_distributor.leave}") + + %tr + %th= t('automatic_call_distributors.show.announce_position') + ":" + %td= @automatic_call_distributor.announce_position + + %tr + %th= t('automatic_call_distributors.show.announce_call_agents') + ":" + %td= @automatic_call_distributor.announce_call_agents + + %tr + %th= t('automatic_call_distributors.show.greeting') + ":" + %td= @automatic_call_distributor.greeting + + %tr + %th= t('automatic_call_distributors.show.goodbye') + ":" + %td= @automatic_call_distributor.goodbye + + %tr + %th= t('automatic_call_distributors.show.music') + ":" + %td= @automatic_call_distributor.music + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @automatic_call_distributor.automatic_call_distributorable, :child => @automatic_call_distributor } + +- if can?( :index, @automatic_call_distributor.phone_numbers ) + %h3= t('automatic_call_distributors.index.phone_numbers') + = render 'phone_numbers/index_core', :phone_numbers => @automatic_call_distributor.phone_numbers + = render :partial => 'shared/create_link', :locals => {:parent => @automatic_call_distributor, :child_class => PhoneNumber, :short_link => true} + +- if can?( :index, @automatic_call_distributor.acd_agents ) + %h3= t('automatic_call_distributors.index.acd_agents') + = render 'acd_agents/index_core', :acd_agents => @automatic_call_distributor.acd_agents + = render :partial => 'shared/create_link', :locals => {:parent => @automatic_call_distributor, :child_class => AcdAgent, :short_link => true} diff --git a/app/views/call_forwards/_form.html.haml b/app/views/call_forwards/_form.html.haml new file mode 100644 index 0000000..7310af3 --- /dev/null +++ b/app/views/call_forwards/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([ @phone_number, @call_forward ]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('call_forwards.form.submit')
\ No newline at end of file diff --git a/app/views/call_forwards/_form_core.html.haml b/app/views/call_forwards/_form_core.html.haml new file mode 100644 index 0000000..3dadb68 --- /dev/null +++ b/app/views/call_forwards/_form_core.html.haml @@ -0,0 +1,15 @@ +.inputs + = f.input :call_forward_case_id, :as => :select, :collection => @available_call_forward_cases.map {|x| [I18n.t("call_forward_cases.#{x.value}"), x.id] }, :label => t('call_forwards.form.call_forward_case_id.label'), :hint => conditional_hint('call_forwards.form.call_forward_case_id.hint'), :include_blank => false + = f.input :timeout, :label => t('call_forwards.form.timeout.label'), :hint => conditional_hint('call_forwards.form.timeout.hint') + + = f.input :call_forwarding_destination , :as => :select, :collection => @call_forwarding_destinations, :label => t('call_forwards.form.call_forwarding_destination.label'), :hint => conditional_hint('call_forwards.form.call_forwarding_destination.hint'), :include_blank => false + + = f.input :destination, :label => t('call_forwards.form.destination.label'), :hint => conditional_hint('call_forwards.form.destination.hint') + + + = f.input :source, :label => t('call_forwards.form.source.label'), :hint => conditional_hint('call_forwards.form.source.hint') + - if GuiFunction.display?('depth_field_in_call_forward_form', current_user) + = f.input :depth, :collection => 1..MAX_CALL_FORWARD_DEPTH, :label => t('call_forwards.form.depth.label'), :hint => conditional_hint('call_forwards.form.depth.hint') + - else + = f.hidden_field :depth + = f.input :active, :label => t('call_forwards.form.active.label'), :hint => conditional_hint('call_forwards.form.active.hint') diff --git a/app/views/call_forwards/_index_core.html.haml b/app/views/call_forwards/_index_core.html.haml new file mode 100644 index 0000000..7733855 --- /dev/null +++ b/app/views/call_forwards/_index_core.html.haml @@ -0,0 +1,31 @@ +%table + %tr + - if !@phone_number + %th= t('call_forwards.index.phone_number_id') + %th= t('call_forwards.index.call_forward_case_id') + %th= t('call_forwards.index.timeout') + %th= t('call_forwards.index.destination') + %th= t('call_forwards.index.source') + - if GuiFunction.display?('depth_field_value_in_index_table', current_user) + %th= t('call_forwards.index.depth') + %th= t('call_forwards.index.active') + + - reset_cycle + - for call_forward in call_forwards + %tr{:class => cycle('odd', 'even')} + - if !@phone_number + %td= call_forward.phone_number + %td= t("call_forward_cases.#{call_forward.call_forward_case.value}") + %td= call_forward.timeout + %td + = call_forward.destination + - if call_forward.call_forwardable_type + %br + = call_forward.call_forwardable_type + - if call_forward.call_forwardable + = ": #{call_forward.call_forwardable}" + %td= call_forward.source + - if GuiFunction.display?('depth_field_value_in_index_table', current_user) + %td= call_forward.depth + %td= call_forward.active + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => call_forward.phone_number, :child => call_forward}
\ No newline at end of file diff --git a/app/views/call_forwards/edit.html.haml b/app/views/call_forwards/edit.html.haml new file mode 100644 index 0000000..5fa9dcd --- /dev/null +++ b/app/views/call_forwards/edit.html.haml @@ -0,0 +1,3 @@ +- title t("call_forwards.edit.page_title", :resource => " for phone number #{@phone_number}" ) + += render "form"
\ No newline at end of file diff --git a/app/views/call_forwards/index.html.haml b/app/views/call_forwards/index.html.haml new file mode 100644 index 0000000..93d64f2 --- /dev/null +++ b/app/views/call_forwards/index.html.haml @@ -0,0 +1,6 @@ +- title t("call_forwards.index.page_title") + +- if @call_forwards.count > 0 + = render "index_core", :call_forwards => @call_forwards + += render :partial => 'shared/create_link', :locals => {:parent => @phone_number, :child_class => CallForward}
\ No newline at end of file diff --git a/app/views/call_forwards/new.html.haml b/app/views/call_forwards/new.html.haml new file mode 100644 index 0000000..960a9e6 --- /dev/null +++ b/app/views/call_forwards/new.html.haml @@ -0,0 +1,3 @@ +- title t("call_forwards.new.page_title") + += render "form" diff --git a/app/views/call_forwards/show.html.haml b/app/views/call_forwards/show.html.haml new file mode 100644 index 0000000..6d1a0c6 --- /dev/null +++ b/app/views/call_forwards/show.html.haml @@ -0,0 +1,33 @@ +- title t("call_forwards.show.page_title") + +%p + %strong= t('call_forwards.show.phone_number_id') + ":" + = @call_forward.phone_number +%p + %strong= t('call_forwards.show.call_forward_case_id') + ":" + = t("call_forward_cases.#{@call_forward.call_forward_case.value}") +%p + %strong= t('call_forwards.show.timeout') + ":" + = @call_forward.timeout +%p + %strong= t('call_forwards.show.destination') + ":" + = @call_forward.destination +- if @call_forward.call_forwardable_type == 'HuntGroup' && @call_forward.call_forwardable.class == HuntGroup + %p + %strong= t('call_forwards.show.hunt_group') + ":" + = @call_forward.call_forwardable +- if @call_forward.call_forwardable_type == 'Voicemail' + %p + %strong= t('call_forwards.show.to_voicemail') + ":" + = 'active' +%p + %strong= t('call_forwards.show.source') + ":" + = @call_forward.source +%p + %strong= t('call_forwards.show.depth') + ":" + = @call_forward.depth +%p + %strong= t('call_forwards.show.active') + ":" + = @call_forward.active + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @phone_number, :child => @call_forward }
\ No newline at end of file diff --git a/app/views/call_histories/_index_core.html.haml b/app/views/call_histories/_index_core.html.haml new file mode 100644 index 0000000..2d7658a --- /dev/null +++ b/app/views/call_histories/_index_core.html.haml @@ -0,0 +1,65 @@ += form_tag(destroy_multiple_sip_account_call_histories_path(@sip_account), :method => :delete, :id => 'call_history_form') do + %header.entries-nav= render :partial => "call_histories/navigation" + .content + %table + - reset_cycle + - for call_history in call_histories + - phone_number = call_history.display_number + - voicemail_message = call_history.voicemail_message + - if phone_number + - phone_book_entry = call_history.phone_book_entry_by_number(phone_number) + %tr.call-history-entry{:class => cycle('odd', 'even')} + %td.select_box= check_box_tag("selected_ids[]", call_history.id, false, :id => "select_item_#{call_history.id}", :class => 'select_item') + %td.thumbnail + - image = call_history.display_image(:small, phone_book_entry) + - if image + = image_tag(image, :itemprop => 'image') + %td.time + - if voicemail_message + .voicemail-message + %a{:href => sip_account_voicemail_messages_path(@sip_account, :anchor => "message_#{voicemail_message.id}")} + = image_tag('icons/gs_envelope_16x.png', :class => 'display') + = call_history.display_call_date(t("call_histories.index.date_format"), t("call_histories.index.date_today_format")) + - elsif call_history.entry_type == 'forwarded' + .call-forwarded= call_history.display_call_date(t("call_histories.index.date_format"), t("call_histories.index.date_today_format")) + - if call_history.callee_account_type.to_s.downcase == 'voicemail' + = t("call_histories.index.voicemail") + - else + = call_history.destination_number + - elsif call_history.entry_type == 'dialed' + .call-placed= call_history.display_call_date(t("call_histories.index.date_format"), t("call_histories.index.date_today_format")) + - elsif call_history.entry_type == 'received' + .call-received= call_history.display_call_date(t("call_histories.index.date_format"), t("call_histories.index.date_today_format")) + - elsif call_history.entry_type == 'missed' + .call-missed= call_history.display_call_date(t("call_histories.index.date_format"), t("call_histories.index.date_today_format")) + - else + .call-unknown + = t("call_histories.index.#{call_history.entry_type}") + = call_history.display_call_date(t("call_histories.index.date_format"), t("call_histories.index.date_today_format")) + - if call_history.forwarding_service && call_history.entry_type != 'forwarded' + = t("call_histories.index.forwarded_by") + = call_history.display_auth_account_name + %td.user + - display_name = call_history.display_name + - if display_name.blank? + - display_name = phone_book_entry.to_s + - if phone_book_entry + %a.name{:href => phone_book_phone_book_entry_path(phone_book_entry.phone_book, phone_book_entry), :itemprop => "name"}= display_name + - else + .name= display_name + .phone= phone_number + %td.status + - if call_history.display_duration + .duration= call_history.display_duration + - else + .disposition= t("call_histories.call_results.#{call_history.result}") + %td.actions + - if @sip_account.registration && can?(:call, call_history) + = link_to t('call_histories.index.actions.call'), call_sip_account_call_history_path(@sip_account, call_history), :method => :put + %td.actions + - if can? :destroy, call_history + = link_to t('call_histories.index.actions.destroy'), sip_account_call_history_path(@sip_account, call_history), :method => :delete + + %footer.entries-nav= render :partial => "call_histories/navigation" + = image_submit_tag('icons/cross-16x.png', :confirm => t("call_histories.index.actions.confirm_selected")) + = t("call_histories.index.actions.destroy_multiple") diff --git a/app/views/call_histories/_navigation.html.haml b/app/views/call_histories/_navigation.html.haml new file mode 100644 index 0000000..a1999d9 --- /dev/null +++ b/app/views/call_histories/_navigation.html.haml @@ -0,0 +1,11 @@ +%nav + %ol.abc + %li + %a{ :href => "?type=" }= t("call_histories.index.navigation.all", :calls => @calls_count) + %a{ :href => "?type=missed" }= t("call_histories.index.navigation.missed", :calls => @calls_missed_count) + %a{ :href => "?type=received" }= t("call_histories.index.navigation.received", :calls => @calls_received_count) + %a{ :href => "?type=dialed" }= t("call_histories.index.navigation.dialed", :calls => @calls_dialed_count) + %a{ :href => "?type=forwarded" }= t("call_histories.index.navigation.forwarded", :calls => @calls_forwarded_count) + +.pagination + = will_paginate @call_histories diff --git a/app/views/call_histories/index.html.haml b/app/views/call_histories/index.html.haml new file mode 100644 index 0000000..adf6838 --- /dev/null +++ b/app/views/call_histories/index.html.haml @@ -0,0 +1,6 @@ +- if @type + - title t("call_histories.index.page_title_#{@type}") +- else + - title t("call_histories.index.page_title") + += render "index_core", :call_histories => @call_histories diff --git a/app/views/calls/_index_core.html.haml b/app/views/calls/_index_core.html.haml new file mode 100644 index 0000000..ddd0650 --- /dev/null +++ b/app/views/calls/_index_core.html.haml @@ -0,0 +1,9 @@ +%table + %tr + %th= t('calls.index.uuid') + + - reset_cycle + - for call in @calls + %tr{:class => cycle('odd', 'even')} + %td + = call.uuid diff --git a/app/views/calls/index.html.haml b/app/views/calls/index.html.haml new file mode 100644 index 0000000..4ea60a6 --- /dev/null +++ b/app/views/calls/index.html.haml @@ -0,0 +1,6 @@ +- title t("calls.index.page_title") + +- if @calls.count > 0 + = render "index_core", :calls => @calls + += render :partial => 'shared/create_link', :locals => {:parent => @parent, :child_class => Call}
\ No newline at end of file diff --git a/app/views/callthroughs/_form.html.haml b/app/views/callthroughs/_form.html.haml new file mode 100644 index 0000000..99f92d0 --- /dev/null +++ b/app/views/callthroughs/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([@tenant, @callthrough]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('callthroughs.form.submit') diff --git a/app/views/callthroughs/_form_core.html.haml b/app/views/callthroughs/_form_core.html.haml new file mode 100644 index 0000000..1f137d9 --- /dev/null +++ b/app/views/callthroughs/_form_core.html.haml @@ -0,0 +1,24 @@ +.inputs + = f.input :name, :label => t('callthroughs.form.name.label'), :hint => conditional_hint('callthroughs.form.name.hint') + + %h2= t('callthroughs.form.phone_numbers.label') + - if !t('callthroughs.form.phone_numbers.hint').blank? + %p= t('callthroughs.form.phone_numbers.hint') + = f.simple_fields_for :phone_numbers do |phone_number| + = render "phone_numbers/form_core", :f => phone_number + %p + + - if @callthrough && @callthrough.access_authorizations.size > 0 + %h2= t('callthroughs.form.access_authorizations.label') + - if !t('callthroughs.form.access_authorizations.hint').blank? + %p= t('callthroughs.form.access_authorizations.hint') + = f.simple_fields_for :access_authorizations do |access_authorization| + = render "access_authorizations/form_core", :f => access_authorization + + - if CALLTHROUGH_HAS_WHITELISTS == true + - if @callthrough && @callthrough.whitelists.size > 0 + %h2= t('callthroughs.form.whitelists.label') + - if !t('callthroughs.form.whitelists.hint').blank? + %p= t('callthroughs.form.whitelists.hint') + = f.simple_fields_for :whitelists do |whitelist| + = render "whitelists/form_core", :f => whitelist diff --git a/app/views/callthroughs/_index_core.html.haml b/app/views/callthroughs/_index_core.html.haml new file mode 100644 index 0000000..f1802d4 --- /dev/null +++ b/app/views/callthroughs/_index_core.html.haml @@ -0,0 +1,17 @@ +%table + %tr + %th= t('callthroughs.index.name') + %th= t('callthroughs.index.phone_numbers') + %th= t('callthroughs.index.access_authorized_phone_numbers') + - if CALLTHROUGH_HAS_WHITELISTS == true + %th= t('callthroughs.index.whitelist_phone_numbers') + + - reset_cycle + - for callthrough in callthroughs + %tr{:class => cycle('odd', 'even')} + %td= callthrough.name + %td=render 'phone_numbers/listing', :phone_numbers => callthrough.phone_numbers + %td=render 'phone_numbers/listing', :phone_numbers => callthrough.access_authorization_phone_numbers + - if CALLTHROUGH_HAS_WHITELISTS == true + %td=render 'phone_numbers/listing', :phone_numbers => callthrough.whitelisted_phone_numbers + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => callthrough.tenant, :child => callthrough}
\ No newline at end of file diff --git a/app/views/callthroughs/edit.html.haml b/app/views/callthroughs/edit.html.haml new file mode 100644 index 0000000..44fe17e --- /dev/null +++ b/app/views/callthroughs/edit.html.haml @@ -0,0 +1,3 @@ +- title t("callthroughs.edit.page_title") + += render "form"
\ No newline at end of file diff --git a/app/views/callthroughs/index.html.haml b/app/views/callthroughs/index.html.haml new file mode 100644 index 0000000..c595351 --- /dev/null +++ b/app/views/callthroughs/index.html.haml @@ -0,0 +1,6 @@ +- title t("callthroughs.index.page_title") + +- if @callthroughs.count > 0 + = render "index_core", :callthroughs => @callthroughs + += render :partial => 'shared/create_link', :locals => {:parent => @parent, :child_class => Callthrough}
\ No newline at end of file diff --git a/app/views/callthroughs/new.html.haml b/app/views/callthroughs/new.html.haml new file mode 100644 index 0000000..ff47c1c --- /dev/null +++ b/app/views/callthroughs/new.html.haml @@ -0,0 +1,3 @@ +- title t("callthroughs.new.page_title") + += render "form" diff --git a/app/views/callthroughs/show.html.haml b/app/views/callthroughs/show.html.haml new file mode 100644 index 0000000..55bd6eb --- /dev/null +++ b/app/views/callthroughs/show.html.haml @@ -0,0 +1,27 @@ +- title t("callthroughs.show.page_title") + +%p + %strong= t('callthroughs.show.name') + ":" + = @callthrough.name + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @callthrough.tenant, :child => @callthrough } + + +%h2= t('callthroughs.form.phone_numbers.label') +- if @callthrough.phone_numbers.count > 0 + = render 'phone_numbers/index_core', :phone_numbers => @callthrough.phone_numbers + %br += render :partial => 'shared/create_link', :locals => {:parent => @callthrough, :child_class => PhoneNumber} + +%h2= t('callthroughs.form.access_authorizations.label') +- if @callthrough.access_authorizations.count > 0 + = render 'access_authorizations/index_core', :access_authorizations => @callthrough.access_authorizations + %br += render :partial => 'shared/create_link', :locals => {:parent => @callthrough, :child_class => AccessAuthorization} + +- if CALLTHROUGH_HAS_WHITELISTS == true + %h2= t('callthroughs.form.whitelists.label') + - if @callthrough.whitelisted_phone_numbers.count > 0 + = render 'whitelists/index_core', :whitelists => @callthrough.whitelists + %br + = render :partial => 'shared/create_link', :locals => {:parent => @callthrough, :child_class => Whitelist}
\ No newline at end of file diff --git a/app/views/conference_invitees/_form.html.haml b/app/views/conference_invitees/_form.html.haml new file mode 100644 index 0000000..400580d --- /dev/null +++ b/app/views/conference_invitees/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([@conference, @conference_invitee]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('conference_invitees.form.submit')
\ No newline at end of file diff --git a/app/views/conference_invitees/_form_core.html.haml b/app/views/conference_invitees/_form_core.html.haml new file mode 100644 index 0000000..3cac18d --- /dev/null +++ b/app/views/conference_invitees/_form_core.html.haml @@ -0,0 +1,7 @@ +.inputs + = f.simple_fields_for :phone_number, @phone_number do |p| + = p.input :number, :label => t('phone_numbers.form.number.label'), :hint => conditional_hint('phone_numbers.form.number.hint') + + = f.input :pin, :label => t('conference_invitees.form.pin.label'), :hint => conditional_hint('conference_invitees.form.pin.hint') + = f.input :speaker, :label => t('conference_invitees.form.speaker.label'), :hint => conditional_hint('conference_invitees.form.speaker.hint') + = f.input :moderator, :label => t('conference_invitees.form.moderator.label'), :hint => conditional_hint('conference_invitees.form.moderator.hint') diff --git a/app/views/conference_invitees/_index_core.html.haml b/app/views/conference_invitees/_index_core.html.haml new file mode 100644 index 0000000..f84af7d --- /dev/null +++ b/app/views/conference_invitees/_index_core.html.haml @@ -0,0 +1,17 @@ +%table + %tr + %th= t('conference_invitees.index.phone_book_entry_id') + %th= t('conference_invitees.index.phone_number') + %th= t('conference_invitees.index.pin') + %th= t('conference_invitees.index.speaker') + %th= t('conference_invitees.index.moderator') + + - reset_cycle + - for conference_invitee in conference_invitees + %tr{:class => cycle('odd', 'even')} + %td= conference_invitee.phone_book_entry || '-' + %td= conference_invitee.phone_number + %td= conference_invitee.pin + %td= conference_invitee.speaker + %td= conference_invitee.moderator + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => conference_invitee.conference, :child => conference_invitee}
\ No newline at end of file diff --git a/app/views/conference_invitees/edit.html.haml b/app/views/conference_invitees/edit.html.haml new file mode 100644 index 0000000..ce90bbe --- /dev/null +++ b/app/views/conference_invitees/edit.html.haml @@ -0,0 +1,3 @@ +- title t("conference_invitees.edit.page_title") + += render "form"
\ No newline at end of file diff --git a/app/views/conference_invitees/index.html.haml b/app/views/conference_invitees/index.html.haml new file mode 100644 index 0000000..2a0c26c --- /dev/null +++ b/app/views/conference_invitees/index.html.haml @@ -0,0 +1,6 @@ +- title t("conference_invitees.index.page_title") + +- if @conference_invitees.count > 0 + = render "index_core", :conference_invitees => @conference_invitees + += render :partial => 'shared/create_link', :locals => {:parent => @conference, :child_class => ConferenceInvitee}
\ No newline at end of file diff --git a/app/views/conference_invitees/new.html.haml b/app/views/conference_invitees/new.html.haml new file mode 100644 index 0000000..780494e --- /dev/null +++ b/app/views/conference_invitees/new.html.haml @@ -0,0 +1,3 @@ +- title t("conference_invitees.new.page_title") + += render "form" diff --git a/app/views/conference_invitees/show.html.haml b/app/views/conference_invitees/show.html.haml new file mode 100644 index 0000000..57c5627 --- /dev/null +++ b/app/views/conference_invitees/show.html.haml @@ -0,0 +1,20 @@ +- title t("conference_invitees.show.page_title") + +%p + %strong= t('conference_invitees.show.conference_id') + ":" + = @conference_invitee.conference +- if @conference_invitee.phone_book_entry_id + %p + %strong= t('conference_invitees.show.phone_book_entry_id') + ":" + = @conference_invitee.phone_book_entry +%p + %strong= t('conference_invitees.show.pin') + ":" + = @conference_invitee.pin +%p + %strong= t('conference_invitees.show.speaker') + ":" + = @conference_invitee.speaker +%p + %strong= t('conference_invitees.show.moderator') + ":" + = @conference_invitee.moderator + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @conference_invitee.conference, :child => @conference_invitee }
\ No newline at end of file diff --git a/app/views/conferences/_form.html.haml b/app/views/conferences/_form.html.haml new file mode 100644 index 0000000..4bee1a4 --- /dev/null +++ b/app/views/conferences/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([ @parent, @conference ]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('conferences.form.submit')
\ No newline at end of file diff --git a/app/views/conferences/_form_core.html.haml b/app/views/conferences/_form_core.html.haml new file mode 100644 index 0000000..04754de --- /dev/null +++ b/app/views/conferences/_form_core.html.haml @@ -0,0 +1,11 @@ +.inputs + = f.input :name, :label => t('conferences.form.name.label'), :hint => conditional_hint('conferences.form.name.hint'), :autofocus => true + - if !f.object.start.nil? + = f.input :start, :label => t('conferences.form.start.label'), :hint => conditional_hint('conferences.form.start.hint'), :include_blank => true, :start_year => Time.now.year, :end_year => Time.now.year + 2 + = f.input :end, :label => t('conferences.form.end.label'), :hint => conditional_hint('conferences.form.end.hint'), :include_blank => true, :start_year => Time.now.year, :end_year => Time.now.year + 2 + = f.input :description, :label => t('conferences.form.description.label'), :hint => conditional_hint('conferences.form.description.hint') + = f.input :pin, :label => t('conferences.form.pin.label'), :hint => conditional_hint('conferences.form.pin.hint') + = f.input :max_members, :collection => 1..MAXIMUM_NUMBER_OF_PEOPLE_IN_A_CONFERENCE, :include_blank => false, :label => t('conferences.form.max_members.label'), :hint => conditional_hint('conferences.form.max_members.hint') + = f.input :open_for_anybody, :label => t('conferences.form.open_for_anybody.label'), :hint => conditional_hint('conferences.form.open_for_anybody.hint') + = f.input :announce_new_member_by_name, :label => t('conferences.form.announce_new_member_by_name.label'), :hint => conditional_hint('conferences.form.announce_new_member_by_name.hint') + = f.input :announce_left_member_by_name, :label => t('conferences.form.announce_left_member_by_name.label'), :hint => conditional_hint('conferences.form.announce_left_member_by_name.hint')
\ No newline at end of file diff --git a/app/views/conferences/_index_core.html.haml b/app/views/conferences/_index_core.html.haml new file mode 100644 index 0000000..4073e83 --- /dev/null +++ b/app/views/conferences/_index_core.html.haml @@ -0,0 +1,53 @@ +%table + %tr + %th= t('conferences.index.name') + - if !conferences.respond_to?('where') || conferences.where(:start => nil).where(:end => nil).count != conferences.count + %th= t('conferences.index.start') + %th= t('conferences.index.end') + %th= t('conferences.index.phone_numbers') + - if !conferences.respond_to?('where') || conferences.where(:pin => '').count != conferences.count + %th= t('conferences.index.pin') + %th= t('conferences.index.max_members') + %th= t('conferences.index.number_of_invitees') + %th= t('conferences.index.flags') + + - reset_cycle + - for conference in conferences + - parent = conference.conferenceable + %tr{:class => cycle('odd', 'even')} + %td= conference.name + - if !conferences.respond_to?('where') || conferences.where(:start => nil).where(:end => nil).count != conferences.count + %td + - if conference.start + = l conference.start, :format => :long + - else + = '-' + %td + - if conference.end + = l conference.end, :format => :long + - else + = '-' + %td + - if conference.phone_numbers.count > 0 + = render 'phone_numbers/listing', :phone_numbers => conference.phone_numbers.order(:number) + %br + = render :partial => 'shared/create_link', :locals => {:parent => conference, :child_class => PhoneNumber, :short_link => true} + + - if !conferences.respond_to?('where') || conferences.where(:pin => '').count != conferences.count + %td + - if !conference.pin.blank? + = conference.pin + - else + = '-' + %td= conference.max_members + %td= conference.conference_invitees.count + %td + %ul + - if conference.open_for_anybody + %li= t('conferences.index.open_for_anybody') + - if conference.announce_new_member_by_name + %li= t('conferences.index.announce_new_member_by_name') + - if conference.announce_left_member_by_name + %li= t('conferences.index.announce_left_member_by_name') + + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => parent, :child => conference}
\ No newline at end of file diff --git a/app/views/conferences/edit.html.haml b/app/views/conferences/edit.html.haml new file mode 100644 index 0000000..bc190e7 --- /dev/null +++ b/app/views/conferences/edit.html.haml @@ -0,0 +1,3 @@ +- title t("conferences.edit.page_title") + += render "form" diff --git a/app/views/conferences/index.html.haml b/app/views/conferences/index.html.haml new file mode 100644 index 0000000..0324acd --- /dev/null +++ b/app/views/conferences/index.html.haml @@ -0,0 +1,6 @@ +- title t("conferences.index.page_title") + +- if @conferences.count > 0 + = render "index_core", :conferences => @conferences + += render :partial => 'shared/create_link', :locals => {:parent => @parent, :child_class => Conference}
\ No newline at end of file diff --git a/app/views/conferences/new.html.haml b/app/views/conferences/new.html.haml new file mode 100644 index 0000000..102f6a9 --- /dev/null +++ b/app/views/conferences/new.html.haml @@ -0,0 +1,3 @@ +- title t("conferences.new.page_title") + += render "form" diff --git a/app/views/conferences/show.html.haml b/app/views/conferences/show.html.haml new file mode 100644 index 0000000..10ebaed --- /dev/null +++ b/app/views/conferences/show.html.haml @@ -0,0 +1,43 @@ +- title t("conferences.show.page_title") + +%p + %strong= t('conferences.show.name') + ":" + = @conference.name +- if @conference.start + %p + %strong= t('conferences.show.start') + ":" + = @conference.start + %p + %strong= t('conferences.show.end') + ":" + = @conference.end +- if !@conference.description.blank? + %p + %strong= t('conferences.show.description') + ":" + = @conference.description +- if !@conference.pin.blank? + %p + %strong= t('conferences.show.pin') + ":" + = @conference.pin +%p + %strong= t('conferences.show.open_for_anybody') + ":" + = @conference.open_for_anybody +%p + %strong= t('conferences.show.announce_new_member_by_name') + ":" + = @conference.announce_new_member_by_name +%p + %strong= t('conferences.show.announce_left_member_by_name') + ":" + = @conference.announce_left_member_by_name + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @parent, :child => @conference } + +%h2= t('phone_numbers.index.page_title') +- if @phone_numbers.count > 0 + = render "phone_numbers/index_core", :phone_numbers => @phone_numbers + += render :partial => 'shared/create_link', :locals => {:parent => @conference, :child_class => PhoneNumber} + +%h2= t("conference_invitees.index.page_title") +- if @conference.conference_invitees.count > 0 + = render "conference_invitees/index_core", :conference_invitees => @conference.conference_invitees + += render :partial => 'shared/create_link', :locals => {:parent => @conference, :child_class => ConferenceInvitee}
\ No newline at end of file diff --git a/app/views/config_polycom/_call_history.xml.haml b/app/views/config_polycom/_call_history.xml.haml new file mode 100644 index 0000000..7d23edd --- /dev/null +++ b/app/views/config_polycom/_call_history.xml.haml @@ -0,0 +1,18 @@ +!!! XML +%html + %head + %title= @phone_xml_object[:title] + %body + %table{ :border => 0 } + %tbody + - @phone_xml_object[:entries].each do |entry| + %tr + %td= entry[:date] + %td= entry[:text] + %td + %a{:href => entry[:url]}= entry[:number] + + %softkey{ :index => 1, :label => 'Home', :action => 'SoftKey:Home' } + %softkey{ :index => 2, :label => 'Refresh', :action => 'SoftKey:Refresh' } + %softkey{ :index => 4, :label => 'Exit', :action => 'SoftKey:Exit' } + %softkey{ :index => 3, :label => 'Back', :action => 'SoftKey:Back' } diff --git a/app/views/config_polycom/_call_history_menu.xml.haml b/app/views/config_polycom/_call_history_menu.xml.haml new file mode 100644 index 0000000..6e56f37 --- /dev/null +++ b/app/views/config_polycom/_call_history_menu.xml.haml @@ -0,0 +1,13 @@ +!!! XML +%html + %head + %title= @phone_xml_object[:title] + %body + - @phone_xml_object[:entries].each do |entry| + %br + %a{ :href => entry[:url]}= entry[:text] + + %softkey{ :index => 1, :label => 'Home', :action => 'SoftKey:Home' } + %softkey{ :index => 2, :label => 'Refresh', :action => 'SoftKey:Refresh' } + %softkey{ :index => 4, :label => 'Exit', :action => 'SoftKey:Exit' } + %softkey{ :index => 3, :label => 'Back', :action => 'SoftKey:Back' } diff --git a/app/views/config_polycom/_phone_book.xml.haml b/app/views/config_polycom/_phone_book.xml.haml new file mode 100644 index 0000000..1066695 --- /dev/null +++ b/app/views/config_polycom/_phone_book.xml.haml @@ -0,0 +1,18 @@ +!!! XML +%html + %head + %title= @phone_xml_object[:title] + %body + %table{ :border => 0 } + %tbody + - @phone_xml_object[:entries].each do |entry| + %tr + %td= entry[:text] + %td + = "#{entry[:type][0]}: " + %a{:href => entry[:url]}= entry[:number] + + %softkey{ :index => 1, :label => 'Home', :action => 'SoftKey:Home' } + %softkey{ :index => 2, :label => 'Refresh', :action => 'SoftKey:Refresh' } + %softkey{ :index => 4, :label => 'Exit', :action => 'SoftKey:Exit' } + %softkey{ :index => 3, :label => 'Back', :action => 'SoftKey:Back' } diff --git a/app/views/config_polycom/config_files.xml.builder b/app/views/config_polycom/config_files.xml.builder new file mode 100644 index 0000000..50819ff --- /dev/null +++ b/app/views/config_polycom/config_files.xml.builder @@ -0,0 +1,12 @@ +xml.instruct! + +xml.tag!('APPLICATION', + 'APP_FILE_PATH' => 'sip.ld', + 'CONFIG_FILES' => "settings-#{@mac_address}.cfg", + 'MISC_FILES' => '', + 'LOG_FILE_DIRECTORY' => '', + 'OVERRIDES_DIRECTORY' => '', + 'CONTACTS_DIRECTORY' => '', + 'LICENSE_DIRECTORY' => '', + 'USER_PROFILES_DIRECTORY' => '', + 'CALL_LISTS_DIRECTORY' => '') diff --git a/app/views/config_polycom/idle_screen.xml.haml b/app/views/config_polycom/idle_screen.xml.haml new file mode 100644 index 0000000..fa52c4f --- /dev/null +++ b/app/views/config_polycom/idle_screen.xml.haml @@ -0,0 +1,7 @@ +!!! XML +%html + %head + %title= @sip_account.caller_name + %body + - @sip_account.phone_numbers.each do |number| + %br= number.number diff --git a/app/views/config_polycom/settings.xml.erb b/app/views/config_polycom/settings.xml.erb new file mode 100644 index 0000000..ea9d325 --- /dev/null +++ b/app/views/config_polycom/settings.xml.erb @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<PHONE_CONFIG> + <ALL + <% @settings.each do |key, value| %> + <%= key %>="<%= value %>" + <% end %> + /> +</PHONE_CONFIG> diff --git a/app/views/config_polycom/settings_directory.xml.haml b/app/views/config_polycom/settings_directory.xml.haml new file mode 100644 index 0000000..107f991 --- /dev/null +++ b/app/views/config_polycom/settings_directory.xml.haml @@ -0,0 +1,16 @@ +!!! XML +%directory + %item_list + %item + %ln= 'Directory' + %ct= '!directory' + %sd= 38 + %item + %ln= 'Call History' + %ct= '!callhistory' + %sd= 39 + %item + %ln= 'Applications' + %ct= '!applications' + %sd= 40 + diff --git a/app/views/config_siemens/_menu_list.xml.haml b/app/views/config_siemens/_menu_list.xml.haml new file mode 100644 index 0000000..70bfc43 --- /dev/null +++ b/app/views/config_siemens/_menu_list.xml.haml @@ -0,0 +1,33 @@ +!!! XML +%IppPhone + %IppDisplay + %IppScreen{:ID => '1', :HiddenCount => (@phone_xml_object[:hidden] ? @phone_xml_object[:hidden].length : '0'), :CommandCount => (@phone_xml_object[:commands] ? @phone_xml_object[:commands].length : '0')} + - if @phone_xml_object[:make_call] + %IppAction{:Type => 'MAKECALL'} + %Number= @phone_xml_object[:make_call] + - if @phone_xml_object[:led] != nil + %IppAction{:Type => (@phone_xml_object[:led] ? 'TURNLEDON' : 'TURNLEDOFF')} + %IppKey{:Keypad => 'YES', :SendKeys => 'YES', :BufferKeys => 'NO', :BufferLength => '0', :TermKey => '', :UrlKey => 'key'} + - if @phone_xml_object[:entries] + %IppList{:Type => 'IMPLICIT', :Count => @phone_xml_object[:entries].length, :Columns => @phone_xml_object[:columns]} + - if @phone_xml_object[:title] + %Title= @phone_xml_object[:title] + %Url= @phone_xml_object[:url] + - @phone_xml_object[:entries].each_with_index do |entry, index| + %Option{:ID => index+1, :Selected => (entry[:selected] ? 'TRUE' : 'FALSE'), :Key => (entry[:key] ? entry[:key] : 'item'), :Value => entry[:value]} + - if entry[:image] + %Image= entry[:image] + %OptionText= entry[:text].to_s + - if entry[:text_center] + %OptionText= entry[:text_center].to_s + - if entry[:text_right] + %OptionText= entry[:text_right].to_s + - if @phone_xml_object[:hidden] + - @phone_xml_object[:hidden].each do |key, value| + %IppHidden{:Type => 'VALUE', :Key => key} + %Value= value + - if @phone_xml_object[:commands] + - @phone_xml_object[:commands].each_with_index do |command, index| + %IppCommand{:Type => command[:type], :DisplayOn => command[:display], :Priority => index, :Key => command[:key], :Value => command[:value], :DisplayOn => command[:display_on], :Select => command[:select], :Default => command[:default], :Auto => command[:auto]} + %Label= command[:label] + %Screen= "1" diff --git a/app/views/config_siemens/clean-up.xml.erb b/app/views/config_siemens/clean-up.xml.erb new file mode 100644 index 0000000..e1cbf93 --- /dev/null +++ b/app/views/config_siemens/clean-up.xml.erb @@ -0,0 +1,5 @@ +<DlsMessage xsi:schemaLocation="http://www.siemens.com/DLS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.siemens.com/DLS"> + <Message nonce="<%=@my_nonce%>"> + <Action>CleanUp</Action> + </Message> +</DlsMessage>
\ No newline at end of file diff --git a/app/views/config_siemens/index.xml.erb b/app/views/config_siemens/index.xml.erb new file mode 100644 index 0000000..0c60b1f --- /dev/null +++ b/app/views/config_siemens/index.xml.erb @@ -0,0 +1,5 @@ +<DlsMessage xsi:schemaLocation="http://www.siemens.com/DLS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.siemens.com/DLS"> + <Message nonce="<%=@my_nonce%>"> + <Action>ReadAllItems</Action> + </Message> +</DlsMessage>
\ No newline at end of file diff --git a/app/views/config_siemens/write.xml.erb b/app/views/config_siemens/write.xml.erb new file mode 100644 index 0000000..ee9e32e --- /dev/null +++ b/app/views/config_siemens/write.xml.erb @@ -0,0 +1,10 @@ +<DlsMessage xsi:schemaLocation="http://www.siemens.com/DLS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.siemens.com/DLS"> + <Message nonce="<%=@my_nonce%>"> + <Action>WriteItems</Action> + <ItemList> + <% @new_settings.each do |setting| %> + <Item name="<%=setting[0]%>" <% if ! setting[1].nil? %>index="<%=setting[1]%>"<%end%>><%=setting[2]%></Item> + <% end %> + </ItemList> + </Message> +</DlsMessage>
\ No newline at end of file diff --git a/app/views/config_snom/_snom_phone_directory.xml.haml b/app/views/config_snom/_snom_phone_directory.xml.haml new file mode 100644 index 0000000..698f2e5 --- /dev/null +++ b/app/views/config_snom/_snom_phone_directory.xml.haml @@ -0,0 +1,19 @@ +!!! XML +%SnomIPPhoneDirectory{:speedselect => 'off'} + %Title= @phone_xml_object[:title] + %Prompt= @phone_xml_object[:prompt] + - @phone_xml_object[:entries].each do |entry| + %DirectoryEntry{:sel => entry[:selected].to_s} + %Name= entry[:text] + %Telephone= entry[:number] + + - if @phone_xml_object[:softkeys] + - @phone_xml_object[:softkeys].each do |softkey| + %SoftKeyItem + %Name= softkey[:name] + - if ! softkey[:label].blank? + %Label= softkey[:label] + - if ! softkey[:url].blank? + %URL= softkey[:url] + - if ! softkey[:softkey].blank? + %URL= softkey[:softkey] diff --git a/app/views/config_snom/_snom_phone_input.xml.haml b/app/views/config_snom/_snom_phone_input.xml.haml new file mode 100644 index 0000000..6038282 --- /dev/null +++ b/app/views/config_snom/_snom_phone_input.xml.haml @@ -0,0 +1,19 @@ +!!! XML +%SnomIPPhoneInput + %Title= @phone_xml_object[:title] + %Prompt= @phone_xml_object[:prompt] + %URL= @phone_xml_object[:url] + %InputItem + %DisplayName= @phone_xml_object[:display_name] + %QueryStringParam= @phone_xml_object[:query_string_param] + %DefaultValue= @phone_xml_object[:default_value] + %InputFlags= @phone_xml_object[:input_flags] + - if @phone_xml_object[:softkeys] + - @phone_xml_object[:softkeys].each do |softkey| + %SoftKeyItem + %Name= softkey[:name] + %Label= softkey[:label] + - if ! softkey[:url].blank? + %URL= softkey[:url] + - if ! softkey[:softkey].blank? + %URL= softkey[:softkey] diff --git a/app/views/config_snom/_snom_phone_menu.xml.haml b/app/views/config_snom/_snom_phone_menu.xml.haml new file mode 100644 index 0000000..de016c0 --- /dev/null +++ b/app/views/config_snom/_snom_phone_menu.xml.haml @@ -0,0 +1,17 @@ +!!! XML +%SnomIPPhoneMenu{:speedselect => 'off'} + %Title= @phone_xml_object[:title] + - @phone_xml_object[:entries].each do |entry| + %MenuItem{:sel => entry[:selected].to_s} + %Name= entry[:text] + %URL= entry[:url] + + - if @phone_xml_object[:softkeys] + - @phone_xml_object[:softkeys].each do |softkey| + %SoftKeyItem + %Name= softkey[:name] + %Label= softkey[:label] + - if ! softkey[:url].blank? + %URL= softkey[:url] + - if ! softkey[:softkey].blank? + %URL= softkey[:softkey] diff --git a/app/views/config_snom/_snom_phone_text.xml.haml b/app/views/config_snom/_snom_phone_text.xml.haml new file mode 100644 index 0000000..6c3773c --- /dev/null +++ b/app/views/config_snom/_snom_phone_text.xml.haml @@ -0,0 +1,16 @@ +!!! XML +%SnomIPPhoneText + %Title= @phone_xml_object[:title] + %Prompt= @phone_xml_object[:prompt] + %Text= @phone_xml_object[:text] + - if @phone_xml_object[:softkeys] + - @phone_xml_object[:softkeys].each do |softkey| + %SoftKeyItem + %Name= softkey[:name] + %Label= softkey[:label] + - if ! softkey[:url].blank? + %URL= softkey[:url] + - if ! softkey[:softkey].blank? + %URL= softkey[:softkey] + -if @phone_xml_object[:fetch_url] + %fetch{:mil => @phone_xml_object[:fetch_mil]}= @phone_xml_object[:fetch_url] diff --git a/app/views/config_snom/call_history.xml.haml b/app/views/config_snom/call_history.xml.haml new file mode 100644 index 0000000..00f9990 --- /dev/null +++ b/app/views/config_snom/call_history.xml.haml @@ -0,0 +1,2 @@ +!!! XML += render @phone_xml_object[:name] diff --git a/app/views/config_snom/idle_screen.xml.haml b/app/views/config_snom/idle_screen.xml.haml new file mode 100644 index 0000000..9476c44 --- /dev/null +++ b/app/views/config_snom/idle_screen.xml.haml @@ -0,0 +1,33 @@ +!!! XML +%screen_description + - if @phone_xml_object[:image] + %Image + %Data{:encoding => 'base64'}= @phone_xml_object[:image][:data] + %LocationX= @phone_xml_object[:image][:location_x] + %LocationY= @phone_xml_object[:image][:location_y] + %Invert= @phone_xml_object[:image][:invert] + - if @phone_xml_object[:clock] + %Clock + %LocationX= @phone_xml_object[:clock][:location_x] + %LocationY= @phone_xml_object[:clock][:location_y] + - if @phone_xml_object[:digital_clock] + %DigitalClock + %LocationX= @phone_xml_object[:digital_clock][:location_x] + %LocationY= @phone_xml_object[:digital_clock][:location_y] + - if @phone_xml_object[:date] + %Date + %LocationX= @phone_xml_object[:date][:location_x] + %LocationY= @phone_xml_object[:date][:location_y] + - if @phone_xml_object[:line] + %Line + %Account + %LocationX= @phone_xml_object[:line][:location_x] + %LocationY= @phone_xml_object[:line][:location_y] + - if @phone_xml_object[:status] + %Status + %LocationX= @phone_xml_object[:status][:location_x] + %LocationY= @phone_xml_object[:status][:location_y] + - if @phone_xml_object[:softkeys] + %Softkeys + %LocationX= @phone_xml_object[:softkeys][:location_x] + %LocationY= @phone_xml_object[:softkeys][:location_y] diff --git a/app/views/config_snom/log_in.xml.haml b/app/views/config_snom/log_in.xml.haml new file mode 100644 index 0000000..1f45d93 --- /dev/null +++ b/app/views/config_snom/log_in.xml.haml @@ -0,0 +1,3 @@ +!!! XML += render @phone_xml_object[:name] + diff --git a/app/views/config_snom/show.xml.haml b/app/views/config_snom/show.xml.haml new file mode 100644 index 0000000..d9953c5 --- /dev/null +++ b/app/views/config_snom/show.xml.haml @@ -0,0 +1,151 @@ +!!! XML +%settings + %phone-settings + %auto_reboot_on_setting_change{:perm => 'RW'}= 'off' + - if !@phone_settings[:setting_server].blank? + %setting_server{:perm => 'RW'}= @phone_settings[:setting_server] + %web_language{:perm => 'RW'}= 'English' + %language{:perm => 'RW'}= @phone_settings[:language] + %timezone{:perm => 'RW'}= 'GER+1' + %date_us_format{:perm => 'RW'}= 'off' + %time_24_format{:perm => 'RW'}= 'on' + %reset_settings{:perm => 'RW'}= '' + %update_policy{:perm => 'RW'}= 'settings_only' + %settings_refresh_timer{:perm => 'RW'}= '0' + %firmware_status{:perm => 'RW'}= '' + %webserver_type{:perm => 'R'}= 'http_https' + %http_scheme{:perm => 'RW'}= 'off' + %http_port{:perm => 'R'}= '80' + %https_port{:perm => 'R'}= '443' + %http_user{:perm => 'R'}= @phone_settings[:http_user] + %http_pass{:perm => 'R'}= @phone_settings[:http_pass] + %admin_mode_password{:perm => 'R'}= @phone_settings[:admin_mode_password] + %tone_scheme{:perm => 'RW'}= @phone_settings[:tone_scheme] + %keytones{:perm => 'RW'}= 'off' + %dtmf_speaker_phone{:perm => 'RW'}= 'off' + %disable_redirection_menu{:perm => 'R'}= 'on' + %retry_after_failed_register{:perm => 'RW'}= '70' + %encode_display_name{:perm => 'R'}= 'on' + %dtmf_payload_type{:perm => 'RW'}= '101' + %ignore_security_warning{:perm => 'R'}= 'on' + %call_completion{:perm => 'RW'}= 'on' + %block_url_dialing{:perm => 'RW'}= 'on' + %redirect_ringing{:perm => 'RW'}= 'on' + %goto_virtual_keys_state_on_activity{:perm => 'RW'}= 'off' + %goto_monitor_state_on_line_activity{:perm => 'RW'}= 'on' + %ringer_animation{:perm => 'RW'}= 'on' + %display_method{:perm => 'RW'}= 'display_name_number' + %callpickup_dialoginfo{:perm => 'RW'}= 'on' + %show_local_line{:perm => 'RW'}= 'off' + %mwi_notification{:perm => 'RW'}= 'silent' + %mwi_dialtone{:perm => 'RW'}= 'normal' + %prefer_saved_over_received_photo{:perm => 'RW'}= 'off' + %no_dnd{:perm => 'RW'}= 'on' + %logon_wizard{:perm => 'RW'}= 'off' + %disable_deflection{:perm => 'RW'}= 'off' + %csta_control{:perm => 'RW'}= 'on' + %save_latest_callrecords_to_flash{:perm => 'RW'}= 'off' + %use_proxy_number_guessing{:perm => 'RW'}= 'off' + %guess_number{:perm => 'RW'}= 'off' + %guess_start_length{:perm => 'RW'}= '3' + %ieee8021x_eap_md5_username{:perm => 'RW'}= PROVISIONING_IEEE8021X_EAP_USERNAME + %ieee8021x_eap_md5_password{:perm => 'RW'}= PROVISIONING_IEEE8021X_EAP_PASSWORD + + - 0.upto(9) do |ringer_idx| + %internal_ringer_text{:idx => ringer_idx, :perm => 'RW'}= "Ringer#{(ringer_idx+1)}" + %internal_ringer_file{:idx => ringer_idx, :perm => 'RW'}= "Ringer#{(ringer_idx+1)}" + + %internal_ringer_text{:idx => 10, :perm => 'RW'}= "Ringer0" + %internal_ringer_file{:idx => 10, :perm => 'RW'}= "Silent" + + %gui_fkey1{:perm => 'R'}= 'none' + %gui_fkey2{:perm => 'R'}= 'none' + %gui_fkey3{:perm => 'R'}= 'none' + %gui_fkey4{:perm => 'R'}= 'none' + + %dkey_menu{:perm => 'RW'}= @dkeys[:menu] + %dkey_retrieve{:perm => 'RW'}= @dkeys[:retrieve] + %dkey_conf{:perm => 'RW'}= @dkeys[:conf] + %dkey_redial{:perm => 'RW'}= @dkeys[:redial] + %dkey_directory{:perm => 'RW'}= @dkeys[:directory] + + %idle_ok_key_action{:perm => 'RW'}= @dkeys[:idle_ok] + %idle_cancel_key_action{:perm => 'RW'}= @dkeys[:idle_cancel] + %idle_up_key_action{:perm => 'RW'}= @dkeys[:idle_up] + %idle_down_key_action{:perm => 'RW'}= @dkeys[:idle_down] + %idle_left_key_action{:perm => 'RW'}= @dkeys[:idle_left] + %idle_right_key_action{:perm => 'RW'}= @dkeys[:idle_right] + + != "\<!-- sip accounts: #{@sip_accounts.count} --\>" + - @sip_accounts.each_with_index do |sip_account, array_index| + - index = array_index + 1 + != "\<!-- sip account #{array_index}: #{sip_account[:idle_text]}, #{sip_account[:active]} --\>" + %user_active{:idx => index, :perm => 'R'}= sip_account[:active] + %user_pname{:idx => index, :perm => 'R'}= sip_account[:pname] + %user_pass{:idx => index, :perm => 'R'}= sip_account[:pass] + %user_host{:idx => index, :perm => 'R'}= sip_account[:host] + %user_outbound{:idx => index, :perm => 'R'}= sip_account[:outbound] + %user_name{:idx => index, :perm => 'R'}= sip_account[:name] + %user_realname{:idx => index, :perm => 'R'}= sip_account[:realname] + %user_idle_text{:idx => index, :perm => 'R'}= sip_account[:idle_text] + %user_mailbox{:idx => index, :perm => 'R'}= sip_account[:mailbox] + %user_expiry{:idx => index, :perm => 'R'}= '' + %user_server_type{:idx => index, :perm => 'R'}= 'default' + %user_send_local_name{:idx => index, :perm => 'RW'}= 'on' + %user_dtmf_info{:idx => index, :perm => 'RW'}= 'off' + %user_dp_exp{:idx => index, :perm => 'RW'}= '' + %user_dp_str{:idx => index, :perm => 'RW'}= '' + %user_dp{:idx => index, :perm => 'RW'}= '' + %user_q{:idx => index, :perm => 'RW'}= '1.0' + %user_failover_identity{:idx => index, :perm => 'RW'}= 'none' + %user_full_sdp_answer{:idx => index, :perm => 'RW'}= 'on' + %user_dynamic_payload{:idx => index, :perm => 'RW'}= 'on' + %user_g726_packing_order{:idx => index, :perm => 'R'}= 'on' + %user_srtp{:idx => index, :perm => 'RW'}= 'off' + %user_savp{:idx => index, :perm => 'RW'}= 'off' + %codec_size{:idx => index, :perm => 'RW'}= '20' + %codec1_name{:idx => index, :perm => 'RW'}= "0" + %codec2_name{:idx => index, :perm => 'RW'}= "8" + %codec3_name{:idx => index, :perm => 'RW'}= "3" + %codec4_name{:idx => index, :perm => 'RW'}= "9" + %codec5_name{:idx => index, :perm => 'RW'}= "2" + %codec6_name{:idx => index, :perm => 'RW'}= "18" + %codec7_name{:idx => index, :perm => 'RW'}= "4" + %record_missed_calls{:idx => index, :perm => 'RW'}= 'on' + %record_received_calls{:idx => index, :perm => 'RW'}= 'off' + %record_missed_calls_cwi_off{:idx => index, :perm => 'RW'}= 'off' + %record_dialed_calls{:idx => index, :perm => 'RW'}= 'off' + + / all sip accounts done + + %functionKeys + - @softkeys.each_with_index do |softkey, index| + - if softkey[:data] + %fkey{:idx => index, :context => (softkey[:context] ? softkey[:context].to_s : 'active'), :label => softkey[:label], :perm => 'RW'}= softkey[:data] + - elsif softkey[:general_type] + %fkey{:idx => index, :context => (softkey[:context] ? softkey[:context].to_s : 'active'), :label => softkey[:label], :perm => 'RW'} + %general{:type => softkey[:general_type]} + %default_state{:name => 'initial'} + %appearance + %line_info_layer + %line_format{:line => '0'}= '$state $type' + %line_format{:line => '1'}= '$continue $name' + - if softkey[:subscription] + %subscription{:type => 'dialog', :to => softkey[:subscription][:to], :for => softkey[:subscription][:for]} + %NotifyParsingRules{:type => 'applies'} + %level1{:translates_to => 'OK'}= "/dialog-info[@entity=\"sip:#{softkey[:subscription][:to]}\"]" + %NotifyParsingRules{:type => 'state'} + %level1{:translates_to => 'available'}= '/dialog-info/dialog/state[.="terminated"]' + %level2{:translates_to => 'ringing'}= '/dialog-info/dialog/state[.="early"]' + %level3{:translates_to => 'active'}= '/dialog-info/dialog/state[.="confirmed"]' + %level4{:fetch_content => 'true'}= '/dialog-info/dialog/state' + %default{:translates_to => 'unknown'} + - if softkey[:actions] + %action + - softkey[:actions].each do |action| + - if action[:type] == :url + %url{:target => action[:target], :when => action[:when]} + + %uploads + - if @state_settings_url + %file{:url => @state_settings_url, :type => "gui_xml_state_settings"} diff --git a/app/views/config_snom/state_settings.xml.haml b/app/views/config_snom/state_settings.xml.haml new file mode 100644 index 0000000..ac0e872 --- /dev/null +++ b/app/views/config_snom/state_settings.xml.haml @@ -0,0 +1,49 @@ +!!! XML +%SnomIPPhoneMenu{:state => 'relevant', :title => "Gemeinschaft #{GEMEINSCHAFT_VERSION}"} + %MenuItem{:name => '$(lang:menu100_phone_book)'} + %URL= "#{@base_url}/#{@sip_account_ids.first}/phone_book.xml" + %Menu{:name => '$(lang:menu100_call_lists)'} + %MenuItem{:name => '$(lang:list_missed)'} + - @sip_account_ids.each_with_index do |id, index| + %If{:condition => "$(current_line)==#{index+1}"} + %URL= "#{@base_url}/#{id}/call_history_missed.xml" + %MenuItem{:name => '$(lang:list_taken)'} + - @sip_account_ids.each_with_index do |id, index| + %If{:condition => "$(current_line)==#{index+1}"} + %URL= "#{@base_url}/#{id}/call_history_received.xml" + %MenuItem{:name => '$(lang:list_dialed)'} + - @sip_account_ids.each_with_index do |id, index| + %If{:condition => "$(current_line)==#{index+1}"} + %URL= "#{@base_url}/#{id}/call_history_dialed.xml" + %MenuItem{:name => '$(lang:sel100_activeline)'} + %Action= 'active_line' + + - if @enable_login + %MenuItem{:name => 'Log in'} + %URL= "#{@base_url}/log_in.xml" + - if @enable_logout + %MenuItem{:name => 'Log out'} + %URL= "#{@base_url}/log_out.xml" + + %Menu{:name => '$(lang:preferences_settings)'} + %MenuItem{:name => '$(lang:menu_gen_contrast)'} + %Action= 'contrast' + %MenuItem{:name => '$(lang:use_backlight)'} + %Action= 'use_backlight' + %MenuItem{:name => '$(lang:use_backlight) $(lang:backlight_when_active)'} + %Action= 'backlight_active' + %MenuItem{:name => '$(lang:use_backlight) $(lang:backlight_when_idle)'} + %Action= 'backlight_idle' + %MenuItem{:name => '$(lang:menu_equalizer)'} + %Action= 'equalizer' + %Menu{:name => '$(lang:maintenance_settings)'} + %MenuItem{:name => '$(lang:system_information_menu)'} + %Action= 'sysinfo' + %MenuItem{:name => '$(lang:sel100_reboot)'} + %Action= 'reboot' + %If{:condition => '$(set:admin_mode)'} + %MenuItem{:name => '$(lang:reset_settings)'} + %Action= 'reset_settings' + %If{:condition => '$(update_available)'} + %MenuItem{:name => '$(lang:update_header)'} + %Action= 'software_update'
\ No newline at end of file diff --git a/app/views/config_snom/switch_protocol.xml.builder b/app/views/config_snom/switch_protocol.xml.builder new file mode 100644 index 0000000..cd71486 --- /dev/null +++ b/app/views/config_snom/switch_protocol.xml.builder @@ -0,0 +1,18 @@ +xml.instruct! # <?xml version="1.0" encoding="UTF-8"?> + +xml.settings { + xml.tag!( 'phone-settings' ) { + xml.auto_reboot_on_setting_change( 'on', :perm => 'RW' ) + xml.settings_refresh_timer( '60', :perm => 'RW' ) + xml.reset_settings( 'main net stack user fkey speeddial phonebook', :perm => 'RW' ) + #xml.dhcp( 'off', :perm => 'RW' ) + #xml.ip_adr( @ip_address, :perm => 'RW' ) + xml.setting_server( @prov_url, :perm => 'RW' ) + } +} + + +# Local Variables: +# mode: ruby +# End: + diff --git a/app/views/fax_accounts/_form.html.haml b/app/views/fax_accounts/_form.html.haml new file mode 100644 index 0000000..0a5a4c0 --- /dev/null +++ b/app/views/fax_accounts/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([ @parent, @fax_account ]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('fax_accounts.form.submit')
\ No newline at end of file diff --git a/app/views/fax_accounts/_form_core.html.haml b/app/views/fax_accounts/_form_core.html.haml new file mode 100644 index 0000000..8153e5f --- /dev/null +++ b/app/views/fax_accounts/_form_core.html.haml @@ -0,0 +1,11 @@ +.inputs + = f.input :name, :label => t('fax_accounts.form.name.label'), :hint => conditional_hint('fax_accounts.form.name.hint') + = f.input :station_id, :label => t('fax_accounts.form.station_id.label'), :hint => conditional_hint('fax_accounts.form.station_id.hint') + = f.input :retries, :label => t('fax_accounts.form.retries.label'), :hint => conditional_hint('fax_accounts.form.retries.hint') + = f.input :email, :label => t('fax_accounts.form.email.label'), :hint => conditional_hint('fax_accounts.form.email.hint') + + = f.input :days_till_auto_delete, :label => t('fax_accounts.form.days_till_auto_delete.label'), :hint => conditional_hint('fax_accounts.form.days_till_auto_delete.hint') + + %h2= t('phone_numbers.name') + = f.simple_fields_for :phone_numbers, @fax_account.phone_numbers do |phone_number| + = render "phone_numbers/form_core", :f => phone_number diff --git a/app/views/fax_accounts/_index_core.html.haml b/app/views/fax_accounts/_index_core.html.haml new file mode 100644 index 0000000..50dc2eb --- /dev/null +++ b/app/views/fax_accounts/_index_core.html.haml @@ -0,0 +1,35 @@ +%table + %tr + %th= t('fax_accounts.index.name') + %th + = t('fax_accounts.index.phone_numbers') + %br + = t('fax_accounts.index.station_id') + %th + = t('fax_accounts.index.received') + = '/' + = t('fax_accounts.index.sent') + %br + %small + = t('fax_accounts.index.last_update') + + - reset_cycle + - for fax_account in fax_accounts + %tr{:class => cycle('odd', 'even')} + %td= truncate(fax_account.name, :length => 15) + %td + =render 'phone_numbers/listing', :phone_numbers => fax_account.phone_numbers.order(:number) + %br + = truncate(fax_account.station_id, :length => 20) + %td + = link_to fax_account.fax_documents.inbound.count, fax_account_fax_documents_path(fax_account, :anchor => "fax_document_#{fax_account.fax_documents.inbound.first.try(:id)}") + = '/' + = link_to fax_account.fax_documents.outbound.count, fax_account_fax_documents_path(fax_account, :anchor => "fax_document_#{fax_account.fax_documents.outbound.first.try(:id)}") + - if fax_account.fax_documents.count > 0 + %br + %small + = time_ago_in_words(fax_account.fax_documents.order(:updated_at).last.updated_at) + %td + - if can?(:new, FaxDocument, :fax_account_id => fax_account.id) + = link_to t('fax_accounts.index.send_a_fax'), new_fax_account_fax_document_path(fax_account) + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => fax_account.fax_accountable, :child => fax_account}
\ No newline at end of file diff --git a/app/views/fax_accounts/edit.html.haml b/app/views/fax_accounts/edit.html.haml new file mode 100644 index 0000000..86f664d --- /dev/null +++ b/app/views/fax_accounts/edit.html.haml @@ -0,0 +1,3 @@ +- title t("fax_accounts.edit.page_title") + += render "form" diff --git a/app/views/fax_accounts/index.html.haml b/app/views/fax_accounts/index.html.haml new file mode 100644 index 0000000..309a10d --- /dev/null +++ b/app/views/fax_accounts/index.html.haml @@ -0,0 +1,6 @@ +- title t("fax_accounts.index.page_title") + +- if @fax_accounts.count > 0 + = render "index_core", {:fax_accounts => @fax_accounts, :fax_accountable => @parent} + += render :partial => 'shared/create_link', :locals => {:parent => @parent, :child_class => FaxAccount}
\ No newline at end of file diff --git a/app/views/fax_accounts/new.html.haml b/app/views/fax_accounts/new.html.haml new file mode 100644 index 0000000..9a67100 --- /dev/null +++ b/app/views/fax_accounts/new.html.haml @@ -0,0 +1,3 @@ +- title t("fax_accounts.new.page_title") + += render "form" diff --git a/app/views/fax_accounts/show.html.haml b/app/views/fax_accounts/show.html.haml new file mode 100644 index 0000000..95fb7b2 --- /dev/null +++ b/app/views/fax_accounts/show.html.haml @@ -0,0 +1,21 @@ +- title t("fax_accounts.show.page_title") + +%p + %strong= t('fax_accounts.show.name') + ":" + = @fax_account.name +%p + %strong= t('fax_accounts.show.email') + ":" + = @fax_account.email +%p + %strong= t('fax_accounts.show.days_till_auto_delete') + ":" + = @fax_account.days_till_auto_delete + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @parent, :child => @fax_account } + += render :partial => 'shared/create_link', :locals => { :parent => @fax_account, :child_class => FaxDocument } + +%h2= t('phone_numbers.index.page_title') +- if @fax_account.phone_numbers.count > 0 + = render "phone_numbers/index_core", :phone_numbers => @fax_account.phone_numbers + %br += render :partial => 'shared/create_link', :locals => { :parent => @fax_account, :child_class => PhoneNumber }
\ No newline at end of file diff --git a/app/views/fax_documents/_form.html.haml b/app/views/fax_documents/_form.html.haml new file mode 100644 index 0000000..e240371 --- /dev/null +++ b/app/views/fax_documents/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([@fax_account,@fax_document]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('fax_documents.form.submit')
\ No newline at end of file diff --git a/app/views/fax_documents/_form_core.html.haml b/app/views/fax_documents/_form_core.html.haml new file mode 100644 index 0000000..2a53cd5 --- /dev/null +++ b/app/views/fax_documents/_form_core.html.haml @@ -0,0 +1,7 @@ +.inputs + = f.input :document, :label => t('fax_documents.form.document.label'), :hint => conditional_hint('fax_documents.form.document.hint') + = f.association :fax_resolution, :label => t('fax_documents.form.fax_resolution.label'), :hint => conditional_hint('fax_documents.form.fax_resolution.hint'), :include_blank => false + + = f.simple_fields_for :destination_phone_number, @phone_number do |u| + = render "phone_numbers/form_core", :f => u + diff --git a/app/views/fax_documents/_index_core.html.haml b/app/views/fax_documents/_index_core.html.haml new file mode 100644 index 0000000..4e15509 --- /dev/null +++ b/app/views/fax_documents/_index_core.html.haml @@ -0,0 +1,33 @@ +%table + %tr + %th= t('fax_documents.index.sent_at') + %th= t('fax_documents.index.state') + %th= t('fax_documents.index.result') + %th + = t('fax_documents.index.phone_number') + %br + = t('fax_documents.index.remote_station_id') + %th= t('fax_documents.index.thumbnails') + + - reset_cycle + - for fax_document in fax_documents + %tr{:class => cycle('odd', 'even'), :id => "fax_document_#{fax_document.id}"} + - if fax_document.sent_at + %td= "#{fax_document.inbound ? '⇨' : '⇦'} #{fax_document.sent_at}".html_safe + %td= t("fax_documents.states.#{fax_document.state}") + %td= t("fax_documents.result_codes.code_#{fax_document.result_code}") + " (#{fax_document.result_code})" + - else + %td{ :colspan => 3 }= t("fax_documents.states.#{fax_document.state}") + %td + - if fax_document.inbound + = "#{fax_document.caller_id_number} #{fax_document.caller_id_name}" + - else + = fax_document.destination_phone_number + %br + = fax_document.remote_station_id + %td + - fax_document.fax_thumbnails.limit(5).each do |fax_thumbnail| + =image_tag fax_thumbnail.thumbnail_url(:mini), :class => 'FaxThumbnail', :alt => "Thumbnail of page \##{fax_thumbnail.position}" + - if can?(:show, fax_document) && !fax_document.document.blank? && File.readable?(fax_document.document.path) + = link_to t('fax_documents.index.actions.download'), fax_account_fax_document_path(@fax_account, fax_document, :format => :pdf), :method => :get + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => fax_document.fax_account, :child => fax_document} diff --git a/app/views/fax_documents/edit.html.haml b/app/views/fax_documents/edit.html.haml new file mode 100644 index 0000000..5da92c6 --- /dev/null +++ b/app/views/fax_documents/edit.html.haml @@ -0,0 +1,9 @@ +- title t("fax_documents.edit.page_title") + += render "form" + +%p + - if can? :edit, @fax_document + = link_to t('fax_documents.edit.actions.edit'), @fax_document + | + = link_to t('fax_documents.edit.actions.view_all'), fax_documents_path diff --git a/app/views/fax_documents/index.html.haml b/app/views/fax_documents/index.html.haml new file mode 100644 index 0000000..11199dd --- /dev/null +++ b/app/views/fax_documents/index.html.haml @@ -0,0 +1,5 @@ +- title t("fax_documents.index.page_title") + += render "index_core", :fax_documents => @fax_documents + += render :partial => 'shared/create_link', :locals => {:parent => @fax_account, :child_class => FaxDocument}
\ No newline at end of file diff --git a/app/views/fax_documents/new.html.haml b/app/views/fax_documents/new.html.haml new file mode 100644 index 0000000..be02860 --- /dev/null +++ b/app/views/fax_documents/new.html.haml @@ -0,0 +1,3 @@ +- title t("fax_documents.new.page_title") + += render "form" diff --git a/app/views/fax_documents/show.html.haml b/app/views/fax_documents/show.html.haml new file mode 100644 index 0000000..4703e1d --- /dev/null +++ b/app/views/fax_documents/show.html.haml @@ -0,0 +1,36 @@ +- title t("fax_documents.show.page_title") +- child = @fax_document +- parent = @fax_document.fax_account + +%p + %strong= t('fax_documents.index.state') + ":" + = t("fax_documents.states.#{@fax_document.state}") + +%p + %strong= t('fax_documents.index.result_code') + ":" + = @fax_document.result_code + +%p + %strong= t('fax_documents.index.result_text') + ":" + = t("fax_documents.result_codes.code_#{@fax_document.result_code}") + +%p + %strong= t('fax_documents.show.document_transferred_pages') + ":" + = @fax_document.document_transferred_pages +%p + %strong= t('fax_documents.show.remote_station_id') + ":" + = @fax_document.remote_station_id +%p + %strong= t('fax_documents.show.fax_resolution') + ":" + = @fax_document.fax_resolution + +- if @fax_document.fax_thumbnails.count > 0 + - i = @fax_document.fax_thumbnails.count + - i = 10 if i > 10 + - @fax_document.fax_thumbnails.limit(i).each do |fax_thumbnail| + =image_tag fax_thumbnail.thumbnail_url(:medium), :class => 'FaxThumbnail', :alt => "Thumbnail of page \##{fax_thumbnail.position}" + +- if @fax_document.document.path + = link_to t("fax_documents.index.actions.download_pdf"), "#{request.protocol}#{request.host_with_port}#{request.fullpath.split("?")[0]}.pdf" + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @fax_document.fax_account, :child => @fax_document } diff --git a/app/views/freeswitch_voicemail_msgs/_index_core.html.haml b/app/views/freeswitch_voicemail_msgs/_index_core.html.haml new file mode 100644 index 0000000..58d9944 --- /dev/null +++ b/app/views/freeswitch_voicemail_msgs/_index_core.html.haml @@ -0,0 +1,12 @@ +%table + %tr + %th= t('freeswitch_voicemail_msgs.index.created_epoch') + %th= t('freeswitch_voicemail_msgs.index.message_len') + %th= t('freeswitch_voicemail_msgs.index.file_path') + + - reset_cycle + - for freeswitch_voicemail_msg in freeswitch_voicemail_msgs + %tr{:class => cycle('odd', 'even')} + %td= freeswitch_voicemail_msg.created_epoch + %td= freeswitch_voicemail_msg.message_len + %td= freeswitch_voicemail_msg.file_path
\ No newline at end of file diff --git a/app/views/freeswitch_voicemail_msgs/index.html.haml b/app/views/freeswitch_voicemail_msgs/index.html.haml new file mode 100644 index 0000000..5083c6f --- /dev/null +++ b/app/views/freeswitch_voicemail_msgs/index.html.haml @@ -0,0 +1,3 @@ +- title t("freeswitch_voicemail_msgs.index.page_title") + += render "index_core", :freeswitch_voicemail_msgs => @freeswitch_voicemail_msgs
\ No newline at end of file diff --git a/app/views/gemeinschaft_setups/new.de.html.haml b/app/views/gemeinschaft_setups/new.de.html.haml new file mode 100644 index 0000000..5e79115 --- /dev/null +++ b/app/views/gemeinschaft_setups/new.de.html.haml @@ -0,0 +1,25 @@ +- title "Konfiguration einer Gemeinschaft #{GEMEINSCHAFT_VERSION} Installation" + += simple_form_for(@gemeinschaft_setup) do |f| + = f.error_notification + + %h2 Admin-Konto + %p + Dieser erste Benutzer des Systems hat automatisch Admin-Rechte. + + = f.simple_fields_for :user, @user do |u| + = render "users/form_core", :f => u + + %h2 SIP-Domain + %p In den meisten Fällen sollten Sie den gleichen Wert für SIP-Realm und SIP-Domain benutzen. Wenn Sie mit diesen Begriffen nichts anfangen können, dann geben Sie hier bitte die IP-Adresse dieses Servers ein. + + = f.simple_fields_for :sip_domain, @sip_domain do |s| + = render "sip_domains/form_core", :f => s + + %h2 Allgemeine Informationen + + = f.association :country, :label => t('gemeinschaft_setups.form.country_id.label'), :hint => conditional_hint('gemeinschaft_setups.form.country_id.hint'), :include_blank => false + = f.association :language, :label => t('gemeinschaft_setups.form.language_id.label'), :hint => conditional_hint('gemeinschaft_setups.form.language_id.hint'), :include_blank => false + + .actions + = f.button :submit, conditional_t('gemeinschaft_setups.form.submit')
\ No newline at end of file diff --git a/app/views/gemeinschaft_setups/new.html.haml b/app/views/gemeinschaft_setups/new.html.haml new file mode 100644 index 0000000..f5f0e81 --- /dev/null +++ b/app/views/gemeinschaft_setups/new.html.haml @@ -0,0 +1,25 @@ +- title "Configure a new Gemeinschaft #{GEMEINSCHAFT_VERSION} server" + += simple_form_for(@gemeinschaft_setup) do |f| + = f.error_notification + + %h3 Admin user account + %p + This is the first user of this system who has admin rights by default. + + = f.simple_fields_for :user, @user do |u| + = render "users/form_core", :f => u + + %h3 SIP domain + %p You should use the same value for the SIP realm as for the SIP domain to ensure compatibility with different phone models. In case you have no clue what we are talking about: Just enter the IP address of this server. + + = f.simple_fields_for :sip_domain, @sip_domain do |s| + = render "sip_domains/form_core", :f => s + + %h3 General information + + = f.association :country, :label => t('gemeinschaft_setups.form.country_id.label'), :hint => conditional_hint('gemeinschaft_setups.form.country_id.hint'), :include_blank => false + = f.association :language, :label => t('gemeinschaft_setups.form.language_id.label'), :hint => conditional_hint('gemeinschaft_setups.form.language_id.hint'), :include_blank => false + + .actions + = f.button :submit, conditional_t('gemeinschaft_setups.form.submit')
\ No newline at end of file diff --git a/app/views/gs_cluster_sync_log_entries/_form.html.haml b/app/views/gs_cluster_sync_log_entries/_form.html.haml new file mode 100644 index 0000000..2c47d88 --- /dev/null +++ b/app/views/gs_cluster_sync_log_entries/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for(@gs_cluster_sync_log_entry) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('gs_cluster_sync_log_entries.form.submit')
\ No newline at end of file diff --git a/app/views/gs_cluster_sync_log_entries/_form_core.html.haml b/app/views/gs_cluster_sync_log_entries/_form_core.html.haml new file mode 100644 index 0000000..50b4630 --- /dev/null +++ b/app/views/gs_cluster_sync_log_entries/_form_core.html.haml @@ -0,0 +1,6 @@ +.inputs + = f.input :gs_node_id, :label => t('gs_cluster_sync_log_entries.form.gs_node_id.label'), :hint => conditional_hint('gs_cluster_sync_log_entries.form.gs_node_id.hint') + = f.input :class_name, :label => t('gs_cluster_sync_log_entries.form.class_name.label'), :hint => conditional_hint('gs_cluster_sync_log_entries.form.class_name.hint') + = f.input :action, :label => t('gs_cluster_sync_log_entries.form.action.label'), :hint => conditional_hint('gs_cluster_sync_log_entries.form.action.hint') + = f.input :content, :label => t('gs_cluster_sync_log_entries.form.content.label'), :hint => conditional_hint('gs_cluster_sync_log_entries.form.content.hint') + = f.input :status, :label => t('gs_cluster_sync_log_entries.form.status.label'), :hint => conditional_hint('gs_cluster_sync_log_entries.form.status.hint') diff --git a/app/views/gs_cluster_sync_log_entries/_index_core.html.haml b/app/views/gs_cluster_sync_log_entries/_index_core.html.haml new file mode 100644 index 0000000..05cbda8 --- /dev/null +++ b/app/views/gs_cluster_sync_log_entries/_index_core.html.haml @@ -0,0 +1,17 @@ +%table + %tr + %th= t('gs_cluster_sync_log_entries.index.gs_node_id') + %th= t('gs_cluster_sync_log_entries.index.class_name') + %th= t('gs_cluster_sync_log_entries.index.action') + %th= t('gs_cluster_sync_log_entries.index.content') + %th= t('gs_cluster_sync_log_entries.index.status') + + - reset_cycle + - for gs_cluster_sync_log_entry in gs_cluster_sync_log_entries + %tr{:class => cycle('odd', 'even')} + %td= gs_cluster_sync_log_entry.gs_node_id + %td= gs_cluster_sync_log_entry.class_name + %td= gs_cluster_sync_log_entry.action + %td= gs_cluster_sync_log_entry.content + %td= gs_cluster_sync_log_entry.status + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:child => gs_cluster_sync_log_entry}
\ No newline at end of file diff --git a/app/views/gs_cluster_sync_log_entries/edit.html.haml b/app/views/gs_cluster_sync_log_entries/edit.html.haml new file mode 100644 index 0000000..b0c65f3 --- /dev/null +++ b/app/views/gs_cluster_sync_log_entries/edit.html.haml @@ -0,0 +1,3 @@ +- title t("gs_cluster_sync_log_entries.edit.page_title") + += render "form"
\ No newline at end of file diff --git a/app/views/gs_cluster_sync_log_entries/index.html.haml b/app/views/gs_cluster_sync_log_entries/index.html.haml new file mode 100644 index 0000000..68be7e0 --- /dev/null +++ b/app/views/gs_cluster_sync_log_entries/index.html.haml @@ -0,0 +1,6 @@ +- title t("gs_cluster_sync_log_entries.index.page_title") + +- if @gs_cluster_sync_log_entries && @gs_cluster_sync_log_entries.count > 0 + = render "index_core", :gs_cluster_sync_log_entries => @gs_cluster_sync_log_entries + += render :partial => 'shared/create_link', :locals => {:child_class => GsClusterSyncLogEntry}
\ No newline at end of file diff --git a/app/views/gs_cluster_sync_log_entries/new.html.haml b/app/views/gs_cluster_sync_log_entries/new.html.haml new file mode 100644 index 0000000..01b795c --- /dev/null +++ b/app/views/gs_cluster_sync_log_entries/new.html.haml @@ -0,0 +1,3 @@ +- title t("gs_cluster_sync_log_entries.new.page_title") + += render "form"
\ No newline at end of file diff --git a/app/views/gs_cluster_sync_log_entries/show.html.haml b/app/views/gs_cluster_sync_log_entries/show.html.haml new file mode 100644 index 0000000..733576d --- /dev/null +++ b/app/views/gs_cluster_sync_log_entries/show.html.haml @@ -0,0 +1,19 @@ +- title t("gs_cluster_sync_log_entries.show.page_title") + +%p + %strong= t('gs_cluster_sync_log_entries.show.gs_node_id') + ":" + = @gs_cluster_sync_log_entry.gs_node_id +%p + %strong= t('gs_cluster_sync_log_entries.show.class_name') + ":" + = @gs_cluster_sync_log_entry.class_name +%p + %strong= t('gs_cluster_sync_log_entries.show.action') + ":" + = @gs_cluster_sync_log_entry.action +%p + %strong= t('gs_cluster_sync_log_entries.show.content') + ":" + = @gs_cluster_sync_log_entry.content +%p + %strong= t('gs_cluster_sync_log_entries.show.status') + ":" + = @gs_cluster_sync_log_entry.status + += render :partial => 'shared/show_edit_destroy_part', :locals => { :child => @gs_cluster_sync_log_entry }
\ No newline at end of file diff --git a/app/views/gs_nodes/_form.html.haml b/app/views/gs_nodes/_form.html.haml new file mode 100644 index 0000000..28a4e5d --- /dev/null +++ b/app/views/gs_nodes/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for(@gs_node) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('gs_nodes.form.submit')
\ No newline at end of file diff --git a/app/views/gs_nodes/_form_core.html.haml b/app/views/gs_nodes/_form_core.html.haml new file mode 100644 index 0000000..2355e52 --- /dev/null +++ b/app/views/gs_nodes/_form_core.html.haml @@ -0,0 +1,7 @@ +.inputs + = f.input :name, :label => t('gs_nodes.form.name.label'), :hint => conditional_hint('gs_nodes.form.name.hint') + = f.input :ip_address, :label => t('gs_nodes.form.ip_address.label'), :hint => conditional_hint('gs_nodes.form.ip_address.hint') + = f.input :site, :label => t('gs_nodes.form.site.label'), :hint => conditional_hint('gs_nodes.form.site.hint') + = f.input :element_name, :label => t('gs_nodes.form.element_name.label'), :hint => conditional_hint('gs_nodes.form.element_name.hint') + = f.input :push_updates_to, :label => t('gs_nodes.form.push_updates_to.label'), :hint => conditional_hint('gs_nodes.form.push_updates_to.hint') + = f.input :accepts_updates_from, :label => t('gs_nodes.form.accepts_updates_from.label'), :hint => conditional_hint('gs_nodes.form.accepts_updates_from.hint') diff --git a/app/views/gs_nodes/_index_core.html.haml b/app/views/gs_nodes/_index_core.html.haml new file mode 100644 index 0000000..72633e1 --- /dev/null +++ b/app/views/gs_nodes/_index_core.html.haml @@ -0,0 +1,19 @@ +%table + %tr + %th= t('gs_nodes.index.name') + %th= t('gs_nodes.index.ip_address') + %th= t('gs_nodes.index.site') + %th= t('gs_nodes.index.element_name') + %th= t('gs_nodes.index.push_updates_to') + %th= t('gs_nodes.index.accepts_updates_from') + + - reset_cycle + - for gs_node in gs_nodes + %tr{:class => cycle('odd', 'even')} + %td= gs_node.name + %td= gs_node.ip_address + %td= gs_node.site + %td= gs_node.element_name + %td= gs_node.push_updates_to + %td= gs_node.accepts_updates_from + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:child => gs_node}
\ No newline at end of file diff --git a/app/views/gs_nodes/edit.html.haml b/app/views/gs_nodes/edit.html.haml new file mode 100644 index 0000000..c025b05 --- /dev/null +++ b/app/views/gs_nodes/edit.html.haml @@ -0,0 +1,3 @@ +- title t("gs_nodes.edit.page_title") + += render "form"
\ No newline at end of file diff --git a/app/views/gs_nodes/index.html.haml b/app/views/gs_nodes/index.html.haml new file mode 100644 index 0000000..4670cef --- /dev/null +++ b/app/views/gs_nodes/index.html.haml @@ -0,0 +1,6 @@ +- title t("gs_nodes.index.page_title") + +- if @gs_nodes && @gs_nodes.count > 0 + = render "index_core", :gs_nodes => @gs_nodes + += render :partial => 'shared/create_link', :locals => {:child_class => GsNode}
\ No newline at end of file diff --git a/app/views/gs_nodes/new.html.haml b/app/views/gs_nodes/new.html.haml new file mode 100644 index 0000000..230ce33 --- /dev/null +++ b/app/views/gs_nodes/new.html.haml @@ -0,0 +1,3 @@ +- title t("gs_nodes.new.page_title") + += render "form"
\ No newline at end of file diff --git a/app/views/gs_nodes/show.html.haml b/app/views/gs_nodes/show.html.haml new file mode 100644 index 0000000..ca70a50 --- /dev/null +++ b/app/views/gs_nodes/show.html.haml @@ -0,0 +1,22 @@ +- title t("gs_nodes.show.page_title") + +%p + %strong= t('gs_nodes.show.name') + ":" + = @gs_node.name +%p + %strong= t('gs_nodes.show.ip_address') + ":" + = @gs_node.ip_address +%p + %strong= t('gs_nodes.show.site') + ":" + = @gs_node.site +%p + %strong= t('gs_nodes.show.element_name') + ":" + = @gs_node.element_name +%p + %strong= t('gs_nodes.show.push_updates_to') + ":" + = @gs_node.push_updates_to +%p + %strong= t('gs_nodes.show.accepts_updates_from') + ":" + = @gs_node.accepts_updates_from + += render :partial => 'shared/show_edit_destroy_part', :locals => { :child => @gs_node }
\ No newline at end of file diff --git a/app/views/gs_nodes/sync.xml.haml b/app/views/gs_nodes/sync.xml.haml new file mode 100644 index 0000000..0f097f8 --- /dev/null +++ b/app/views/gs_nodes/sync.xml.haml @@ -0,0 +1,88 @@ +!!! XML +%gemeinschaft_sync{ :date => Time.now, :newer_as => @newer_as, :class => @request_class } + %node{ :id => @node.id, :ip_address => @node.ip_address, :name => @node.name } + + - if !@tenants.blank? + %tenants + - @tenants.each do |tenant| + %tenant{ :name => tenant.name } + + - if !@user_groups.blank? + %user_groups + - @user_groups.each do |user_group| + %user_group{ :name => user_group.name, :description => user_group.description, :position => user_group.position, :tenant => user_group.tenant.try(:name), :created_at => user_group.created_at, :updated_at => user_group.updated_at } + + - if !@users.blank? + %users + - @users.each do |user| + %user{ :user_name => user.user_name, :first_name => user.first_name, :middle_name => user.middle_name, :last_name => user.last_name, :password_digest => user.password_digest, :pin_salt => user.pin_salt, :pin_hash => user.pin_hash, :male => user.male.to_s, :send_voicemail_as_email_attachment => user.send_voicemail_as_email_attachment.to_s, :uuid => user.uuid, :language => user.language.try(:code), :importer_checksum => user.importer_checksum, :email => user.email, :created_at => user.created_at, :updated_at => user.updated_at, :current_tenant => user.current_tenant.try(:name), :gs_node => user.gs_node.try(:name) } + + - if !@user_group_memberships.blank? + %user_group_memberships + - @user_group_memberships.each do |membership| + %user_group_membership{ :user_group => membership.user_group.try(:name), :user_uuid => membership.user.try(:uuid), :state => membership.state, :created_at => membership.created_at, :updated_at => membership.updated_at } + + - if !@sip_accounts.blank? + %sip_accounts + - @sip_accounts.each do |sip_account| + %sip_account{ :sip_accountable_type => sip_account.sip_accountable_type, :sip_accountable_uuid => sip_account.sip_accountable.try(:uuid), :uuid => sip_account.uuid, :auth_name => sip_account.auth_name, :caller_name => sip_account.caller_name, :password => sip_account.password, :voicemail_pin => sip_account.voicemail_pin, :created_at => sip_account.created_at, :updated_at => sip_account.updated_at, :tenant => sip_account.tenant.try(:name), :sip_domain => sip_account.sip_domain.try(:host), :call_waiting => sip_account.call_waiting.to_s, :clir => sip_account.clir.to_s, :clip_no_screening => sip_account.clip_no_screening, :clip => sip_account.clip.to_s, :description => sip_account.description, :callforward_rules_act_per_sip_account => sip_account.callforward_rules_act_per_sip_account.to_s, :hotdeskable => sip_account.hotdeskable.to_s, :gs_node => sip_account.gs_node.try(:name) } + + - if !@conferences.blank? + %conferences + - @conferences.each do |conference| + %conference{ :uuid => conference.uuid, :name => conference.name, :start => conference.start, :end => conference.end, :conferenceable_type => conference.conferenceable_type, :conferenceable_uuid => conference.conferenceable.try(:uuid), :description => conference.description, :pin => conference.pin, :state => conference.state, :open_for_anybody => conference.open_for_anybody.to_s, :max_members => conference.max_members, :announce_new_member_by_name => conference.announce_new_member_by_name.to_s, :announce_left_member_by_name => conference.announce_left_member_by_name.to_s, :created_at => conference.created_at, :updated_at => conference.updated_at } + + - if !@fax_accounts.blank? + %fax_accounts + - @fax_accounts.each do |fax_account| + %fax_account{ :uuid => fax_account.uuid, :name => fax_account.name, :email => fax_account.email, :tenant => fax_account.tenant.try(:name), :fax_accountable_type => fax_account.fax_accountable_type, :fax_accountable_uuid => fax_account.fax_accountable.try(:uuid), :station_id => fax_account.station_id, :days_till_auto_delete => fax_account.days_till_auto_delete, :retries => fax_account.retries, :created_at => fax_account.created_at, :updated_at => fax_account.updated_at } + + - if !@phone_books.blank? + %phone_books + - @phone_books.each do |phone_book| + %phone_book{ :uuid => phone_book.uuid, :name => phone_book.name, :description => phone_book.description, :state => phone_book.state, :phone_bookable_type => phone_book.phone_bookable_type, :phone_bookable_uuid => phone_book.phone_bookable.try(:uuid), :created_at => phone_book.created_at, :updated_at => phone_book.updated_at } + + - if !@phone_book_entries.blank? + %phone_book_entries + - @phone_book_entries.each do |phone_book_entry| + %phone_book_entry{ :uuid => phone_book_entry.uuid, :phone_book_uuid => phone_book_entry.phone_book.try(:uuid), :created_at => phone_book_entry.created_at, :updated_at => phone_book_entry.updated_at, :first_name => phone_book_entry.first_name, :middle_name => phone_book_entry.middle_name, :last_name => phone_book_entry.last_name, :title => phone_book_entry.title, :nickname => phone_book_entry.nickname, :organization => phone_book_entry.organization, :is_organization => phone_book_entry.is_organization.to_s, :department => phone_book_entry.department, :job_title => phone_book_entry.job_title, :is_male => phone_book_entry.is_male.to_s, :birthday => phone_book_entry.birthday, :birth_name => phone_book_entry.birth_name, :state => phone_book_entry.state, :description => phone_book_entry.description, :position => phone_book_entry.position, :homepage_personal => phone_book_entry.homepage_personal, :homepage_organization => phone_book_entry.homepage_organization, :twitter_account => phone_book_entry.twitter_account, :facebook_account => phone_book_entry.facebook_account, :google_plus_account => phone_book_entry.google_plus_account, :xing_account => phone_book_entry.xing_account, :linkedin_account => phone_book_entry.linkedin_account, :mobileme_account => phone_book_entry.mobileme_account, :created_at => phone_book_entry.created_at, :updated_at => phone_book_entry.updated_at, :first_name_phonetic => phone_book_entry.first_name_phonetic, :last_name_phonetic => phone_book_entry.last_name_phonetic, :organization_phonetic => phone_book_entry.organization_phonetic } + + - if !@phone_numbers.blank? + %phone_numbers + - @phone_numbers.each do |phone_number| + %phone_number{ :uuid => phone_number.uuid, :name => phone_number.name, :number => phone_number.number, :phone_numberable_type => phone_number.phone_numberable_type, :phone_numberable_uuid => phone_number.phone_numberable.try(:uuid), :position => phone_number.position, :gs_node => phone_number.gs_node.try(:name), :created_at => phone_number.created_at, :updated_at => phone_number.updated_at } + + - if !@call_forwards.blank? + %call_forwards + - @call_forwards.each do |call_forward| + %call_forward{ :uuid => call_forward.uuid, :service => call_forward.call_forward_case.try(:value), :timeout => call_forward.timeout, :destination => call_forward.destination, :source => call_forward.source, :active => call_forward.active.to_s, :created_at => call_forward.created_at, :updated_at => call_forward.updated_at, :phone_number_uuid => call_forward.phone_number.try(:uuid), :depth => call_forward.depth, :call_forwardable_type => call_forward.call_forwardable_type, :call_forwardable_uuid => call_forward.call_forwardable.try(:uuid), :position => call_forward.position} + + - if !@softkeys.blank? + %softkeys + - @softkeys.each do |softkey| + %softkey{ :uuid => softkey.uuid, :function => softkey.softkey_function.try(:name), :number => softkey.number, :label => softkey.label, :position => softkey.position, :created_at => softkey.created_at, :updated_at => softkey.updated_at, :sip_account_uuid => softkey.sip_account.try(:uuid), :call_forward_uuid => softkey.call_forward.try(:uuid) } + + - if !@ringtones.blank? + %ringtones + - @ringtones.each do |ringtone| + %ringtone{ :bellcore_id => ringtone.bellcore_id, :ringtoneable_type => ringtone.ringtoneable_type, :ringtoneable_uuid => ringtone.ringtoneable.try(:uuid), :created_at => ringtone.created_at, :updated_at => ringtone.updated_at } + + - if !@conference_invitees.blank? + %conference_invitees + - @conference_invitees.each do |conference_invitee| + %conference_invitee{ :uuid => conference_invitee.uuid, :conference_uuid => conference_invitee.conference.try(:uuid), :phone_number_uuid => conference_invitee.phone_number.try(:uuid), :pin => conference_invitee.pin, :speaker => conference_invitee.speaker.to_s, :moderator => conference_invitee.moderator.to_s, :phone_book_entry_uuid => conference_invitee.phone_book_entry.try(:uuid), :created_at => conference_invitee.created_at, :updated_at => conference_invitee.updated_at } + + - if !@fax_documents.blank? + %fax_documents + - @fax_documents.each do |fax_document| + %fax_document{ :inbound => fax_document.inbound.to_s, :state => fax_document.state, :transmission_time => fax_document.transmission_time, :sent_at => fax_document.sent_at, :document_total_pages => fax_document.document_total_pages, :document_transferred_pages => fax_document.document_transferred_pages, :ecm_requested => fax_document.ecm_requested.to_s, :ecm_used => fax_document.ecm_used.to_s, :image_resolution => fax_document.image_resolution, :image_size => fax_document.image_size, :local_station_id => fax_document.local_station_id, :result_code => fax_document.result_code, :remote_station_id => fax_document.remote_station_id, :success => fax_document.success.to_s, :transfer_rate => fax_document.transfer_rate, :document => fax_document.document, :created_at => fax_document.created_at, :updated_at => fax_document.updated_at, :fax_account_uuid => fax_document.fax_account.try(:uuid), :caller_id_number => fax_document.caller_id_number, :caller_id_name => fax_document.caller_id_name, :retry_counter => fax_document.retry_counter, :tiff => fax_document.tiff, :fax_resolution => fax_document.fax_resolution.try(:resolution_value), :document => fax_document.document.to_s,:uuid => fax_document.uuid } + + - if !@call_histories.blank? + %call_histories + - @call_histories.each do |call_history| + %call_history{ :call_historyable_type => call_history.call_historyable_type, :call_historyable_uuid => call_history.call_historyable_uuid, :entry_type => call_history.entry_type, :caller_account_type => call_history.caller_account_type, :caller_account_uuid => call_history.caller_account_uuid, :caller_id_number => call_history.caller_id_number, :caller_id_name => call_history.caller_id_name, :caller_channel_uuid => call_history.caller_channel_uuid, :callee_account_type => call_history.callee_account_type, :callee_account_uuid => call_history.callee_account_uuid, :callee_id_number => call_history.callee_id_number, :callee_id_name => call_history.callee_id_name, :auth_account_type => call_history.auth_account_type, :auth_account_uuid => call_history.auth_account_uuid, :forwarding_service => call_history.forwarding_service, :destination_number => call_history.destination_number, :start_stamp => call_history.start_stamp, :duration => call_history.duration, :result => call_history.result, :read_flag => call_history.read_flag.to_s, :returned_flag => call_history.returned_flag.to_s, :created_at => call_history.created_at, :updated_at => call_history.updated_at } + + - if !@deleted_items.blank? + %deleted_items + - @deleted_items.each do |deleted_item| + %deleted_item{ :class_name => deleted_item['class_name'], :uuid => deleted_item['uuid'] } diff --git a/app/views/gui_functions/_form.html.haml b/app/views/gui_functions/_form.html.haml new file mode 100644 index 0000000..0b2a201 --- /dev/null +++ b/app/views/gui_functions/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for(@gui_function) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('gui_functions.form.submit')
\ No newline at end of file diff --git a/app/views/gui_functions/_form_core.html.haml b/app/views/gui_functions/_form_core.html.haml new file mode 100644 index 0000000..c2d6cd7 --- /dev/null +++ b/app/views/gui_functions/_form_core.html.haml @@ -0,0 +1,10 @@ +.inputs + = f.input :category, :label => t('gui_functions.form.category.label'), :hint => conditional_hint('gui_functions.form.category.hint') + = f.input :name, :label => t('gui_functions.form.name.label'), :hint => conditional_hint('gui_functions.form.name.hint') + = f.input :description, :label => t('gui_functions.form.description.label'), :hint => conditional_hint('gui_functions.form.description.hint') + + - counter = 0 + = f.fields_for :gui_function_memberships do |gui_function_membership| + = gui_function_membership.hidden_field :user_group_id + = gui_function_membership.input :activated, :label => @user_groups[counter], :hint => conditional_hint('gui_functions.form.activated.hint') + - counter = counter + 1
\ No newline at end of file diff --git a/app/views/gui_functions/_index_core.html.haml b/app/views/gui_functions/_index_core.html.haml new file mode 100644 index 0000000..093a0d7 --- /dev/null +++ b/app/views/gui_functions/_index_core.html.haml @@ -0,0 +1,26 @@ +%table + %tr + %th= t('gui_functions.index.category') + %th= t('gui_functions.index.name') + - @user_groups.each do |user_group| + %th= user_group + + - reset_cycle + - for gui_function in gui_functions + %tr{:class => cycle('odd', 'even')} + %td= gui_function.category + %td + = gui_function.name + - if !gui_function.description.blank? + %br + %i= gui_function.description + - @user_groups.each do |user_group| + - if gui_function.gui_function_memberships.find_by_user_group_id(user_group.id) + - if gui_function.gui_function_memberships.find_by_user_group_id(user_group.id).activated == true + %td= 'x' + - else + %td= '' + - else + %td= 'x' + + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:child => gui_function}
\ No newline at end of file diff --git a/app/views/gui_functions/edit.html.haml b/app/views/gui_functions/edit.html.haml new file mode 100644 index 0000000..f43b5bc --- /dev/null +++ b/app/views/gui_functions/edit.html.haml @@ -0,0 +1,3 @@ +- title t("gui_functions.edit.page_title") + += render "form"
\ No newline at end of file diff --git a/app/views/gui_functions/index.html.haml b/app/views/gui_functions/index.html.haml new file mode 100644 index 0000000..ef909f0 --- /dev/null +++ b/app/views/gui_functions/index.html.haml @@ -0,0 +1,6 @@ +- title t("gui_functions.index.page_title") + +- if @gui_functions && @gui_functions.count > 0 + = render "index_core", :gui_functions => @gui_functions + += render :partial => 'shared/create_link', :locals => {:child_class => GuiFunction}
\ No newline at end of file diff --git a/app/views/gui_functions/new.html.haml b/app/views/gui_functions/new.html.haml new file mode 100644 index 0000000..6c57e9c --- /dev/null +++ b/app/views/gui_functions/new.html.haml @@ -0,0 +1,3 @@ +- title t("gui_functions.new.page_title") + += render "form"
\ No newline at end of file diff --git a/app/views/gui_functions/show.html.haml b/app/views/gui_functions/show.html.haml new file mode 100644 index 0000000..0fc2dd9 --- /dev/null +++ b/app/views/gui_functions/show.html.haml @@ -0,0 +1,18 @@ +- title t("gui_functions.show.page_title") + +%p + %strong= t('gui_functions.show.name') + ":" + = @gui_function.name +%p + %strong= t('gui_functions.show.description') + ":" + = @gui_function.description + +- @user_groups.each do |user_group| + %p + %strong= "#{user_group}:" + - if @gui_function.gui_function_memberships.where(:user_group_id => user_group.id, :activated => true).count > 0 + = 'x' + - else + = 'not activated' + += render :partial => 'shared/show_edit_destroy_part', :locals => { :child => @gui_function }
\ No newline at end of file diff --git a/app/views/hunt_group_members/_form.html.haml b/app/views/hunt_group_members/_form.html.haml new file mode 100644 index 0000000..1ab7850 --- /dev/null +++ b/app/views/hunt_group_members/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([@hunt_group, @hunt_group_member]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('hunt_group_members.form.submit')
\ No newline at end of file diff --git a/app/views/hunt_group_members/_form_core.html.haml b/app/views/hunt_group_members/_form_core.html.haml new file mode 100644 index 0000000..2f03fce --- /dev/null +++ b/app/views/hunt_group_members/_form_core.html.haml @@ -0,0 +1,4 @@ +.inputs + = f.input :name, :label => t('hunt_group_members.form.name.label'), :hint => conditional_hint('hunt_group_members.form.name.hint') + = f.input :active, :label => t('hunt_group_members.form.active.label'), :hint => conditional_hint('hunt_group_members.form.active.hint') + = f.input :can_switch_status_itself, :label => t('hunt_group_members.form.can_switch_status_itself.label'), :hint => conditional_hint('hunt_group_members.form.can_switch_status_itself.hint') diff --git a/app/views/hunt_group_members/_index_core.html.haml b/app/views/hunt_group_members/_index_core.html.haml new file mode 100644 index 0000000..46b64c8 --- /dev/null +++ b/app/views/hunt_group_members/_index_core.html.haml @@ -0,0 +1,20 @@ +%table + %tr + %th= t('hunt_group_members.index.name') + %th= t('hunt_group_members.index.active') + %th= t('hunt_group_members.index.can_switch_status_itself') + %th= t('hunt_group_members.index.phone_numbers') + + - reset_cycle + - for hunt_group_member in hunt_group_members + %tr{:class => cycle('odd', 'even')} + %td= hunt_group_member.name + %td= hunt_group_member.active + %td= hunt_group_member.can_switch_status_itself + %td + - if hunt_group_member.phone_numbers.count > 0 + =render 'phone_numbers/listing', :phone_numbers => hunt_group_member.phone_numbers + %br + = render :partial => 'shared/create_link', :locals => {:parent => hunt_group_member, :child_class => PhoneNumber, :short_link => true} + + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => hunt_group_member.hunt_group, :child => hunt_group_member}
\ No newline at end of file diff --git a/app/views/hunt_group_members/_listing.html.haml b/app/views/hunt_group_members/_listing.html.haml new file mode 100644 index 0000000..b31fd22 --- /dev/null +++ b/app/views/hunt_group_members/_listing.html.haml @@ -0,0 +1,8 @@ +- amount_of_hunt_group_members = hunt_group_members.count +- if amount_of_hunt_group_members > 0 + - if amount_of_hunt_group_members < 30 + = hunt_group_members.map{|hunt_group_member| hunt_group_member}.join(', ') + - else + = hunt_group_members.limit(15).map{|hunt_group_member| hunt_group_member}.join(', ') + ', ' + = '[...]' + = hunt_group_members.offset(amount_of_hunt_group_members - 15).map{|hunt_group_member| hunt_group_member}.join(', ')
\ No newline at end of file diff --git a/app/views/hunt_group_members/edit.html.haml b/app/views/hunt_group_members/edit.html.haml new file mode 100644 index 0000000..93d7b0a --- /dev/null +++ b/app/views/hunt_group_members/edit.html.haml @@ -0,0 +1,3 @@ +- title t("hunt_group_members.edit.page_title") + += render "form" diff --git a/app/views/hunt_group_members/index.html.haml b/app/views/hunt_group_members/index.html.haml new file mode 100644 index 0000000..99dc929 --- /dev/null +++ b/app/views/hunt_group_members/index.html.haml @@ -0,0 +1,6 @@ +- title t("hunt_group_members.index.page_title") + +- if @hunt_group_members.count > 0 + = render "index_core", :hunt_group_members => @hunt_group_members + += render :partial => 'shared/create_link', :locals => {:parent => @hunt_group, :child_class => HuntGroupMember}
\ No newline at end of file diff --git a/app/views/hunt_group_members/new.html.haml b/app/views/hunt_group_members/new.html.haml new file mode 100644 index 0000000..99f52ad --- /dev/null +++ b/app/views/hunt_group_members/new.html.haml @@ -0,0 +1,3 @@ +- title t("hunt_group_members.new.page_title") + += render "form" diff --git a/app/views/hunt_group_members/show.html.haml b/app/views/hunt_group_members/show.html.haml new file mode 100644 index 0000000..80123b8 --- /dev/null +++ b/app/views/hunt_group_members/show.html.haml @@ -0,0 +1,19 @@ +- title t("hunt_group_members.show.page_title") + +%p + %strong= t('hunt_group_members.show.name') + ":" + = @hunt_group_member.name +%p + %strong= t('hunt_group_members.show.active') + ":" + = @hunt_group_member.active +%p + %strong= t('hunt_group_members.show.can_switch_status_itself') + ":" + = @hunt_group_member.can_switch_status_itself + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @hunt_group, :child => @hunt_group_member } + +%h2= t('hunt_group_members.form.phone_numbers.label') +- if @hunt_group_member.phone_numbers.count > 0 + = render 'phone_numbers/index_core', :phone_numbers => @hunt_group_member.phone_numbers + %br += render :partial => 'shared/create_link', :locals => {:parent => @hunt_group_member, :child_class => PhoneNumber}
\ No newline at end of file diff --git a/app/views/hunt_groups/_form.html.haml b/app/views/hunt_groups/_form.html.haml new file mode 100644 index 0000000..bc2663b --- /dev/null +++ b/app/views/hunt_groups/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([@tenant, @hunt_group]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('hunt_groups.form.submit')
\ No newline at end of file diff --git a/app/views/hunt_groups/_form_core.html.haml b/app/views/hunt_groups/_form_core.html.haml new file mode 100644 index 0000000..10a0111 --- /dev/null +++ b/app/views/hunt_groups/_form_core.html.haml @@ -0,0 +1,4 @@ +.inputs + = f.input :name, :label => t('hunt_groups.form.name.label'), :hint => conditional_hint('hunt_groups.form.name.hint') + = f.input :strategy, :as => :select, :label => t('hunt_groups.form.strategy.label'), :hint => conditional_hint('hunt_groups.form.strategy.hint'), :include_blank => false, :collection => HUNT_GROUP_STRATEGIES.map {|x| [I18n.t('hunt_groups.strategies.' + x), x] } + = f.input :seconds_between_jumps, :collection => VALID_SECONDS_BETWEEN_JUMPS_VALUES, :label => t('hunt_groups.form.seconds_between_jumps.label'), :hint => conditional_hint('hunt_groups.form.seconds_between_jumps.hint')
\ No newline at end of file diff --git a/app/views/hunt_groups/_index_core.html.haml b/app/views/hunt_groups/_index_core.html.haml new file mode 100644 index 0000000..3000e97 --- /dev/null +++ b/app/views/hunt_groups/_index_core.html.haml @@ -0,0 +1,34 @@ +- show_seconds = hunt_groups.map{|x| ! x.seconds_between_jumps.nil? }.include?(true) + +%table + %tr + %th= t('hunt_groups.index.name') + %th= t('hunt_groups.index.strategy') + - if show_seconds + %th= t('hunt_groups.index.seconds_between_jumps') + %th= t('hunt_groups.index.phone_numbers') + %th= t('hunt_groups.index.hunt_group_members') + + - reset_cycle + - for hunt_group in hunt_groups + %tr{:class => cycle('odd', 'even')} + %td= hunt_group.name + %td= t("hunt_groups.strategies.#{hunt_group.strategy}") + - if show_seconds + %td= hunt_group.seconds_between_jumps + %td + - if hunt_group.phone_numbers.count > 0 + =render 'phone_numbers/listing', :phone_numbers => hunt_group.phone_numbers + %br + = render :partial => 'shared/create_link', :locals => {:parent => hunt_group, :child_class => PhoneNumber, :short_link => true} + + %td + - if hunt_group.hunt_group_members.count > 3 + = link_to hunt_group.hunt_group_members.count, hunt_group_hunt_group_members_path(hunt_group) + %br + - elsif hunt_group.hunt_group_members.count > 0 + =render 'hunt_group_members/listing', :hunt_group_members => hunt_group.hunt_group_members + %br + = render :partial => 'shared/create_link', :locals => {:parent => hunt_group, :child_class => HuntGroupMember, :short_link => true} + + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => hunt_group.tenant, :child => hunt_group}
\ No newline at end of file diff --git a/app/views/hunt_groups/edit.html.haml b/app/views/hunt_groups/edit.html.haml new file mode 100644 index 0000000..f2ef998 --- /dev/null +++ b/app/views/hunt_groups/edit.html.haml @@ -0,0 +1,3 @@ +- title t("hunt_groups.edit.page_title") + += render "form" diff --git a/app/views/hunt_groups/index.html.haml b/app/views/hunt_groups/index.html.haml new file mode 100644 index 0000000..62bc2aa --- /dev/null +++ b/app/views/hunt_groups/index.html.haml @@ -0,0 +1,6 @@ +- title t("hunt_groups.index.page_title") + +- if @hunt_groups.count > 0 + = render "index_core", :hunt_groups => @hunt_groups + += render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => HuntGroup}
\ No newline at end of file diff --git a/app/views/hunt_groups/new.html.haml b/app/views/hunt_groups/new.html.haml new file mode 100644 index 0000000..a40e579 --- /dev/null +++ b/app/views/hunt_groups/new.html.haml @@ -0,0 +1,3 @@ +- title t("hunt_groups.new.page_title") + += render "form" diff --git a/app/views/hunt_groups/show.html.haml b/app/views/hunt_groups/show.html.haml new file mode 100644 index 0000000..009af50 --- /dev/null +++ b/app/views/hunt_groups/show.html.haml @@ -0,0 +1,26 @@ +- title t("hunt_groups.show.page_title") + +%p + %strong= t('hunt_groups.show.name') + ":" + = @hunt_group.name +%p + %strong= t('hunt_groups.show.strategy') + ":" + = t("hunt_groups.strategies.#{@hunt_group.strategy}") +- if @hunt_group.seconds_between_jumps + %p + %strong= t('hunt_groups.show.seconds_between_jumps') + ":" + = @hunt_group.seconds_between_jumps + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @tenant, :child => @hunt_group } + +%h2= t('hunt_groups.form.phone_numbers.label') +- if @hunt_group.phone_numbers.count > 0 + = render 'phone_numbers/index_core', :phone_numbers => @hunt_group.phone_numbers + %br += render :partial => 'shared/create_link', :locals => {:parent => @hunt_group, :child_class => PhoneNumber} + +%h2= t('hunt_groups.form.hunt_group_members.label') +- if @hunt_group.hunt_group_members.count > 0 + = render 'hunt_group_members/index_core', :hunt_group_members => @hunt_group.hunt_group_members + %br += render :partial => 'shared/create_link', :locals => {:parent => @hunt_group, :child_class => HuntGroupMember}
\ No newline at end of file diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml new file mode 100644 index 0000000..f2aff0a --- /dev/null +++ b/app/views/layouts/application.html.haml @@ -0,0 +1,46 @@ +!!! 5 +<!--[if lt IE 7]> <html lang="en" class="no-js ie6"> <![endif]--> +<!--[if IE 7]> <html lang="en" class="no-js ie7"> <![endif]--> +<!--[if IE 8]> <html lang="en" class="no-js ie8"> <![endif]--> +<!--[if gt IE 8]><!--> +%html.no-js{ :lang => "en" } + ~#OPTIMIZE Make html lang attribute reflect the actual language. + <!--<![endif]--> + %header + %meta{ :charset => "utf-8" }/ + ~#OPTIMIZE "/" seems to be supposed to make an empty element tag, but it doesn't work. HAML bug? + %title + = content_for?(:title) ? yield(:title) : "Untitled" + %meta{ :name => "viewport", :content => "width=device-width, initial-scale=1.0" }/ + = stylesheet_link_tag "application" + = javascript_include_tag "application" + = csrf_meta_tag + = yield(:head) + + %body + #container + = render :partial => "shared/header" + = render :partial => "shared/system_message" + = render :partial => "shared/flash", :locals => { :flash => flash} + + #content{:role => 'main'} + .light + %header.main + .breadcrumbs= render_breadcrumbs :separator => ' » ' + - if show_title? + %h1= yield(:title) + = yield + + %footer#main + %ul + - if GuiFunction.display?('amooma_commercial_support_link_in_footer', current_user) + %li + %a{:href => "http://www.amooma.de"} Kommerzieller Support und Consulting + - if GuiFunction.display?('gemeinschaft_mailinglist_link_in_footer', current_user) + %li + %a{:href => "https://groups.google.com/group/gs5-users/"} Kostenlose Mailingliste + + .amooma-logo + %span brought to you by + %a{ :target => '_blank', :href => "http://www.amooma.de/" } Amooma + diff --git a/app/views/manufacturers/_form.html.haml b/app/views/manufacturers/_form.html.haml new file mode 100644 index 0000000..d89c603 --- /dev/null +++ b/app/views/manufacturers/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for(@manufacturer) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('manufacturers.form.submit')
\ No newline at end of file diff --git a/app/views/manufacturers/_form_core.html.haml b/app/views/manufacturers/_form_core.html.haml new file mode 100644 index 0000000..84b85c9 --- /dev/null +++ b/app/views/manufacturers/_form_core.html.haml @@ -0,0 +1,4 @@ +.inputs + = f.input :name, :label => t('manufacturers.form.name.label'), :hint => conditional_hint('manufacturers.form.name.hint') + = f.input :ieee_name, :label => t('manufacturers.form.ieee_name.label'), :hint => conditional_hint('manufacturers.form.ieee_name.hint') + = f.input :homepage_url, :label => t('manufacturers.form.homepage_url.label'), :hint => conditional_hint('manufacturers.form.homepage_url.hint') diff --git a/app/views/manufacturers/_index_core.html.haml b/app/views/manufacturers/_index_core.html.haml new file mode 100644 index 0000000..8937909 --- /dev/null +++ b/app/views/manufacturers/_index_core.html.haml @@ -0,0 +1,18 @@ +%table + %tr + %th= t('manufacturers.index.name') + %th= t('manufacturers.index.ieee_name') + %th= t('manufacturers.index.homepage_url') + %th= t('manufacturers.index.phone_models') + + - reset_cycle + - for manufacturer in manufacturers + %tr{:class => cycle('odd', 'even')} + %td= manufacturer.name + %td= manufacturer.ieee_name + %td + - if manufacturer.homepage_url + =link_to manufacturer.homepage_url, manufacturer.homepage_url + %td + = manufacturer.phone_models.map{|x| x.to_s }.join(', ') + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:child => manufacturer}
\ No newline at end of file diff --git a/app/views/manufacturers/edit.html.haml b/app/views/manufacturers/edit.html.haml new file mode 100644 index 0000000..61bcba0 --- /dev/null +++ b/app/views/manufacturers/edit.html.haml @@ -0,0 +1,3 @@ +- title t("manufacturers.edit.page_title") + += render "form" diff --git a/app/views/manufacturers/index.html.haml b/app/views/manufacturers/index.html.haml new file mode 100644 index 0000000..43fecc6 --- /dev/null +++ b/app/views/manufacturers/index.html.haml @@ -0,0 +1,5 @@ +- title t("manufacturers.index.page_title") + += render "index_core", :manufacturers => @manufacturers + += render :partial => 'shared/create_link', :locals => {:child_class => Manufacturer}
\ No newline at end of file diff --git a/app/views/manufacturers/new.html.haml b/app/views/manufacturers/new.html.haml new file mode 100644 index 0000000..4fb9dbf --- /dev/null +++ b/app/views/manufacturers/new.html.haml @@ -0,0 +1,3 @@ +- title t("manufacturers.new.page_title") + += render "form" diff --git a/app/views/manufacturers/show.html.haml b/app/views/manufacturers/show.html.haml new file mode 100644 index 0000000..1b8383b --- /dev/null +++ b/app/views/manufacturers/show.html.haml @@ -0,0 +1,18 @@ +- title t("manufacturers.show.page_title") + +%p + %strong= t('manufacturers.show.name') + ":" + = @manufacturer.name +%p + %strong= t('manufacturers.show.ieee_name') + ":" + = @manufacturer.ieee_name +%p + %strong= t('manufacturers.show.homepage_url') + ":" + - if @manufacturer.homepage_url + =link_to @manufacturer.homepage_url, @manufacturer.homepage_url + += render :partial => 'shared/show_edit_destroy_part', :locals => { :child => @manufacturer } + +%h2=t("phone_models.index.page_title") + +=render 'phone_models/index_core', :phone_models => @manufacturer.phone_models
\ No newline at end of file diff --git a/app/views/notifications/new_fax.text.erb b/app/views/notifications/new_fax.text.erb new file mode 100644 index 0000000..579cc18 --- /dev/null +++ b/app/views/notifications/new_fax.text.erb @@ -0,0 +1,8 @@ +Hello <%= @fax[:greeting] %>, + +You've just received a fax document on your Gemeinschaft account <%= @fax[:account_name] %>. + + Caller: <%= @fax[:from] %> +Remote Station: <%= @fax[:remote_station_id] %> + Local Station: <%= @fax[:local_station_id] %> +Receiving Time: <%= @fax[:date] %> diff --git a/app/views/notifications/new_password.text.erb b/app/views/notifications/new_password.text.erb new file mode 100644 index 0000000..a865960 --- /dev/null +++ b/app/views/notifications/new_password.text.erb @@ -0,0 +1,3 @@ +Hello <%= @message[:greeting] %>, + +Your password has been reset to: <%= @password %> diff --git a/app/views/notifications/new_pin.text.erb b/app/views/notifications/new_pin.text.erb new file mode 100644 index 0000000..11cf17b --- /dev/null +++ b/app/views/notifications/new_pin.text.erb @@ -0,0 +1,7 @@ +Hello <%= @pin[:greeting] %>, + +the PIN of one of your conference rooms has been changed. + +Conference: <%= @pin[:conference] %> + Numbers: <%= @pin[:phone_numbers] %> + New PIN: <%= @pin[:pin] %> diff --git a/app/views/notifications/new_voicemail.text.erb b/app/views/notifications/new_voicemail.text.erb new file mode 100644 index 0000000..adeabda --- /dev/null +++ b/app/views/notifications/new_voicemail.text.erb @@ -0,0 +1,9 @@ +Hello <%= @voicemail[:greeting] %>, + +You've just received a voicemail on your Gemeinschaft account <%= @voicemail[:destination] %>. + + From: <%= @voicemail[:from] %> + To: <%= @voicemail[:to] %> +Receiving Time: <%= @voicemail[:date] %> +Message length: <%= @voicemail[:duration] %> + File: <%= @voicemail[:file_name] %> diff --git a/app/views/page/conference.html.haml b/app/views/page/conference.html.haml new file mode 100644 index 0000000..061dfd1 --- /dev/null +++ b/app/views/page/conference.html.haml @@ -0,0 +1,80 @@ +- conf_call_topic = "The next big thing" +- title "Conference Call \u2013 Topic: #{conf_call_topic}" + + +%section.conference + + %section.panel.speakers.first + %header + %h3 Speakers + ~# Naming this class message, since when you add or chat you're sending + ~# a message to the server. + %form.message + %input{:placeholder => '# '} + .actors + - 2.times do + .actor + .info + - user = current_user #FIXME + - avatar_url = user.image_url(:mini) || 'stubs/user-36x.jpg' + = image_tag avatar_url.to_s, :class => 'display', :alt => "[ ]" + %span.name Fake Stefan + %span.status Joined at 03:00 + .voice-actions + %a.make.listener{ :href => '#', :title => "Make listener" } Make listener + %a.voice.unmuted{ :href => '#', :title => "Mute" } Mute + %a.remove{ :href => '#', :title => "Remove from conference" } Remove + + + %section.panel.listeners + %header + %h3 Listeners + ~# Naming this class message, since when you add or chat you're sending + ~# a message to the server. + %form.message + %input{:placeholder => '# '} + .actors + - 5.times do + .actor + .info + - user = current_user #FIXME + - avatar_url = user.image_url(:mini) || 'stubs/user-36x.jpg' + = image_tag avatar_url.to_s, :class => 'display', :alt => "[ ]" + %span.name Fake Stefan + %span.status Joined at 03:00 + .voice-actions + %a.make.speaker{ :href => '#', :title => "Make speaker" } Make speaker + %a.voice.unmuted{ :href => '#', :title => "Mute" } Mute + %a.remove{ :href => '#', :title => "Remove from conference" } Remove + + + %section.panel.log.last + %header + %h3 Log + ~# Naming this class message, since when you add or chat you're sending + ~# a message to the server. + %form.message + %input{:placeholder => 'Write a Message...'} + .messages + %div + %span.name Mario: + %span.content Sorry for the Delay! + %div.status + %span.name 03:11: + %span.content Fake Stefan is now a Speaker. + %div.status + %span.name 03:10: + %span.content Stefan Wintermeyer Left. + %div + %span.name Stefan: + %span.content Hello World. + %div.status + %span.name 03:00: + %span.content Stefan Wintermeyer Joined. + %div + %span.name Herpiti Derp: + %span.content Cool Conference Room! + %div + %span.name Pamela: + %span.content I'm here to sing along. + diff --git a/app/views/page/index.de.html.haml b/app/views/page/index.de.html.haml new file mode 100644 index 0000000..2928319 --- /dev/null +++ b/app/views/page/index.de.html.haml @@ -0,0 +1,16 @@ +- title "Gemeinschaft #{GEMEINSCHAFT_VERSION}" + +%div + %h3 Aktueller Mandant + - if current_user && current_user.current_tenant + %strong= current_user.current_tenant + - else + %strong= "(none)" + +%div + %h3 Aktuelle Gruppenzugehörigkeiten + %ul + - (current_user.try(:user_groups) || []).each do |group| + %li + %strong= "#{group.name}" + = "(aus Mandant: %s)" % group.tenant diff --git a/app/views/page/index.html.haml b/app/views/page/index.html.haml new file mode 100644 index 0000000..9621395 --- /dev/null +++ b/app/views/page/index.html.haml @@ -0,0 +1,16 @@ +- title "Gemeinschaft #{GEMEINSCHAFT_VERSION}" + +%div + %h3 Current tenant + - if current_user && current_user.current_tenant + %strong= current_user.current_tenant + - else + %strong= "(none)" + +%div + %h3 Current user groups + %ul + - (current_user.try(:user_groups) || []).each do |group| + %li + %strong= "#{group.name}" + = "(from tenant: %s)" % group.tenant diff --git a/app/views/phone_book_entries/_form.html.haml b/app/views/phone_book_entries/_form.html.haml new file mode 100644 index 0000000..c73d10a --- /dev/null +++ b/app/views/phone_book_entries/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([@phone_book, @phone_book_entry]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('phone_book_entries.form.submit')
\ No newline at end of file diff --git a/app/views/phone_book_entries/_form_core.html.haml b/app/views/phone_book_entries/_form_core.html.haml new file mode 100644 index 0000000..c05139e --- /dev/null +++ b/app/views/phone_book_entries/_form_core.html.haml @@ -0,0 +1,27 @@ +.inputs + = f.input :is_male, :collection => [[true, t('phone_book_entries.form.gender.male')], [false, t('phone_book_entries.form.gender.female')]], :label_method => :last, :value_method => :first, :label => t('phone_book_entries.form.male.label'), :hint => conditional_hint('phone_book_entries.form.gender.hint'), :label => t('phone_book_entries.form.gender.label'), :as => :radio + = f.input :first_name, :label => t('phone_book_entries.form.first_name.label'), :hint => conditional_hint('phone_book_entries.form.first_name.hint') + = f.input :middle_name, :label => t('phone_book_entries.form.middle_name.label'), :hint => conditional_hint('phone_book_entries.form.middle_name.hint') + = f.input :last_name, :label => t('phone_book_entries.form.last_name.label'), :hint => conditional_hint('phone_book_entries.form.last_name.hint') + = f.input :birth_name, :label => t('phone_book_entries.form.birth_name.label'), :hint => conditional_hint('phone_book_entries.form.birth_name.hint') + = f.input :title, :label => t('phone_book_entries.form.title.label'), :hint => conditional_hint('phone_book_entries.form.title.hint') + = f.input :nickname, :label => t('phone_book_entries.form.nickname.label'), :hint => conditional_hint('phone_book_entries.form.nickname.hint') + = f.input :organization, :label => t('phone_book_entries.form.organization.label'), :hint => conditional_hint('phone_book_entries.form.organization.hint') + / = f.input :is_organization, :label => t('phone_book_entries.form.is_organization.label'), :hint => conditional_hint('phone_book_entries.form.is_organization.hint') + = f.input :department, :label => t('phone_book_entries.form.department.label'), :hint => conditional_hint('phone_book_entries.form.department.hint') + = f.input :job_title, :label => t('phone_book_entries.form.job_title.label'), :hint => conditional_hint('phone_book_entries.form.job_title.hint') + + = f.input :birthday, :label => t('phone_book_entries.form.birthday.label'), :hint => conditional_hint('phone_book_entries.form.birthday.hint'), :start_year => Date.today.year - 100, :end_year => Date.today.year - 0, :order => [:day, :month, :year], :include_blank => true + + = f.input :description, :label => t('phone_book_entries.form.description.label'), :hint => conditional_hint('phone_book_entries.form.description.hint') + + = f.input :image, { :as => :file, :label => t('phone_book_entries.form.image.label'), :hint => conditional_hint('phone_book_entries.form.image.hint') } + + = f.input :homepage_organization, :label => t('phone_book_entries.form.homepage_organization.label'), :hint => conditional_hint('phone_book_entries.form.homepage_organization.hint') + = f.input :homepage_personal, :label => t('phone_book_entries.form.homepage_personal.label'), :hint => conditional_hint('phone_book_entries.form.homepage_personal.hint') + / = f.input :twitter_account, :label => t('phone_book_entries.form.twitter_account.label'), :hint => conditional_hint('phone_book_entries.form.twitter_account.hint') + / = f.input :facebook_account, :label => t('phone_book_entries.form.facebook_account.label'), :hint => conditional_hint('phone_book_entries.form.facebook_account.hint') + / = f.input :google_plus_account, :label => t('phone_book_entries.form.google_plus_account.label'), :hint => conditional_hint('phone_book_entries.form.google_plus_account.hint') + / = f.input :xing_account, :label => t('phone_book_entries.form.xing_account.label'), :hint => conditional_hint('phone_book_entries.form.xing_account.hint') + / = f.input :linkedin_account, :label => t('phone_book_entries.form.linkedin_account.label'), :hint => conditional_hint('phone_book_entries.form.linkedin_account.hint') + diff --git a/app/views/phone_book_entries/_index_core.de.html.haml b/app/views/phone_book_entries/_index_core.de.html.haml new file mode 100644 index 0000000..01be65f --- /dev/null +++ b/app/views/phone_book_entries/_index_core.de.html.haml @@ -0,0 +1,36 @@ +~# To Look for the other fields, please look into Git History. +%section.phone-book-entries + %header.entries-nav= render :partial => "phone_book_entries/navigation" + .content + - reset_cycle + %table + - for entry in phone_book_entries + ~# Dear IE7, + ~# Because of you we have to do this with a table. + ~# With Love, + ~# Mario. + %tr.phone-book-entry{:class => cycle('odd', 'even'), :"itemscope itemtype" => "http://schema.org/Person"} + %td.thumbnail + = image_tag(entry.image_url(:small).to_s, :itemprop => 'image') + %td.user + - if entry.is_organization == true + %a.name{:href=> phone_book_phone_book_entry_path(entry.phone_book, entry), :itemprop => "name"}= entry + - else + %a.name{:href=> phone_book_phone_book_entry_path(entry.phone_book, entry), :itemprop => "name"}= entry + %a.company{:href=> phone_book_phone_book_entry_path(entry.phone_book, entry), :itemprop => 'memberOf'}= entry.organization + %td.contact + - if @found_phone_numbers and @found_phone_numbers.where(:phone_numberable_id => entry.id) + %a.phone{:href=> phone_book_phone_book_entry_path(entry.phone_book, entry), :itemprop => 'telephone'}= @found_phone_numbers.where(:phone_numberable_id => entry.id).first + - elsif entry.phone_numbers.first + %a.phone{:href=> phone_book_phone_book_entry_path(entry.phone_book, entry), :itemprop => 'telephone'}= entry.phone_numbers.first + - if entry.phone_numbers.count > 1 + %a.more{:href => phone_book_phone_book_entry_path(entry.phone_book, entry)}= t('phone_book_entries.index.more_numbers', :numbers => (entry.phone_numbers.count-1)) + %td.extra + - if !entry.description.blank? + %strong Beschreibung: + %div= entry.description + - if can? :edit, entry + %td= link_to t('phone_book_entries.index.actions.edit'), edit_phone_book_phone_book_entry_path( entry.phone_book, entry ) + - if can? :destroy, entry + %td= link_to t('phone_book_entries.index.actions.destroy'), [entry.phone_book, entry], :confirm => t('phone_book_entries.index.actions.confirm'), :method => :delete + %footer.entries-nav= render :partial => "phone_book_entries/navigation" diff --git a/app/views/phone_book_entries/_index_core.html.haml b/app/views/phone_book_entries/_index_core.html.haml new file mode 100644 index 0000000..d9cfe10 --- /dev/null +++ b/app/views/phone_book_entries/_index_core.html.haml @@ -0,0 +1,36 @@ +~# To Look for the other fields, please look into Git History. +%section.phone-book-entries + %header.entries-nav= render :partial => "phone_book_entries/navigation" + .content + - reset_cycle + %table + - for entry in phone_book_entries + ~# Dear IE7, + ~# Because of you we have to do this with a table. + ~# With Love, + ~# Mario. + %tr.phone-book-entry{:class => cycle('odd', 'even'), :"itemscope itemtype" => "http://schema.org/Person"} + %td.thumbnail + = image_tag(entry.image_url(:small).to_s, :itemprop => 'image') + %td.user + - if entry.is_organization == true + %a.name{:href=> phone_book_phone_book_entry_path(entry.phone_book, entry), :itemprop => "name"}= entry + - else + %a.name{:href=> phone_book_phone_book_entry_path(entry.phone_book, entry), :itemprop => "name"}= entry + %a.company{:href=> phone_book_phone_book_entry_path(entry.phone_book, entry), :itemprop => 'memberOf'}= entry.organization + %td.contact + - if @found_phone_numbers and @found_phone_numbers.where(:phone_numberable_id => entry.id) + %a.phone{:href=> phone_book_phone_book_entry_path(entry.phone_book, entry), :itemprop => 'telephone'}= @found_phone_numbers.where(:phone_numberable_id => entry.id).first + - elsif entry.phone_numbers.first + %a.phone{:href=> phone_book_phone_book_entry_path(entry.phone_book, entry), :itemprop => 'telephone'}= entry.phone_numbers.first + - if entry.phone_numbers.count > 1 + %a.more{:href => phone_book_phone_book_entry_path(entry.phone_book, entry)}= t('phone_book_entries.index.more_numbers', :numbers => (entry.phone_numbers.count-1)) + %td.extra + - if !entry.description.blank? + %strong Description: + %div= entry.description + - if can? :edit, entry + %td= link_to t('phone_book_entries.index.actions.edit'), edit_phone_book_phone_book_entry_path( entry.phone_book, entry ) + - if can? :destroy, entry + %td= link_to t('phone_book_entries.index.actions.destroy'), [entry.phone_book, entry], :confirm => t('phone_book_entries.index.actions.confirm'), :method => :delete + %footer.entries-nav= render :partial => "phone_book_entries/navigation" diff --git a/app/views/phone_book_entries/_navigation.html.haml b/app/views/phone_book_entries/_navigation.html.haml new file mode 100644 index 0000000..dd1e8a7 --- /dev/null +++ b/app/views/phone_book_entries/_navigation.html.haml @@ -0,0 +1,8 @@ +%nav + %ol.abc + - %w{# A B C D E F G H I J K L M N O P Q R S T U V W X Y Z}.each do |char| + %li + %a{ :href => "?name=#{char}" }= char + +.pagination + = will_paginate @phone_book_entries diff --git a/app/views/phone_book_entries/edit.html.haml b/app/views/phone_book_entries/edit.html.haml new file mode 100644 index 0000000..d4fad4d --- /dev/null +++ b/app/views/phone_book_entries/edit.html.haml @@ -0,0 +1,9 @@ +- title t("phone_book_entries.edit.page_title") + += render "form" + +%p + - if can? :edit, @phone_book_entry + = link_to t('phone_book_entries.edit.actions.edit'), @phone_book_entry + | + = link_to t('phone_book_entries.edit.actions.view_all'), phone_book_entries_path diff --git a/app/views/phone_book_entries/index.html.haml b/app/views/phone_book_entries/index.html.haml new file mode 100644 index 0000000..6a17eb9 --- /dev/null +++ b/app/views/phone_book_entries/index.html.haml @@ -0,0 +1,19 @@ +- title t("phone_book_entries.index.page_title") + +- if @phone_books + %p + = t('phone_book_entries.index.available_phone_books') + - @phone_books.each do |phone_book| + = link_to phone_book, phone_book + - if can?(:create, PhoneBookEntry, :phone_book_id => phone_book.id) + ( + = link_to "#{t('phone_book_entries.index.create_new_phone_book_entry')}", new_phone_book_phone_book_entry_path(phone_book) + ) + - if phone_book != @phone_books.last + \, + +- if @phone_book_entries.count > 0 + = render "index_core", :phone_book_entries => @phone_book_entries + +- if @phone_book + = render :partial => 'shared/create_link', :locals => {:parent => @phone_book, :child_class => PhoneBookEntry}
\ No newline at end of file diff --git a/app/views/phone_book_entries/new.html.haml b/app/views/phone_book_entries/new.html.haml new file mode 100644 index 0000000..d72d1a4 --- /dev/null +++ b/app/views/phone_book_entries/new.html.haml @@ -0,0 +1,3 @@ +- title t("phone_book_entries.new.page_title") + += render "form" diff --git a/app/views/phone_book_entries/show.html.haml b/app/views/phone_book_entries/show.html.haml new file mode 100644 index 0000000..b6e8c6e --- /dev/null +++ b/app/views/phone_book_entries/show.html.haml @@ -0,0 +1,146 @@ +- title nil + +%section.phone-book-entry + .content + %header + %h1.username + %a= @phone_book_entry + - if !@phone_book_entry.organization.blank? + .work + %a= @phone_book_entry.organization + - if !@phone_book_entry.department.blank? + \/ + = @phone_book_entry.department + - if !@phone_book_entry.job_title.blank? + \/ + = @phone_book_entry.job_title + .personal + - if !@phone_book_entry.nickname.blank? + %span.nickname + a.k.a + %strong= @phone_book_entry.nickname + - if @phone_book_entry.birthday + %span.birthday + = l(@phone_book_entry.birthday) + .tags + %a= l @phone_book_entry.created_at.utc.getlocal, :format => :short + , + %a= l @phone_book_entry.updated_at.utc.getlocal, :format => :short + %section.activity + - if @user_log + %h2 User Log + - @user_log.each do |log_entry| + - if log_entry[:type] == 'voicemail' + .entry.voice-message + %span.motive + = log_entry[:text] + %span.timestamp + = log_entry[:timestamp] + - if log_entry[:type] == 'fax_document' + .entry.fax + %span.motive + = log_entry[:text] + %span.timestamp + = log_entry[:timestamp] + - if log_entry[:type] == 'call_placed' + .entry.phone + %span.motive + log_entry[:text] + %span.timestamp + = log_entry[:timestamp] + - elsif log_entry[:type] == 'call_received' + .entry.phone + %span.motive + = log_entry[:text] + %span.timestamp + = log_entry[:timestamp] + - elsif log_entry[:type] == 'call_missed' + .entry.phone-down + %span.motive + = log_entry[:text] + %span.timestamp + = log_entry[:timestamp] + + .sidebar + = image_tag @phone_book_entry.image_url(:profile).to_s, :class => 'display' + %p.description + = @phone_book_entry.description + .widget.phones + - @phone_book_entry.phone_numbers.each do |phone_number| + - case phone_number.name + - when /fax/ + .fax + %a= phone_number + %span= phone_number.name + - when /home/ + .home + %a= phone_number + %span= phone_number.name + - when /mobile/ + .cellphone + %a= phone_number + %span= phone_number.name + - when /office/ + .office + %a= phone_number + %span= phone_number.name + - else + .phone + %a= phone_number + %span= phone_number.name + = link_to t('phone_book_entries.show.manage_phone_numbers'), phone_book_entry_phone_numbers_path(@phone_book_entry) + + .widget.adresses + - @phone_book_entry.addresses.each do |address| + .home + %strong + - if !address.line1.blank? + = address.line1 + %br + - if !address.line2.blank? + = address.line1 + %br + - if !address.street.blank? + = address.street + %br + - if !address.city.blank? + = "#{address.city} #{address.zip_code}" + %br + - if !address.country.blank? + = address.country.to_s + %br + / %span Home + / .office + .widget.social + - if !@phone_book_entry.homepage_organization.blank? + .home + %a= @phone_book_entry.homepage_organization + %span www + - if !@phone_book_entry.homepage_personal.blank? + .home + %a= @phone_book_entry.homepage_personal + %span www + - if !@phone_book_entry.twitter_account.blank? + .twitter + %a= @phone_book_entry.twitter_account + %span Twitter + - if !@phone_book_entry.google_plus_account.blank? + .google_plus + %a= @phone_book_entry.google_plus_account + %span Google+ + - if !@phone_book_entry.facebook_account.blank? + .facebook + %a= @phone_book_entry.facebook_account + %span Facebook + - if !@phone_book_entry.xing_account.blank? + .xing + %a= @phone_book_entry.xing_account + %span Xing + - if !@phone_book_entry.linkedin_account.blank? + .linkedin + %a= @phone_book_entry.linkedin_account + %span LinkedIn + - if !@phone_book_entry.mobileme_account.blank? + .mobileme + %a= @phone_book_entry.mobileme_account + %span MobileMe diff --git a/app/views/phone_book_entries/show.html.haml.examlple b/app/views/phone_book_entries/show.html.haml.examlple new file mode 100644 index 0000000..176ad04 --- /dev/null +++ b/app/views/phone_book_entries/show.html.haml.examlple @@ -0,0 +1,194 @@ +- title nil + +%section.phone-book-entry + .content + %header + %h1.username + %a= @phone_book_entry + - if !@phone_book_entry.organization.blank? + .work + %a= @phone_book_entry.organization + - if !@phone_book_entry.department.blank? + \/ + = @phone_book_entry.department + - if !@phone_book_entry.job_title.blank? + \/ + = @phone_book_entry.job_title + .personal + - if !@phone_book_entry.nickname.blank? + %span.nickname + a.k.a + %strong= @phone_book_entry.nickname + - if @phone_book_entry.birthday + %span.birthday + = l(@phone_book_entry.birthday) + .tags + %a Developer + , + %a Worked With + , + %a Friend + %section.activity + %h2 User Log + = form_tag '/entry-whatever' do + %textarea{ :placeholder => "Leave a comment ..." } + .comment + = image_tag @phone_book_entry.image_url(:mini).to_s, :class => 'display' + .info + %span.commenter Random User + %span.time at 10:00 of Today + .body + I'm really tired and I still have to pack part of my baggage... + - 3.times do + .entry.voice-message + %span.motive + Stefan Wintermeyer left you a voice message 4 minutes ago. + %span.timestamp + 10:30 03/11/2011 + .entry.phone + %span.motive + Called Stefan Wintermeyer at + %a Work + with a duration of 5 minutes. + %span.timestamp + 10:30 03/11/2011 + .comment + = image_tag @phone_book_entry.image_url(:mini).to_s, :class => 'display' + .info + %span.commenter Random User + %span.time at 10:00 of Today + .body + I'm really tired and I still have to pack part of my baggage... + .entry.fax + %span.motive + Stefan Wintermeyer sent you a fax with 3 pages. + %span.timestamp + 10:30 03/11/2011 + + .entry.phone-down + %span.motive + Missed call from Stephan Wintermeyer. + %span.timestamp + 10:30 03/11/2011 + + .sidebar + = image_tag @phone_book_entry.image_url(:profile).to_s, :class => 'display' + %p.description + = @phone_book_entry.description + .widget.phones + - @phone_book_entry.phone_numbers.each do |phone_number| + - case phone_number.name + - when /fax/ + .fax + %a= phone_number + %span= phone_number.name + - when /home/ + .home + %a= phone_number + %span= phone_number.name + - when /mobile/ + .cellphone + %a= phone_number + %span= phone_number.name + - when /office/ + .office + %a= phone_number + %span= phone_number.name + - else + .phone + %a= phone_number + %span= phone_number.name + = link_to t('phone_book_entries.show.manage_phone_numbers'), phone_book_entry_phone_numbers_path(@phone_book_entry) + + .widget.adresses + - @phone_book_entry.addresses.each do |address| + .home + %strong + - if !address.line1.blank? + = address.line1 + %br + - if !address.line2.blank? + = address.line1 + %br + - if !address.street.blank? + = address.street + %br + - if !address.city.blank? + = "#{address.city} #{address.zip_code}" + %br + - if !address.country.blank? + = address.country.to_s + %br + / %span Home + / .office + .widget.social + - if !@phone_book_entry.homepage_organization.blank? + .home + %a= @phone_book_entry.homepage_organization + %span www + - if !@phone_book_entry.homepage_personal.blank? + .home + %a= @phone_book_entry.homepage_personal + %span www + - if !@phone_book_entry.twitter_account.blank? + .twitter + %a= @phone_book_entry.twitter_account + %span Twitter + - if !@phone_book_entry.google_plus_account.blank? + .google_plus + %a= @phone_book_entry.google_plus_account + %span Google+ + - if !@phone_book_entry.facebook_account.blank? + .facebook + %a= @phone_book_entry.facebook_account + %span Facebook + - if !@phone_book_entry.xing_account.blank? + .xing + %a= @phone_book_entry.xing_account + %span Xing + - if !@phone_book_entry.linkedin_account.blank? + .linkedin + %a= @phone_book_entry.linkedin_account + %span LinkedIn + - if !@phone_book_entry.mobileme_account.blank? + .mobileme + %a= @phone_book_entry.mobileme_account + %span MobileMe + +/ = debug @phone_book_entry.attributes + + +/ %p +/ %strong= t('phone_book_entries.show.first_name') + ":" +/ = @phone_book_entry.first_name +/ %p +/ %strong= t('phone_book_entries.show.middle_name') + ":" +/ = @phone_book_entry.middle_name +/ %p +/ %strong= t('phone_book_entries.show.last_name') + ":" +/ = @phone_book_entry.last_name +/ %p +/ %strong= t('phone_book_entries.show.title') + ":" +/ = @phone_book_entry.title +/ %p +/ %strong= t('phone_book_entries.show.nickname') + ":" +/ = @phone_book_entry.nickname +/ %p +/ %strong= t('phone_book_entries.show.organization') + ":" +/ = @phone_book_entry.organization +/ %p +/ %strong= t('phone_book_entries.show.department') + ":" +/ = @phone_book_entry.department +/ %p +/ %strong= t('phone_book_entries.show.job_title') + ":" +/ = @phone_book_entry.job_title +/ +/ %p +/ = link_to t('phone_book_entries.show.actions.edit'), edit_phone_book_phone_book_entry_path( @phone_book_entry.phone_book, @phone_book_entry ) +/ | +/ - if can? :destroy, @phone_book_entry +/ = link_to t('phone_book_entries.show.actions.destroy'), phone_book_phone_book_entry_path( @phone_book_entry.phone_book, @phone_book_entry ), :confirm => t('phone_book_entries.show.actions.confirm'), :method => :delete +/ +/ - if @phone_book_entry.phone_numbers.count > 0 +/ = render "phone_numbers/index_core", :phone_numbers => @phone_book_entry.phone_numbers +/ diff --git a/app/views/phone_books/_form.html.haml b/app/views/phone_books/_form.html.haml new file mode 100644 index 0000000..245426b --- /dev/null +++ b/app/views/phone_books/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([ @parent, @phone_book ]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('phone_books.form.submit')
\ No newline at end of file diff --git a/app/views/phone_books/_form_core.html.haml b/app/views/phone_books/_form_core.html.haml new file mode 100644 index 0000000..0e36c4d --- /dev/null +++ b/app/views/phone_books/_form_core.html.haml @@ -0,0 +1,3 @@ +.inputs + = f.input :name, :label => t('phone_books.form.name.label'), :hint => conditional_hint('phone_books.form.name.hint'), :autofocus => true + = f.input :description, :label => t('phone_books.form.description.label'), :hint => conditional_hint('phone_books.form.description.hint') diff --git a/app/views/phone_books/_index_core.html.haml b/app/views/phone_books/_index_core.html.haml new file mode 100644 index 0000000..5f50675 --- /dev/null +++ b/app/views/phone_books/_index_core.html.haml @@ -0,0 +1,16 @@ +%table + %tr + %th= t('phone_books.index.name') + %th= t('phone_books.index.description') + %th= t('phone_books.index.count') + + - reset_cycle + - for phone_book in phone_books + %tr{:class => cycle('odd', 'even')} + %td= phone_book.name + %td= phone_book.description + %td + = number_with_delimiter( phone_book.phone_book_entries.count ) + = render :partial => 'shared/create_link', :locals => {:parent => phone_book, :child_class => PhoneBookEntry, :short_link => true} + + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => phone_book.phone_bookable, :child => phone_book}
\ No newline at end of file diff --git a/app/views/phone_books/edit.html.haml b/app/views/phone_books/edit.html.haml new file mode 100644 index 0000000..36c945b --- /dev/null +++ b/app/views/phone_books/edit.html.haml @@ -0,0 +1,3 @@ +- title t("phone_books.edit.page_title", :resource => @phone_book) + += render "form" diff --git a/app/views/phone_books/index.html.haml b/app/views/phone_books/index.html.haml new file mode 100644 index 0000000..52b4e9b --- /dev/null +++ b/app/views/phone_books/index.html.haml @@ -0,0 +1,6 @@ +- title t("phone_books.index.page_title") + +- if @phone_books.count > 0 + = render "index_core", :phone_books => @phone_books + += render :partial => 'shared/create_link', :locals => {:parent => @parent, :child_class => PhoneBook}
\ No newline at end of file diff --git a/app/views/phone_books/new.html.haml b/app/views/phone_books/new.html.haml new file mode 100644 index 0000000..e96ce1e --- /dev/null +++ b/app/views/phone_books/new.html.haml @@ -0,0 +1,3 @@ +- title t("phone_books.new.page_title") + += render "form"
\ No newline at end of file diff --git a/app/views/phone_books/show.html.haml b/app/views/phone_books/show.html.haml new file mode 100644 index 0000000..047b15e --- /dev/null +++ b/app/views/phone_books/show.html.haml @@ -0,0 +1,13 @@ +- title @phone_book +- if ! @phone_book.description.blank? + %p + %strong= t('phone_books.show.description') + ":" + = @phone_book.description + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @phone_book.phone_bookable, :child => @phone_book } + +%h2= t("phone_book_entries.index.page_title") +- if @phone_book_entries.count > 0 + = render "phone_book_entries/index_core", :phone_book_entries => @phone_book_entries + += render :partial => 'shared/create_link', :locals => {:parent => @phone_book, :child_class => PhoneBookEntry}
\ No newline at end of file diff --git a/app/views/phone_models/_form.html.haml b/app/views/phone_models/_form.html.haml new file mode 100644 index 0000000..45c176f --- /dev/null +++ b/app/views/phone_models/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([ @manufacturer, @phone_model ]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('phone_models.form.submit')
\ No newline at end of file diff --git a/app/views/phone_models/_form_core.html.haml b/app/views/phone_models/_form_core.html.haml new file mode 100644 index 0000000..b406d6e --- /dev/null +++ b/app/views/phone_models/_form_core.html.haml @@ -0,0 +1,4 @@ +.inputs + = f.input :name, :label => t('phone_models.form.name.label'), :hint => conditional_hint('phone_models.form.name.hint') + = f.input :product_manual_homepage_url, :label => t('phone_models.form.product_manual_homepage_url.label'), :hint => conditional_hint('phone_models.form.product_manual_homepage_url.hint') + = f.input :product_homepage_url, :label => t('phone_models.form.product_homepage_url.label'), :hint => conditional_hint('phone_models.form.product_homepage_url.hint') diff --git a/app/views/phone_models/_index_core.html.haml b/app/views/phone_models/_index_core.html.haml new file mode 100644 index 0000000..b07eb68 --- /dev/null +++ b/app/views/phone_models/_index_core.html.haml @@ -0,0 +1,19 @@ +%table + %tr + %th= t('phone_models.index.name') + %th= t('phone_models.index.product_manual_homepage_url') + %th= t('phone_models.index.product_homepage_url') + %th= t('phone_models.index.number_of_phones') + + - reset_cycle + - for phone_model in phone_models + %tr{:class => cycle('odd', 'even')} + %td= phone_model.name + %td + - if phone_model.product_manual_homepage_url + =link_to truncate(phone_model.product_manual_homepage_url, :length => 40), phone_model.product_manual_homepage_url + %td + - if phone_model.product_homepage_url + =link_to truncate(phone_model.product_homepage_url, :length => 40), phone_model.product_homepage_url + %td= phone_model.phones.count + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:child => phone_model, :parent => phone_model.manufacturer}
\ No newline at end of file diff --git a/app/views/phone_models/edit.html.haml b/app/views/phone_models/edit.html.haml new file mode 100644 index 0000000..bf31ffc --- /dev/null +++ b/app/views/phone_models/edit.html.haml @@ -0,0 +1,3 @@ +- title t("phone_models.edit.page_title") + += render "form" diff --git a/app/views/phone_models/index.html.haml b/app/views/phone_models/index.html.haml new file mode 100644 index 0000000..90aa4ce --- /dev/null +++ b/app/views/phone_models/index.html.haml @@ -0,0 +1,6 @@ +- title t("phone_models.index.page_title") + +- if @phone_models.count > 0 + = render "index_core", :phone_models => @phone_models + += render :partial => 'shared/create_link', :locals => {:parent => @manufacturer, :child_class => PhoneModel}
\ No newline at end of file diff --git a/app/views/phone_models/new.html.haml b/app/views/phone_models/new.html.haml new file mode 100644 index 0000000..9e900d4 --- /dev/null +++ b/app/views/phone_models/new.html.haml @@ -0,0 +1,3 @@ +- title t("phone_models.new.page_title") + += render "form" diff --git a/app/views/phone_models/show.html.haml b/app/views/phone_models/show.html.haml new file mode 100644 index 0000000..06fae4b --- /dev/null +++ b/app/views/phone_models/show.html.haml @@ -0,0 +1,18 @@ +- title t("phone_models.show.page_title") + +%p + %strong= t('phone_models.show.name') + ":" + = @phone_model.name +%p + %strong= t('phone_models.show.manufacturer_id') + ":" + = @phone_model.manufacturer +%p + %strong= t('phone_models.show.product_manual_homepage_url') + ":" + - if @phone_model.product_manual_homepage_url + =link_to @phone_model.product_manual_homepage_url, @phone_model.product_manual_homepage_url +%p + %strong= t('phone_models.show.product_homepage_url') + ":" + - if @phone_model.product_homepage_url + =link_to @phone_model.product_homepage_url, @phone_model.product_homepage_url + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @manufacturer, :child => @phone_model }
\ No newline at end of file diff --git a/app/views/phone_number_ranges/_form.html.haml b/app/views/phone_number_ranges/_form.html.haml new file mode 100644 index 0000000..a86d45b --- /dev/null +++ b/app/views/phone_number_ranges/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([ @phone_number_range.phone_number_rangeable, @phone_number_range ]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('phone_number_ranges.form.submit')
\ No newline at end of file diff --git a/app/views/phone_number_ranges/_form_core.html.haml b/app/views/phone_number_ranges/_form_core.html.haml new file mode 100644 index 0000000..f553f08 --- /dev/null +++ b/app/views/phone_number_ranges/_form_core.html.haml @@ -0,0 +1,3 @@ +.inputs + = f.input :name, :label => t('phone_number_ranges.form.name.label'), :hint => conditional_hint('phone_number_ranges.form.name.hint') + = f.input :description, :label => t('phone_number_ranges.form.description.label'), :hint => conditional_hint('phone_number_ranges.form.description.hint') diff --git a/app/views/phone_number_ranges/_index_core.html.haml b/app/views/phone_number_ranges/_index_core.html.haml new file mode 100644 index 0000000..24ea96d --- /dev/null +++ b/app/views/phone_number_ranges/_index_core.html.haml @@ -0,0 +1,21 @@ +%table + %tr + %th= t('phone_number_ranges.index.name') + %th= t('phone_number_ranges.index.description') + %th= t('phone_number_ranges.index.numbers') + %th= t('phone_number_ranges.index.amount') + + - reset_cycle + - for phone_number_range in phone_number_ranges + %tr{:class => cycle('odd', 'even')} + %td= t("phone_number_ranges.ranges.#{phone_number_range}.label") + %td= t("phone_number_ranges.ranges.#{phone_number_range}.description") + %td + - if phone_number_range.phone_numbers.count > 0 + = render 'phone_numbers/listing', :phone_numbers => phone_number_range.phone_numbers.order(:number) + %br + = render :partial => 'shared/create_link', :locals => {:parent => phone_number_range, :child_class => PhoneNumber, :short_link => true} + + %td= phone_number_range.phone_numbers.count + - if phone_number_range.phone_number_rangeable.class != Country + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => phone_number_range.phone_number_rangeable, :child => phone_number_range}
\ No newline at end of file diff --git a/app/views/phone_number_ranges/edit.html.haml b/app/views/phone_number_ranges/edit.html.haml new file mode 100644 index 0000000..fbf6d12 --- /dev/null +++ b/app/views/phone_number_ranges/edit.html.haml @@ -0,0 +1,3 @@ +- title t("phone_number_ranges.edit.page_title", :resource => @phone_number_range) + += render "form" diff --git a/app/views/phone_number_ranges/index.html.haml b/app/views/phone_number_ranges/index.html.haml new file mode 100644 index 0000000..56cf137 --- /dev/null +++ b/app/views/phone_number_ranges/index.html.haml @@ -0,0 +1,6 @@ +- title t("phone_number_ranges.index.page_title") + +- if @phone_number_ranges.count > 0 + = render "index_core", :phone_number_ranges => @phone_number_ranges + += render :partial => 'shared/create_link', :locals => {:child_class => PhoneNumberRange}
\ No newline at end of file diff --git a/app/views/phone_number_ranges/new.html.haml b/app/views/phone_number_ranges/new.html.haml new file mode 100644 index 0000000..d26b34d --- /dev/null +++ b/app/views/phone_number_ranges/new.html.haml @@ -0,0 +1,3 @@ +- title t("phone_number_ranges.new.page_title") + += render "form" diff --git a/app/views/phone_number_ranges/show.html.haml b/app/views/phone_number_ranges/show.html.haml new file mode 100644 index 0000000..64df556 --- /dev/null +++ b/app/views/phone_number_ranges/show.html.haml @@ -0,0 +1,18 @@ +- title t("phone_number_ranges.show.page_title") + +%p + %strong= t('phone_number_ranges.show.name') + ":" + = t("phone_number_ranges.ranges.#{@phone_number_range}.label") +%p + %strong= t('phone_number_ranges.show.description') + ":" + = @phone_number_range.description + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @phone_number_range.phone_number_rangeable, :child => @phone_number_range } + +%h2= t("phone_number_ranges.show.phone_numbers") + +- if @phone_number_range.try(:phone_numbers).try(:count).to_i > 0 + = render "phone_numbers/index_core", :phone_numbers => @phone_number_range.phone_numbers.order(:number) + +%p + = render :partial => 'shared/create_link', :locals => {:parent => @phone_number_range, :child_class => PhoneNumber}
\ No newline at end of file diff --git a/app/views/phone_numbers/_form.html.haml b/app/views/phone_numbers/_form.html.haml new file mode 100644 index 0000000..2812e21 --- /dev/null +++ b/app/views/phone_numbers/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([ @parent, @phone_number ]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('phone_numbers.form.submit') diff --git a/app/views/phone_numbers/_form_core.html.haml b/app/views/phone_numbers/_form_core.html.haml new file mode 100644 index 0000000..add3039 --- /dev/null +++ b/app/views/phone_numbers/_form_core.html.haml @@ -0,0 +1,10 @@ +.inputs + + - if @phone_book_entry + = f.input :name, :collection => ['Office', 'Home', 'Mobile', 'Fax'], :include_blank => false, :label => t('phone_numbers.form.name.label'), :hint => conditional_hint('phone_numbers.form.name.hint') + = f.input :number, :label => t('phone_numbers.form.number.label'), :hint => conditional_hint('phone_numbers.form.number.hint') + - else + - if @callthrough || @hunt_group_member || @access_authorization || @current_user.current_tenant.array_of_available_internal_extensions_and_dids.count == 0 || @current_user.current_tenant.array_of_available_internal_extensions_and_dids.count > 250 + = f.input :number, :label => t('phone_numbers.form.number.label'), :hint => conditional_hint('phone_numbers.form.number.hint') + - else + = f.input :number, :collection => @current_user.current_tenant.array_of_available_internal_extensions_and_dids, :label => t('phone_numbers.form.number.label'), :hint => conditional_hint('phone_numbers.form.number.hint'), :include_blank => false diff --git a/app/views/phone_numbers/_index_core.html.haml b/app/views/phone_numbers/_index_core.html.haml new file mode 100644 index 0000000..06b27c8 --- /dev/null +++ b/app/views/phone_numbers/_index_core.html.haml @@ -0,0 +1,13 @@ +%table + %tr + - if phone_numbers.count > 1 && phone_numbers.first.phone_numberable_type == 'PhoneBookEntry' + %th= t('phone_numbers.index.name') + %th= t('phone_numbers.index.number') + + - reset_cycle + - for phone_number in phone_numbers.order(:position) + %tr{:class => cycle('odd', 'even')} + - if phone_number.phone_numberable_type == 'PhoneBookEntry' + %td= phone_number.name + %td= phone_number + = render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => phone_number.phone_numberable, :child => phone_number} diff --git a/app/views/phone_numbers/_listing.html.haml b/app/views/phone_numbers/_listing.html.haml new file mode 100644 index 0000000..ca002c5 --- /dev/null +++ b/app/views/phone_numbers/_listing.html.haml @@ -0,0 +1,8 @@ +- amount_of_phone_numbers = phone_numbers.count +- if amount_of_phone_numbers > 0 + - if amount_of_phone_numbers < 110 + = phone_numbers.map{|number| number}.join(', ') + - else + = phone_numbers.limit(30).map{|number| number}.join(', ') + ', ' + = '[...]' + = phone_numbers.offset(amount_of_phone_numbers - 30).map{|number| number}.join(', ')
\ No newline at end of file diff --git a/app/views/phone_numbers/edit.html.haml b/app/views/phone_numbers/edit.html.haml new file mode 100644 index 0000000..d238d3d --- /dev/null +++ b/app/views/phone_numbers/edit.html.haml @@ -0,0 +1,3 @@ +- title t("phone_numbers.edit.page_title", :resource => "" ) + += render "form"
\ No newline at end of file diff --git a/app/views/phone_numbers/index.html.haml b/app/views/phone_numbers/index.html.haml new file mode 100644 index 0000000..2161739 --- /dev/null +++ b/app/views/phone_numbers/index.html.haml @@ -0,0 +1,6 @@ +- title @parent + +- if @phone_numbers.count > 0 + = render "index_core", :phone_numbers => @phone_numbers + += render :partial => 'shared/create_link', :locals => {:parent => @parent, :child_class => PhoneNumber}
\ No newline at end of file diff --git a/app/views/phone_numbers/new.html.haml b/app/views/phone_numbers/new.html.haml new file mode 100644 index 0000000..e91f4f4 --- /dev/null +++ b/app/views/phone_numbers/new.html.haml @@ -0,0 +1,3 @@ +- title t("phone_numbers.new.page_title") + += render "form" diff --git a/app/views/phone_numbers/show.html.haml b/app/views/phone_numbers/show.html.haml new file mode 100644 index 0000000..30c48bc --- /dev/null +++ b/app/views/phone_numbers/show.html.haml @@ -0,0 +1,27 @@ +- title t("phone_numbers.show.page_title") + +- if @phone_number.phone_numberable.class == PhoneBookEntry + %p + %strong= t('phone_numbers.show.name') + ":" + = @phone_number.name + +%p + %strong= t('phone_numbers.show.number') + ":" + = @phone_number.to_s + +- if @ringtoneable_classes.has_key?(@phone_number.phone_numberable.class.to_s) + %p + %strong= t('ringtones.name') + ':' + - if @phone_number.ringtones.count > 0 + = link_to @phone_number.ringtones.first, phone_number_ringtone_path(@phone_number, @phone_number.ringtones.first) + - else + = link_to t('ringtones.set_a_ringtone'), new_phone_number_ringtone_path(@phone_number) + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @phone_number.phone_numberable, :child => @phone_number } + +- if @forwardable_classes.has_key?(@phone_number.phone_numberable.class.to_s) + %h3= t("call_forwards.index.page_title") + - if @phone_number.call_forwards.length > 0 + = render "call_forwards/index_core", :call_forwards => @phone_number.call_forwards + + = render :partial => 'shared/create_link', :locals => {:parent => @phone_number, :child_class => CallForward}
\ No newline at end of file diff --git a/app/views/phone_sip_accounts/_form.html.haml b/app/views/phone_sip_accounts/_form.html.haml new file mode 100644 index 0000000..c2558b8 --- /dev/null +++ b/app/views/phone_sip_accounts/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([@phone, @phone_sip_account]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('phone_sip_accounts.form.submit')
\ No newline at end of file diff --git a/app/views/phone_sip_accounts/_form_core.html.haml b/app/views/phone_sip_accounts/_form_core.html.haml new file mode 100644 index 0000000..81f1121 --- /dev/null +++ b/app/views/phone_sip_accounts/_form_core.html.haml @@ -0,0 +1,2 @@ +.inputs + = f.association :sip_account, :collection => @available_sip_accounts, :label => t('phone_sip_accounts.form.sip_account_id.label'), :hint => conditional_hint('phone_sip_accounts.form.sip_account_id.hint'), :include_blank => false diff --git a/app/views/phone_sip_accounts/_index_core.html.haml b/app/views/phone_sip_accounts/_index_core.html.haml new file mode 100644 index 0000000..89afb2b --- /dev/null +++ b/app/views/phone_sip_accounts/_index_core.html.haml @@ -0,0 +1,13 @@ +%table + %tr + %th= t('phone_sip_accounts.index.phone_id') + %th= t('phone_sip_accounts.index.sip_account_id') + %th= t('phone_sip_accounts.index.position') + + - reset_cycle + - for phone_sip_account in phone_sip_accounts + %tr{:class => cycle('odd', 'even')} + %td= phone_sip_account.phone + %td= phone_sip_account.sip_account + %td= phone_sip_account.position + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => phone_sip_account.phone, :child => phone_sip_account}
\ No newline at end of file diff --git a/app/views/phone_sip_accounts/index.html.haml b/app/views/phone_sip_accounts/index.html.haml new file mode 100644 index 0000000..a9e3f85 --- /dev/null +++ b/app/views/phone_sip_accounts/index.html.haml @@ -0,0 +1,6 @@ +- title t("phone_sip_accounts.index.page_title") + +- if @phone_sip_accounts.count > 0 + =render "index_core", :phone_sip_accounts => @phone_sip_accounts + += render :partial => 'shared/create_link', :locals => {:parent => @phone, :child_class => PhoneSipAccount}
\ No newline at end of file diff --git a/app/views/phone_sip_accounts/new.html.haml b/app/views/phone_sip_accounts/new.html.haml new file mode 100644 index 0000000..bfe40b8 --- /dev/null +++ b/app/views/phone_sip_accounts/new.html.haml @@ -0,0 +1,3 @@ +- title t("phone_sip_accounts.new.page_title") + += render "form" diff --git a/app/views/phone_sip_accounts/show.html.haml b/app/views/phone_sip_accounts/show.html.haml new file mode 100644 index 0000000..0dd5a9b --- /dev/null +++ b/app/views/phone_sip_accounts/show.html.haml @@ -0,0 +1,13 @@ +- title t("phone_sip_accounts.show.page_title") + +%p + %strong= t('phone_sip_accounts.show.phone_id') + ":" + = @phone_sip_account.phone +%p + %strong= t('phone_sip_accounts.show.sip_account_id') + ":" + = @phone_sip_account.sip_account +%p + %strong= t('phone_sip_accounts.show.position') + ":" + = @phone_sip_account.position + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @phone, :child => @phone_sip_account }
\ No newline at end of file diff --git a/app/views/phones/_form.html.haml b/app/views/phones/_form.html.haml new file mode 100644 index 0000000..9bfa226 --- /dev/null +++ b/app/views/phones/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([ @phoneable, @phone ]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('phones.form.submit')
\ No newline at end of file diff --git a/app/views/phones/_form_core.html.haml b/app/views/phones/_form_core.html.haml new file mode 100644 index 0000000..51ceff5 --- /dev/null +++ b/app/views/phones/_form_core.html.haml @@ -0,0 +1,8 @@ +.inputs + = f.input :mac_address, :label => t('phones.form.mac_address.label'), :hint => conditional_hint('phones.form.mac_address.hint') + = f.association :phone_model, :label => t('phones.form.phone_model_id.label'), :hint => conditional_hint('phones.form.phone_model_id.hint') + = f.input :hot_deskable, :label => t('phones.form.hot_deskable.label'), :hint => conditional_hint('phones.form.hot_deskable.hint') + - if defined? NIGHTLY_REBOOT_OF_PHONES && NIGHTLY_REBOOT_OF_PHONES == true + = f.input :nightly_reboot, :label => t('phones.form.nightly_reboot.label'), :hint => conditional_hint('phones.form.nightly_reboot.hint') + - if defined? PROVISIONING_KEY_LENGTH && PROVISIONING_KEY_LENGTH > 0 + = f.input :provisioning_key_active, :label => t('phones.form.provisioning_key_active.label'), :hint => conditional_hint('phones.form.provisioning_key_active.hint') diff --git a/app/views/phones/_index_core.html.haml b/app/views/phones/_index_core.html.haml new file mode 100644 index 0000000..c442d7f --- /dev/null +++ b/app/views/phones/_index_core.html.haml @@ -0,0 +1,15 @@ +%table + %tr + %th= t('phones.index.mac_address') + %th= t('phones.index.phone_model_id') + %th= t('phones.index.hot_deskable') + %th= t('phones.index.ip_address') + + - reset_cycle + - for phone in phones + %tr{:class => cycle('odd', 'even')} + %td= phone.pretty_mac_address + %td= phone.phone_model + %td= phone.hot_deskable + %td= phone.ip_address + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => phone.phoneable, :child => phone}
\ No newline at end of file diff --git a/app/views/phones/edit.html.haml b/app/views/phones/edit.html.haml new file mode 100644 index 0000000..cdbacac --- /dev/null +++ b/app/views/phones/edit.html.haml @@ -0,0 +1,3 @@ +- title t("phones.edit.page_title", :resource => @phone.mac_address) + += render "form" diff --git a/app/views/phones/index.html.haml b/app/views/phones/index.html.haml new file mode 100644 index 0000000..785adf9 --- /dev/null +++ b/app/views/phones/index.html.haml @@ -0,0 +1,6 @@ +- title t("phones.index.page_title") + +- if @phones.count > 0 + = render "index_core", :phones => @phones + += render :partial => 'shared/create_link', :locals => {:parent => @parent, :child_class => Phone}
\ No newline at end of file diff --git a/app/views/phones/new.html.haml b/app/views/phones/new.html.haml new file mode 100644 index 0000000..61923cc --- /dev/null +++ b/app/views/phones/new.html.haml @@ -0,0 +1,3 @@ +- title t("phones.new.page_title") + += render "form" diff --git a/app/views/phones/show.html.haml b/app/views/phones/show.html.haml new file mode 100644 index 0000000..2664ffa --- /dev/null +++ b/app/views/phones/show.html.haml @@ -0,0 +1,31 @@ +- title t("phones.show.page_title") + +%p + %strong= t('phones.show.mac_address') + ":" + = @phone.pretty_mac_address +%p + %strong= t('phones.show.phone_model_id') + ":" + = @phone.phone_model +%p + %strong= t('phones.show.hot_deskable') + ":" + = @phone.hot_deskable +- if defined? NIGHTLY_REBOOT_OF_PHONES && NIGHTLY_REBOOT_OF_PHONES == true + %p + %strong= t('phones.show.nightly_reboot') + ":" + = @phone.nightly_reboot + +- if defined? PROVISIONING_KEY_LENGTH && PROVISIONING_KEY_LENGTH > 0 + %p + %strong= t('phones.show.provisioning_key_active') + ":" + = @phone.provisioning_key_active +%p + %strong= t('phones.show.ip_address') + ":" + = @phone.ip_address + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @phone.phoneable, :child => @phone } + +%h2= t("phones.sip_accounts.title") +- if @phone.phone_sip_accounts.count > 0 + = render "phone_sip_accounts/index_core", :phone_sip_accounts => @phone.phone_sip_accounts + += render :partial => 'shared/create_link', :locals => {:parent => @phone, :child_class => PhoneSipAccount} diff --git a/app/views/ringtones/_form.html.haml b/app/views/ringtones/_form.html.haml new file mode 100644 index 0000000..7dbfcb0 --- /dev/null +++ b/app/views/ringtones/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([@parent,@ringtone]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('ringtones.form.submit')
\ No newline at end of file diff --git a/app/views/ringtones/_form_core.html.haml b/app/views/ringtones/_form_core.html.haml new file mode 100644 index 0000000..e44c950 --- /dev/null +++ b/app/views/ringtones/_form_core.html.haml @@ -0,0 +1,3 @@ +.inputs + / = f.input :audio, :label => t('ringtones.form.audio.label'), :hint => conditional_hint('ringtones.form.audio.hint') + = f.input :bellcore_id, :collection => 0..10, :label => t('ringtones.form.bellcore_id.label'), :hint => conditional_hint('ringtones.form.bellcore_id.hint'), :include_blank => true diff --git a/app/views/ringtones/_index_core.html.haml b/app/views/ringtones/_index_core.html.haml new file mode 100644 index 0000000..c39357a --- /dev/null +++ b/app/views/ringtones/_index_core.html.haml @@ -0,0 +1,11 @@ +%table + %tr + %th= t('ringtones.index.audio') + %th= t('ringtones.index.bellcore_id') + + - reset_cycle + - for ringtone in ringtones + %tr{:class => cycle('odd', 'even')} + %td= ringtone.audio + %td= ringtone.bellcore_id + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => ringtone.ringtoneable, :child => ringtone}
\ No newline at end of file diff --git a/app/views/ringtones/edit.html.haml b/app/views/ringtones/edit.html.haml new file mode 100644 index 0000000..6779190 --- /dev/null +++ b/app/views/ringtones/edit.html.haml @@ -0,0 +1,3 @@ +- title t("ringtones.edit.page_title") + += render "form" diff --git a/app/views/ringtones/index.html.haml b/app/views/ringtones/index.html.haml new file mode 100644 index 0000000..4da75fa --- /dev/null +++ b/app/views/ringtones/index.html.haml @@ -0,0 +1,6 @@ +- title t("ringtones.index.page_title") + +- if @ringtones.count > 0 + = render "index_core", :ringtones => @ringtones + += render :partial => 'shared/create_link', :locals => {:parent => @parent, :child_class => Ringtone}
\ No newline at end of file diff --git a/app/views/ringtones/new.html.haml b/app/views/ringtones/new.html.haml new file mode 100644 index 0000000..025f440 --- /dev/null +++ b/app/views/ringtones/new.html.haml @@ -0,0 +1,3 @@ +- title t("ringtones.new.page_title") + += render "form" diff --git a/app/views/ringtones/show.html.haml b/app/views/ringtones/show.html.haml new file mode 100644 index 0000000..408b808 --- /dev/null +++ b/app/views/ringtones/show.html.haml @@ -0,0 +1,12 @@ +- title t("ringtones.show.page_title") + +- if 1 == 2 + %p + %strong= t('ringtones.show.audio') + ":" + = @ringtone.audio + +%p + %strong= t('ringtones.show.bellcore_id') + ":" + = @ringtone.bellcore_id + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @ringtone.ringtoneable, :child => @ringtone }
\ No newline at end of file diff --git a/app/views/sessions/new.html.haml b/app/views/sessions/new.html.haml new file mode 100644 index 0000000..8ad77b1 --- /dev/null +++ b/app/views/sessions/new.html.haml @@ -0,0 +1,8 @@ +- title t("sessions.new.page_title") + += simple_form_for :sessions, :url => sessions_path do |t| + = t.input :login_data, :label => t('sessions.form.email'), :autofocus => true + = t.input :password, :label => t('sessions.form.password'), :required => false + = t.input :reset_password, :label => t('sessions.form.reset_password'), :as => :boolean + .actions + = t.button :submit, :value => 'Login' diff --git a/app/views/shared/_create_link.html.haml b/app/views/shared/_create_link.html.haml new file mode 100644 index 0000000..103c82b --- /dev/null +++ b/app/views/shared/_create_link.html.haml @@ -0,0 +1,11 @@ +- if !(defined? parent).nil? && !(defined? child_class).nil? + - if can? :create, parent.send(child_class.name.underscore.pluralize).build + %p + - if t("#{child_class.name.underscore.pluralize}.index.actions.create_for").include?('translation missing') || (!(defined? short_link).nil? && short_link == true) + = link_to t("#{child_class.name.underscore.pluralize}.index.actions.create"), method( :"new_#{parent.class.name.underscore}_#{child_class.name.underscore}_path" ).(parent) + - else + = link_to t("#{child_class.name.underscore.pluralize}.index.actions.create_for", :resource => parent.to_s), method( :"new_#{parent.class.name.underscore}_#{child_class.name.underscore}_path" ).(parent) +- elsif !(defined? child_class).nil? + - if can? :create, child_class + %p + = link_to t("#{child_class.name.underscore.pluralize}.index.actions.create"), method( :"new_#{child_class.name.underscore}_path" ).()
\ No newline at end of file diff --git a/app/views/shared/_flash.html.haml b/app/views/shared/_flash.html.haml new file mode 100644 index 0000000..320fd15 --- /dev/null +++ b/app/views/shared/_flash.html.haml @@ -0,0 +1,19 @@ +- flash.each do |type, msg| + .flash{:class => type} + .light + .sign= resolve_flash_sign(type) + .message= msg + + +-# These are the available types: +-# +-# .flash.notice +-# .light +-# .sign i +-# .message Lorem ipsum dolor sit amet, consectetur adipisicing eli.w +-# +-# .flash.warning +-# .light +-# .sign ! +-# .message Lorem ipsum dolor sit amet, consectetur adipisicing eli.w + diff --git a/app/views/shared/_header.de.html.haml b/app/views/shared/_header.de.html.haml new file mode 100644 index 0000000..c6205ae --- /dev/null +++ b/app/views/shared/_header.de.html.haml @@ -0,0 +1,41 @@ +%header#main + .light + %h1.gemeinschaft-logo + - if @current_user && @current_user.current_tenant + = link_to "Gemeinschaft", tenant_path(@current_user.current_tenant) + - else + = link_to "Gemeinschaft", root_url + + - if current_user + = form_tag '/search' do + %div.search-box + - if GuiFunction.display?('search_field_in_top_navigation_bar', current_user) + %input.text{:value => 'Suchen ...', :name => 'q'} + %input{:type => 'submit', :value => ''} + + / Adjustable Navigation. + - if current_user + - if navigation_items.size > 0 + - navigation_items.each do |item| + - if GuiFunction.display?('navigation_items_in_top_navigation_bar', current_user) + %span + = link_to item[:title], item[:url] + + - if current_user + .user-context + %a.user{:href => tenant_user_path(current_user.current_tenant.id, current_user.id)} + - if GuiFunction.display?('user_avatar_in_top_navigation_bar', current_user) + - if current_user.image? && current_user.image_url(:mini) + = image_tag current_user.image_url(:mini).to_s, :class => 'display' + - else + - if current_user.male? + = image_tag 'icons/user-male-16x.png', :class => 'display logged-out' + - else + = image_tag 'icons/user-female-16x.png', :class => 'display logged-out' + = current_user + = link_to( "[x]", log_out_path, :class => 'logout', :title => "Abmelden" ) # Temporary way of logging out. + - else + .user-context + = link_to "Registrieren", sign_up_path + oder + = link_to "Anmelden", log_in_path diff --git a/app/views/shared/_header.html.haml b/app/views/shared/_header.html.haml new file mode 100644 index 0000000..377d8e0 --- /dev/null +++ b/app/views/shared/_header.html.haml @@ -0,0 +1,41 @@ +%header#main + .light + %h1.gemeinschaft-logo + - if @current_user && @current_user.current_tenant + = link_to "Gemeinschaft", tenant_path(@current_user.current_tenant) + - else + = link_to "Gemeinschaft", root_url + + - if current_user + = form_tag '/search' do + %div.search-box + - if GuiFunction.display?('search_field_in_top_navigation_bar', current_user) + %input.text{:value => 'Search ...', :name => 'q'} + %input{:type => 'submit', :value => ''} + + / Adjustable Navigation. + - if current_user + - if navigation_items.size > 0 + - navigation_items.each do |item| + - if GuiFunction.display?('navigation_items_in_top_navigation_bar', current_user) + %span + = link_to item[:title], item[:url] + + - if current_user + .user-context + %a.user{:href => tenant_user_path(current_user.current_tenant.id, current_user.id)} + - if GuiFunction.display?('user_avatar_in_top_navigation_bar', current_user) + - if current_user.image? && current_user.image_url(:mini) + = image_tag current_user.image_url(:mini).to_s, :class => 'display' + - else + - if current_user.male? + = image_tag 'icons/user-male-16x.png', :class => 'display logged-out' + - else + = image_tag 'icons/user-female-16x.png', :class => 'display logged-out' + = current_user + = link_to( "[x]", log_out_path, :class => 'logout', :title => "Log out" ) # Temporary way of logging out. + - else + .user-context + = link_to "Sign up", sign_up_path + or + = link_to "Log in", log_in_path diff --git a/app/views/shared/_index_view_edit_destroy_part.html.haml b/app/views/shared/_index_view_edit_destroy_part.html.haml new file mode 100644 index 0000000..06ec904 --- /dev/null +++ b/app/views/shared/_index_view_edit_destroy_part.html.haml @@ -0,0 +1,29 @@ +- style = 'width:35px' + +- if !(defined? parent).nil? && !(defined? child).nil? + %td{ :style => style } + - if can? :show, child + = link_to t("#{child.class.name.underscore.pluralize}.index.actions.show"), method( :"#{parent.class.name.underscore}_#{child.class.name.underscore}_path" ).(parent, child) + %td{ :style => style } + - if can? :edit, child + = link_to t("#{child.class.name.underscore.pluralize}.index.actions.edit"), method( :"edit_#{parent.class.name.underscore}_#{child.class.name.underscore}_path" ).(parent, child) + %td{ :style => style } + - if can? :destroy, child + = link_to t("#{child.class.name.underscore.pluralize}.index.actions.destroy"), method( :"#{parent.class.name.underscore}_#{child.class.name.underscore}_path" ).(parent, child), :method => :delete + - if child.respond_to?(:move_up?) or child and child.respond_to?(:move_down?) + %td{ :style => style } + - if can? :move_down, child and child.respond_to?(:move_down?) and child.move_down? + = link_to '⇩'.html_safe, method( :"move_lower_#{parent.class.name.underscore}_#{child.class.name.underscore}_path" ).(parent, child), :method => :put + - if can? :move_up, child and child.respond_to?(:move_up?) and child.move_up? + = link_to '⇧'.html_safe, method( :"move_higher_#{parent.class.name.underscore}_#{child.class.name.underscore}_path" ).(parent, child), :method => :put + +- elsif !(defined? child).nil? + %td{ :style => style } + - if can? :show, child + = link_to t("#{child.class.name.underscore.pluralize}.index.actions.show"), method( :"#{child.class.name.underscore}_path" ).(child) + %td{ :style => style } + - if can? :edit, child + = link_to t("#{child.class.name.underscore.pluralize}.index.actions.edit"), method( :"edit_#{child.class.name.underscore}_path" ).(child) + %td{ :style => style } + - if can? :destroy, child + = link_to t("#{child.class.name.underscore.pluralize}.index.actions.destroy"), method( :"#{child.class.name.underscore}_path" ).(child), :method => :delete
\ No newline at end of file diff --git a/app/views/shared/_show_edit_destroy_part.html.haml b/app/views/shared/_show_edit_destroy_part.html.haml new file mode 100644 index 0000000..aff18d1 --- /dev/null +++ b/app/views/shared/_show_edit_destroy_part.html.haml @@ -0,0 +1,16 @@ +%p + - if !(defined? parent).nil? && !(defined? child).nil? + - if can? :edit, child + = link_to t("#{child.class.name.underscore.pluralize}.show.actions.edit"), method( :"edit_#{parent.class.name.underscore}_#{child.class.name.underscore}_path" ).(parent, child) + - if can? :destroy, child + - if can? :edit, child + | + = link_to t("#{child.class.name.underscore.pluralize}.show.actions.destroy"), method( :"#{parent.class.name.underscore}_#{child.class.name.underscore}_path" ).(parent, child), :method => :delete + + - elsif !(defined? child).nil? + - if can? :edit, child + = link_to t("#{child.class.name.underscore.pluralize}.show.actions.edit"), method( :"edit_#{child.class.name.underscore}_path" ).(child) + - if can? :destroy, child + - if can? :edit, child + | + = link_to t("#{child.class.name.underscore.pluralize}.show.actions.destroy"), method( :"#{child.class.name.underscore}_path" ).(child), :method => :delete
\ No newline at end of file diff --git a/app/views/shared/_system_message.html.haml b/app/views/shared/_system_message.html.haml new file mode 100644 index 0000000..4aabb9c --- /dev/null +++ b/app/views/shared/_system_message.html.haml @@ -0,0 +1,10 @@ +- if current_user + .flash.notice#system_message_display + .light + .sign i + .message#system_message This is the place to display incoming calls and other stuff. + + = subscribe_to "/users/#{current_user.id}/system_messages" + + :javascript + $('#system_message_display').hide()
\ No newline at end of file diff --git a/app/views/sip_accounts/_form.html.haml b/app/views/sip_accounts/_form.html.haml new file mode 100644 index 0000000..f209bf4 --- /dev/null +++ b/app/views/sip_accounts/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([ @parent, @sip_account ]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('sip_accounts.form.submit')
\ No newline at end of file diff --git a/app/views/sip_accounts/_form_core.html.haml b/app/views/sip_accounts/_form_core.html.haml new file mode 100644 index 0000000..dbd27fe --- /dev/null +++ b/app/views/sip_accounts/_form_core.html.haml @@ -0,0 +1,12 @@ +.inputs + = f.input :auth_name, :as => :string, :label => t('sip_accounts.form.auth_name.label'), :hint => conditional_hint('sip_accounts.form.auth_name.hint') + = f.input :password, :as => :string, :label => t('sip_accounts.form.password.label'), :hint => conditional_hint('sip_accounts.form.password.hint') + = f.input :caller_name, :as => :string, :label => t('sip_accounts.form.caller_name.label'), :hint => conditional_hint('sip_accounts.form.caller_name.hint') + = f.input :voicemail_pin, :as => :string, :label => t('sip_accounts.form.voicemail_pin.label'), :hint => conditional_hint('sip_accounts.form.voicemail_pin.hint') + = f.input :call_waiting, :label => t('sip_accounts.form.call_waiting.label'), :hint => conditional_hint('sip_accounts.form.call_waiting.hint') + = f.input :clir, :label => t('sip_accounts.form.clir.label'), :hint => conditional_hint('sip_accounts.form.clir.hint') + = f.input :clip, :label => t('sip_accounts.form.clip.label'), :hint => conditional_hint('sip_accounts.form.clip.hint') + = f.input :hotdeskable, :label => t('sip_accounts.form.hotdeskable.label'), :hint => conditional_hint('sip_accounts.form.hotdeskable.hint') + = f.input :clip_no_screening, :label => t('sip_accounts.form.clip_no_screening.label'), :hint => conditional_hint('sip_accounts.form.clip_no_screening.hint') + - if CallForward.where(:phone_number_id => @sip_account.phone_number_ids).count == 0 || @sip_account.callforward_rules_act_per_sip_account == true + = f.input :callforward_rules_act_per_sip_account, :label => t('sip_accounts.form.callforward_rules_act_per_sip_account.label'), :hint => conditional_hint('sip_accounts.form.callforward_rules_act_per_sip_account.hint') diff --git a/app/views/sip_accounts/_index_core.html.haml b/app/views/sip_accounts/_index_core.html.haml new file mode 100644 index 0000000..7f8dcd2 --- /dev/null +++ b/app/views/sip_accounts/_index_core.html.haml @@ -0,0 +1,28 @@ +%table + %tr + %th= t('sip_accounts.index.online') + %th= t('sip_accounts.index.caller_name') + %th= t('sip_accounts.index.phone_numbers') + %th= t('phones.name') + + - reset_cycle + - for sip_account in sip_accounts + %tr{:class => cycle('odd', 'even')} + %td + - if sip_account.registration + %img{:src => '/assets/icons/phone-down-green-32x.png'} + - else + %img{:src => '/assets/icons/phone-down-grey-32x.png'} + %td + = sip_account.caller_name + - phone_numbers = sip_account.phone_numbers + %td + - if sip_account.phone_numbers.count > 0 + = render 'phone_numbers/listing', :phone_numbers => sip_account.phone_numbers.order(:number) + %br + = render :partial => 'shared/create_link', :locals => {:parent => sip_account, :child_class => PhoneNumber, :short_link => true} + + %td + - sip_account.phones.each do |phone| + = link_to phone.to_s, method( :"#{phone.phoneable_type.underscore}_phone_path" ).( phone.phoneable_id, phone ) + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => sip_account.sip_accountable, :child => sip_account}
\ No newline at end of file diff --git a/app/views/sip_accounts/edit.html.haml b/app/views/sip_accounts/edit.html.haml new file mode 100644 index 0000000..c070ff7 --- /dev/null +++ b/app/views/sip_accounts/edit.html.haml @@ -0,0 +1,3 @@ +- title t("sip_accounts.edit.page_title") + += render "form" diff --git a/app/views/sip_accounts/index.html.haml b/app/views/sip_accounts/index.html.haml new file mode 100644 index 0000000..1131770 --- /dev/null +++ b/app/views/sip_accounts/index.html.haml @@ -0,0 +1,6 @@ +- title t("sip_accounts.index.page_title") + +- if @sip_accounts.count > 0 + = render "index_core", :sip_accounts => @sip_accounts + += render :partial => 'shared/create_link', :locals => {:parent => @parent, :child_class => SipAccount}
\ No newline at end of file diff --git a/app/views/sip_accounts/new.html.haml b/app/views/sip_accounts/new.html.haml new file mode 100644 index 0000000..9d44680 --- /dev/null +++ b/app/views/sip_accounts/new.html.haml @@ -0,0 +1,3 @@ +- title t("sip_accounts.new.page_title") + += render "form" diff --git a/app/views/sip_accounts/show.html.haml b/app/views/sip_accounts/show.html.haml new file mode 100644 index 0000000..c6344cd --- /dev/null +++ b/app/views/sip_accounts/show.html.haml @@ -0,0 +1,50 @@ +- title t("sip_accounts.show.page_title") + +%p + %strong= t('sip_accounts.show.auth_name') + ":" + = @sip_account.auth_name +%p + %strong= t('sip_accounts.show.caller_name') + ":" + = @sip_account.caller_name +%p + %strong= t('sip_accounts.show.password') + ":" + = @sip_account.password +%p + %strong= t('sip_accounts.show.call_waiting') + ":" + = @sip_account.call_waiting +%p + %strong= t('sip_accounts.show.clir') + ":" + = @sip_account.clir +%p + %strong= t('sip_accounts.show.clip_no_screening') + ":" + = @sip_account.clip_no_screening +%p + %strong= t('sip_accounts.show.hotdeskable') + ":" + = @sip_account.hotdeskable +%p + %strong= t('sip_accounts.show.callforward_rules_act_per_sip_account') + ":" + = @sip_account.callforward_rules_act_per_sip_account +- if @sip_account.registration.try(:network_ip) && @sip_account.registration.try(:network_port) + %p + %strong= t('sip_accounts.show.registration') + ":" + = "#{@sip_account.registration.network_ip}:#{@sip_account.registration.network_port}" +- if @sip_account.registration.try(:expires) + %p + %strong= t('sip_accounts.show.expires') + ":" + = "#{@sip_account.registration.try(:expires) - Time.now.to_i} s" + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @sip_account.sip_accountable, :child => @sip_account } + +- if @sip_account.phone_numbers.count > 0 || can?(:create, @sip_account.phone_numbers.build) + %h2= t('phone_numbers.index.page_title') + - if @sip_account.phone_numbers.count > 0 + = render "phone_numbers/index_core", :phone_numbers => @sip_account.phone_numbers + %br + = render :partial => 'shared/create_link', :locals => { :parent => @sip_account, :child_class => PhoneNumber } + +- if @sip_account.softkeys.count > 0 || can?(:create, @sip_account.softkeys.build) + %h2= t("softkeys.index.page_title") + - if @sip_account.softkeys.count > 0 + = render "softkeys/index_core", :softkeys => @sip_account.softkeys + %br + = render :partial => 'shared/create_link', :locals => { :parent => @sip_account, :child_class => Softkey }
\ No newline at end of file diff --git a/app/views/sip_domains/_form.html.haml b/app/views/sip_domains/_form.html.haml new file mode 100644 index 0000000..2d662af --- /dev/null +++ b/app/views/sip_domains/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for(@sip_domain) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('sip_domains.form.submit')
\ No newline at end of file diff --git a/app/views/sip_domains/_form_core.html.haml b/app/views/sip_domains/_form_core.html.haml new file mode 100644 index 0000000..a7f024f --- /dev/null +++ b/app/views/sip_domains/_form_core.html.haml @@ -0,0 +1,3 @@ +.inputs + = f.input :host, :label => t('sip_domains.form.host.label'), :hint => conditional_hint('sip_domains.form.host.hint') + = f.input :realm, :label => t('sip_domains.form.realm.label'), :hint => conditional_hint('sip_domains.form.realm.hint') diff --git a/app/views/sip_domains/_index_core.html.haml b/app/views/sip_domains/_index_core.html.haml new file mode 100644 index 0000000..37374f2 --- /dev/null +++ b/app/views/sip_domains/_index_core.html.haml @@ -0,0 +1,11 @@ +%table + %tr + %th= t('sip_domains.index.host') + %th= t('sip_domains.index.realm') + + - reset_cycle + - for sip_domain in sip_domains + %tr{:class => cycle('odd', 'even')} + %td= sip_domain.host + %td= sip_domain.realm + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:child => sip_domain}
\ No newline at end of file diff --git a/app/views/sip_domains/edit.html.haml b/app/views/sip_domains/edit.html.haml new file mode 100644 index 0000000..dcf8d6b --- /dev/null +++ b/app/views/sip_domains/edit.html.haml @@ -0,0 +1,3 @@ +- title t("sip_domains.edit.page_title") + += render "form" diff --git a/app/views/sip_domains/index.html.haml b/app/views/sip_domains/index.html.haml new file mode 100644 index 0000000..6de7f42 --- /dev/null +++ b/app/views/sip_domains/index.html.haml @@ -0,0 +1,6 @@ +- title t("sip_domains.index.page_title") + +- if @sip_domains.count > 0 + = render "index_core", :sip_domains => @sip_domains + += render :partial => 'shared/create_link', :locals => {:child_class => SipDomain}
\ No newline at end of file diff --git a/app/views/sip_domains/new.html.haml b/app/views/sip_domains/new.html.haml new file mode 100644 index 0000000..12ff340 --- /dev/null +++ b/app/views/sip_domains/new.html.haml @@ -0,0 +1,3 @@ +- title t("sip_domains.new.page_title") + += render "form" diff --git a/app/views/sip_domains/show.html.haml b/app/views/sip_domains/show.html.haml new file mode 100644 index 0000000..e136eaf --- /dev/null +++ b/app/views/sip_domains/show.html.haml @@ -0,0 +1,10 @@ +- title t("sip_domains.show.page_title") + +%p + %strong= t('sip_domains.show.host') + ":" + = @sip_domain.host +%p + %strong= t('sip_domains.show.realm') + ":" + = @sip_domain.realm + += render :partial => 'shared/show_edit_destroy_part', :locals => { :child => @sip_domain }
\ No newline at end of file diff --git a/app/views/softkeys/_form.html.haml b/app/views/softkeys/_form.html.haml new file mode 100644 index 0000000..5b799b6 --- /dev/null +++ b/app/views/softkeys/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([@sip_account, @softkey]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('softkeys.form.submit')
\ No newline at end of file diff --git a/app/views/softkeys/_form_core.html.haml b/app/views/softkeys/_form_core.html.haml new file mode 100644 index 0000000..b833aad --- /dev/null +++ b/app/views/softkeys/_form_core.html.haml @@ -0,0 +1,12 @@ +%script{:type => "text/javascript"} + :plain + call_forwarding_function_name = "#{I18n.t('softkeys.functions.call_forwarding')}" + hold_function_name = "#{I18n.t('softkeys.functions.hold')}" + deactivated_function_name = "#{I18n.t('softkeys.functions.deactivated')}" + +.inputs + = f.input :softkey_function_id, :as => :select, :collection => @softkey_functions.map {|x| [I18n.t("softkeys.functions.#{x}"), x.id] }, :label => t('softkeys.form.function.label'), :hint => conditional_hint('softkeys.form.function.hint'), :include_blank => false + - if @available_call_forwards && @available_call_forwards.count > 0 + = f.association :call_forward, :collection => @available_call_forwards, :label => t('softkeys.form.call_forward.label'), :hint => conditional_hint('softkeys.form.call_forward.hint'), :include_blank => false + = f.input :number, :label => t('softkeys.form.number.label'), :hint => conditional_hint('softkeys.form.number.hint') + = f.input :label, :label => t('softkeys.form.label.label'), :hint => conditional_hint('softkeys.form.label.hint') diff --git a/app/views/softkeys/_index_core.html.haml b/app/views/softkeys/_index_core.html.haml new file mode 100644 index 0000000..fd3dca8 --- /dev/null +++ b/app/views/softkeys/_index_core.html.haml @@ -0,0 +1,14 @@ +%table + %tr + %th= t('softkeys.index.function') + %th= t('softkeys.index.number') + %th= t('softkeys.index.label') + + - reset_cycle + - for softkey in softkeys.order(:position) + %tr{:class => cycle('odd', 'even')} + %td + =softkey.to_s + %td= softkey.number + %td= softkey.label + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => softkey.sip_account, :child => softkey}
\ No newline at end of file diff --git a/app/views/softkeys/edit.html.haml b/app/views/softkeys/edit.html.haml new file mode 100644 index 0000000..54d53fc --- /dev/null +++ b/app/views/softkeys/edit.html.haml @@ -0,0 +1,3 @@ +- title t("softkeys.edit.page_title") + += render "form"
\ No newline at end of file diff --git a/app/views/softkeys/index.html.haml b/app/views/softkeys/index.html.haml new file mode 100644 index 0000000..8bdc00e --- /dev/null +++ b/app/views/softkeys/index.html.haml @@ -0,0 +1,6 @@ +- title t("softkeys.index.page_title") + +- if @softkeys.count > 0 + = render "index_core", :softkeys => @softkeys + += render :partial => 'shared/create_link', :locals => {:parent => @sip_account, :child_class => Softkey}
\ No newline at end of file diff --git a/app/views/softkeys/new.html.haml b/app/views/softkeys/new.html.haml new file mode 100644 index 0000000..593add6 --- /dev/null +++ b/app/views/softkeys/new.html.haml @@ -0,0 +1,3 @@ +- title t("softkeys.new.page_title") + += render "form" diff --git a/app/views/softkeys/show.html.haml b/app/views/softkeys/show.html.haml new file mode 100644 index 0000000..9ab4333 --- /dev/null +++ b/app/views/softkeys/show.html.haml @@ -0,0 +1,7 @@ +- title t("softkeys.show.page_title") + +%p + %strong= t('softkeys.show.function') + ":" + =@softkey.to_s + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @softkey.sip_account, :child => @softkey }
\ No newline at end of file diff --git a/app/views/system_messages/_form.html.haml b/app/views/system_messages/_form.html.haml new file mode 100644 index 0000000..036ee00 --- /dev/null +++ b/app/views/system_messages/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([@user, @system_message]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('system_messages.form.submit')
\ No newline at end of file diff --git a/app/views/system_messages/_form_core.html.haml b/app/views/system_messages/_form_core.html.haml new file mode 100644 index 0000000..a85db28 --- /dev/null +++ b/app/views/system_messages/_form_core.html.haml @@ -0,0 +1,2 @@ +.inputs + = f.input :content, :label => t('system_messages.form.content.label'), :hint => conditional_hint('system_messages.form.content.hint') diff --git a/app/views/system_messages/_index_core.html.haml b/app/views/system_messages/_index_core.html.haml new file mode 100644 index 0000000..157d964 --- /dev/null +++ b/app/views/system_messages/_index_core.html.haml @@ -0,0 +1,11 @@ +%table + %tr + %th= t('system_messages.index.created_at') + %th= t('system_messages.index.content') + + - reset_cycle + - for system_message in system_messages + %tr{:class => cycle('odd', 'even')} + %td= system_message.created_at + %td= system_message.content + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:child => system_message}
\ No newline at end of file diff --git a/app/views/system_messages/index.html.haml b/app/views/system_messages/index.html.haml new file mode 100644 index 0000000..da77e18 --- /dev/null +++ b/app/views/system_messages/index.html.haml @@ -0,0 +1,3 @@ +- title t("system_messages.index.page_title") + += render "index_core", :system_messages => @system_messages
\ No newline at end of file diff --git a/app/views/system_messages/new.html.haml b/app/views/system_messages/new.html.haml new file mode 100644 index 0000000..3afdb24 --- /dev/null +++ b/app/views/system_messages/new.html.haml @@ -0,0 +1,3 @@ +- title t("system_messages.new.page_title") + += render "form" diff --git a/app/views/system_messages/show.html.haml b/app/views/system_messages/show.html.haml new file mode 100644 index 0000000..694e4c1 --- /dev/null +++ b/app/views/system_messages/show.html.haml @@ -0,0 +1,8 @@ +- title t("system_messages.show.page_title") + +%p + %strong= t('system_messages.show.created_at') + ":" + = @system_message.created_at +%p + %strong= t('system_messages.show.content') + ":" + = @system_message.content diff --git a/app/views/tenants/_admin_area.de.html.haml b/app/views/tenants/_admin_area.de.html.haml new file mode 100644 index 0000000..b9b47d5 --- /dev/null +++ b/app/views/tenants/_admin_area.de.html.haml @@ -0,0 +1,118 @@ +%p + Sie sind Mitglied der + = link_to 'Admin Gruppe', tenant_user_group_path(@tenant, @tenant.user_groups.find_by_name('Admins')) + und haben deshalb besondere Rechte. Aber wie Peter Parker schon sagte: "With great power comes great responsibility." + +%p + Dieser Mandant hat + = link_to pluralize(@tenant.user_groups.count, 'user group'), tenant_user_groups_path(@tenant) + - if @tenant.user_groups.count < 5 + = "(#{@tenant.user_groups.order(:name).map{|group| group.to_s }.join(', ')})" + die in Summe + = link_to pluralize(@tenant.users.count, 'user'), tenant_users_path(@tenant) + verwalten. + Das System kann + = PhoneModel.count + verschiedene Telefonmodelle von den folgenden Herstellern verwalten: + - Manufacturer.all.each do |manufacturer| + - if manufacturer != Manufacturer.last && manufacturer != Manufacturer.limit(Manufacturer.count - 1).last + = succeed ', ' do + =link_to manufacturer, manufacturer_path(manufacturer) + - elsif manufacturer == Manufacturer.limit(Manufacturer.count - 1).last + = succeed ' und ' do + =link_to manufacturer, manufacturer_path(manufacturer) + - else + = succeed '.' do + =link_to manufacturer, manufacturer_path(manufacturer) + +%h3 SIP-Konten und Telefone + +%table + %tr{:class => 'even'} + %th + %th + = @tenant + %th + Alle Benutzer von + = "\"#{@tenant}\"" + %tr{:class => 'odd'} + %td + SIP-Konten + %td + = link_to @tenant.sip_accounts.count.to_s, tenant_sip_accounts_path(@tenant) + = render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => SipAccount} + %td= @tenant.users_sip_accounts.count.to_s + %tr{:class => 'even'} + %td + Telefone + %td + = link_to @tenant.phones.count.to_s, tenant_phones_path(@tenant) + = render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => Phone} + %td= @tenant.users_phones.count.to_s + +%h3 Allgemein + +%table + %tr{:class => 'even'} + %th + Funktion + %th + Anzahl + %th + %tr{:class => 'odd'} + %td + Callthrough + %td + = link_to @tenant.callthroughs.count.to_s, tenant_callthroughs_path(@tenant) + %td + = render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => Callthrough} + %tr{:class => 'even'} + %td + Konferenzen + %td + = link_to @tenant.conferences.count.to_s, tenant_conferences_path(@tenant) + %td + = render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => Conference} + %tr{:class => 'odd'} + %td + Rufgruppen + %td + = link_to @tenant.hunt_groups.count.to_s, tenant_hunt_groups_path(@tenant) + %td + = render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => HuntGroup} + %tr{:class => 'even'} + %td + Warteschleifen + %td + = link_to @tenant.automatic_call_distributors.count.to_s, tenant_automatic_call_distributors_path(@tenant) + %td + = render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => AutomaticCallDistributor} + %tr{:class => 'odd'} + %td + Oberflächen-Funktionen + %td + = link_to GuiFunction.count.to_s, gui_functions_path + %td + +-# Phone books +-# +- if GuiFunction.display?('show_phone_books_in_user_show_view', current_user) + - if can?( :index, PhoneBook ) + %h2=t("phone_books.index.page_title") + = render "phone_books/index_core", :phone_books => @tenant.phone_books + = render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => PhoneBook} + +- if STRICT_INTERNAL_EXTENSION_HANDLING == true + %h3= t('phone_number_ranges.index.page_title') + + - if @tenant.created_at > (Time.now - 15.minutes) && Delayed::Job.count > 0 && @tenant.phone_number_ranges.find_by_name(INTERNAL_EXTENSIONS).try(:phone_numbers).try(:count).to_i == 0 + Der Mandant + = "\"#{@tenant}\"" + wurde erst vor + = distance_of_time_in_words_to_now(@tenant.created_at) + erstellt. Es gibt immer noch nicht abgeschlossene + = pluralize(Delayed::Job.count, 'Hintergrundprozesse') + \. Bitte warten Sie noch ein paar Minuten und laden anschließend diese Seite erneut. + - else + =render 'phone_number_ranges/index_core', :phone_number_ranges => (@tenant.phone_number_ranges + @tenant.country.phone_number_ranges.where(:name => SERVICE_NUMBERS)) + =render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => PhoneNumberRange} diff --git a/app/views/tenants/_admin_area.html.haml b/app/views/tenants/_admin_area.html.haml new file mode 100644 index 0000000..d648143 --- /dev/null +++ b/app/views/tenants/_admin_area.html.haml @@ -0,0 +1,116 @@ +%p + You belong to the + = link_to 'admin group', tenant_user_group_path(@tenant, @tenant.user_groups.find_by_name('Admins')) + and therefore have super powers. But always remember Peter Parker's: "With great power comes great responsibility." + +%p + = succeed '.' do + This tenant has + = link_to pluralize(@tenant.user_groups.count, 'user group'), tenant_user_groups_path(@tenant) + - if @tenant.user_groups.count < 5 + = "(#{@tenant.user_groups.order(:name).map{|group| group.to_s }.join(', ')})" + which handle a total of + = link_to pluralize(@tenant.users.count, 'user'), tenant_users_path(@tenant) + This system can setup + = PhoneModel.count + different phone models from the manufacturers + - Manufacturer.all.each do |manufacturer| + - if manufacturer != Manufacturer.last && manufacturer != Manufacturer.limit(Manufacturer.count - 1).last + = succeed ', ' do + =link_to manufacturer, manufacturer_path(manufacturer) + - elsif manufacturer == Manufacturer.limit(Manufacturer.count - 1).last + = succeed ' and ' do + =link_to manufacturer, manufacturer_path(manufacturer) + - else + = succeed '.' do + =link_to manufacturer, manufacturer_path(manufacturer) + +%h3 SIP-Accounts and Phones + +%table + %tr{:class => 'even'} + %th + %th + = @tenant + %th + All users of + = "\"#{@tenant}\"" + %tr{:class => 'odd'} + %td + SIP accounts + %td + = link_to @tenant.sip_accounts.count.to_s, tenant_sip_accounts_path(@tenant) + = render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => SipAccount} + %td= @tenant.users_sip_accounts.count.to_s + %tr{:class => 'even'} + %td + Phones + %td + = link_to @tenant.phones.count.to_s, tenant_phones_path(@tenant) + = render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => Phone} + %td= @tenant.users_phones.count.to_s + +%h3 Misc + +%table + %tr{:class => 'even'} + %th + Feature + %th + Counter + %th + %tr{:class => 'odd'} + %td + Callthroughs + %td + = link_to @tenant.callthroughs.count.to_s, tenant_callthroughs_path(@tenant) + %td + = render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => Callthrough} + %tr{:class => 'even'} + %td + Conferences + %td + = link_to @tenant.conferences.count.to_s, tenant_conferences_path(@tenant) + %td + = render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => Conference} + %tr{:class => 'odd'} + %td + Hunt groups + %td + = link_to @tenant.hunt_groups.count.to_s, tenant_hunt_groups_path(@tenant) + %td + = render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => HuntGroup} + %tr{:class => 'even'} + %td + ACDs + %td + = link_to @tenant.automatic_call_distributors.count.to_s, tenant_automatic_call_distributors_path(@tenant) + %td + = render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => AutomaticCallDistributor} + %tr{:class => 'odd'} + %td + GUI functions + %td + = link_to GuiFunction.count.to_s, gui_functions_path + %td + +-# Phone books +-# +- if GuiFunction.display?('show_phone_books_in_user_show_view', current_user) + - if can?( :index, PhoneBook ) + %h2=t("phone_books.index.page_title") + = render "phone_books/index_core", :phone_books => @tenant.phone_books + = render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => PhoneBook} + +- if STRICT_INTERNAL_EXTENSION_HANDLING == true + %h3= t('phone_number_ranges.index.page_title') + + - if @tenant.created_at > (Time.now - 15.minutes) && Delayed::Job.count > 0 && @tenant.phone_number_ranges.find_by_name(INTERNAL_EXTENSIONS).try(:phone_numbers).try(:count).to_i == 0 + This tenant was created + = distance_of_time_in_words_to_now(@tenant.created_at) + ago. There are still + = pluralize(Delayed::Job.count, 'background job') + not finished. This can take a couple of minutes. Please reload this page later. + - else + =render 'phone_number_ranges/index_core', :phone_number_ranges => (@tenant.phone_number_ranges + @tenant.country.phone_number_ranges.where(:name => SERVICE_NUMBERS)) + =render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => PhoneNumberRange} diff --git a/app/views/tenants/_form.html.haml b/app/views/tenants/_form.html.haml new file mode 100644 index 0000000..2ca8a69 --- /dev/null +++ b/app/views/tenants/_form.html.haml @@ -0,0 +1,24 @@ += simple_form_for(@tenant) do |f| + = f.error_notification + + = render "form_core", :f => f + + = f.association :country, :label => t('tenants.form.country_id.label'), :hint => conditional_hint('tenants.form.country_id.hint'), :include_blank => false + = f.association :language, :label => t('tenants.form.language_id.label'), :hint => conditional_hint('tenants.form.language_id.hint'), :include_blank => false + + = f.association :sip_domain, :label => t('tenants.form.sip_domain.label'), :hint => conditional_hint('tenants.form.sip_domain.hint'), :include_blank => false + + = f.input :from_field_voicemail_email, :label => t('tenants.form.from_field_voicemail_email.label'), :hint => conditional_hint('tenants.form.from_field_voicemail_email.hint') + = f.input :from_field_pin_change_email, :label => t('tenants.form.from_field_pin_change_email.label'), :hint => conditional_hint('tenants.form.from_field_pin_change_email.hint') + + - if STRICT_INTERNAL_EXTENSION_HANDLING == true || STRICT_DID_HANDLING == true + %h2= t('tenants.form.phone_numbers') + %p= t('tenants.form.intro') + + - if STRICT_INTERNAL_EXTENSION_HANDLING == true + = f.input :internal_extension_ranges, :label => t('tenants.form.internal_extension_ranges.label'), :hint => conditional_hint('tenants.form.internal_extension_ranges.hint') + - if STRICT_DID_HANDLING == true + = f.input :did_list, :label => t('tenants.form.did_list.label'), :hint => conditional_hint('tenants.form.did_list.hint') + + .actions + = f.button :submit, conditional_t('tenants.form.submit')
\ No newline at end of file diff --git a/app/views/tenants/_form_core.html.haml b/app/views/tenants/_form_core.html.haml new file mode 100644 index 0000000..4eb2ccc --- /dev/null +++ b/app/views/tenants/_form_core.html.haml @@ -0,0 +1,3 @@ +.inputs + = f.input :name, :label => t('tenants.form.name.label'), :hint => conditional_hint('tenants.form.name.hint') + = f.input :description, :label => t('tenants.form.description.label'), :hint => conditional_hint('tenants.form.description.hint') diff --git a/app/views/tenants/_index_core.html.haml b/app/views/tenants/_index_core.html.haml new file mode 100644 index 0000000..60afeee --- /dev/null +++ b/app/views/tenants/_index_core.html.haml @@ -0,0 +1,17 @@ +%table + %tr + %th= t('tenants.index.name') + %th= t('tenants.index.description') + + - reset_cycle + - for tenant in tenants + %tr{:class => cycle('odd', 'even')} + %td= tenant.name + %td= tenant.description + %td + - if current_user && current_user.current_tenant != tenant && current_user.tenants.include?(tenant) + = simple_form_for([current_user.current_tenant, current_user]) do |f| + = f.hidden_field :current_tenant_id, :value => tenant.id + .actions + = f.button :submit, conditional_t('tenants.switch_to_tenant') + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:child => tenant}
\ No newline at end of file diff --git a/app/views/tenants/edit.html.haml b/app/views/tenants/edit.html.haml new file mode 100644 index 0000000..159f4fd --- /dev/null +++ b/app/views/tenants/edit.html.haml @@ -0,0 +1,3 @@ +- title t("tenants.edit.page_title", :resource => @tenant ) + += render "form" diff --git a/app/views/tenants/index.html.haml b/app/views/tenants/index.html.haml new file mode 100644 index 0000000..1783825 --- /dev/null +++ b/app/views/tenants/index.html.haml @@ -0,0 +1,6 @@ +- title t("tenants.index.page_title") + +- if @tenants.count > 0 + = render "index_core", :tenants => @tenants + += render :partial => 'shared/create_link', :locals => {:child_class => Tenant}
\ No newline at end of file diff --git a/app/views/tenants/new.html.haml b/app/views/tenants/new.html.haml new file mode 100644 index 0000000..dca3809 --- /dev/null +++ b/app/views/tenants/new.html.haml @@ -0,0 +1,3 @@ +- title t("tenants.new.page_title") + += render "form" diff --git a/app/views/tenants/show.html.haml b/app/views/tenants/show.html.haml new file mode 100644 index 0000000..cb2b895 --- /dev/null +++ b/app/views/tenants/show.html.haml @@ -0,0 +1,14 @@ +- title t("tenants.show.page_title") + +%p + %strong= t('tenants.show.name') + ":" + = @tenant.name +- if !@tenant.description.blank? + %p + %strong= t('tenants.show.description') + ":" + = @tenant.description + += render :partial => 'shared/show_edit_destroy_part', :locals => { :child => @tenant } + +- if @tenant.user_groups.where(:name => 'Admins').count > 0 && @tenant.user_groups.where(:name => 'Admins').first.users.include?(current_user) + = render 'admin_area'
\ No newline at end of file diff --git a/app/views/user_group_memberships/_form.html.haml b/app/views/user_group_memberships/_form.html.haml new file mode 100644 index 0000000..3c0fee1 --- /dev/null +++ b/app/views/user_group_memberships/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([@user_group, @user_group_membership]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('user_group_memberships.form.submit')
\ No newline at end of file diff --git a/app/views/user_group_memberships/_form_core.html.haml b/app/views/user_group_memberships/_form_core.html.haml new file mode 100644 index 0000000..e77427f --- /dev/null +++ b/app/views/user_group_memberships/_form_core.html.haml @@ -0,0 +1,2 @@ +.inputs + = f.input :user_id, :label => t('hunt_groups.form.user.label'), :hint => conditional_hint('hunt_groups.form.user.hint'), :collection => @potential_users, :include_blank => false
\ No newline at end of file diff --git a/app/views/user_group_memberships/_index_core.html.haml b/app/views/user_group_memberships/_index_core.html.haml new file mode 100644 index 0000000..3c3cebe --- /dev/null +++ b/app/views/user_group_memberships/_index_core.html.haml @@ -0,0 +1,13 @@ +%table + %tr + %th= t('user_group_memberships.index.tenant') + %th= t('user_group_memberships.index.user_group') + %th= t('user_group_memberships.index.user') + + - reset_cycle + - for user_group_membership in user_group_memberships + %tr{:class => cycle('odd', 'even')} + %td= user_group_membership.user_group.tenant + %td= user_group_membership.user_group + %td= user_group_membership.user + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => user_group_membership.user_group, :child => user_group_membership}
\ No newline at end of file diff --git a/app/views/user_group_memberships/edit.html.haml b/app/views/user_group_memberships/edit.html.haml new file mode 100644 index 0000000..2080c87 --- /dev/null +++ b/app/views/user_group_memberships/edit.html.haml @@ -0,0 +1,3 @@ +- title t("user_group_memberships.edit.page_title", :resource => @user_group_membership) + += render "form" diff --git a/app/views/user_group_memberships/index.html.haml b/app/views/user_group_memberships/index.html.haml new file mode 100644 index 0000000..1d7927b --- /dev/null +++ b/app/views/user_group_memberships/index.html.haml @@ -0,0 +1,7 @@ +- title t("user_group_memberships.index.page_title") + +- if @user_group_memberships.count > 0 + = render "index_core", :user_group_memberships => @user_group_memberships + +- if @potential_users_count > 0 + = render :partial => 'shared/create_link', :locals => {:parent => @user_group, :child_class => UserGroupMembership}
\ No newline at end of file diff --git a/app/views/user_group_memberships/new.html.haml b/app/views/user_group_memberships/new.html.haml new file mode 100644 index 0000000..9d59fdd --- /dev/null +++ b/app/views/user_group_memberships/new.html.haml @@ -0,0 +1,3 @@ +- title t("user_group_memberships.new.page_title") + += render "form" diff --git a/app/views/user_group_memberships/show.html.haml b/app/views/user_group_memberships/show.html.haml new file mode 100644 index 0000000..0e5cfab --- /dev/null +++ b/app/views/user_group_memberships/show.html.haml @@ -0,0 +1,7 @@ +- title t("user_group_memberships.show.page_title") + +%p + %strong= t('user_group_memberships.show.user') + ":" + = @user_group_membership.user + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @user_group, :child => @user_group_membership }
\ No newline at end of file diff --git a/app/views/user_groups/_form.html.haml b/app/views/user_groups/_form.html.haml new file mode 100644 index 0000000..cbe3cc4 --- /dev/null +++ b/app/views/user_groups/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for(@user_group) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('user_groups.form.submit')
\ No newline at end of file diff --git a/app/views/user_groups/_form_core.html.haml b/app/views/user_groups/_form_core.html.haml new file mode 100644 index 0000000..a45ba6f --- /dev/null +++ b/app/views/user_groups/_form_core.html.haml @@ -0,0 +1,3 @@ +.inputs + = f.input :name, :label => t('user_groups.form.name.label'), :hint => conditional_hint('user_groups.form.name.hint') + = f.input :description, :label => t('user_groups.form.description.label'), :hint => conditional_hint('user_groups.form.description.hint') diff --git a/app/views/user_groups/_index_core.html.haml b/app/views/user_groups/_index_core.html.haml new file mode 100644 index 0000000..d2b6e88 --- /dev/null +++ b/app/views/user_groups/_index_core.html.haml @@ -0,0 +1,24 @@ +%table + %tr + %th= t('user_groups.index.name') + %th= t('user_groups.index.description') + - if @user + %th= t('user_groups.index.tenant_id') + - else + %th= t('user_groups.index.members') + + - reset_cycle + - for user_group in user_groups + %tr{:class => cycle('odd', 'even')} + %td= user_group.name + %td= user_group.description + - if @user + %td= user_group.tenant + - else + %td + =render 'users/listing', :users => user_group.users + - if user_group.users.count > 1 + %br + = render :partial => 'shared/create_link', :locals => {:parent => user_group, :child_class => UserGroupMembership} + + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => user_group.tenant, :child => user_group} diff --git a/app/views/user_groups/edit.html.haml b/app/views/user_groups/edit.html.haml new file mode 100644 index 0000000..35514e0 --- /dev/null +++ b/app/views/user_groups/edit.html.haml @@ -0,0 +1,3 @@ +- title t("user_groups.edit.page_title", :resource => @user_group) + += render "form" diff --git a/app/views/user_groups/index.html.haml b/app/views/user_groups/index.html.haml new file mode 100644 index 0000000..545b838 --- /dev/null +++ b/app/views/user_groups/index.html.haml @@ -0,0 +1,6 @@ +- title t("user_groups.index.page_title") + +- if @user_groups.count > 0 + = render "index_core", :user_groups => @user_groups + += render :partial => 'shared/create_link', :locals => {:parent => @parent, :child_class => UserGroup}
\ No newline at end of file diff --git a/app/views/user_groups/new.html.haml b/app/views/user_groups/new.html.haml new file mode 100644 index 0000000..dfef18e --- /dev/null +++ b/app/views/user_groups/new.html.haml @@ -0,0 +1,3 @@ +- title t("user_groups.new.page_title") + += render "form" diff --git a/app/views/user_groups/show.html.haml b/app/views/user_groups/show.html.haml new file mode 100644 index 0000000..00bdeb7 --- /dev/null +++ b/app/views/user_groups/show.html.haml @@ -0,0 +1,20 @@ +- title t("user_groups.show.page_title") + +%p + %strong= t('user_groups.show.name') + ":" + = @user_group.name +%p + %strong= t('user_groups.show.description') + ":" + = @user_group.description +%p + %strong= t('user_groups.show.tenant_id') + ":" + = @user_group.tenant + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @user_group.tenant, :child => @user_group } + +%h2=t("user_group_memberships.index.page_title") + +- if @user_group.user_group_memberships.count > 0 + = render "user_group_memberships/index_core", :user_group_memberships => @user_group.user_group_memberships + += render :partial => 'shared/create_link', :locals => {:parent => @user_group, :child_class => UserGroupMembership}
\ No newline at end of file diff --git a/app/views/users/_form.html.haml b/app/views/users/_form.html.haml new file mode 100644 index 0000000..9a75677 --- /dev/null +++ b/app/views/users/_form.html.haml @@ -0,0 +1,16 @@ +- if @parent && @parent.class == Tenant + = simple_form_for([@parent, @user]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('users.form.submit') +- else + = simple_form_for(@user) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('users.form.submit') diff --git a/app/views/users/_form_core.html.haml b/app/views/users/_form_core.html.haml new file mode 100644 index 0000000..8e18d12 --- /dev/null +++ b/app/views/users/_form_core.html.haml @@ -0,0 +1,27 @@ +.inputs + - if GuiFunction.display?('name_data_fields_in_user_edit_form', current_user) + = f.input :male, :collection => [[true, t('users.form.gender.male')], [false, t('users.form.gender.female')]], :label_method => :last, :value_method => :first, :label => t('users.form.male.label'), :hint => conditional_hint('users.form.gender.hint'), :label => t('users.form.gender.label'), :as => :radio + = f.input :first_name, :label => t('users.form.first_name.label'), :hint => conditional_hint('users.form.first_name.hint'), :autofocus => true + = f.input :middle_name, :label => t('users.form.middle_name.label'), :hint => conditional_hint('users.form.middle_name.hint') + = f.input :last_name, :label => t('users.form.last_name.label'), :hint => conditional_hint('users.form.last_name.hint') + - if GuiFunction.display?('user_name_field_in_user_edit_form', current_user) + = f.input :user_name, :label => t('users.form.user_name.label'), :hint => conditional_hint('users.form.user_name.hint') + - if GuiFunction.display?('email_field_in_user_edit_form', current_user) + = f.input :email, :label => t('users.form.email.label'), :hint => conditional_hint('users.form.email.hint') + + - if GuiFunction.display?('password_fields_in_user_edit_form', current_user) + = f.input :password, :label => t('users.form.password.label'), :hint => conditional_hint('users.form.password.hint'), :as => :password + = f.input :password_confirmation, :label => t('users.form.password_confirmation.label'), :hint => conditional_hint('users.form.password_confirmation.hint'), :as => :password + + - if GuiFunction.display?('pin_fields_in_user_edit_form', current_user) + = f.input :new_pin, :label => t('users.form.new_pin.label'), :hint => conditional_hint('users.form.new_pin.hint'), :as => :password + = f.input :new_pin_confirmation, :label => t('users.form.new_pin_confirmation.label'), :hint => conditional_hint('users.form.new_pin_confirmation.hint'), :as => :password + + = f.input :image, { :as => :file, :label => t('users.form.image.label'), :hint => conditional_hint('users.form.image.hint') } + - if @user && @user.image? + %p + =link_to 'Destroy avatar', tenant_user_destroy_avatar_path(@tenant, @user) + + = f.input :language_id, :collection => Language.all, :label => t('users.form.language_id.label'), :hint => conditional_hint('users.form.language_id.hint'), :include_blank => false + + /= f.input :send_voicemail_as_email_attachment, :label => t('users.form.send_voicemail_as_email_attachment.label'), :hint => conditional_hint('users.form.send_voicemail_as_email_attachment.hint') diff --git a/app/views/users/_index_core.html.haml b/app/views/users/_index_core.html.haml new file mode 100644 index 0000000..51c15de --- /dev/null +++ b/app/views/users/_index_core.html.haml @@ -0,0 +1,18 @@ +%table + %tr + %th + %th= t('users.index.user_name') + %th= t('users.index.email') + %th= t('users.index.first_name') + %th= t('users.index.last_name') + + - reset_cycle + - for user in users + %tr{:class => cycle('odd', 'even')} + %td + = image_tag user.image_url(:mini).to_s if user.image_url(:mini) + %td= user.user_name + %td= user.email + %td= user.first_name + %td= user.last_name + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => @tenant, :child => user}
\ No newline at end of file diff --git a/app/views/users/_listing.html.haml b/app/views/users/_listing.html.haml new file mode 100644 index 0000000..0a97ad1 --- /dev/null +++ b/app/views/users/_listing.html.haml @@ -0,0 +1,8 @@ +- amount_of_users = users.count +- if amount_of_users > 0 + - if amount_of_users < 30 + = users.map{|user| user}.join(', ') + - else + = users.limit(15).map{|user| user}.join(', ') + ', ' + = '[...]' + = users.offset(amount_of_users - 15).map{|user| user}.join(', ')
\ No newline at end of file diff --git a/app/views/users/edit.html.haml b/app/views/users/edit.html.haml new file mode 100644 index 0000000..96272f5 --- /dev/null +++ b/app/views/users/edit.html.haml @@ -0,0 +1,3 @@ +- title t("users.edit.page_title", :resource => @user) + += render "form" diff --git a/app/views/users/index.html.haml b/app/views/users/index.html.haml new file mode 100644 index 0000000..892e035 --- /dev/null +++ b/app/views/users/index.html.haml @@ -0,0 +1,6 @@ +- title t("users.index.page_title") + +- if @users.count > 0 + = render "index_core", :users => @users + += render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => User}
\ No newline at end of file diff --git a/app/views/users/new.html.haml b/app/views/users/new.html.haml new file mode 100644 index 0000000..a014611 --- /dev/null +++ b/app/views/users/new.html.haml @@ -0,0 +1,3 @@ +- title t("users.new.page_title") + += render "form" diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml new file mode 100644 index 0000000..7730447 --- /dev/null +++ b/app/views/users/show.html.haml @@ -0,0 +1,96 @@ +- title "User: #{@user}" + +#user-show + %aside + = image_tag @user.image_url(:small).to_s, class: 'display' if @user.image? && @user.image_url(:small) + %p + %strong= t('users.show.user_name') + ":" + = @user.user_name + %p + %strong= t('users.show.email') + ":" + = @user.email + + %p.controls + = render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @tenant, :child => @user } + + - @user.sip_accounts.each do |sip_account| + - phone_number = sip_account.phone_numbers.order(:number).last + - if phone_number && !phone_number.number.blank? && phone_number.number[0] != '+' + %p + %strong= sip_account.phone_numbers.order(:number).last.number + %p + =link_to t("call_histories.index.page_title"), sip_account_call_histories_path(sip_account) + %br + =link_to t("voicemail_messages.index.page_title"), sip_account_voicemail_messages_path(sip_account) + %br + =link_to t("call_forwards.index.page_title"), phone_number_call_forwards_path(phone_number) + %br + =link_to t("voicemail_settings.index.page_title"), sip_account_voicemail_settings_path(sip_account) + %br + =link_to t("softkeys.index.page_title"), sip_account_softkeys_path(sip_account) + %br + =link_to t("ringtones.show.page_title"), phone_number_ringtones_path(phone_number) + + - if @user.conferences.any? + %p + %strong= t("conferences.index.page_title") + - @user.conferences.each do |conference| + %p + =link_to conference, edit_user_conference_path(@user, conference) + + + %section + -# Phone books + -# + - if GuiFunction.display?('show_phone_books_in_user_show_view', current_user) + - if can?( :index, PhoneBook ) + %h2=t("phone_books.index.page_title") + = render "phone_books/index_core", :phone_books => @phone_books + = render :partial => 'shared/create_link', :locals => {:parent => @user, :child_class => PhoneBook} + + -# User groups (only if the current user can edit or destroy them) + -# + - if @user.user_groups.map{ |x| can?( :edit, x ) || can?( :destroy, x ) }.include?(true) + - if can?( :index, UserGroup ) + %h2=t("user_groups.index.page_title") + - if @user.user_groups.count > 0 + = render "user_groups/index_core", :user_groups => @user.user_groups.where(:tenant_id => @tenant.id).order(:name) + = render :partial => 'shared/create_link', :locals => {:parent => @tenant, :child_class => UserGroup} + + -# SIP accounts + -# + - if (can?( :index, SipAccount ) && @user.sip_accounts.count > 0 ) || can?( :create, SipAccount ) + %h2= t('sip_accounts.index.page_title') + - if can?( :index, SipAccount ) && @user.sip_accounts.count > 0 + = render "sip_accounts/index_core", :sip_accounts => @user.sip_accounts + = render :partial => 'shared/create_link', :locals => {:parent => @user, :child_class => SipAccount} + + -# Phones + -# + - if (can?( :index, Phone, :phoneable => @user ) && @user.phones.count > 0 ) || can?( :create, Phone, :phoneable => @user ) + %h2= t('phones.index.page_title') + - if can?( :index, Phone, :phoneable => @user ) && @user.phones.count > 0 + = render "phones/index_core", :phones => @user.phones + = render :partial => 'shared/create_link', :locals => {:parent => @user, :child_class => Phone} + + -# FaxAccount + -# + - if (can?( :index, FaxAccount ) && @user.fax_accounts.count > 0 ) || can?( :create, FaxAccount ) + %h2= t('fax_accounts.index.page_title') + - if can?( :index, FaxAccount ) && @user.fax_accounts.count > 0 + = render "fax_accounts/index_core", {:fax_accounts => @user.fax_accounts, :fax_accountable => @user} + = render :partial => 'shared/create_link', :locals => {:parent => @user, :child_class => FaxAccount} + + -# Conferences + -# + - if (can?( :index, Conference ) && @user.conferences.count > 0 ) || can?( :create, Conference ) + %h2= t('conferences.index.page_title') + - if can?( :index, Conference ) && @user.conferences.count > 0 + = render "conferences/index_core", :conferences => @user.conferences + = render :partial => 'shared/create_link', :locals => {:parent => @user, :child_class => Conference} + + -# Tenants + -# + - if can?( :index, Tenant ) && @user.tenants.count > 1 + %h2=t("tenants.index.page_title") + = render "tenants/index_core", :tenants => @user.tenants
\ No newline at end of file diff --git a/app/views/voicemail_messages/_index_core.html.haml b/app/views/voicemail_messages/_index_core.html.haml new file mode 100644 index 0000000..b8e47af --- /dev/null +++ b/app/views/voicemail_messages/_index_core.html.haml @@ -0,0 +1,44 @@ += form_tag(destroy_multiple_sip_account_voicemail_messages_path(@sip_account), :method => :delete, :id => 'voicemail_message_form') do + %header.entries-nav= render :partial => "voicemail_messages/navigation" + .content + %table + - reset_cycle + - for voicemail_message in voicemail_messages + %tr.voicemail-messages-entry{:class => cycle('odd', 'even'), :id => "message_#{voicemail_message.uuid}"} + %td.select_box= check_box_tag("selected_uuids[]", voicemail_message.uuid, false, :uuid => "select_item_#{voicemail_message.uuid}", :class => 'select_item') + %td.time + .voicemail-received + = voicemail_message.format_date(voicemail_message.created_epoch, t("voicemail_messages.index.date_format"), t("voicemail_messages.index.date_today_format")) + + - read_date = voicemail_message.format_date(voicemail_message.read_epoch, t("voicemail_messages.index.date_format"), t("voicemail_messages.index.date_today_format")) + - if read_date + .voicemail-read + = read_date + %td.folder + = t("voicemail_messages.index.mailbox.#{voicemail_message.in_folder}") + %td.user + .name= voicemail_message.cid_name + .phone= voicemail_message.cid_number + %td.status + .duration= voicemail_message.display_duration + %td + - if ! voicemail_message.flags.blank? + = t("voicemail_messages.index.flags.#{voicemail_message.flags}") + %td.actions + - if can?(:show, voicemail_message) && File.readable?(voicemail_message.file_path) + = link_to t('voicemail_messages.index.actions.download'), sip_account_voicemail_message_path(@sip_account, voicemail_message, :format => :wav), :method => :get + %td.actions + - if @sip_account.registration && can?(:call, voicemail_message) + = link_to t('voicemail_messages.index.actions.call'), call_sip_account_voicemail_message_path(@sip_account, voicemail_message), :method => :put + %td.actions + - if can?(:edit, voicemail_message) && voicemail_message.read_epoch > 0 + = link_to t('voicemail_messages.index.actions.mark_unread'), mark_unread_sip_account_voicemail_message_path(@sip_account, voicemail_message), :method => :put + - else + = link_to t('voicemail_messages.index.actions.mark_read'), mark_read_sip_account_voicemail_message_path(@sip_account, voicemail_message), :method => :put + %td.actions + - if can? :destroy, voicemail_message + = link_to t('voicemail_messages.index.actions.destroy'), sip_account_voicemail_message_path(@sip_account, voicemail_message), :method => :delete + + %footer.entries-nav= render :partial => "voicemail_messages/navigation" + = image_submit_tag('icons/cross-16x.png', :confirm => t("voicemail_messages.index.actions.confirm_selected")) + = t("voicemail_messages.index.actions.destroy_multiple") diff --git a/app/views/voicemail_messages/_navigation.html.haml b/app/views/voicemail_messages/_navigation.html.haml new file mode 100644 index 0000000..2277bf2 --- /dev/null +++ b/app/views/voicemail_messages/_navigation.html.haml @@ -0,0 +1,9 @@ +%nav + %ol.abc + %li + %a{ :href => "?type=" }= t('voicemail_messages.index.navigation.all', :count => @messages_count) + %a{ :href => "?type=read" }= t('voicemail_messages.index.navigation.read', :count => @messages_read_count) + %a{ :href => "?type=unread" }= t('voicemail_messages.index.navigation.unread', :count => @messages_unread_count) + +.pagination + = will_paginate @voicemail_messages diff --git a/app/views/voicemail_messages/index.html.haml b/app/views/voicemail_messages/index.html.haml new file mode 100644 index 0000000..53f8090 --- /dev/null +++ b/app/views/voicemail_messages/index.html.haml @@ -0,0 +1,6 @@ +- if @type + - title t("voicemail_messages.index.page_title_#{@type}") +- else + - title t("voicemail_messages.index.page_title") + += render "index_core", :voicemail_messages => @voicemail_messages diff --git a/app/views/voicemail_settings/_form.html.haml b/app/views/voicemail_settings/_form.html.haml new file mode 100644 index 0000000..6d5f845 --- /dev/null +++ b/app/views/voicemail_settings/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([@sip_account,@voicemail_setting]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('voicemail_settings.form.submit')
\ No newline at end of file diff --git a/app/views/voicemail_settings/_form_core.html.haml b/app/views/voicemail_settings/_form_core.html.haml new file mode 100644 index 0000000..08bdfc2 --- /dev/null +++ b/app/views/voicemail_settings/_form_core.html.haml @@ -0,0 +1,11 @@ +.inputs + + = f.input :greeting_path, :as => :select, :label => t('voicemail_settings.form.greeting.label'), :hint => conditional_hint('voicemail_settings.form.greeting.hint'), :collection => @greeting_files + = f.input :name_path, :as => :select, :label => t('voicemail_settings.form.name.label'), :hint => conditional_hint('voicemail_settings.form.name.hint'), :collection => @name_files + + = f.input :password, :label => t('voicemail_settings.form.pin.label'), :hint => conditional_hint('voicemail_settings.form.pin.hint') + + = f.input :notify, :as => :boolean, :label => t('voicemail_settings.form.notify.label'), :hint => conditional_hint('voicemail_settings.form.notify.hint') + = f.input :attachment, :as => :boolean, :label => t('voicemail_settings.form.attachment.label'), :hint => conditional_hint('voicemail_settings.form.attachment.hint') + = f.input :mark_read, :as => :boolean, :label => t('voicemail_settings.form.mark_read.label'), :hint => conditional_hint('voicemail_settings.form.mark_read.hint') + = f.input :purge, :as => :boolean, :label => t('voicemail_settings.form.purge.label'), :hint => conditional_hint('voicemail_settings.form.purge.hint') diff --git a/app/views/voicemail_settings/edit.html.haml b/app/views/voicemail_settings/edit.html.haml new file mode 100644 index 0000000..6bd7031 --- /dev/null +++ b/app/views/voicemail_settings/edit.html.haml @@ -0,0 +1,3 @@ +- title t("voicemail_settings.edit.page_title") + += render "form" diff --git a/app/views/voicemail_settings/show.html.haml b/app/views/voicemail_settings/show.html.haml new file mode 100644 index 0000000..30e12d0 --- /dev/null +++ b/app/views/voicemail_settings/show.html.haml @@ -0,0 +1,26 @@ +- title t("voicemail_settings.show.page_title") + +%p + %strong= t('voicemail_settings.show.greeting_path') + ":" + = File.basename(@voicemail_setting.greeting_path.to_s) + +%p + %strong= t('voicemail_settings.show.name_path') + ":" + = File.basename(@voicemail_setting.name_path.to_s) + +%p + %strong= t('voicemail_settings.show.flags') + ":" + - if @voicemail_setting.notify + %br + = "- " + t('voicemail_settings.show.notify') + - if @voicemail_setting.attachment + %br + = "- " + t('voicemail_settings.show.attachment') + - if @voicemail_setting.mark_read + %br + = "- " + t('voicemail_settings.show.mark_read') + - if @voicemail_setting.purge + %br + = "- " + t('voicemail_settings.show.purge') + += link_to t('voicemail_settings.actions.edit'), edit_sip_account_voicemail_setting_path(@sip_account, @voicemail_setting) diff --git a/app/views/whitelists/_form.html.haml b/app/views/whitelists/_form.html.haml new file mode 100644 index 0000000..c7f787a --- /dev/null +++ b/app/views/whitelists/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for([@parent, @whitelist]) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('whitelists.form.submit')
\ No newline at end of file diff --git a/app/views/whitelists/_form_core.html.haml b/app/views/whitelists/_form_core.html.haml new file mode 100644 index 0000000..38f1487 --- /dev/null +++ b/app/views/whitelists/_form_core.html.haml @@ -0,0 +1,8 @@ +.inputs + = f.input :name, :label => t('whitelists.form.name.label'), :hint => conditional_hint('whitelists.form.name.hint') + + %h3= t('whitelists.form.phone_numbers.label') + - if !t('whitelists.form.phone_numbers.hint').blank? + %p= t('whitelists.form.phone_numbers.hint') + = f.simple_fields_for :phone_numbers do |phone_number| + = render "phone_numbers/form_core", :f => phone_number diff --git a/app/views/whitelists/_index_core.html.haml b/app/views/whitelists/_index_core.html.haml new file mode 100644 index 0000000..b4c5b0c --- /dev/null +++ b/app/views/whitelists/_index_core.html.haml @@ -0,0 +1,15 @@ +%table + %tr + %th= t('whitelists.index.name') + %th= t('whitelists.index.phone_numbers') + + - reset_cycle + - for whitelist in whitelists + %tr{:class => cycle('odd', 'even')} + %td= whitelist.name || '-' + %td + = render 'phone_numbers/listing', :phone_numbers => whitelist.phone_numbers + %br + = render :partial => 'shared/create_link', :locals => {:parent => whitelist, :child_class => PhoneNumber, :short_link => true} + + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => whitelist.whitelistable, :child => whitelist}
\ No newline at end of file diff --git a/app/views/whitelists/edit.html.haml b/app/views/whitelists/edit.html.haml new file mode 100644 index 0000000..9f8af90 --- /dev/null +++ b/app/views/whitelists/edit.html.haml @@ -0,0 +1,3 @@ +- title t("whitelists.edit.page_title") + += render "form" diff --git a/app/views/whitelists/index.html.haml b/app/views/whitelists/index.html.haml new file mode 100644 index 0000000..0873189 --- /dev/null +++ b/app/views/whitelists/index.html.haml @@ -0,0 +1,6 @@ +- title t("whitelists.index.page_title") + +- if @whitelists.count > 0 + = render "index_core", :whitelists => @whitelists + += render :partial => 'shared/create_link', :locals => {:parent => @parent, :child_class => Whitelist}
\ No newline at end of file diff --git a/app/views/whitelists/new.html.haml b/app/views/whitelists/new.html.haml new file mode 100644 index 0000000..f1101ad --- /dev/null +++ b/app/views/whitelists/new.html.haml @@ -0,0 +1,3 @@ +- title t("whitelists.new.page_title") + += render "form" diff --git a/app/views/whitelists/show.html.haml b/app/views/whitelists/show.html.haml new file mode 100644 index 0000000..77652f9 --- /dev/null +++ b/app/views/whitelists/show.html.haml @@ -0,0 +1,7 @@ +- title t("whitelists.show.page_title") + +%p + %strong= t('whitelists.show.name') + ":" + = @whitelist.name + += render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @parent, :child => @whitelist }
\ No newline at end of file diff --git a/config.ru b/config.ru new file mode 100644 index 0000000..03f69d2 --- /dev/null +++ b/config.ru @@ -0,0 +1,4 @@ +# This file is used by Rack-based servers to start the application. + +require ::File.expand_path('../config/environment', __FILE__) +run Gemeinschaft42c::Application diff --git a/config/application.rb b/config/application.rb new file mode 100644 index 0000000..d09cf24 --- /dev/null +++ b/config/application.rb @@ -0,0 +1,65 @@ +require File.expand_path('../boot', __FILE__) + +require 'rails/all' + +if defined?(Bundler) + # If you precompile assets before deploying to production, use this line + Bundler.require *Rails.groups(:assets => %w(development test)) + # If you want your assets lazily compiled in production, use this line + # Bundler.require(:default, :assets, Rails.env) +end + +module Gemeinschaft42c + class Application < Rails::Application + # Settings in config/environments/* take precedence over those specified here. + # Application configuration should go into files in config/initializers + # -- all .rb files in that directory are automatically loaded. + + # Custom directories with classes and modules you want to be autoloadable. + # config.autoload_paths += %W(#{config.root}/extras) + + # Only load the plugins named here, in the order given (default is alphabetical). + # :all can be used as a placeholder for all plugins not explicitly named. + # config.plugins = [ :exception_notification, :ssl_requirement, :all ] + + # --- rather use Apache + Passenger for SSL -----{ + #config.plugins = [ :exception_notification, :ssl_requirement, :all ] + + #config.middleware.insert_before ActionDispatch::Static, "Rack::SSL" + #config.middleware.insert_before ActionDispatch::Static, Rack::SSL, :exclude => proc { |env| env['HTTPS'] != 'on' } + #config.force_ssl = true + + #require 'rack/ssl' + #config.middleware.use Rack::SSL + # -----------------------------------------------} + + + # Activate observers that should always be running. + # config.active_record.observers = :cacher, :garbage_collector, :forum_observer + + # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. + # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. + # config.time_zone = 'Central Time (US & Canada)' + config.time_zone = 'Berlin' + config.active_record.default_timezone = :utc + + # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. + config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '*', '*', '*.{rb,yml}').to_s] + # config.i18n.default_locale = :de + + # Configure the default encoding used in templates for Ruby 1.9. + config.encoding = "utf-8" + + # Configure sensitive parameters which will be filtered from the log file. + config.filter_parameters += [:password, :password_confirmation, :pin, :voicemail_pin] + + # Enable the asset pipeline + config.assets.enabled = true + + # Version of your assets, change this if you want to expire all your assets + config.assets.version = '1.0' + + # Load the PhoneControllers + config.autoload_paths += %W(#{config.root}/lib/phone_controllers) + end +end diff --git a/config/boot.rb b/config/boot.rb new file mode 100644 index 0000000..4489e58 --- /dev/null +++ b/config/boot.rb @@ -0,0 +1,6 @@ +require 'rubygems' + +# Set up gems listed in the Gemfile. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) + +require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 0000000..4ea8d4a --- /dev/null +++ b/config/database.yml @@ -0,0 +1,22 @@ +development: + adapter: sqlite3 + database: db/development.sqlite3 + pool: 5 + timeout: 5000 + +production: + adapter: sqlite3 + database: db/development.sqlite3 + pool: 5 + timeout: 5000 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. + +test: + adapter: sqlite3 + database: db/test<%= ENV['TEST_ENV_NUMBER'] %>.sqlite3 + pool: 5 + timeout: 5000 + diff --git a/config/environment.rb b/config/environment.rb new file mode 100644 index 0000000..efe867e --- /dev/null +++ b/config/environment.rb @@ -0,0 +1,5 @@ +# Load the rails application +require File.expand_path('../application', __FILE__) + +# Initialize the rails application +Gemeinschaft42c::Application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb new file mode 100644 index 0000000..bd34e4f --- /dev/null +++ b/config/environments/development.rb @@ -0,0 +1,45 @@ +Gemeinschaft42c::Application.configure do + # Settings specified here will take precedence over those in config/application.rb + + # In the development environment your application's code is reloaded on + # every request. This slows down response time but is perfect for development + # since you don't have to restart the web server when you make code changes. + config.cache_classes = false + + # Log error messages when you accidentally call methods on nil. + config.whiny_nils = true + + # Show full error reports and disable caching + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + + # Don't care if the mailer can't send + config.action_mailer.raise_delivery_errors = false + + # Print deprecation notices to the Rails logger + config.active_support.deprecation = :log + + # Only use best-standards-support built into browsers + config.action_dispatch.best_standards_support = :builtin + + # Raise exception on mass assignment protection for Active Record models + config.active_record.mass_assignment_sanitizer = :strict + + # Log the query plan for queries taking more than this (works + # with SQLite, MySQL, and PostgreSQL) + config.active_record.auto_explain_threshold_in_seconds = 0.5 + + # Do not compress assets + config.assets.compress = false + + # Expands the lines which load the assets + config.assets.debug = true + + # Enable Hirb: + extend Hirb::Console + Hirb::View.enable +end + + + + diff --git a/config/environments/production.rb b/config/environments/production.rb new file mode 100644 index 0000000..4e5bdd8 --- /dev/null +++ b/config/environments/production.rb @@ -0,0 +1,65 @@ +Gemeinschaft42c::Application.configure do + # Settings specified here will take precedence over those in config/application.rb + + # Code is not reloaded between requests + config.cache_classes = true + + # Full error reports are disabled and caching is turned on + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Disable Rails's static asset server (Apache or nginx will already do this) + config.serve_static_assets = true + + # Compress JavaScripts and CSS + config.assets.compress = true + + # Don't fallback to assets pipeline if a precompiled asset is missed + config.assets.compile = false + + # Generate digests for assets URLs + config.assets.digest = true + + # Defaults to Rails.root.join("public/assets") + # config.assets.manifest = YOUR_PATH + + # Specifies the header that your server uses for sending files + # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + # config.force_ssl = true + + # See everything in the log (default is :info) + # config.log_level = :debug + + # Use a different logger for distributed setups + # config.logger = SyslogLogger.new + + # Use a different cache store in production + # config.cache_store = :mem_cache_store + + # Enable serving of images, stylesheets, and JavaScripts from an asset server + # config.action_controller.asset_host = "http://assets.example.com" + + # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) + # config.assets.precompile += %w( search.js ) + config.assets.initialize_on_precompile = false + + # Disable delivery errors, bad email addresses will be ignored + # config.action_mailer.raise_delivery_errors = false + + # Enable threaded mode + # config.threadsafe! + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation can not be found) + config.i18n.fallbacks = true + + # Send deprecation notices to registered listeners + config.active_support.deprecation = :notify + + # Enable Hirb: + extend Hirb::Console + Hirb::View.enable +end diff --git a/config/environments/test.rb b/config/environments/test.rb new file mode 100644 index 0000000..f2d3a67 --- /dev/null +++ b/config/environments/test.rb @@ -0,0 +1,37 @@ +Gemeinschaft42c::Application.configure do + # Settings specified here will take precedence over those in config/application.rb + + # The test environment is used exclusively to run your application's + # test suite. You never need to work with it otherwise. Remember that + # your test database is "scratch space" for the test suite and is wiped + # and recreated between test runs. Don't rely on the data there! + config.cache_classes = true + + # Configure static asset server for tests with Cache-Control for performance + config.serve_static_assets = true + config.static_cache_control = "public, max-age=3600" + + # Log error messages when you accidentally call methods on nil + config.whiny_nils = true + + # Show full error reports and disable caching + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + + # Raise exceptions instead of rendering exception templates + config.action_dispatch.show_exceptions = false + + # Disable request forgery protection in test environment + config.action_controller.allow_forgery_protection = false + + # Tell Action Mailer not to deliver emails to the real world. + # The :test delivery method accumulates sent emails in the + # ActionMailer::Base.deliveries array. + config.action_mailer.delivery_method = :test + + # Raise exception on mass assignment protection for Active Record models + config.active_record.mass_assignment_sanitizer = :strict + + # Print deprecation notices to the stderr + config.active_support.deprecation = :stderr +end diff --git a/config/hirb.yml b/config/hirb.yml new file mode 100644 index 0000000..f011c9d --- /dev/null +++ b/config/hirb.yml @@ -0,0 +1,201 @@ +:output: + CallForwardCase: + :options: + :fields: + - id + - value + CallForward: + :options: + :fields: + - id + - phone_number + - call_forward_case + - timeout + - destination + - source + - depth + - active + PhoneNumberRange: + :options: + :fields: + - id + - name + - description + - phone_number_rangeable_type + - phone_number_rangeable + AreaCode: + :options: + :fields: + - id + - country + - name + - area_code + - central_office_code + Manufacturer: + :options: + :fields: + - id + - name + - homepage_url + - state + PhoneModel: + :options: + :fields: + - id + - name + - manufacturer + - state + Oui: + :options: + :fields: + - id + - manufacturer + - value + - state + User: + :options: + :fields: + - id + - user_name + - last_name + - email + - current_tenant_id + - current_tenant + Tenant: + :options: + :fields: + - id + - name + - state + - country + - sip_domain + SipDomain: + :options: + :fields: + - id + - host + - realm + PhoneBook: + :options: + :fields: + - id + - name + - phone_bookable_type + - phone_bookable_id + - phone_bookable + - state + PhoneBookEntry: + :options: + :fields: + - id + - phone_book_id + - phone_book + - first_name + - organization + PhoneNumber: + :options: + :fields: + - id + - name + - number + - phone_numberable_type + - phone_numberable_id + Phone: + :options: + :fields: + - id + - phone_model + - mac_address + - ip_address + - last_ip_address + - phoneable_type + - phoneable_id + - phoneable + SipAccount: + :options: + :fields: + - id + - sip_accountable_type + - sip_accountable + - auth_name + - caller_name + - voicemail_pin + TenantMembership: + :options: + :fields: + - id + - tenant + - user + - state + UserGroupMembership: + :options: + :fields: + - id + - user_group + - user + UserGroup: + :options: + :fields: + - id + - name + - tenant + - position + Country: + :options: + :fields: + - id + - name + - country_code + - international_call_prefix + - trunk_prefix + Conference: + :options: + :fields: + - id + - name + - start + - conferenceable_type + - conferenceable + ConferenceInvitee: + :options: + :fields: + - id + - conference + - phone_number + - phone_book_entry_id + - speaker + - moderator + - pin + FaxDocument: + :options: + :fields: + - id + - destination_phone_number + - document + Callthrough: + :options: + :fields: + - id + - tenant + - name + Language: + :options: + :fields: + - id + - name + - code + SoftkeyFunction: + :options: + :fields: + - id + - name + - position + Api::Row: + :options: + :fields: + - id + - last_name + - first_name + - user_name + - user + diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb new file mode 100644 index 0000000..59385cd --- /dev/null +++ b/config/initializers/backtrace_silencers.rb @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. +# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } + +# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. +# Rails.backtrace_cleaner.remove_silencers! diff --git a/config/initializers/connectivity_check.rb b/config/initializers/connectivity_check.rb new file mode 100644 index 0000000..583f372 --- /dev/null +++ b/config/initializers/connectivity_check.rb @@ -0,0 +1,23 @@ +require 'socket' +require 'timeout' + +module Connectivity + + def self.port_open?( ip_addr, port ) + begin + Timeout::timeout(5) { + s = TCPSocket.new( ip_addr, port ) + s.close + return true + } + rescue Errno::ECONNREFUSED + rescue Errno::EHOSTUNREACH + rescue Errno::EADDRNOTAVAIL + rescue SocketError + rescue Timeout::Error + end + + return false + end + +end diff --git a/config/initializers/gemeinschaft_parameters.rb b/config/initializers/gemeinschaft_parameters.rb new file mode 100644 index 0000000..6e89abd --- /dev/null +++ b/config/initializers/gemeinschaft_parameters.rb @@ -0,0 +1,81 @@ +# Use this file to set generic parameters for Gemeinschaft + +GEMEINSCHAFT_VERSION = '5.0' +SUPER_TENANT_NAME = 'Super-Tenant' + +# System defaults +MINIMUM_PIN_LENGTH = 4 +MAXIMUM_PIN_LENGTH = 10 + +# GUI +GUI_REDIRECT_HTTPS = false + +# Phone numbers +# Only touch this if you know what you are doing! +STRICT_INTERNAL_EXTENSION_HANDLING = false +STRICT_DID_HANDLING = false + +# SIP defaults +DEFAULT_LENGTH_SIP_AUTH_NAME = 10 +DEFAULT_LENGTH_SIP_PASSWORD = 15 +CALL_WAITING = false +DEFAULT_CLIR_SETTING = false +DEFAULT_CLIP_SETTING = true + +TO_S_MAX_CALLER_NAME_LENGTH = 20 +TO_S_MAX_LENGTH_OF_AUTH_NAME = 8 + +# Pagination defaults +DEFAULT_PAGINATION_ENTRIES_PER_PAGE = 50 + +# Conference defaults +MAXIMUM_NUMBER_OF_PEOPLE_IN_A_CONFERENCE = 100 +DEFAULT_MAX_CONFERENCE_MEMBERS = 10 + +# Misc defaults +MAX_EXTENSION_LENGTH = 6 + +# Fax defaults +DEFAULT_NUMBER_OF_RETRIES = 3 +DAYS_TILL_AUTO_DELETE = 90 + +# Names of PhoneNumberRanges +INTERNAL_EXTENSIONS = 'internal_extensions' +SERVICE_NUMBERS = 'service_numbers' +DIRECT_INWARD_DIALING_NUMBERS = 'direct_inward_dialing_numbers' + +# Callthrough defaults +CALLTHROUGH_HAS_WHITELISTS = true + +# Hunt groups +HUNT_GROUP_STRATEGIES = ['ring_all', 'ring_recursively'] +VALID_SECONDS_BETWEEN_JUMPS_VALUES = (1 .. 60).to_a.map{|x| x * 2} + +# Callforward +DEFAULT_CALL_FORWARD_DEPTH = 1 +MAX_CALL_FORWARD_DEPTH = 40 +CALLFORWARD_DESTINATION_DEFAULT = '+49' +CALLFORWARD_RULES_ACT_PER_SIP_ACCOUNT_DEFAULT = true + +# Phone +PROVISIONING_AUTO_ADD_PHONE = true +PROVISIONING_AUTO_ADD_SIP_ACCOUNT = true +PROVISIONING_AUTO_TENANT_ID = 2 +PROVISIONING_AUTO_SIP_ACCOUNT_CALLER_PREFIX = 'Gemeinschaft ' +PROVISIONING_IEEE8021X_EAP_USERNAME = '' +PROVISIONING_IEEE8021X_EAP_PASSWORD = '' +NIGHTLY_REBOOT_OF_PHONES = true +SIEMENS_HISTORY_RELOAD_TIMES = {0..6 => 600, 7..20 => 40, 21..24 => 300} + +# API configuration +DEFAULT_API_TENANT_ID = 2 +REMOTE_IP_ADDRESS_WHITELIST = [] # e.g. ['10.0.0.1'] +IMPORT_CSV_FILE = '/var/tmp/ExampleVoipCsvExport.csv' +DOUBLE_CHECK_POSITIVE_USERS_CSV = '/var/tmp/ExampleDoubleCheckVoipCsvExport.csv' +IMPORT_CSV_ENCODING = 'UTF-8' +USER_NAME_PREFIX = 'dtc' +CALLTHROUGH_NAME_TO_BE_USED_FOR_DEFAULT_ACTIVATION = 'Callthrough for employees' + +# GS Cluster configuration +WRITE_GS_CLUSTER_SYNC_LOG = true +HOMEBASE_IP_ADDRESS = '0.0.0.0' diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb new file mode 100644 index 0000000..9e8b013 --- /dev/null +++ b/config/initializers/inflections.rb @@ -0,0 +1,10 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format +# (all these examples are active by default): +# ActiveSupport::Inflector.inflections do |inflect| +# inflect.plural /^(ox)$/i, '\1en' +# inflect.singular /^(ox)en/i, '\1' +# inflect.irregular 'person', 'people' +# inflect.uncountable %w( fish sheep ) +# end diff --git a/config/initializers/load_extensions.rb b/config/initializers/load_extensions.rb new file mode 100644 index 0000000..d86993c --- /dev/null +++ b/config/initializers/load_extensions.rb @@ -0,0 +1 @@ +require 'activerecord_extensions' diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb new file mode 100644 index 0000000..6c89b3c --- /dev/null +++ b/config/initializers/mime_types.rb @@ -0,0 +1,9 @@ +# Be sure to restart your server when you modify this file. + +# Add new mime types for use in respond_to blocks: +# Mime::Type.register "text/richtext", :rtf +# Mime::Type.register_alias "text/html", :iphone + +# ".htm" file extension for Snom provisioning: +Mime::Type.register 'text/html', :htm +Mime::Type.register 'audio/x-wav', :wav diff --git a/config/initializers/secret_token.rb b/config/initializers/secret_token.rb new file mode 100644 index 0000000..6f04f07 --- /dev/null +++ b/config/initializers/secret_token.rb @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# Your secret key for verifying the integrity of signed cookies. +# If you change this key, all old signed cookies will become invalid! +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +Gemeinschaft42c::Application.config.secret_token = '9a59cac7fe4b23e0253a7beb341d9498d721923e966b45983f441f991e81f758067a6d9a949247d489773288284ab96b5015be52bf7b2834e666d43f864034e4' diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb new file mode 100644 index 0000000..f77495c --- /dev/null +++ b/config/initializers/session_store.rb @@ -0,0 +1,8 @@ +# Be sure to restart your server when you modify this file. + +# Gemeinschaft42c::Application.config.session_store :cookie_store, key: '_Gemeinschaft42c_session' + +# Use the database for sessions instead of the cookie-based default, +# which shouldn't be used to store highly confidential information +# (create the session table with "rails generate session_migration") +Gemeinschaft42c::Application.config.session_store :active_record_store diff --git a/config/initializers/simple_form.rb b/config/initializers/simple_form.rb new file mode 100644 index 0000000..2e3d9fb --- /dev/null +++ b/config/initializers/simple_form.rb @@ -0,0 +1,19 @@ +# Use this setup block to configure all options available in SimpleForm. +SimpleForm.setup do |config| + config.wrappers :tag => :div, :class => :input, + :error_class => :field_with_errors do |b| + + # Form extensions + b.use :html5 + b.optional :pattern + b.use :maxlength + b.use :placeholder + b.use :readonly + + # Form components + b.use :label_input + b.use :error, :wrap_with => { :tag => :span, :class => :error } + b.use :hint, :wrap_with => { :tag => :span, :class => :hint } + end + +end diff --git a/config/initializers/validators.rb b/config/initializers/validators.rb new file mode 100644 index 0000000..4f6c223 --- /dev/null +++ b/config/initializers/validators.rb @@ -0,0 +1,242 @@ +ActiveRecord::Base.class_eval do + + def self.validate_mac_address( attr_names, options={} ) + validates_format_of( attr_names, { + :with => /^ [0-9A-F]{2} (?: [0-9A-F]{2} ){5} $/x, + :allow_blank => false, + :allow_nil => false, + }.merge!( options ) ) + end + + def self.validate_ip_address( attr_names, options={} ) + validates_format_of( attr_names, { + :with => /^ + (?:25[0-5]|(?:2[0-4]|1\d|[1-9])?\d) + (?:\.(?:25[0-5]|(?:2[0-4]|1\d|[1-9])?\d)){3} + $/x, # OPTIMIZE IPv6 + :allow_blank => false, + :allow_nil => false, + }.merge!( options ) ) + end + + def self.validate_ip_port( attr_names, options={} ) + validates_numericality_of( attr_names, { + :only_integer => true, + :greater_than => 0, + :less_than => 65536, + :allow_nil => false, + }.merge!( options ) ) + end + + def self.validate_netmask( attr_names, options={} ) + configuration = { + :allow_nil => false, + :allow_blank => false, + :with => + /^( + ((128|192|224|240|248|252|254)\.0\.0\.0) + |(255\.(0|128|192|224|240|248|252|254)\.0\.0) + |(255\.255\.(0|128|192|224|240|248|252|254)\.0) + |(255\.255\.255\.(0|128|192|224|240|248|252|254)) + )$/x + } + configuration.merge!( options ) + validates_format_of( attr_names, configuration ) + end + + def self.validate_hostname_or_ip( attr_names, options={} ) + # Validate the server. This is the "host" rule from RFC 3261 + # (but the patterns for IPv4 and IPv6 addresses have been fixed here). + configuration = { + :allow_nil => false, + :allow_blank => false, + :with => + /^ + (?: + (?: + (?: + (?: + [A-Za-z0-9] | + [A-Za-z0-9] [A-Za-z0-9\-]* [A-Za-z0-9] + ) + \. + )* + (?: + [A-Za-z] | + [A-Za-z] [A-Za-z0-9\-]* [A-Za-z0-9] + ) + \.? + ) + | + (?: + (?: 25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d ) + (?: \. (?: 25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d ) ){3} + ) + | + ( + ( + ( [0-9A-Fa-f]{1,4} [:] ){7} ( [0-9A-Fa-f]{1,4} | [:] ) + )| + ( + ( [0-9A-Fa-f]{1,4} [:] ){6} + ( + [:] [0-9A-Fa-f]{1,4} | + ( + ( 25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d ) + ( \. ( 25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d ) ){3} + ) | [:] + ) + )| + ( + ( [0-9A-Fa-f]{1,4} [:] ){5} + ( + ( + ( [:] [0-9A-Fa-f]{1,4} ){1,2} + )| + [:]( + ( 25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d ) + ( \. ( 25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d ) ){3} + )| + [:] + ) + )| + ( + ( [0-9A-Fa-f]{1,4} [:] ){4} + ( + ( ( [:] [0-9A-Fa-f]{1,4} ){1,3} ) | + ( + ( [:] [0-9A-Fa-f]{1,4} )? [:] + ( + ( 25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d ) + ( \. ( 25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d ) ){3} + ) + ) | [:] + ) + )| + ( + ( [0-9A-Fa-f]{1,4} [:] ){3} + ( + ( ( [:] [0-9A-Fa-f]{1,4} ){1,4} ) | + ( + ( [:] [0-9A-Fa-f]{1,4} ){0,2} [:] + ( + ( 25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d ) + ( \. ( 25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d ) ){3} + ) + ) | [:] + ) + )| + ( + ( [0-9A-Fa-f]{1,4} [:] ){2} + ( + ( ( [:] [0-9A-Fa-f]{1,4} ){1,5} ) | + ( + ( [:] [0-9A-Fa-f]{1,4} ){0,3} [:] + ( + ( 25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d ) + ( \. ( 25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d ) ){3} + ) + ) | [:] + ) + )| + ( + ( [0-9A-Fa-f]{1,4} [:] ){1} + ( + ( ( [:] [0-9A-Fa-f]{1,4} ){1,6} ) | + ( + ( [:] [0-9A-Fa-f]{1,4} ){0,4} [:] + ( + ( 25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d ) + ( \. ( 25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d ) ){3} + ) + ) | [:] + ) + )| + ( + [:] + ( + ( ( [:] [0-9A-Fa-f]{1,4} ){1,7} ) | + ( + ( [:] [0-9A-Fa-f]{1,4} ){0,5} [:] + ( + ( 25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d ) + ( \. ( 25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d ) ){3} + ) + ) | [:] + ) + ) + ) + ) + $/x + } + configuration.merge!( options ) + validates_format_of( attr_names, configuration ) + end + + # Validate SIP username. This is the "user" rule from RFC 3261. + def self.validate_username( attr_names, options={} ) + configuration = { + :allow_nil => false, + :allow_blank => false, + :with => + /^ + (?: + (?: + [A-Za-z0-9] | + [\-_.!~*'()] + ) | + %[0-9A-F]{2} | + [&=+$,;?\/] + ){1,255} + $/x + } + configuration.merge!( options ) + validates_format_of( attr_names, configuration ) + end + + # Validate SIP password. + def self.validate_sip_password( attr_names, options={} ) + configuration = { + :allow_nil => true, + :allow_blank => true, + :with => + /^ + (?: + (?: + [A-Za-z0-9] | + [\-_.!~*'()] + ) | + %[0-9A-F]{2} | + [&=+$,] + ){0,255} + $/x + } + configuration.merge!( options ) + validates_format_of( attr_names, configuration ) + end + +end + + + +module CustomValidators + + # Validates a URL. + # TODO Convert this into a proper ActiveRecord validator. + # + def self.validate_url( url_str ) + return false if url_str.blank? + require 'uri' + begin + uri = URI.parse( url_str ) + return false if ! uri.absolute? + return false if ! uri.hierarchical? + return false if !( uri.is_a?( URI::HTTP ) || uri.is_a?( URI::HTTPS ) ) + rescue URI::InvalidURIError + return false + end + return true + end + +end + diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb new file mode 100644 index 0000000..999df20 --- /dev/null +++ b/config/initializers/wrap_parameters.rb @@ -0,0 +1,14 @@ +# Be sure to restart your server when you modify this file. +# +# This file contains settings for ActionController::ParamsWrapper which +# is enabled by default. + +# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. +ActiveSupport.on_load(:action_controller) do + wrap_parameters format: [:json] +end + +# Disable root element in JSON by default. +ActiveSupport.on_load(:active_record) do + self.include_root_in_json = false +end diff --git a/config/locales/carrierwave/de.yml b/config/locales/carrierwave/de.yml new file mode 100644 index 0000000..08dbd26 --- /dev/null +++ b/config/locales/carrierwave/de.yml @@ -0,0 +1,5 @@ +de: + carrierwave: + errors: + integrity: 'Kein Bild.' + processing: 'Das Bild kann nicht in der Größe verändert werden.'
\ No newline at end of file diff --git a/config/locales/carrierwave/en.yml b/config/locales/carrierwave/en.yml new file mode 100644 index 0000000..2fa50f2 --- /dev/null +++ b/config/locales/carrierwave/en.yml @@ -0,0 +1,5 @@ +en: + carrierwave: + errors: + integrity: 'Not an image.' + processing: 'Cannot resize image.'
\ No newline at end of file diff --git a/config/locales/de.yml b/config/locales/de.yml new file mode 100644 index 0000000..087a2d3 --- /dev/null +++ b/config/locales/de.yml @@ -0,0 +1,196 @@ +de: + date: + abbr_day_names: + - So + - Mo + - Di + - Mi + - Do + - Fr + - Sa + abbr_month_names: + - + - Jan + - Feb + - Mär + - Apr + - Mai + - Jun + - Jul + - Aug + - Sep + - Okt + - Nov + - Dez + day_names: + - Sonntag + - Montag + - Dienstag + - Mittwoch + - Donnerstag + - Freitag + - Samstag + formats: + default: ! '%d.%m.%Y' + long: ! '%e. %B %Y' + short: ! '%e. %b' + month_names: + - + - Januar + - Februar + - März + - April + - Mai + - Juni + - Juli + - August + - September + - Oktober + - November + - Dezember + order: + - :day + - :month + - :year + datetime: + distance_in_words: + about_x_hours: + one: etwa eine Stunde + other: etwa %{count} Stunden + about_x_months: + one: etwa ein Monat + other: etwa %{count} Monate + about_x_years: + one: etwa ein Jahr + other: etwa %{count} Jahre + almost_x_years: + one: fast ein Jahr + other: fast %{count} Jahre + half_a_minute: eine halbe Minute + less_than_x_minutes: + one: weniger als eine Minute + other: weniger als %{count} Minuten + less_than_x_seconds: + one: weniger als eine Sekunde + other: weniger als %{count} Sekunden + over_x_years: + one: mehr als ein Jahr + other: mehr als %{count} Jahre + x_days: + one: ein Tag + other: ! '%{count} Tage' + x_minutes: + one: eine Minute + other: ! '%{count} Minuten' + x_months: + one: ein Monat + other: ! '%{count} Monate' + x_seconds: + one: eine Sekunde + other: ! '%{count} Sekunden' + prompts: + day: Tag + hour: Stunden + minute: Minuten + month: Monat + second: Sekunden + year: Jahr + errors: + format: ! '%{attribute} %{message}' + messages: + accepted: muss akzeptiert werden + blank: muss ausgefüllt werden + confirmation: stimmt nicht mit der Bestätigung überein + empty: muss ausgefüllt werden + equal_to: muss genau %{count} sein + even: muss gerade sein + exclusion: ist nicht verfügbar + greater_than: muss größer als %{count} sein + greater_than_or_equal_to: muss größer oder gleich %{count} sein + inclusion: ist kein gültiger Wert + invalid: ist nicht gültig + less_than: muss kleiner als %{count} sein + less_than_or_equal_to: muss kleiner oder gleich %{count} sein + not_a_number: ist keine Zahl + not_an_integer: muss ganzzahlig sein + odd: muss ungerade sein + record_invalid: ! 'Gültigkeitsprüfung ist fehlgeschlagen: %{errors}' + taken: ist bereits vergeben + too_long: ist zu lang (nicht mehr als %{count} Zeichen) + too_short: ist zu kurz (nicht weniger als %{count} Zeichen) + wrong_length: hat die falsche Länge (muss genau %{count} Zeichen haben) + template: + body: ! 'Bitte überprüfen Sie die folgenden Felder:' + header: + one: ! 'Konnte %{model} nicht speichern: ein Fehler.' + other: ! 'Konnte %{model} nicht speichern: %{count} Fehler.' + helpers: + select: + prompt: Bitte wählen + submit: + create: ! '%{model} anlegen' + submit: ! '%{model} speichern' + update: ! '%{model} aktualisieren' + number: + currency: + format: + delimiter: . + format: ! '%n %u' + precision: 2 + separator: ! ',' + significant: false + strip_insignificant_zeros: false + unit: € + format: + delimiter: . + precision: 2 + separator: ! ',' + significant: false + strip_insignificant_zeros: false + human: + decimal_units: + format: ! '%n %u' + units: + billion: + one: Milliarde + other: Milliarden + million: Millionen + quadrillion: + one: Billiarde + other: Billiarden + thousand: Tausend + trillion: Billionen + unit: '' + format: + delimiter: '' + precision: 1 + significant: true + strip_insignificant_zeros: true + storage_units: + format: ! '%n %u' + units: + byte: + one: Byte + other: Bytes + gb: GB + kb: KB + mb: MB + tb: TB + percentage: + format: + delimiter: '' + precision: + format: + delimiter: '' + support: + array: + last_word_connector: ! ' und ' + two_words_connector: ! ' und ' + words_connector: ! ', ' + time: + am: vormittags + formats: + default: ! '%A, %d. %B %Y, %H:%M Uhr' + long: ! '%A, %d. %B %Y, %H:%M Uhr' + short: ! '%d. %B, %H:%M Uhr' + pm: nachmittags
\ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml new file mode 100644 index 0000000..dabe98d --- /dev/null +++ b/config/locales/en.yml @@ -0,0 +1,6 @@ +# Don't add anything here! +en: + dont_use_me: "NO ME USES!" + # Temporal, just till we create the Voice Mail stuff. + voice_mail: "Voice Mail" + fax: "Fax"
\ No newline at end of file diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml new file mode 100644 index 0000000..1db7297 --- /dev/null +++ b/config/locales/simple_form.de.yml @@ -0,0 +1,24 @@ +de: + simple_form: + "yes": 'Ja' + "no": 'Nein' + required: + text: 'benötigt' + mark: '*' + # You can uncomment the line below if you need to overwrite the whole required html. + # When using html, text and mark won't be used. + # html: '<abbr title="required">*</abbr>' + error_notification: + default_message: "Es wurden Validierungsfehler bei Ihren Eingaben gefunden." + # Labels and hints examples + # labels: + # password: 'Password' + # user: + # new: + # email: 'E-mail para efetuar o sign in.' + # edit: + # email: 'E-mail.' + # hints: + # username: 'User name to sign in.' + # password: 'No special characters, please.' + diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml new file mode 100644 index 0000000..007d9f9 --- /dev/null +++ b/config/locales/simple_form.en.yml @@ -0,0 +1,24 @@ +en: + simple_form: + "yes": 'Yes' + "no": 'No' + required: + text: 'required' + mark: '*' + # You can uncomment the line below if you need to overwrite the whole required html. + # When using html, text and mark won't be used. + # html: '<abbr title="required">*</abbr>' + error_notification: + default_message: "Some errors were found, please take a look." + # Labels and hints examples + # labels: + # password: 'Password' + # user: + # new: + # email: 'E-mail para efetuar o sign in.' + # edit: + # email: 'E-mail.' + # hints: + # username: 'User name to sign in.' + # password: 'No special characters, please.' + diff --git a/config/locales/views/access_authorizations/de.yml b/config/locales/views/access_authorizations/de.yml new file mode 100644 index 0000000..1d38cc1 --- /dev/null +++ b/config/locales/views/access_authorizations/de.yml @@ -0,0 +1,45 @@ +de: + access_authorizations: + name: 'Zugangsberechtigung' + controller: + successfuly_created: 'Eine neue Zugangsberechtigung wurde erstellt.' + successfuly_updated: 'Die Zugangsberechtigung wurde aktualisiert.' + successfuly_destroyed: 'Die Zugangsberechtigung wurde gelöscht.' + index: + page_title: 'Zugangsberechtigungen' + name: 'Name' + login: 'Login' + pin: 'PIN' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Zugangsberechtigung löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neue Zugangsberechtigung für %{resource} anlegen' + show: + page_title: 'Zugangsberechtigung anzeigen' + name: 'Name' + login: 'Login' + pin: 'PIN' + index_phone_numbers: 'Telefonnummern' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Zugangsberechtigung löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle Zugangsberechtigungen anzeigen.' + new: + page_title: 'Neue Zugangsberechtigung anlegen' + edit: + page_title: 'Zugangsberechtigung bearbeiten' + form: + name: + label: 'Name' + hint: '' + login: + label: 'Login' + hint: '' + pin: + label: 'PIN' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/access_authorizations/en.yml b/config/locales/views/access_authorizations/en.yml new file mode 100644 index 0000000..9832e4a --- /dev/null +++ b/config/locales/views/access_authorizations/en.yml @@ -0,0 +1,48 @@ +en: + access_authorizations: + name: 'Access authorization' + controller: + successfuly_created: 'Successfully created access authorization.' + successfuly_updated: 'Successfully updated access authorization.' + successfuly_destroyed: 'Successfully destroyed access authorization.' + index: + page_title: 'Access authorizations' + name: 'Name' + login: 'Login' + pin: 'PIN' + actions: + confirm: 'Are you sure you want to delete this access authorization?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New access authorization for %{resource}' + show: + page_title: 'Show access authorization' + name: 'Name' + login: 'Login' + pin: 'PIN' + index_phone_numbers: 'Phone numbers' + actions: + confirm: 'Are you sure you want to delete this access authorization?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View All' + new: + page_title: 'New access authorization' + edit: + page_title: 'Editing access authorization' + actions: + edit: 'Edit' + view_all: 'View All' + form: + name: + label: 'Name' + hint: '' + login: + label: 'Login' + hint: '' + pin: + label: 'PIN' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/acd_agents/de.yml b/config/locales/views/acd_agents/de.yml new file mode 100644 index 0000000..8613313 --- /dev/null +++ b/config/locales/views/acd_agents/de.yml @@ -0,0 +1,76 @@ +de: + acd_agents: + name: 'Agent' + controller: + successfuly_created: 'Ein neuer Agent wurde angelegt.' + successfuly_updated: 'Der Agent wurde aktualisiert.' + successfuly_destroyed: 'Der Agent wurde gelöscht.' + index: + page_title: 'Agenten' + uuid: 'UUID' + name: 'Name' + status: 'Status' + automatic_call_distributor_id: 'Warteschleifen ID' + last_call: 'Letzter Anruf' + calls_answered: 'Anrufe' + destination_type: 'Zieltyp' + destination_id: 'Ziel ID' + destination: 'Ziel' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Agenten löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neuen Agenten für %{resource} anlegen' + show: + page_title: 'Agent' + uuid: 'UUID' + name: 'Name' + status: 'Status' + automatic_call_distributor_id: 'Warteschleifen ID' + last_call: 'Letzter Anruf' + calls_answered: 'Anrufe' + destination_type: 'Zieltyp' + destination_id: 'Ziel ID' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Agenten löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + new: + page_title: 'Neuer Agent' + actions: + back_to_list: 'Zurück zur Übersicht' + edit: + page_title: 'Agent bearbeiten' + actions: + back_to_list: 'Zurück zur Übersicht' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + form: + uuid: + label: 'UUID' + hint: '' + name: + label: 'Name' + hint: '' + status: + label: 'Status' + hint: '' + automatic_call_distributor_id: + label: 'Warteschleifen ID' + hint: '' + last_call: + label: 'Letzter Anruf' + hint: '' + calls_answered: + label: 'Anrufe' + hint: '' + destination_type: + label: 'Zieltyp' + hint: '' + destination_id: + label: 'Ziel ID' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/acd_agents/en.yml b/config/locales/views/acd_agents/en.yml new file mode 100644 index 0000000..8287dd7 --- /dev/null +++ b/config/locales/views/acd_agents/en.yml @@ -0,0 +1,76 @@ +en: + acd_agents: + name: 'ACD Agent' + controller: + successfuly_created: 'Successfully created Agent.' + successfuly_updated: 'Successfully updated Agent.' + successfuly_destroyed: 'Successfully destroyed Agent.' + index: + page_title: 'Agents' + uuid: 'UUID' + name: 'Name' + status: 'Status' + automatic_call_distributor_id: 'ACD ID' + last_call: 'Last call' + calls_answered: 'Calls answered' + destination_type: 'Destination type' + destination_id: 'Destination ID' + destination: 'Destination' + actions: + confirm: 'Are you sure you want to delete this Agent?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New Agent for %{resource}' + show: + page_title: 'Show Agent' + uuid: 'UUID' + name: 'Name' + status: 'Status' + automatic_call_distributor_id: 'ACD ID' + last_call: 'Last call' + calls_answered: 'Calls answered' + destination_type: 'Destination type' + destination_id: 'Destination' + actions: + confirm: 'Are you sure you want to delete this element?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View All' + new: + page_title: 'New Agent' + actions: + back_to_list: 'Back to Index' + edit: + page_title: 'Editing Agent' + actions: + back_to_list: 'Back to Index' + edit: 'Edit' + view_all: 'View All' + form: + uuid: + label: 'UUID' + hint: 'Universally unique identifier' + name: + label: 'Name' + hint: 'Entry name' + status: + label: 'Status' + hint: 'Entry status' + automatic_call_distributor_id: + label: 'ACD ID' + hint: 'ID of parent ACD' + last_call: + label: 'Last call' + hint: 'Last answered call' + calls_answered: + label: 'Calls answered' + hint: 'Number of calls' + destination_type: + label: 'Destination type' + hint: '' + destination_id: + label: 'Destination' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/acd_callers/de.yml b/config/locales/views/acd_callers/de.yml new file mode 100644 index 0000000..5195217 --- /dev/null +++ b/config/locales/views/acd_callers/de.yml @@ -0,0 +1,70 @@ +de: + acd_callers: + name: 'Vermittelte Anrufer' + controller: + successfuly_created: 'Ein neuer vermittelter Anrufer wurde angelegt.' + successfuly_updated: 'Der vermittelte Anrufer wurde aktualisiert.' + successfuly_destroyed: 'Der vermittelte Anrufer wurde gelöscht.' + index: + page_title: 'Vermittelte Anrufer auflisten' + channel_uuid: 'Kanal UUID' + automatic_call_distributor_id: 'Warteschleife' + status: 'Status' + enter_time: 'Eintrittszeit' + agent_answer_time: 'Antwortzeit Agent' + callback_number: 'Vermittelte Durchwahl' + callback_attempts: 'Vermittlungsversuche' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen vermittelten Anrufer löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neuen vermittelten Anrufer für %{resource} anlegen' + show: + page_title: 'Vermittelte Anrufer anzeigen' + channel_uuid: 'Kanal UUID' + automatic_call_distributor_id: 'Warteschleife' + status: 'Status' + enter_time: 'Eintrittszeit' + agent_answer_time: 'Antwortzeit Agent' + callback_number: 'Vermittelte Durchwahl' + callback_attempts: 'Vermittlungsversuche' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen vermittelten Anrufer löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + new: + page_title: 'Neuer vermittelter Anrufer' + actions: + back_to_list: 'Zurück zur Übersicht' + edit: + page_title: 'Vermittelten Anrufer bearbeiten' + actions: + back_to_list: 'Zurück zur Übersicht' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + form: + channel_uuid: + label: 'Kanal UUID' + hint: '' + automatic_call_distributor_id: + label: 'Warteschleife' + hint: '' + status: + label: 'Status' + hint: '' + enter_time: + label: 'Eintrittszeit' + hint: '' + agent_answer_time: + label: 'Antwortzeit Agent' + hint: '' + callback_number: + label: 'Vermittelte Durchwahl' + hint: '' + callback_attempts: + label: 'Vermittllungsversuche' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/acd_callers/en.yml b/config/locales/views/acd_callers/en.yml new file mode 100644 index 0000000..c435f3f --- /dev/null +++ b/config/locales/views/acd_callers/en.yml @@ -0,0 +1,70 @@ +en: + acd_callers: + name: 'Negotiated callers' + controller: + successfuly_created: 'Successfully created negotiated caller.' + successfuly_updated: 'Successfully updated negotiated caller.' + successfuly_destroyed: 'Successfully destroyed negotiated caller.' + index: + page_title: 'Listing negotiated callers' + channel_uuid: 'Channel UUID' + automatic_call_distributor_id: 'Automatic call distributor' + status: 'Status' + enter_time: 'Enter time' + agent_answer_time: 'Agent answer time' + callback_number: 'Callback number' + callback_attempts: 'Callback attempts' + actions: + confirm: 'Are you sure you want to delete this negotiated caller?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New negotiated callers for %{resource}' + show: + page_title: 'Show negotiated callers' + channel_uuid: 'Channel UUID' + automatic_call_distributor_id: 'Automatic call distributor' + status: 'Status' + enter_time: 'Enter time' + agent_answer_time: 'Agent answer time' + callback_number: 'Callback number' + callback_attempts: 'Callback attempts' + actions: + confirm: 'Are you sure you want to delete this negotiated caller?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View All' + new: + page_title: 'New negotiated caller' + actions: + back_to_list: 'Back to Index' + edit: + page_title: 'Editing negotiated callers' + actions: + back_to_list: 'Back to Index' + edit: 'Edit' + view_all: 'View All' + form: + channel_uuid: + label: 'Channel UUID' + hint: '' + automatic_call_distributor_id: + label: 'Automatic call distributor' + hint: '' + status: + label: 'Status' + hint: '' + enter_time: + label: 'Enter time' + hint: '' + agent_answer_time: + label: 'Agent answer time' + hint: '' + callback_number: + label: 'Callback number' + hint: '' + callback_attempts: + label: 'Callback attempts' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/addresses/de.yml b/config/locales/views/addresses/de.yml new file mode 100644 index 0000000..96d280b --- /dev/null +++ b/config/locales/views/addresses/de.yml @@ -0,0 +1,72 @@ +de: + addresses: + name: 'Adresse' + controller: + successfuly_created: 'Eine neue Adresse wurde angelegt.' + successfuly_updated: 'Die Adresse wurde aktualisiert.' + successfuly_destroyed: 'Die Adresse wurde gelöscht.' + index: + page_title: 'Adressen' + phone_book_entry_id: 'Telefonbuch Eintrag' + line1: 'Zeile1' + line2: 'Zeile2' + street: 'Straße' + zip_code: 'PLZ' + city: 'Stadt' + country_id: 'Land' + position: 'Position' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Adresse löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + show: + page_title: 'Adresse bearbeiten' + phone_book_entry_id: 'Telefonbuch Eintrag' + line1: 'Zeile1' + line2: 'Zeile2' + street: 'Straße' + zip_code: 'PLZ' + city: 'Stadt' + country_id: 'Land' + position: 'Position' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Adresse löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle Adressen anzeigen' + new: + page_title: 'Neue Adresse' + actions: + edit: + page_title: 'Adresse edtieren' + actions: + edit: 'Bearbeiten' + view_all: 'Alle Adressen anzeigen' + form: + phone_book_entry_id: + label: 'Telefonbuch Eintrag' + hint: '' + line1: + label: 'Zeile1' + hint: '' + line2: + label: 'Zeile2' + hint: '' + street: + label: 'Straße' + hint: '' + zip_code: + label: 'PLZ' + hint: '' + city: + label: 'Stadt' + hint: '' + country_id: + label: 'Land' + hint: '' + position: + label: 'Position' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/addresses/en.yml b/config/locales/views/addresses/en.yml new file mode 100644 index 0000000..56550f1 --- /dev/null +++ b/config/locales/views/addresses/en.yml @@ -0,0 +1,72 @@ +en: + addresses: + name: 'Address' + controller: + successfuly_created: 'Successfully created address.' + successfuly_updated: 'Successfully updated address.' + successfuly_destroyed: 'Successfully destroyed address.' + index: + page_title: 'Addresses' + phone_book_entry_id: 'Phone book entry' + line1: 'Line1' + line2: 'Line2' + street: 'Street' + zip_code: 'Zip code' + city: 'City' + country_id: 'Country' + position: 'Position' + actions: + confirm: 'Are you sure you want to delete this address?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + show: + page_title: 'Editing Address' + phone_book_entry_id: 'Phone book entry' + line1: 'Line1' + line2: 'Line2' + street: 'Street' + zip_code: 'Zip code' + city: 'City' + country_id: 'Country' + position: 'Position' + actions: + confirm: 'Are you sure you want to delete this address?' + destroy: 'Delete address' + edit: 'Edit address' + view_all: 'View all addresses' + new: + page_title: 'New Address' + actions: + edit: + page_title: 'Editing Address' + actions: + edit: 'Edit address' + view_all: 'View all addresses' + form: + phone_book_entry_id: + label: 'Phone book entry' + hint: '' + line1: + label: 'Line1' + hint: '' + line2: + label: 'Line2' + hint: '' + street: + label: 'Street' + hint: '' + zip_code: + label: 'Zip code' + hint: '' + city: + label: 'City' + hint: '' + country_id: + label: 'Country' + hint: '' + position: + label: 'Position' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/automatic_call_distributors/de.yml b/config/locales/views/automatic_call_distributors/de.yml new file mode 100644 index 0000000..8618325 --- /dev/null +++ b/config/locales/views/automatic_call_distributors/de.yml @@ -0,0 +1,131 @@ +de: + automatic_call_distributors: + name: 'Warteschleife' + controller: + successfuly_created: 'Eine neue Warteschleife wurde angelegt.' + successfuly_updated: 'Die Warteschleife wurde aktualisiert.' + successfuly_destroyed: 'Die Warteschleife wurde gelöscht.' + index: + page_title: 'Warteschleifen' + uuid: 'UUID' + name: 'Name' + strategy: 'Strategie' + max_callers: 'Max. Anrufer' + agent_timeout: 'Agenten-Timeout' + retry_timeout: 'Wiederh.-Timeout' + join: 'Betreten' + leave: 'Verlassen' + phone_numbers: 'Telefonnummern' + acd_agents: 'Agenten' + gs_node_id: 'Knoten' + announce_position: 'Position ansagen' + announce_call_agents: 'Anruf ansagen' + greeting: 'Begrüßung' + goodbye: 'Abschied' + music: 'Musik' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Warteschleife löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neue Warteschleife für %{resource} anlegen' + show: + page_title: 'Warteschleife' + uuid: 'UUID' + name: 'Name' + strategy: 'Strategie' + max_callers: 'Max. Anrufer' + agent_timeout: 'Agenten-Timeout' + retry_timeout: 'Wiederholungs-Timeout' + join: 'Betreten' + leave: 'Verlassen' + gs_node_id: 'GS Knoten' + announce_position: 'Position ansagen' + announce_call_agents: 'Anruf ansagen' + greeting: 'Begrüßung' + goodbye: 'Abschied' + music: 'Musik' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Warteschleife löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + new: + page_title: 'Neue Warteschleife' + actions: + back_to_list: 'Zurück zur Übersicht' + edit: + page_title: 'Warteschleife bearbeiten' + actions: + back_to_list: 'Zurück zur Übersicht' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + form: + uuid: + label: 'UUID' + hint: '' + name: + label: 'Name' + hint: '' + strategy: + label: 'Strategie' + hint: '' + automatic_call_distributorable_type: + label: 'Warteschleifen-Typ' + hint: '' + automatic_call_distributorable_id: + label: 'Warteschleifen ID' + hint: '' + max_callers: + label: 'Maximale Anzahl von Anrufern' + hint: '' + agent_timeout: + label: 'Agenten-Timeout' + hint: '' + retry_timeout: + label: 'Wiederholungs-Timeout' + hint: '' + join: + label: 'Betreten' + hint: '' + leave: + label: 'Verlassen' + hint: '' + gs_node_id: + label: 'GS Knoten' + hint: '' + announce_position: + label: 'Position ansagen' + hint: '' + announce_call_agents: + label: 'Anruf ansagen' + hint: '' + greeting: + label: 'Begrüßung' + hint: '' + goodbye: + label: 'Abschied' + hint: '' + music: + label: 'Musik' + hint: '' + button: 'Absenden' + strategies: + round_robin: 'Ringverteilung' + ring_all: 'Alle' + join_on: + agents_available: 'Agenten verfügbar' + agents_active: 'Agenten aktiv' + always: 'Immer' + leave_on: + no_agents_available_timeout: 'Keine Agenten verfügbar oder Zeitüberschreitung' + no_agents_active_timeout: 'Keine Agenten aktiv oder Zeitüberschreitung' + no_agents_available: 'Keine Agenten verfügbar' + no_agents_active: 'Keine Agenten aktiv' + timeout: 'Zeitüberschreitung' + never: 'Nie' + announce_position: + never: 'Nie' + on_change_only: 'Ansage bei jeder Änderung der Position' + every_x_seconds: "Ansage alle %{x_seconds} Sekunden und nach jeder Änderung der Position" diff --git a/config/locales/views/automatic_call_distributors/en.yml b/config/locales/views/automatic_call_distributors/en.yml new file mode 100644 index 0000000..d481406 --- /dev/null +++ b/config/locales/views/automatic_call_distributors/en.yml @@ -0,0 +1,136 @@ +en: + automatic_call_distributors: + name: 'ACD' + controller: + successfuly_created: 'Successfully created ACD.' + successfuly_updated: 'Successfully updated ACD.' + successfuly_destroyed: 'Successfully destroyed ACD.' + index: + page_title: 'ACDs' + uuid: 'UUID' + name: 'Name' + strategy: 'Strategy' + automatic_call_distributorable_type: 'Automatic call distributorable type' + automatic_call_distributorable_id: 'Automatic call distributorable' + max_callers: 'Max callers' + agent_timeout: 'Agent timeout' + retry_timeout: 'Retry timeout' + join: 'Join' + leave: 'Leave' + phone_numbers: 'Phone numbers' + acd_agents: 'Agents' + gs_node_id: 'Gs node' + announce_position: 'Announce position' + announce_call_agents: 'Accounce calling' + greeting: 'Greeting' + goodbye: 'Goodbye' + music: 'Music' + actions: + confirm: 'Are you sure you want to delete this ACD?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New ACD for %{resource}' + show: + page_title: 'Show ACD' + uuid: 'UUID' + name: 'Name' + strategy: 'Strategy' + automatic_call_distributorable_type: 'Automatic call distributorable type' + automatic_call_distributorable_id: 'Automatic call distributorable' + max_callers: 'Max callers' + agent_timeout: 'Agent timeout' + retry_timeout: 'Retry timeout' + join: 'Join' + leave: 'Leave' + gs_node_id: 'Gs node' + announce_position: 'Announce position' + announce_call_agents: 'Accounce call' + greeting: 'Greeting' + goodbye: 'Goodbye' + music: 'Music' + actions: + confirm: 'Are you sure you want to delete this element?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View All' + new: + page_title: 'New ACD' + actions: + back_to_list: 'Back to Index' + edit: + page_title: 'Editing ACD' + actions: + back_to_list: 'Back to Index' + edit: 'Edit' + view_all: 'View All' + form: + uuid: + label: 'UUID' + hint: '' + name: + label: 'Name' + hint: '' + strategy: + label: 'Strategy' + hint: '' + automatic_call_distributorable_type: + label: 'Automatic call distributorable type' + hint: '' + automatic_call_distributorable_id: + label: 'Automatic call distributorable' + hint: '' + max_callers: + label: 'Max callers' + hint: '' + agent_timeout: + label: 'Agent timeout' + hint: '' + retry_timeout: + label: 'Retry timeout' + hint: '' + join: + label: 'Join' + hint: '' + leave: + label: 'Leave' + hint: '' + gs_node_id: + label: 'Gs node' + hint: '' + announce_position: + label: 'Announce position' + hint: '' + announce_call_agents: + label: 'Accounce calling' + hint: '' + greeting: + label: 'Greeting' + hint: '' + goodbye: + label: 'Goodbye' + hint: '' + music: + label: 'Music' + hint: '' + button: 'Submit' + strategies: + round_robin: 'Round robin' + ring_all: 'Ring all' + join_on: + agents_available: 'Agents available' + agents_active: 'Agents active' + always: 'Always' + leave_on: + no_agents_available_timeout: 'No agents available or timeout' + no_agents_active_timeout: 'No agents active or timeout' + no_agents_available: 'No agents available' + no_agents_active: 'No agents active' + timeout: 'Timeout' + never: 'Never' + announce_position: + never: 'Never' + on_change_only: 'After a position update' + every_x_seconds: "Every %{x_seconds} seconds and after each position update" + diff --git a/config/locales/views/call_forward_cases/de.yml b/config/locales/views/call_forward_cases/de.yml new file mode 100644 index 0000000..b9d6598 --- /dev/null +++ b/config/locales/views/call_forward_cases/de.yml @@ -0,0 +1,7 @@ +de: + call_forward_cases: + always: 'immer' + busy: 'besetzt' + noanswer: 'keine Antwort' + offline: 'offline' + assistant: 'Chef-Sekretärin-Schaltung'
\ No newline at end of file diff --git a/config/locales/views/call_forward_cases/en.yml b/config/locales/views/call_forward_cases/en.yml new file mode 100644 index 0000000..3a3734c --- /dev/null +++ b/config/locales/views/call_forward_cases/en.yml @@ -0,0 +1,7 @@ +en: + call_forward_cases: + always: 'always' + busy: 'busy' + noanswer: 'no answer' + offline: 'offline' + assistant: 'assistant'
\ No newline at end of file diff --git a/config/locales/views/call_forwards/de.yml b/config/locales/views/call_forwards/de.yml new file mode 100644 index 0000000..1ec7af7 --- /dev/null +++ b/config/locales/views/call_forwards/de.yml @@ -0,0 +1,77 @@ +de: + call_forwards: + name: 'Rufumleitung' + controller: + successfuly_created: 'Eine neue Rufumleitung wurde erstellt.' + successfuly_updated: 'Die Rufumleitung wurde aktualisiert.' + successfuly_destroyed: 'Die Rufumleitung wurde gelöscht.' + index: + page_title: 'Rufumleitungen' + phone_number_id: 'Telefonnummer' + call_forward_case_id: 'Art der Rufumleitung' + timeout: 'Zeitüberschreitung' + destination: 'Ziel' + hunt_group: 'Rufgruppen-Verknüpfung' + to_voicemail: 'Anrufbeantworter' + source: 'Quelle' + depth: 'Weiterleitungs-Sprünge' + active: 'Aktiv' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Rufumleitung löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neue Rufumleitung für die %{resource} anlegen' + show: + page_title: 'Rufumleitung anzeigen' + phone_number_id: 'Telefonnummer' + call_forward_case_id: 'Art der Rufumleitung' + timeout: 'Zeitüberschreitung' + destination: 'Ziel' + hunt_group: 'Rufgruppen-Verknüpfung' + to_voicemail: 'Anrufbeantworter' + source: 'Quelle' + depth: 'Weiterleitungs-Sprünge' + active: 'Aktiv' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Rufumleitung löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle Rufumleitung anzeigen' + new: + page_title: 'Neue Rufumleitung anlegen' + edit: + page_title: 'Rufumleitung bearbeiten' + form: + phone_number_id: + label: 'Telefonnummer' + hint: '' + call_forward_case_id: + label: 'Art der Rufumleitung' + hint: '' + timeout: + label: 'Zeitüberschreitung in Sekunden' + hint: 'Nur für "nicht antworten" benötigt.' + destination: + label: 'Ziel Telefonnummer' + hint: 'Format: +49-30-2270 für eine normale Telefonnummer. 42 für eine interne Durchwahl.' + call_forwarding_destination: + label: 'Zielauswahl' + hint: 'Ziel auswählen oder Rufnummer unten eingeben.' + hunt_group: + label: 'Rufgruppen-Verknüpfung' + hint: 'Die hier ausgewählt Rufgruppe schaltet automatisch diese Weiterleitung an und aus.' + to_voicemail: + label: 'Weiterleitung zum Anrufbeantworter' + hint: 'Kann nicht zusammen mit einer Ziel Telefonnummer aktiert werden.' + source: + label: 'Quelle' + hint: "Für welche Anrufer Telefonnummern soll diese Rufumleitung aktiv sein? Wenn kein Eintrag, dann für alle." + depth: + label: 'Weiterleitungs-Sprünge' + hint: "Die maximale Anzahl von Rufumleitungssprüngen. 1 bedeutet, das der Anruf nur bis zum nächsten Ziel weitergeleitet wird." + active: + label: 'Aktiv' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/call_forwards/en.yml b/config/locales/views/call_forwards/en.yml new file mode 100644 index 0000000..4b9af7d --- /dev/null +++ b/config/locales/views/call_forwards/en.yml @@ -0,0 +1,77 @@ +en: + call_forwards: + name: 'Call forward' + controller: + successfuly_created: 'Successfully created call forward.' + successfuly_updated: 'Successfully updated call forward.' + successfuly_destroyed: 'Successfully destroyed call forward.' + index: + page_title: 'Call forwards' + phone_number_id: 'Phone number' + call_forward_case_id: 'Call forward case' + timeout: 'Timeout' + destination: 'Destination' + to_voicemail: 'Voicemail' + source: 'Source' + depth: 'Hops' + active: 'Active' + hunt_group: 'Hunt group connection' + actions: + confirm: 'Are you sure you want to delete this call forward?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New call forward for phone number %{resource}' + show: + page_title: 'Show call forward' + phone_number_id: 'Phone number' + call_forward_case_id: 'Call forward case' + timeout: 'Timeout' + destination: 'Destination' + hunt_group: 'Hunt group connection' + to_voicemail: 'Voicemail' + source: 'Source' + depth: 'Hops' + active: 'Active' + actions: + confirm: 'Are you sure you want to delete this call forward?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View all call forwards' + new: + page_title: 'New call forward' + edit: + page_title: 'Editing call forward' + form: + phone_number_id: + label: 'Phone number' + hint: '' + call_forward_case_id: + label: 'Call forward case' + hint: '' + timeout: + label: 'Timeout in seconds' + hint: 'Just needed for the "noanswer" case.' + destination: + label: 'Destination phone number' + hint: 'Format: +49-30-2270 for a normal phone number or 42 for an internal extension.' + call_forwarding_destination: + label: 'Destination Type' + hint: 'Select destination or enter phone number below' + hunt_group: + label: 'Hunt group connection' + hint: 'This hunt group auto activates this call forward.' + to_voicemail: + label: 'Forward to voicemail' + hint: 'Can not be activated together with a destination number.' + source: + label: 'Source' + hint: "The caller's ID for which this call forward should be valid. Leave empty for calls from any number." + depth: + label: 'Hops' + hint: "The maximum number of hops you want to permit. 1 means calls won't be forwarded beyond the next destination." + active: + label: 'Active' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/call_histories/de.yml b/config/locales/views/call_histories/de.yml new file mode 100644 index 0000000..3f0459d --- /dev/null +++ b/config/locales/views/call_histories/de.yml @@ -0,0 +1,66 @@ +de: + call_histories: + name: 'Anrufliste' + controller: + successfuly_created: 'Ein neuer Eintrag wurde erstellt.' + successfuly_updated: 'Der Eintrag wurde aktualisiert.' + successfuly_destroyed: 'Der Eintrag wurde gelöscht.' + index: + page_title: 'Anrufliste' + page_title_missed: 'Verpasste Anrufe' + page_title_dialed: 'Gewählte Anrufe' + page_title_received: 'Empfangene Anrufe' + page_title_forwarded: 'Weitergeleitete Anrufe' + count: 'Anzahl der Anruflisteeinträge' + missed: 'Verpasst' + dialed: 'Gewählt' + received: 'Empfangen' + forwarded: 'Weitergeleitet' + all: 'Alle' + date_format: '%d.%m.%Y %H:%M' + date_today_format: '%H:%M' + forwarded_by: 'via' + navigation: + missed: 'Verpasst: %{calls}' + dialed: 'Gewählt: %{calls}' + received: 'Empfangen: %{calls}' + forwarded: 'Weitergeleitet: %{calls}' + all: 'Alle: %{calls}' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Eintrag löschen möchten?' + confirm_selected: 'Sind Sie sicher, dass Sie die markierten Einträge löschen möchten?' + destroy: 'Löschen' + destroy_multiple: 'Markierte Einträge löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neuen Eintrag für %{resource} anlegen' + call: 'Anrufen' + show: + page_title: 'Eintrag anzeigen' + name: 'Name' + description: 'Beschreibung' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Eintrag löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Anrufliste anzeigen' + new: + page_title: 'Neuer Eintrag' + edit: + page_title: 'Anrufliste "%{resource}" bearbeiten' + form: + name: + label: 'Name' + hint: '' + description: + label: 'Beschreibung' + hint: '' + button: 'Absenden' + call_results: + UNSPECIFIED: '' + ORIGINATOR_CANCEL: 'Abgebrochen' + UNALLOCATED_NUMBER: 'Rufnummer nicht bekannt' + NO_USER_RESPONSE: 'Keine Antwort' + NOANSWER: 'Keine Antwort' + USER_NOT_REGISTERED: 'Offline' diff --git a/config/locales/views/call_histories/en.yml b/config/locales/views/call_histories/en.yml new file mode 100644 index 0000000..5adf453 --- /dev/null +++ b/config/locales/views/call_histories/en.yml @@ -0,0 +1,67 @@ +en: + call_histories: + name: 'Call History' + controller: + successfuly_created: 'Successfully created call log entry.' + successfuly_updated: 'Successfully updated call log entry.' + successfuly_destroyed: 'Successfully destroyed call log entry.' + index: + page_title: 'Call History' + page_title_missed: 'Missed calls' + page_title_dialed: 'Placed calls' + page_title_received: 'Received calls' + page_title_forwarded: 'Forwarded calls' + count: 'Number of entries' + missed: 'Missed' + dialed: 'Placed' + received: 'Received' + forwarded: 'Forwarded' + all: 'All' + date_format: '%m/%d/%Y %H:%M' + date_today_format: '%H:%M' + forwarded_by: 'via' + navigation: + missed: 'Missed:%{calls}' + dialed: 'Placed:%{calls}' + received: 'Received:%{calls}' + forwarded: 'Forwarded:%{calls}' + all: 'All:%{calls}' + actions: + confirm: 'Are you sure you want to delete this call log entry?' + confirm_selected: 'Are you sure you want to delete all selected entries?' + destroy: 'Delete' + destroy_multiple: 'Delete selected entries' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New call log entry for %{resource}' + call: 'Call' + show: + page_title: 'Call History' + name: 'Name' + description: 'Description' + actions: + confirm: 'Are you sure you want to delete this call log entry?' + destroy: 'Delete call log entry' + edit: 'Edit call log entry' + view_all: 'View all call log entries' + new: + page_title: 'New call log entry' + edit: + page_title: 'Editing call log "%{resource}"' + form: + name: + label: 'Name' + hint: '' + description: + label: 'Description' + hint: '' + button: 'Submit' + call_results: + UNSPECIFIED: '' + ORIGINATOR_CANCEL: 'Cancelled' + UNALLOCATED_NUMBER: 'Unallocated number' + NO_USER_RESPONSE: 'No user response' + NOANSWER: 'No answer' + USER_NOT_REGISTERED: 'Offline' +
\ No newline at end of file diff --git a/config/locales/views/callthroughs/de.yml b/config/locales/views/callthroughs/de.yml new file mode 100644 index 0000000..c884ffa --- /dev/null +++ b/config/locales/views/callthroughs/de.yml @@ -0,0 +1,58 @@ +de: + callthroughs: + none: 'keiner' + name: 'Callthrough' + controller: + successfuly_created: 'Ein neuer Callthrough wurde erstellt.' + successfuly_updated: 'Der Callthrough wurde aktualisiert.' + successfuly_destroyed: 'Der Callthrough wurde gelöscht.' + index: + page_title: 'Callthrough' + name: 'Name' + sip_account_id: 'SIP-Konto' + phone_numbers: 'Telefonnummern' + access_authorized_phone_numbers: 'Zugelassene Telefonnummern' + whitelist_phone_numbers: 'Positivliste für Zielrufnummern' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Callthrough löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neuen Callthrough für %{resource} anlegen' + show: + page_title: 'Callthrough anzeigen' + name: 'Name' + sip_account_id: 'SIP-Konto' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Callthrough löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle Callthrough anzeigen' + new: + page_title: 'Neuen Callthrough anlegen' + edit: + page_title: 'Callthrough bearbeiten' + actions: + edit: 'Bearbeiten' + view_all: 'Alle Callthrough anzeigen' + form: + phone_numbers: + label: 'Telefonnummern' + hint: 'Unter diesen Telefonnummern ist dieser Callthrough erreichbar.' + access_authorizations: + label: 'Zugangsberechtigungen' + hint: 'Definiert, welche Telefonnummern bzw. Login/PIN Kombinationen diesen Callthrough benuzten dürfen.' + whitelists: + label: 'Positivliste für Zielrufnummern' + hint: "Wenn hier Zielrufnummern gesetzt sind, können nur diese Ziele angerufen werden." + name: + label: 'Name' + hint: '' + sip_account: + label: 'SIP-Konto' + hint: 'Wenn angegeben, werden alle Anrufe dieses Callthrough mit diesem SIP-Konto durchgeführt.' + sip_account_id: + label: 'SIP-Konto ID' + hint: 'Die SIP-Konto ID finden Sie in der URL des entsprechenden SIP-Kontos.' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/callthroughs/en.yml b/config/locales/views/callthroughs/en.yml new file mode 100644 index 0000000..648fb93 --- /dev/null +++ b/config/locales/views/callthroughs/en.yml @@ -0,0 +1,58 @@ +en: + callthroughs: + none: 'none' + name: 'Callthrough' + controller: + successfuly_created: 'Successfully created callthrough.' + successfuly_updated: 'Successfully updated callthrough.' + successfuly_destroyed: 'Successfully destroyed callthrough.' + index: + page_title: 'Callthroughs' + name: 'Name' + sip_account_id: 'SIP account' + phone_numbers: 'Phone numbers' + access_authorized_phone_numbers: 'Authorized phone numbers' + whitelist_phone_numbers: 'Whitelisted destinations' + actions: + confirm: 'Are you sure you want to delete this callthrough?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New callthrough for %{resource}' + show: + page_title: 'Show callthrough' + name: 'Name' + sip_account_id: 'SIP account' + actions: + confirm: 'Are you sure you want to delete this callthrough?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View All' + new: + page_title: 'New callthrough' + edit: + page_title: 'Editing callthrough' + actions: + edit: 'Edit' + view_all: 'View All' + form: + phone_numbers: + label: 'Phone numbers' + hint: 'Use these phone numbers to access the callthrough.' + access_authorizations: + label: 'Access authorizations' + hint: 'Define what phone numbers and/or login/pin combinations can use this callthrough.' + whitelists: + label: 'Whitelists for possible destinations' + hint: "When set only these phone numbers can be called with this callthrough. It is optional. If you don't create a whitelist a user of this callthrough can call any phone number with it." + name: + label: 'Name' + hint: '' + sip_account: + label: 'SIP account' + hint: 'When chosen all callthroughs will be routed through this SIP account.' + sip_account_id: + label: 'SIP account ID' + hint: 'Please look up the SIP account ID in the URL of the SIP account.' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/conference_invitees/de.yml b/config/locales/views/conference_invitees/de.yml new file mode 100644 index 0000000..915bc5c --- /dev/null +++ b/config/locales/views/conference_invitees/de.yml @@ -0,0 +1,55 @@ +de: + conference_invitees: + name: 'Telefonkonferenz Teilnehmer' + controller: + successfuly_created: 'Ein Teilnehmer wurde der Telefonkonferenz hinzugefügt.' + successfuly_updated: 'Der Teilnehmer wurde aktualisiert.' + successfuly_destroyed: 'Der Teilnehmer wurde aus der Telefonkonferenz entfernt.' + index: + page_title: 'Telefonkonferenz Teilnehmer' + conference_id: 'Telefonkonferenz' + phone_book_entry_id: 'Telefonbucheintrag' + pin: 'PIN' + speaker: 'Sprecher-Freischaltung' + moderator: 'Moderator' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Teilnehmer aus der Telefonkonferenz entfernen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neuen Teilnehmer für die Telefonkonferenz %{resource} hinzufügen' + show: + page_title: 'Teilnehmer anzeigen' + conference_id: 'Telefonkonferenz' + phone_book_entry_id: 'Telefonbucheintrag' + pin: 'PIN' + speaker: 'Sprecher-Freischaltung' + moderator: 'Moderator' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Teilnehmer aus der Telefonkonferenz entfernen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle Telefonkonferenz Teilnehmer anzeigen' + new: + page_title: 'Neuen Teilnehmer hinzufügen' + actions: + edit: + page_title: 'Teilnehmer bearbeiten' + form: + conference_id: + label: 'Telefonkonferenz' + hint: '' + phone_book_entry_id: + label: 'Telefonbucheintrag' + hint: '' + pin: + label: 'PIN' + hint: '' + speaker: + label: 'Sprecher-Freischaltung' + hint: '' + moderator: + label: 'Moderator' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/conference_invitees/en.yml b/config/locales/views/conference_invitees/en.yml new file mode 100644 index 0000000..7e1a1ca --- /dev/null +++ b/config/locales/views/conference_invitees/en.yml @@ -0,0 +1,54 @@ +en: + conference_invitees: + name: 'Conference invitee' + controller: + successfuly_created: 'Successfully created conference invitee.' + successfuly_updated: 'Successfully updated conference invitee.' + successfuly_destroyed: 'Successfully destroyed conference invitee.' + index: + page_title: 'Conference invitees' + conference_id: 'Conference' + phone_book_entry_id: 'Phone book entry' + pin: 'PIN' + speaker: 'Speaker' + moderator: 'Moderator' + actions: + confirm: 'Are you sure you want to delete this invitee?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New conference invitee for %{resource}' + show: + page_title: 'Show conference invitee' + conference_id: 'Conference' + phone_book_entry_id: 'Phone book entry' + pin: 'PIN' + speaker: 'Speaker' + moderator: 'Moderator' + actions: + confirm: 'Are you sure you want to delete this invitee?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View all conference invitees' + new: + page_title: 'New conference invitee' + edit: + page_title: 'Editing Conference invitee' + form: + conference_id: + label: 'Conference' + hint: '' + phone_book_entry_id: + label: 'Phone book entry' + hint: '' + pin: + label: 'PIN' + hint: '' + speaker: + label: 'Speaker' + hint: '' + moderator: + label: 'Moderator' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/conferences/de.yml b/config/locales/views/conferences/de.yml new file mode 100644 index 0000000..cf3c080 --- /dev/null +++ b/config/locales/views/conferences/de.yml @@ -0,0 +1,81 @@ +de: + conferences: + name: 'Telefonkonferenz' + new_name_scaffold: 'Telefonkonferenz Nr. %{counter}' + controller: + successfuly_created: 'Eine neue Telefonkonferenz wurde erstellt.' + successfuly_updated: 'Die Telefonkonferenz wurde aktualisiert.' + successfuly_destroyed: 'Die Telefonkonferenz wurde gelöscht.' + index: + page_title: 'Telefonkonferenzen' + name: 'Name' + start: 'Start' + end: 'Ende' + description: 'Beschreibung' + phone_numbers: 'Telefonnummern' + pin: 'PIN' + max_members: 'Maximale Teilnehmeranzahl' + number_of_invitees: 'Teilnehmeranzahl' + flags: 'Flags' + open_for_anybody: 'Offen für alle' + announce_new_member_by_name: 'Betreten ankündigen' + announce_left_member_by_name: 'Verlassen ankündigen' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Telefonkonferenz löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neue Telefonkonferenz für %{resource} anlegen' + show: + page_title: 'Telefonkonferenz anzeigen' + name: 'Name' + start: 'Start' + end: 'Ende' + description: 'Beschreibung' + pin: 'PIN' + max_members: 'Maximale Teilnehmeranzahl' + open_for_anybody: 'Für jeden offen?' + announce_new_member_by_name: 'Neue Teilnehmer werden angekündigt' + announce_left_member_by_name: 'Die Konferenz verlassende Teilnehmer werden angekündigt' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Telefonkonferenz löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle Telefonkonferenzen anzeigen' + view_all_of: 'Alle Telefonkonferenzen von %{resource} anzeigen' + new: + page_title: 'Neue Telefonkonferenz' + name: "Telefonkonferenz von %{resource}" + actions: + edit: + page_title: 'Telefonkonferenz bearbeiten' + form: + name: + label: 'Name' + hint: '' + start: + label: 'Start' + hint: 'Bitte eine Start-Zeit eintragen, falls diese Telefonkonferenz zeitlich begrenzt ist.' + end: + label: 'Ende' + hint: 'Bitte eine Ende-Zeit eintragen, falls diese Telefonkonferenz zeitlich begrenzt ist.' + description: + label: 'Beschreibung' + hint: '' + max_members: + label: 'Maximale Teilnehmeranzahl' + hint: 'Die maximale Anzahl von Teilnehmern in dieser Telefonkonferenz. Der Inhaber der Konferenz kann immer eintreten (auch wenn die Konferenz bereits voll ist).' + pin: + label: 'PIN' + hint: '' + open_for_anybody: + label: 'Für jeden offen?' + hint: 'Jeder kann an dieser Konferenz teilnehmen.' + announce_new_member_by_name: + label: 'Neue Teilnehmer werden angekündigt' + hint: 'Ein neuer Teilnehmer wird bei Eintritt in die Konferenz vorgestellt.' + announce_left_member_by_name: + label: 'Die Konferenz verlassende Teilnehmer werden angekündigt' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/conferences/en.yml b/config/locales/views/conferences/en.yml new file mode 100644 index 0000000..7967123 --- /dev/null +++ b/config/locales/views/conferences/en.yml @@ -0,0 +1,81 @@ +en: + conferences: + name: 'Conference' + new_name_scaffold: 'Conference #%{counter}' + controller: + successfuly_created: 'Successfully created the conference.' + successfuly_updated: 'Successfully updated the conference.' + successfuly_destroyed: 'Successfully destroyed the conference.' + index: + page_title: 'Conferences' + name: 'Name' + start: 'Start' + end: 'End' + description: 'Description' + phone_numbers: 'Phone numbers' + pin: 'PIN' + max_members: 'Max members' + number_of_invitees: 'Number of invitees' + flags: 'Flags' + open_for_anybody: 'Open for anybody' + announce_new_member_by_name: 'Announce entering' + announce_left_member_by_name: 'Announce leaving' + actions: + confirm: 'Are you sure you want to delete this conference?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New conference for %{resource}' + show: + page_title: 'Show conference' + name: 'Name' + start: 'Start' + end: 'End' + description: 'Description' + pin: 'PIN' + max_members: 'Max members' + open_for_anybody: 'Open for anybody' + announce_new_member_by_name: 'Announce new member' + announce_left_member_by_name: 'Announce left member' + actions: + confirm: 'Are you sure you want to delete this conference?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View all conferences' + view_all_of: 'View all conferences of %{resource}' + new: + page_title: 'New conference' + name: "%{resource}'s conference" + actions: + edit: + page_title: 'Editing conference' + form: + name: + label: 'Name' + hint: '' + start: + label: 'Start' + hint: 'Please enter a start time if this conference is only valid in a special time window.' + end: + label: 'End' + hint: 'Please enter an end time if this conference is only valid in a special time window.' + description: + label: 'Description' + hint: '' + max_members: + label: 'Max members' + hint: 'The maximum number of people in this conference. The owner of a conference can always enter the conference (even a full one).' + pin: + label: 'PIN' + hint: '' + open_for_anybody: + label: 'Open for anybody' + hint: '' + announce_new_member_by_name: + label: 'Announce a new conference member' + hint: 'A new member has to record his name which played before joining the conference.' + announce_left_member_by_name: + label: 'Announce the leaving of a member' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/fax_accounts/de.yml b/config/locales/views/fax_accounts/de.yml new file mode 100644 index 0000000..5977bb3 --- /dev/null +++ b/config/locales/views/fax_accounts/de.yml @@ -0,0 +1,60 @@ +de: + fax_accounts: + name: 'Software-Fax-Konto' + controller: + successfuly_created: 'Eine neues Software-Fax-Konto wurde erstellt.' + successfuly_updated: 'Das Software-Fax-Konto wurde aktualisiert.' + successfuly_destroyed: 'Das Software-Fax-Konto wurde gelöscht.' + index: + page_title: 'Software-Fax-Konten' + name: 'Name' + station_id: 'Stations-ID' + email: 'E-Mail' + phone_numbers: 'Telefonnummern' + days_till_auto_delete: 'Anzahl der Tage bis Faxe gelöscht werden' + thumbnails: 'Vorschaubilder' + received: 'Empfangen' + sent: 'Gesendet' + retries: 'Max. Anzahl von Sendeversuchen' + last_update: 'Letzter Eintrag' + send_a_fax: 'Fax versenden' + actions: + confirm: 'Sind Sie sicher, dass Sie dieses Software-Fax-Konto löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neues Fax-Konto für %{resource} anlegen' + show: + page_title: 'Software-Fax-Konto anzeigen' + name: 'Name' + station_id: 'Stations-ID' + email: 'E-Mail' + days_till_auto_delete: 'Anzahl der Tage bis Faxe gelöscht werden' + retries: 'Max. Anzahl von Sendeversuchen' + actions: + confirm: 'Sind Sie sicher, dass Sie dieses Software-Fax-Konto löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + new: + page_title: 'Neues Software-Fax-Konto' + edit: + page_title: 'Software-Fax-Konto bearbeiten' + form: + name: + label: 'Name' + hint: '' + station_id: + label: 'Stations-ID' + hint: 'Die "Stations-ID" wird in der Kopfzeile eines Faxes als Absender angezeigt.' + email: + label: 'E-Mail' + hint: 'Ein PDF eines empfangenen Faxes wird an diese E-Mail Adresse verschickt.' + days_till_auto_delete: + label: 'Anzahl der Tage bis Faxe gelöscht werden' + hint: '' + retries: + label: 'Max. Anzahl von Sendeversuchen' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/fax_accounts/en.yml b/config/locales/views/fax_accounts/en.yml new file mode 100644 index 0000000..55e935a --- /dev/null +++ b/config/locales/views/fax_accounts/en.yml @@ -0,0 +1,60 @@ +en: + fax_accounts: + name: 'Fax account' + controller: + successfuly_created: 'Successfully created fax account.' + successfuly_updated: 'Successfully updated fax account.' + successfuly_destroyed: 'Successfully destroyed fax account.' + index: + page_title: 'Fax accounts' + name: 'Name' + station_id: 'Station ID' + email: 'E-Mail' + phone_numbers: 'Phone numbers' + days_till_auto_delete: 'Day till auto delete' + thumbnails: 'Thumbnails' + received: 'Received' + sent: 'Sent' + retries: 'Retries' + last_update: 'last update' + send_a_fax: 'send a fax' + actions: + confirm: 'Are you sure you want to delete this fax account?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New fax account for %{resource}' + show: + page_title: 'Show fax account' + name: 'Name' + station_id: 'Station ID' + email: 'E-Mail' + days_till_auto_delete: 'Day till auto delete' + retries: 'Retries' + actions: + confirm: 'Are you sure you want to delete this fax account?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View All' + new: + page_title: 'New fax account' + edit: + page_title: 'Editing fax account' + form: + name: + label: 'Name' + hint: '' + station_id: + label: 'Station ID' + hint: 'This string will be displayed in the top row of a fax before the fax number.' + email: + label: 'E-Mail' + hint: 'A PDF of each received fax will be send by e-mail to this address.' + days_till_auto_delete: + label: 'Days till auto delete' + hint: 'Number of days after faxes can be automaticaly deleted in the system to save storage space.' + retries: + label: 'Retries' + hint: 'Number of times the system retries to send the fax in case the destination is busy.' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/fax_documents/de.yml b/config/locales/views/fax_documents/de.yml new file mode 100644 index 0000000..066746d --- /dev/null +++ b/config/locales/views/fax_documents/de.yml @@ -0,0 +1,148 @@ +de: + fax_documents: + name: 'Fax-Dokument' + controller: + successfuly_created: 'Eine neues Fax-Dokument wurde erstellt.' + successfuly_updated: 'Das Fax-Dokument wurde aktualisiert.' + successfuly_destroyed: 'Das Fax-Dokument wurde gelöscht.' + states: + queued_for_sending: 'In der Warteschleife zum Versand' + sending: 'Wird aktuell versendet' + unsuccessful: 'Nicht erfolgreich' + successful: 'Erfolgreich' + inbound: 'Eingehendes Fax' + result_codes: + code_0: 'OK' + code_2: 'Timeout beim Warten auf den Beginn der Kommunikation' + code_3: 'Timeout beim Warten auf die erste Nachricht' + code_5: 'Der HDLC Träger wurde nicht rechtzeitig beendet' + code_6: 'Training mit allen kompatiblen Modems fehlgeschlagen' + code_13: 'Unerwartete Nachricht erhalten' + code_14: 'Fehlerhafte Antwort auf DCS oder Training' + code_15: 'DCN von der Gegenstelle empfangen nachdem eine Seite gesendet wurde' + code_17: 'DCN empfangen während DIS erwartet wurde' + code_20: 'Keine Antwort auf DCS oder TCF empfangen' + code_23: 'Fehlerhafte ECM Antwort vom Sender empfangen' + code_31: 'Timer T2 abgelaufen während auf die Seite gewartet wurde' + code_32: 'Timer T2 abgelaufen während auf die nächse Seite gewartet wurde' + code_48: 'Nach zu vielen Fehlversuchen beendet' + code_49: 'Anruf vorzeitig beendet' + index: + page_title: 'Fax-Dokumente' + inbound: 'Eingehend' + state: 'Status' + transmission_time: 'Übertragungszeit' + sent_at: 'Sendezeit' + document_total_pages: 'Seitenanzahl' + document_transferred_pages: 'Übertragene Seiten' + ecm_requested: 'Fehlerkorrektur (ECM) angefragt' + ecm_used: 'Fehlerkorrektur (ECM) verwendet' + image_resolution: 'Bildauflösung' + image_size: 'Bildgröße' + local_station_id: 'Lokale Gegenstelle' + result_code: 'Ergebnis-Code' + result_text: 'Ergebnis-Text' + remote_station_id: 'Stations-ID der Gegenstelle' + success: 'Erfolgreich' + transfer_rate: 'Transferrate' + fax_resolution: 'Fax Auflösung' + document: 'Dokument' + destination_phone_number: 'Ziel Fax-Nummer' + actions: + confirm: 'Sind Sie sicher, dass Sie dieses Fax-Dokument löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neues Fax-Dokument' + download_pdf: 'PDF herunterladen' + show: + page_title: 'Fax-Dokument anzeigen' + inbound: 'Eingehend' + state: 'Status' + transmission_time: 'Übertragungszeit' + sent_at: 'Sendezeit' + document_total_pages: 'Seitenanzahl' + document_transferred_pages: 'Übertragende Seiten' + ecm_requested: 'Fehlerkorrektur (ECM) angefragt' + ecm_used: 'Fehlerkorrektur (ECM) verwendet' + image_resolution: 'Bildauflösung' + image_size: 'Bildgröße' + local_station_id: 'Lokale Gegenstelle' + result_code: 'Ergebnis-Code' + result_text: 'Ergebnis-Text' + remote_station_id: 'Stations-ID der Gegenstelle' + success: 'Erfolgreich' + transfer_rate: 'Transferrate' + document: 'Dokument' + fax_resolution: 'Fax Auflösung' + destination_phone_number: 'Ziel Fax-Nummer' + actions: + confirm: 'Sind Sie sicher, dass Sie dieses Fax-Dokument löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + view_all_for: 'Alle Fax-Dokumente von %{resource} anzeigen' + new: + page_title: 'Neues Fax-Dokument' + edit: + page_title: 'Fax-Dokument bearbeiten' + form: + inbound: + label: 'Eingehend' + hint: '' + state: + label: 'Status' + hint: '' + transmission_time: + label: 'Übertragungszeit' + hint: '' + sent_at: + label: 'Sendezeit' + hint: '' + document_total_pages: + label: 'Seitenanzahl' + hint: '' + document_transferred_pages: + label: 'Übertragene Seiten' + hint: '' + ecm_requested: + label: 'Fehlerkorrektur (ECM) angefragt' + hint: '' + ecm_used: + label: 'Fehlerkorrektur (ECM) verwendet' + hint: '' + image_resolution: + label: 'Bildauflösung' + hint: '' + image_size: + label: 'Bildgröße' + hint: '' + local_station_id: + label: 'Lokale Gegenstelle' + hint: '' + result_code: + label: 'Ergebnis-Code' + hint: '' + result_text: + label: 'Ergebnis-Text' + hint: '' + remote_station_id: + label: 'Stations-ID der Gegenstelle' + hint: '' + success: + label: 'Erfolgreich' + hint: '' + transfer_rate: + label: 'Transferrate' + hint: '' + document: + label: 'Dokument' + hint: '' + fax_resolution: + label: 'Fax Auflösung' + hint: '' + destination_phone_number: + label: 'Ziel Fax-Nummer' + hint: '' + button: 'Absenden' + diff --git a/config/locales/views/fax_documents/en.yml b/config/locales/views/fax_documents/en.yml new file mode 100644 index 0000000..bedb2e6 --- /dev/null +++ b/config/locales/views/fax_documents/en.yml @@ -0,0 +1,148 @@ +en: + fax_documents: + name: 'Fax' + controller: + successfuly_created: 'Successfully created fax.' + successfuly_updated: 'Successfully updated fax.' + successfuly_destroyed: 'Successfully destroyed fax.' + states: + queued_for_sending: 'Queued for sending' + sending: 'Currently sending' + unsuccessful: 'Unsuccessful' + successful: 'Successful' + inbound: 'Inbound' + result_codes: + code_0: 'OK' + code_2: 'Timed out waiting for initial communication' + code_3: 'Timed out waiting for the first message' + code_5: 'The HDLC carrier did not stop in a timely manner' + code_6: 'Failed to train with any of the compatible modems' + code_13: 'Unexpected message received' + code_14: 'Received bad response to DCS or training' + code_15: 'Received a DCN from remote after sending a page' + code_17: 'Received a DCN while waiting for a DIS' + code_20: 'Received no response to DCS or TCF' + code_23: 'Invalid ECM response received from transmitter' + code_31: 'Timer T2 expired while waiting for fax page' + code_32: 'Timer T2 expired while waiting for next fax page' + code_48: 'Disconnected after permitted retries' + code_49: 'The call dropped prematurely' + index: + page_title: 'Faxes' + inbound: 'Inbound' + state: 'State' + transmission_time: 'Transmission time' + sent_at: 'Sent at' + document_total_pages: 'Document total pages' + document_transferred_pages: 'Document transferred pages' + ecm_requested: 'ECM requested' + ecm_used: 'ECM used' + image_resolution: 'Image resolution' + image_size: 'Image size' + local_station_id: 'Local station' + result_code: 'Result code' + result_text: 'Result text' + remote_station_id: 'Remote station ID' + success: 'Success' + transfer_rate: 'Transfer rate' + fax_resolution: 'Resolution' + document: 'Document' + destination_phone_number: 'Destination phone number' + actions: + confirm: 'Are you sure you want to delete this fax?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New fax document' + download_pdf: 'Download PDF' + show: + page_title: 'Show Fax' + inbound: 'Inbound' + state: 'State' + transmission_time: 'Transmission time' + sent_at: 'Sent at' + document_total_pages: 'Document total pages' + document_transferred_pages: 'Document transferred pages' + ecm_requested: 'ECM requested' + ecm_used: 'ECM used' + image_resolution: 'Image resolution' + image_size: 'Image size' + local_station_id: 'Local station' + result_code: 'Result code' + result_text: 'Result text' + remote_station_id: 'Remote station ID' + success: 'Success' + transfer_rate: 'Transfer rate' + document: 'Document' + fax_resolution: 'Resolution' + destination_phone_number: 'Destination phone number' + actions: + confirm: 'Are you sure you want to delete this fax?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View all' + view_all_for: 'View all faxes of %{resource}' + new: + page_title: 'New Fax' + edit: + page_title: 'Editing Fax' + form: + inbound: + label: 'Inbound' + hint: '' + state: + label: 'State' + hint: '' + transmission_time: + label: 'Transmission time' + hint: '' + sent_at: + label: 'Sent at' + hint: '' + document_total_pages: + label: 'Document total pages' + hint: '' + document_transferred_pages: + label: 'Document transferred pages' + hint: '' + ecm_requested: + label: 'ECM requested' + hint: '' + ecm_used: + label: 'ECM used' + hint: '' + image_resolution: + label: 'Image resolution' + hint: '' + image_size: + label: 'Image size' + hint: '' + local_station_id: + label: 'Local station' + hint: '' + result_code: + label: 'Result code' + hint: '' + result_text: + label: 'Result text' + hint: '' + remote_station_id: + label: 'Remote station ID' + hint: '' + success: + label: 'Success' + hint: '' + transfer_rate: + label: 'Transfer rate' + hint: '' + document: + label: 'Document' + hint: '' + fax_resolution: + label: 'Resolution' + hint: '' + destination_phone_number: + label: 'Destination phone number' + hint: '' + button: 'Submit' + diff --git a/config/locales/views/gemeinschaft_setups/de.yml b/config/locales/views/gemeinschaft_setups/de.yml new file mode 100644 index 0000000..a1dd989 --- /dev/null +++ b/config/locales/views/gemeinschaft_setups/de.yml @@ -0,0 +1,28 @@ +de: + gemeinschaft_setups: + name: 'Gemeinschaft Installations-Assistent' + initial_setup: + tenant_name_default: 'Beispiel-Firma GmbH' + access_denied_only_available_on_a_new_system: 'Zugriff verweigert! Der Installations-Assistent ist nur auf einem frisch installierten System verfügbar.' + successful_setup: 'Ihr Admin-Konto wurde erstellt. Als nächsten Schritt müssen Sie einen Mandant anlegen.' + admin_name: 'admin' + super_tenant_description: 'Die Benutzer dieses Mandanten haben besondere Rechte. Sie können andere Mandanten anlegen und löschen.' + admin_group_name: 'Admins' + user_group_name: 'Benutzer' + super_admin_group_name: 'Super-Admins' + new: + page_title: 'Gemeinschaft Setup' + form: + user_id: + label: 'Benutzer' + hint: '' + sip_domain_id: + label: 'SIP-Domain' + hint: '' + country_id: + label: 'Land' + hint: '' + language_id: + label: 'Sprache' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/gemeinschaft_setups/en.yml b/config/locales/views/gemeinschaft_setups/en.yml new file mode 100644 index 0000000..aef2686 --- /dev/null +++ b/config/locales/views/gemeinschaft_setups/en.yml @@ -0,0 +1,28 @@ +en: + gemeinschaft_setups: + name: 'Gemeinschaft Setup Wizard' + initial_setup: + tenant_name_default: 'Snake Oil, Ltd.' + access_denied_only_available_on_a_new_system: 'Access denied! The setup wizard is only available on a new system.' + successful_setup: 'Your admin account has been created. The next step is to create a tenant.' + admin_name: 'admin' + super_tenant_description: 'The members of this tenant have the power to create and delete other tenants.' + admin_group_name: 'Admins' + user_group_name: 'Users' + super_admin_group_name: 'Super-Admins' + new: + page_title: 'New Gemeinschaft setup' + form: + user_id: + label: 'User' + hint: '' + sip_domain_id: + label: 'SIP domain' + hint: '' + country_id: + label: 'Country' + hint: '' + language_id: + label: 'Language' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/gs_cluster_sync_log_entries/de.yml b/config/locales/views/gs_cluster_sync_log_entries/de.yml new file mode 100644 index 0000000..454c317 --- /dev/null +++ b/config/locales/views/gs_cluster_sync_log_entries/de.yml @@ -0,0 +1,60 @@ +de: + gs_cluster_sync_log_entries: + name: 'GS Cluster Synclog Einträge' + controller: + successfuly_created: 'Ein Eintrag im GS Cluster Synclog wurde erstellt.' + successfuly_updated: 'Der Eintrag im GS Cluster Synclog wurde aktualisiert.' + successfuly_destroyed: 'Der Eintrag im GS Cluster Synclog wurde gelöscht.' + index: + page_title: 'GS Cluster Synclog Einträge auflisten' + gs_node_id: 'GS Knoten' + class_name: 'Klasse' + action: 'Aktion' + content: 'Inhalt' + status: 'Status' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Eintrag aus dem GS Cluster Synclog löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neuen GS Cluster Synclog Eintrag für %{resource}' + show: + page_title: 'Eintrag anzeigen' + gs_node_id: 'GS Knoten' + class_name: 'Klasse' + action: 'Aktion' + content: 'Inhalt' + status: 'Status' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Eintrag aus dem GS Cluster Synclog löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + new: + page_title: 'Neuer Eintrag' + actions: + back_to_list: 'Zurück zur Übersicht' + edit: + page_title: 'Eintrag bearbeiten' + actions: + back_to_list: 'Zurück zur Übersicht' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + form: + gs_node_id: + label: 'GS Knoten' + hint: '' + class_name: + label: 'Klasse' + hint: '' + action: + label: 'Aktion' + hint: '' + content: + label: 'Inhalt' + hint: '' + status: + label: 'Status' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/gs_cluster_sync_log_entries/en.yml b/config/locales/views/gs_cluster_sync_log_entries/en.yml new file mode 100644 index 0000000..63c0cbb --- /dev/null +++ b/config/locales/views/gs_cluster_sync_log_entries/en.yml @@ -0,0 +1,60 @@ +en: + gs_cluster_sync_log_entries: + name: 'Gs cluster sync log entry' + controller: + successfuly_created: 'Successfully created Gs cluster sync log entry.' + successfuly_updated: 'Successfully updated Gs cluster sync log entry.' + successfuly_destroyed: 'Successfully destroyed Gs cluster sync log entry.' + index: + page_title: 'Listing Gs cluster sync log entry' + gs_node_id: 'Gs node' + class_name: 'Class name' + action: 'Action' + content: 'Content' + status: 'Status' + actions: + confirm: 'Are you sure you want to delete this Gs cluster sync log entry?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New Gs cluster sync log entry for %{resource}' + show: + page_title: 'Show Gs cluster sync log entry' + gs_node_id: 'Gs node' + class_name: 'Class name' + action: 'Action' + content: 'Content' + status: 'Status' + actions: + confirm: 'Are you sure you want to delete this element?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View All' + new: + page_title: 'New Gs cluster sync log entry' + actions: + back_to_list: 'Back to Index' + edit: + page_title: 'Editing Gs cluster sync log entry' + actions: + back_to_list: 'Back to Index' + edit: 'Edit' + view_all: 'View All' + form: + gs_node_id: + label: 'Gs node' + hint: '' + class_name: + label: 'Class name' + hint: '' + action: + label: 'Action' + hint: '' + content: + label: 'Content' + hint: '' + status: + label: 'Status' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/gs_nodes/de.yml b/config/locales/views/gs_nodes/de.yml new file mode 100644 index 0000000..62b935e --- /dev/null +++ b/config/locales/views/gs_nodes/de.yml @@ -0,0 +1,65 @@ +de: + gs_nodes: + name: 'GS Knoten' + controller: + successfuly_created: 'Ein GS Knoten wurde erstellt.' + successfuly_updated: 'Der GS Knoten wurde aktualisiert.' + successfuly_destroyed: 'Der GS Knoten wurde gelöscht.' + index: + page_title: 'GS Knoten auflisten' + name: 'Name' + ip_address: 'IP-Adresse' + push_updates_to: 'Änderungen an diesen Konten senden?' + accepts_updates_from: 'Änderungen von diesem Konten akzeptieren?' + site: 'Standort' + element_name: 'Elementenname' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen GS Knoten löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neuer GS Knoten für %{resource}' + show: + page_title: 'GS Knoten anzeigen' + name: 'Name' + ip_address: 'IP-Adresse' + push_updates_to: 'Änderungen an diesen Konten senden?' + accepts_updates_from: 'Änderungen von diesem Konten akzeptieren?' + site: 'Standort' + element_name: 'Elementenname' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen GS Knoten löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + new: + page_title: 'Neuer GS Knoten' + actions: + back_to_list: 'Zurück zur Übersicht' + edit: + page_title: 'GS Knoten bearbeiten' + actions: + back_to_list: 'Zurück zur Übersicht' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + form: + name: + label: 'Name' + hint: '' + ip_address: + label: 'IP-Adresse' + hint: '' + site: + label: 'Standort' + hint: 'z.B. http://0.0.0.0:3000/' + element_name: + label: 'Elementenname' + hint: '' + push_updates_to: + label: 'Änderungen senden' + hint: '' + accepts_updates_from: + label: 'Änderungen empfangen' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/gs_nodes/en.yml b/config/locales/views/gs_nodes/en.yml new file mode 100644 index 0000000..8fd7c03 --- /dev/null +++ b/config/locales/views/gs_nodes/en.yml @@ -0,0 +1,65 @@ +en: + gs_nodes: + name: 'GS Node' + controller: + successfuly_created: 'Successfully created GS Node.' + successfuly_updated: 'Successfully updated GS Node.' + successfuly_destroyed: 'Successfully destroyed GS Node.' + index: + page_title: 'Listing GS Nodes' + name: 'Name' + ip_address: 'IP address' + push_updates_to: 'Push updates to this node?' + accepts_updates_from: 'Accepts updates from this node?' + site: 'Site' + element_name: 'Element name' + actions: + confirm: 'Are you sure you want to delete this GS Node?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New GS Node for %{resource}' + show: + page_title: 'Show GS Node' + name: 'Name' + ip_address: 'IP address' + push_updates_to: 'Push updates' + site: 'Site' + element_name: 'Element name' + accepts_updates_from: 'Accepts updates from this node?' + actions: + confirm: 'Are you sure you want to delete this element?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View All' + new: + page_title: 'New GS Node' + actions: + back_to_list: 'Back to Index' + edit: + page_title: 'Editing GS Node' + actions: + back_to_list: 'Back to Index' + edit: 'Edit' + view_all: 'View All' + form: + name: + label: 'Name' + hint: '' + ip_address: + label: 'IP address' + hint: '' + site: + label: 'Site' + hint: 'e.g. http://0.0.0.0:3000/' + element_name: + label: 'Element name' + hint: '' + push_updates_to: + label: 'Push updates to' + hint: '' + accepts_updates_from: + label: 'Accepts updates from this node?' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/gui_functions/de.yml b/config/locales/views/gui_functions/de.yml new file mode 100644 index 0000000..9f95f7d --- /dev/null +++ b/config/locales/views/gui_functions/de.yml @@ -0,0 +1,50 @@ +de: + gui_functions: + name: 'Oberflächen-Funktionen' + controller: + successfuly_created: 'Eine Oberflächen-Funktion wurde erstellt.' + successfuly_updated: 'Die Oberflächen-Funktion wurde aktualisiert.' + successfuly_destroyed: 'Die Oberflächen-Funktion wurde gelöscht.' + index: + page_title: 'Oberflächen-Funktionen' + category: 'Kategorie' + name: 'Name' + description: 'Beschreibung' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Oberflächen-Funktion löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neue Oberflächen-Funktion für %{resource}' + show: + page_title: 'Oberflächen-Funktion anzeigen' + category: 'Kategorie' + name: 'Name' + description: 'Beschreibung' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Oberflächen-Funktion löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + new: + page_title: 'Neue Oberflächen-Funktion' + actions: + back_to_list: 'Zurück zur Übersicht' + edit: + page_title: 'Oberflächen-Funktion bearbeiten' + actions: + back_to_list: 'Zurück zur Übersicht' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + form: + category: + label: 'Kategorie' + hint: '' + name: + label: 'Name' + hint: '' + description: + label: 'Beschreibung' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/gui_functions/en.yml b/config/locales/views/gui_functions/en.yml new file mode 100644 index 0000000..346f27a --- /dev/null +++ b/config/locales/views/gui_functions/en.yml @@ -0,0 +1,50 @@ +de: + gui_functions: + name: 'Gui Function' + controller: + successfuly_created: 'Successfully created a gui function.' + successfuly_updated: 'Successfully updated a gui function.' + successfuly_destroyed: 'Successfully destroyed a gui function.' + index: + page_title: 'Listing of all gui functions' + category: 'Category' + name: 'Name' + description: 'Description' + actions: + confirm: 'Are you sure you want to delete this gui function?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New gui function for %{resource}' + show: + page_title: 'Show gui function' + category: 'Category' + name: 'Name' + description: 'Description' + actions: + confirm: 'Are you sure you want to delete this element?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View All' + new: + page_title: 'New gui function' + actions: + back_to_list: 'Back to Index' + edit: + page_title: 'Editing gui function' + actions: + back_to_list: 'Back to Index' + edit: 'Edit' + view_all: 'View All' + form: + category: + label: 'Category' + hint: '' + name: + label: 'Name' + hint: '' + description: + label: 'Description' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/hunt_group_members/de.yml b/config/locales/views/hunt_group_members/de.yml new file mode 100644 index 0000000..f98ecfc --- /dev/null +++ b/config/locales/views/hunt_group_members/de.yml @@ -0,0 +1,48 @@ +de: + hunt_group_members: + name: 'Rufgruppen-Teilnehmer' + controller: + successfuly_created: 'Ein neuer Rufgruppen-Teilnehmer wurde eingetragen.' + successfuly_updated: 'Der Rufgruppen-Teilnehmer wurde aktualisiert.' + successfuly_destroyed: 'Der Rufgruppen-Teilnehmer wurde entfernt.' + index: + page_title: 'Rufgruppen-Teilnehmer' + name: 'Name' + active: 'Aktiv' + phone_numbers: 'Telefonnummern' + can_switch_status_itself: 'Darf seinen Status selbst ändern' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Rufgruppen-Teilnehmer entfernen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neuen Teilnehmer für %{resource} anlegen' + show: + page_title: 'Teilnehmer anzeigen' + name: 'Name' + active: 'Aktiv' + can_switch_status_itself: 'Darf seinen Status selber ändern' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Rufgruppen-Teilnehmer entfernen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle Rufgruppen-Teilnehmer anzeigen' + new: + page_title: 'Neuen Teilnehmer anlegen' + edit: + page_title: 'Teilnehmer bearbeiten' + form: + name: + label: 'Name' + hint: '' + active: + label: 'Aktiv' + hint: '' + can_switch_status_itself: + label: 'Darf seinen Status selber ändern' + hint: 'Diese Funktion geht nur von Nummern innerhalb der Anlage.' + phone_numbers: + label: 'Telefonnummern' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/hunt_group_members/en.yml b/config/locales/views/hunt_group_members/en.yml new file mode 100644 index 0000000..0daa146 --- /dev/null +++ b/config/locales/views/hunt_group_members/en.yml @@ -0,0 +1,48 @@ +en: + hunt_group_members: + name: 'Hunt group member' + controller: + successfuly_created: 'Successfully created hunt group member.' + successfuly_updated: 'Successfully updated hunt group member.' + successfuly_destroyed: 'Successfully destroyed hunt group member.' + index: + page_title: 'Hunt group members' + name: 'Name' + active: 'Active' + phone_numbers: 'Phone numbers' + can_switch_status_itself: 'Can switch status itself' + actions: + confirm: 'Are you sure you want to delete this hunt group member?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New hunt group member for %{resource}' + show: + page_title: 'Show hunt group member' + name: 'Name' + active: 'Active' + can_switch_status_itself: 'Can switch status itself' + actions: + confirm: 'Are you sure you want to delete this element?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View All' + new: + page_title: 'New hunt group member' + edit: + page_title: 'Editing hunt group member' + form: + name: + label: 'Name' + hint: '' + active: + label: 'Active' + hint: '' + can_switch_status_itself: + label: 'Can switch status itself' + hint: 'This feature is only accessible from internal extensions.' + phone_numbers: + label: 'Phone numbers' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/hunt_groups/de.yml b/config/locales/views/hunt_groups/de.yml new file mode 100644 index 0000000..6865d18 --- /dev/null +++ b/config/locales/views/hunt_groups/de.yml @@ -0,0 +1,57 @@ +de: + hunt_groups: + name: 'Rufgruppe' + strategies: + ring_all: 'Alle Rufgruppen-Teilnehmer klingeln parallel' + ring_recursively: 'Die Rufgruppen-Teilnehmer klingeln rekursiv' + controller: + successfuly_created: 'Eine neue Rufgruppe wurde angelegt.' + successfuly_updated: 'Die Rufgruppe wurde aktualisiert.' + successfuly_destroyed: 'Die Rufgruppe wurde gelöscht.' + index: + page_title: 'Rufgruppen' + name: 'Name' + strategy: 'Verteilungs-Strategie' + seconds_between_jumps: 'Sekunden zwischen Sprüngen' + phone_numbers: 'Telefonnummern' + hunt_group_members: 'Rufgruppen-Teilnehmer' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Rufgruppe löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neue Rufgruppe für %{resource} anlegen' + show: + page_title: 'Rufgruppe anzeigen' + name: 'Name' + strategy: 'Verteilungs-Strategie' + seconds_between_jumps: 'Sekunden zwischen Sprüngen' + phone_numbers: 'Telefonnummern' + hunt_group_members: 'Rufgruppen-Teilnehmer' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Rufgruppe löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle Rufgruppen anzeigen' + new: + page_title: 'Neue Rufgruppe' + edit: + page_title: 'Rufgruppe bearbeiten' + form: + name: + label: 'Name' + hint: '' + strategy: + label: 'Verteilungs-Strategie' + hint: '' + seconds_between_jumps: + label: 'Sekunden zwischen Sprüngen' + hint: '' + phone_numbers: + label: 'Telefonnummern' + hint: '' + hunt_group_members: + label: 'Rufgruppen-Teilnehmer' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/hunt_groups/en.yml b/config/locales/views/hunt_groups/en.yml new file mode 100644 index 0000000..7039c97 --- /dev/null +++ b/config/locales/views/hunt_groups/en.yml @@ -0,0 +1,57 @@ +en: + hunt_groups: + name: 'Hunt group' + strategies: + ring_all: 'Ring all' + ring_recursively: 'Ring recursively' + controller: + successfuly_created: 'Successfully created hunt group.' + successfuly_updated: 'Successfully updated hunt group.' + successfuly_destroyed: 'Successfully destroyed hunt group.' + index: + page_title: 'Hunt groups' + name: 'Name' + strategy: 'Strategy' + seconds_between_jumps: 'S.b.jumps' + phone_numbers: 'Phone numbers' + hunt_group_members: 'Members' + actions: + confirm: 'Are you sure you want to delete this hunt group?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New hunt group for %{resource}' + show: + page_title: 'Show hunt group' + name: 'Name' + strategy: 'Strategy' + seconds_between_jumps: 'Seconds between jumps' + phone_numbers: 'Phone numbers' + hunt_group_members: 'Members' + actions: + confirm: 'Are you sure you want to delete this hunt group?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View All' + new: + page_title: 'New hunt group' + edit: + page_title: 'Editing hunt group' + form: + name: + label: 'Name' + hint: '' + strategy: + label: 'Strategy' + hint: '' + seconds_between_jumps: + label: 'Seconds between jumps' + hint: 'Set when the strategy is not "Ring all".' + phone_numbers: + label: 'Phone numbers' + hint: '' + hunt_group_members: + label: 'Members of this hunt group' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/manufacturers/de.yml b/config/locales/views/manufacturers/de.yml new file mode 100644 index 0000000..6620e22 --- /dev/null +++ b/config/locales/views/manufacturers/de.yml @@ -0,0 +1,49 @@ +de: + manufacturers: + name: 'Hersteller' + controller: + successfuly_created: 'Ein neuer Hersteller wurde eingetragen.' + successfuly_updated: 'Der Hersteller wurde aktualisiert.' + successfuly_destroyed: 'Der Hersteller wurde gelöscht.' + index: + page_title: 'Hersteller' + name: 'Name' + ieee_name: 'IEEE name' + homepage_url: 'Homepage URL' + phone_models: 'Telefonmodelle' + state: 'Status' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Hersteller löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + show: + page_title: 'Hersteller anzeigen' + name: 'Name' + ieee_name: 'IEEE name' + homepage_url: 'Homepage URL' + state: 'Status' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Hersteller löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + new: + page_title: 'Neuer Hersteller' + edit: + page_title: 'Hersteller bearbeiten' + form: + name: + label: 'Name' + hint: '' + ieee_name: + label: 'IEEE name' + hint: '' + homepage_url: + label: 'Homepage URL' + hint: '' + state: + label: 'Status' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/manufacturers/en.yml b/config/locales/views/manufacturers/en.yml new file mode 100644 index 0000000..f4804cf --- /dev/null +++ b/config/locales/views/manufacturers/en.yml @@ -0,0 +1,49 @@ +en: + manufacturers: + name: 'Manufacturer' + controller: + successfuly_created: 'Successfully created manufacturer.' + successfuly_updated: 'Successfully updated manufacturer.' + successfuly_destroyed: 'Successfully destroyed manufacturer.' + index: + page_title: 'Manufacturers' + name: 'Name' + ieee_name: 'IEEE name' + homepage_url: 'Homepage URL' + phone_models: 'Phone models' + state: 'State' + actions: + confirm: 'Are you sure you want to delete this manufacturer?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + show: + page_title: 'Show manufacturer' + name: 'Name' + ieee_name: 'IEEE name' + homepage_url: 'Homepage URL' + state: 'State' + actions: + confirm: 'Are you sure you want to delete this manufacturer?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View All' + new: + page_title: 'New manufacturer' + edit: + page_title: 'Editing manufacturer' + form: + name: + label: 'Name' + hint: '' + ieee_name: + label: 'IEEE name' + hint: '' + homepage_url: + label: 'Homepage URL' + hint: '' + state: + label: 'State' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/notifications/de.yml b/config/locales/views/notifications/de.yml new file mode 100644 index 0000000..a66cf76 --- /dev/null +++ b/config/locales/views/notifications/de.yml @@ -0,0 +1,4 @@ +de: + notifications: + new_pin: + subject: 'Neue PIN'
\ No newline at end of file diff --git a/config/locales/views/notifications/en.yml b/config/locales/views/notifications/en.yml new file mode 100644 index 0000000..c256a37 --- /dev/null +++ b/config/locales/views/notifications/en.yml @@ -0,0 +1,4 @@ +en: + notifications: + new_pin: + subject: 'New PIN'
\ No newline at end of file diff --git a/config/locales/views/pages/de.yml b/config/locales/views/pages/de.yml new file mode 100644 index 0000000..bcdd28e --- /dev/null +++ b/config/locales/views/pages/de.yml @@ -0,0 +1,9 @@ +de: + pages: + name: "Webseiten" + welcome_please_log_in: "Willkommen bei Gemeinschaft! Bitte melden Sie sich beim System mit Ihrem Benutzer-Namen und Ihrem Passwort an." + not_enough_rights: "Kontaktieren Sie bitte Ihre System-Administration" # Not Enough rights to access anything in the Navigation. + controller: + access_denied_login_first: 'Zugriff verweigert! Bitte melden Sie sich erst an.' + index: + name: "Startseite"
\ No newline at end of file diff --git a/config/locales/views/pages/en.yml b/config/locales/views/pages/en.yml new file mode 100644 index 0000000..589d4ea --- /dev/null +++ b/config/locales/views/pages/en.yml @@ -0,0 +1,9 @@ +en: + pages: + name: "Pages" + welcome_please_log_in: "Welcome to Gemeinschaft! Please Log In to access the System!" + not_enough_rights: "Contact Administration" # Not Enough rights to access anything in the Navigation. + controller: + access_denied_login_first: 'Access denied! You need to login first.' + index: + name: "Home"
\ No newline at end of file diff --git a/config/locales/views/phone_book_entries/de.yml b/config/locales/views/phone_book_entries/de.yml new file mode 100644 index 0000000..ba9983e --- /dev/null +++ b/config/locales/views/phone_book_entries/de.yml @@ -0,0 +1,161 @@ +de: + phone_book_entries: + name: "Telefonbucheintrag" + controller: + successfuly_created: 'Der Telefonbucheintrag "%{resource}" wurde erstellt.' + successfuly_updated: 'Der Telefonbucheintrag "%{resource}" wurde aktualisiert.' + successfuly_destroyed: 'Der Telefonbucheintrag wurde gelöscht.' + index: + page_title: 'Telefonbucheinträge' + phone_book_id: 'Telefonbuch' + first_name: 'Vorname' + middle_name: 'Zweiter Vorname' + last_name: 'Nachname' + title: 'Akademischer Titel' + nickname: 'Spitzname' + organization: 'Organisation' + is_organization: 'Ist eine Organisation' + department: 'Abteilung' + job_title: 'Position (Berufsbezeichnung)' + is_male: 'männlich' + birthday: 'Geburtstag' + birth_name: 'Mädchenname' + state: 'Status' + description: 'Beschreibung' + position: 'Position' + homepage_personal: 'Persönliche Homepage' + homepage_organization: 'Homepage der Organisation' + twitter_account: 'Twitter Konto' + facebook_account: 'Facebook Konto' + google_plus_account: 'Google+ Konto' + xing_account: 'Xing Konto' + linkedin_account: 'LinkedIn Konto' + mobileme_account: 'iCloud Konto' + more_numbers: 'und %{numbers} weitere' + available_phone_books: 'Verfügbare Telefonbücher:' + create_new_phone_book_entry: 'Einen neuen Eintrag anlegen' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Telefonbucheintrag löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neuen Telefonbucheintrag für %{resource} anlegen' + show: + page_title: 'Telefonbucheinträge für "%{resource}"' + phone_book_id: 'Telefonbuch' + first_name: 'Vorname' + middle_name: 'Zweiter Vorname' + last_name: 'Nachname' + title: 'Akademischer Titel' + nickname: 'Spitzname' + organization: 'Organisation' + is_organization: 'Ist eine Organisation' + department: 'Abteilung' + job_title: 'Position (Berufsbezeichnung)' + is_male: 'männlich' + birthday: 'Geburtstag' + birth_name: 'Mädchenname' + state: 'Status' + description: 'Beschreibung' + position: 'Position' + homepage_personal: 'Persönliche Homepage' + homepage_organization: 'Homepage der Organisation' + twitter_account: 'Twitter Konto' + facebook_account: 'Facebook Konto' + google_plus_account: 'Google+ Konto' + xing_account: 'Xing Konto' + linkedin_account: 'LinkedIn Konto' + mobileme_account: 'iCloud Konto' + manage_phone_number: 'Telefonnummern verwalten.' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Telefonbucheintrag löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle Telefonbucheinträge anzeigen' + new: + page_title: 'Neuer Telefonbucheintrag' + edit: + page_title: 'Telefonbucheintrag bearbeiten' + form: + phone_book_id: + label: 'Tefonbuch' + hint: '' + first_name: + label: 'Vorname' + hint: '' + middle_name: + label: 'Zweiter Vorname' + hint: '' + gender: + label: 'Geschlecht' + hint: '' + male: "männlich" + female: "weiblich" + last_name: + label: 'Nachname' + hint: '' + title: + label: 'Akademischer Titel' + hint: 'z.B. Dr.' + nickname: + label: 'Spitzname' + hint: '' + organization: + label: 'Firmenname' + hint: 'Organisation' + is_organization: + label: 'Ist eine Organisation' + hint: '' + department: + label: 'Abteilung' + hint: '' + job_title: + label: 'Position' + hint: 'z.B. Geschäftsführer' + is_male: + label: 'Ist männlich' + hint: '' + birthday: + label: 'Geburtstag' + hint: '' + birth_name: + label: 'Mädchenname' + hint: '' + state: + label: 'Status' + hint: '' + description: + label: 'Beschreibung' + hint: '' + position: + label: 'Position' + hint: '' + homepage_personal: + label: 'Persönliche Homepage' + hint: '' + homepage_organization: + label: 'Firmen Homepage' + hint: 'http://www.example.com' + twitter_account: + label: 'Twitter Konto' + hint: '@example' + facebook_account: + label: 'Facebook Konto' + hint: '' + google_plus_account: + label: 'Google+ Konto' + hint: '' + xing_account: + label: 'Xing Konto' + hint: '' + linkedin_account: + label: 'LinkedIn Konto' + hint: '' + mobileme_account: + label: 'iCloud Konto' + hint: '' + image: + label: 'Avatar Foto' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/phone_book_entries/en.yml b/config/locales/views/phone_book_entries/en.yml new file mode 100644 index 0000000..3c861a0 --- /dev/null +++ b/config/locales/views/phone_book_entries/en.yml @@ -0,0 +1,161 @@ +en: + phone_book_entries: + name: "Phone Book Entries" + controller: + successfuly_created: 'Successfully created phone book entry "%{resource}".' + successfuly_updated: 'Successfully updated phone book entry "%{resource}".' + successfuly_destroyed: 'Successfully destroyed phone book entry.' + index: + page_title: 'Phone book entries' + phone_book_id: 'Phone book' + first_name: 'First name' + middle_name: 'Middle name' + last_name: 'Last name' + title: 'Title' + nickname: 'Nickname' + organization: 'Organization' + is_organization: 'Is organization' + department: 'Department' + job_title: 'Job title' + is_male: 'Is male' + birthday: 'Birthday' + birth_name: 'Birth name' + state: 'State' + description: 'Description' + position: 'Position' + homepage_personal: 'Homepage personal' + homepage_organization: 'Homepage organization' + twitter_account: 'Twitter account' + facebook_account: 'Facebook account' + google_plus_account: 'Google+ account' + xing_account: 'Xing account' + linkedin_account: 'LinkedIn account' + mobileme_account: 'iCloud account' + more_numbers: 'plus %{numbers} more' + available_phone_books: 'Available phone books:' + create_new_phone_book_entry: 'create a new entry' + actions: + confirm: 'Are you sure you want to delete this phone book entry?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New phone book entry for %{resource}' + show: + page_title: 'Phone book entry for "%{resource}"' + phone_book_id: 'Phone book' + first_name: 'First name' + middle_name: 'Middle name' + last_name: 'Last name' + title: 'Title' + nickname: 'Nickname' + organization: 'Organization' + is_organization: 'Is organization' + department: 'Department' + job_title: 'Job title' + is_male: 'Is male' + birthday: 'Birthday' + birth_name: 'Birth name' + state: 'State' + description: 'Description' + position: 'Position' + homepage_personal: 'Homepage personal' + homepage_organization: 'Homepage organization' + twitter_account: 'Twitter account' + facebook_account: 'Facebook account' + google_plus_account: 'Google+ account' + xing_account: 'Xing account' + linkedin_account: 'LinkedIn account' + mobileme_account: 'iCloud account' + manage_phone_number: 'Manage phone number.' + actions: + confirm: 'Are you sure you want to delete this phone book entry?' + destroy: 'Delete phone book entry' + edit: 'Edit phone book entry' + view_all: 'View all phone book entries' + new: + page_title: 'New entry' + edit: + page_title: 'Editing phone book entry' + form: + phone_book_id: + label: 'Phone book' + hint: '' + first_name: + label: 'First name' + hint: '' + middle_name: + label: 'Middle name' + hint: '' + gender: + label: 'Gender' + hint: '' + male: "Male" + female: "Female" + last_name: + label: 'Last name' + hint: '' + title: + label: 'Academic title' + hint: 'Dr.' + nickname: + label: 'Nickname' + hint: '' + organization: + label: 'Company name' + hint: 'Organization' + is_organization: + label: 'Is organization' + hint: '' + department: + label: 'Department' + hint: '' + job_title: + label: 'Job title' + hint: 'CEO' + is_male: + label: 'Is male' + hint: '' + birthday: + label: 'Birthday' + hint: '' + birth_name: + label: 'Name at birth' + hint: '' + state: + label: 'State' + hint: '' + description: + label: 'Description' + hint: '' + position: + label: 'Position' + hint: '' + homepage_personal: + label: 'Personal Home Page' + hint: '' + homepage_organization: + label: 'Organization Home Page' + hint: 'http://www.example.com' + twitter_account: + label: 'Twitter account' + hint: '@example' + facebook_account: + label: 'Facebook account' + hint: '' + google_plus_account: + label: 'Google+ account' + hint: '' + xing_account: + label: 'Xing account' + hint: '' + linkedin_account: + label: 'LinkedIn account' + hint: '' + mobileme_account: + label: 'iCloud account' + hint: '' + image: + label: 'Photo' + hint: 'Avatar photo' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/phone_books/de.yml b/config/locales/views/phone_books/de.yml new file mode 100644 index 0000000..8aa6acf --- /dev/null +++ b/config/locales/views/phone_books/de.yml @@ -0,0 +1,46 @@ +de: + phone_books: + name: 'Telefonbuch' + general_phone_book: + name: 'Firmentelefonbuch' + description: 'Ein für alle Benutzer von %{resource} lesbares Telefonbuch.' + private_phone_book: + name: 'Privates Telefonbuch von %{resource}' + description: 'Ein privates Telefonbuch.' + controller: + successfuly_created: 'Ein neues Telefonbuch wurde erstellt.' + successfuly_updated: 'Ein Telefonbuch wurde aktualisiert.' + successfuly_destroyed: 'Ein Telefonbuch wurde gelöscht.' + index: + page_title: 'Telefonbücher' + name: 'Name des Telefonbuches' + description: 'Beschreibung' + count: 'Anzahl der Telefonbucheinträge' + actions: + confirm: 'Sind Sie sicher, dass Sie dieses Telefonbuch löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neues Telefonbuch für %{resource} anlegen' + show: + page_title: 'Telefonbuch anzeigen' + name: 'Name' + description: 'Beschreibung' + actions: + confirm: 'Sind Sie sicher, dass Sie dieses Telefonbuch löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle Telefonbücher anzeigen' + new: + page_title: 'Neues Telefonbuch' + edit: + page_title: 'Telefonbuch "%{resource}" bearbeiten' + form: + name: + label: 'Name' + hint: '' + description: + label: 'Beschreibung' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/phone_books/en.yml b/config/locales/views/phone_books/en.yml new file mode 100644 index 0000000..fcab06d --- /dev/null +++ b/config/locales/views/phone_books/en.yml @@ -0,0 +1,46 @@ +en: + phone_books: + name: 'Phone book' + general_phone_book: + name: 'Public phone book' + description: 'Readable for all users of %{resource}.' + private_phone_book: + name: "%{resource}'s phone book" + description: 'A private phone book.' + controller: + successfuly_created: 'Successfully created phone book.' + successfuly_updated: 'Successfully updated phone book.' + successfuly_destroyed: 'Successfully destroyed phone book.' + index: + page_title: 'Phone books' + name: 'Name of the phone book' + description: 'Description' + count: 'Number of entries' + actions: + confirm: 'Are you sure you want to delete this phone book?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New phone book for %{resource}' + show: + page_title: 'Phone Book' + name: 'Name' + description: 'Description' + actions: + confirm: 'Are you sure you want to delete this phone book?' + destroy: 'Delete phone book' + edit: 'Edit phone book' + view_all: 'View all phone books' + new: + page_title: 'New phone book' + edit: + page_title: 'Editing phone book "%{resource}"' + form: + name: + label: 'Name' + hint: '' + description: + label: 'Description' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/phone_models/de.yml b/config/locales/views/phone_models/de.yml new file mode 100644 index 0000000..36d1663 --- /dev/null +++ b/config/locales/views/phone_models/de.yml @@ -0,0 +1,50 @@ +de: + phone_models: + name: 'Telefonmodell' + controller: + successfuly_created: 'Ein Telefonmodell wurde erstellt.' + successfuly_updated: 'Das Telefonmodell wurde aktualisiert.' + successfuly_destroyed: 'Das Telefonmodell wurde gelöscht.' + index: + page_title: 'Telefonmodelle' + name: 'Name' + manufacturer_id: 'Hersteller' + product_manual_homepage_url: 'Betriebsanleitung URL' + product_homepage_url: 'Produkt-Homepage URL' + number_of_phones: 'Anzahl von Telefonen' + actions: + confirm: 'Sind Sie sicher, dass Sie dieses Telefonmodell löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + show: + page_title: 'Telefonmodell anzeigen' + name: 'Name' + manufacturer_id: '' + manufacturer_id: 'Hersteller' + product_manual_homepage_url: 'Betriebsanleitung URL' + product_homepage_url: 'Produkt-Homepage URL' + actions: + confirm: 'Sind Sie sicher, dass Sie dieses Telefonmodell löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle Telefonmodelle anzeigen' + new: + page_title: 'Neues Telefonmodell' + edit: + page_title: 'Telefonmodell bearbeiten' + form: + name: + label: 'Name' + hint: '' + manufacturer_id: + label: 'Hersteller' + hint: '' + product_manual_homepage_url: + label: 'Betriebsanleitung URL' + hint: '' + product_homepage_url: + label: 'Produkt-Homepage URL' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/phone_models/en.yml b/config/locales/views/phone_models/en.yml new file mode 100644 index 0000000..c500920 --- /dev/null +++ b/config/locales/views/phone_models/en.yml @@ -0,0 +1,49 @@ +en: + phone_models: + name: 'Phone model' + controller: + successfuly_created: 'Successfully created phone model.' + successfuly_updated: 'Successfully updated phone model.' + successfuly_destroyed: 'Successfully destroyed phone model.' + index: + page_title: 'Phone models' + name: 'Name' + manufacturer_id: 'Manufacturer' + product_manual_homepage_url: 'Product manual homepage URL' + product_homepage_url: 'Product homepage URL' + number_of_phones: 'System count' + actions: + confirm: 'Are you sure you want to delete this phone model?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + show: + page_title: 'Show phone model' + name: 'Name' + manufacturer_id: 'Manufacturer' + product_manual_homepage_url: 'Product manual homepage URL' + product_homepage_url: 'Product homepage URL' + actions: + confirm: 'Are you sure you want to delete this phone model?' + destroy: 'Delete phone model' + edit: 'Edit phone model' + view_all: 'View all phone models' + new: + page_title: 'New phone model' + edit: + page_title: 'Editing phone model' + form: + name: + label: 'Name' + hint: '' + manufacturer_id: + label: 'Manufacturer' + hint: '' + product_manual_homepage_url: + label: 'Product manual homepage URL' + hint: '' + product_homepage_url: + label: 'Product homepage URL' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/phone_number_ranges/de.yml b/config/locales/views/phone_number_ranges/de.yml new file mode 100644 index 0000000..af97171 --- /dev/null +++ b/config/locales/views/phone_number_ranges/de.yml @@ -0,0 +1,51 @@ +de: + phone_number_ranges: + ranges: + internal_extensions: + label: 'Durchwahlen (intern)' + description: 'Interne Durchwahlen, auch Extensions genannt.' + direct_inward_dialing_numbers: + label: 'Anschlussnummern des öffentlichen Telefonnetzes (DIDs und MSNs)' + description: 'Mit diesen Telefonnummern sind Sie aus dem öffentlichen Telefonnetz erreichbar.' + service_numbers: + label: 'Service Telefonnummern' + description: 'Spezielle Telefonnummern, die nich für interne Durchwahlen benutzt werden sollten (z.B. die Feuerwehr oder Polizei).' + name: 'Rufnummernbereich' + controller: + successfuly_created: 'Ein neuer Rufnummernbereich wurde angelegt.' + successfuly_updated: 'Der Rufnummernbereich wurde aktualisiert.' + successfuly_destroyed: 'Der Rufnummernbereich wurde gelöscht.' + index: + page_title: 'Rufnummernbereiche' + name: 'Name' + description: 'Beschreibung' + amount: 'Anzahl von Rufnummern' + phone_numbers: 'Telefonnummern' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Rufnummernbereich löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Rufnummernbereich für %{resource} anlegen' + show: + page_title: 'Rufnummernbereich anzeigen' + name: 'Name' + description: 'Beschreibung' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Rufnummernbereich löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle Rufnummernbereiche anzeigen' + new: + page_title: 'Neuer Rufnummernbereich' + edit: + page_title: 'Rufnummernbereich "%{resource}" bearbeiten' + form: + name: + label: 'Name' + hint: '' + description: + label: 'Beschreibung' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/phone_number_ranges/en.yml b/config/locales/views/phone_number_ranges/en.yml new file mode 100644 index 0000000..a22a2c0 --- /dev/null +++ b/config/locales/views/phone_number_ranges/en.yml @@ -0,0 +1,51 @@ +en: + phone_number_ranges: + ranges: + internal_extensions: + label: 'Internal extensions' + description: 'These are your internal extensions.' + direct_inward_dialing_numbers: + label: 'Direct inward dialing numbers (DIDs and MSNs)' + description: 'These are the phone numbers which have to be dialed from the PSTN to access your PBX.' + service_numbers: + label: 'Service numbers' + description: 'Numbers for special services (e.g. emergency numbers). It is not possible to use one of these numbers as an internal extension.' + name: 'Phone number range' + controller: + successfuly_created: 'Successfully created phone number range.' + successfuly_updated: 'Successfully updated phone number range.' + successfuly_destroyed: 'Successfully destroyed phone number range.' + index: + page_title: 'Phone number ranges' + name: 'Name' + description: 'Description' + amount: 'Amount of numbers' + phone_numbers: 'Phone numbers' + actions: + confirm: 'Are you sure you want to delete this phone number range?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New phone number range for %{resource}' + show: + page_title: 'Show phone number range' + name: 'Name' + description: 'Description' + actions: + confirm: 'Are you sure you want to delete this phone number range?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View' + new: + page_title: 'New phone number range' + edit: + page_title: 'Editing phone number range "%{resource}"' + form: + name: + label: 'Name' + hint: '' + description: + label: 'Description' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/phone_numbers/de.yml b/config/locales/views/phone_numbers/de.yml new file mode 100644 index 0000000..40fc7f8 --- /dev/null +++ b/config/locales/views/phone_numbers/de.yml @@ -0,0 +1,64 @@ +de: + phone_numbers: + name: "Telefonnummer" + controller: + successfuly_created: 'Eine neue Telefonnummer wurde eingetragen.' + successfuly_updated: 'Die Telefonnummer wurde aktualisiert.' + successfuly_destroyed: 'Die Telefonnummer wurde gelöscht.' + index: + page_title: 'Telefonnummern' + phone_book_id: 'Telefonbuch' + name: 'Name' + number: 'Telefonnummer' + country_code: 'Ländervorwahl' + area_code: 'Ortsvorwahl' + subscriber_number: 'Telefonnummer' + extension: 'Durchwahl' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Telefonnummer löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neue Telefonnummer für %{resource} anlegen' + show: + page_title: 'Telefonnummer anzeigen' + phone_book_id: 'Telefonbuch' + name: 'Name' + number: 'Telefonnummer' + country_code: 'Ländervorwahl' + area_code: 'Ortsvorwahl' + subscriber_number: 'Telefonnummer' + extension: 'Durchwahl' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Telefonnummer löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'View all phone numbers' + new: + page_title: 'Neue Telefonnummer' + edit: + page_title: 'Telefonnummer bearbeiten' + form: + phone_book_id: + label: 'Telefonbuch' + hint: '' + name: + label: 'Name' + hint: '' + number: + label: 'Telefonnummer' + hint: 'Format: +49-30-2270 für eine normale Telefonnummer. 42 für eine interne Durchwahl.' + country_code: + label: 'Ländervorwahl' + hint: '' + area_code: + label: 'Ortsvorwahl' + hint: '' + subscriber_number: + label: 'Telefonnummer' + hint: '' + extension: + label: 'Durchwahl' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/phone_numbers/en.yml b/config/locales/views/phone_numbers/en.yml new file mode 100644 index 0000000..31af5c8 --- /dev/null +++ b/config/locales/views/phone_numbers/en.yml @@ -0,0 +1,64 @@ +en: + phone_numbers: + name: "Phone numbers" + controller: + successfuly_created: 'Successfully created phone number.' + successfuly_updated: 'Successfully updated phone number.' + successfuly_destroyed: 'Successfully destroyed phone number.' + index: + page_title: 'Phone numbers' + phone_book_id: 'Phone book' + name: 'Name' + number: 'Number' + country_code: 'Country code' + area_code: 'Area code' + subscriber_number: 'Subscriber number' + extension: 'Extension' + actions: + confirm: 'Are you sure you want to delete this phone number?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New phone number for %{resource}' + show: + page_title: 'Show Phone number' + phone_book_id: 'Phone book' + name: 'Name' + number: 'Number' + country_code: 'Country code' + area_code: 'Area code' + subscriber_number: 'Subscriber number' + extension: 'Extension' + actions: + confirm: 'Are you sure you want to delete this phone number?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View all phone numbers' + new: + page_title: 'New Phone number' + edit: + page_title: 'Editing Phone number' + form: + phone_book_id: + label: 'Phone book' + hint: '' + name: + label: 'Name' + hint: '' + number: + label: 'Phone number' + hint: 'Format: +49-30-2270 for a normal phone number or 42 for an internal extension.' + country_code: + label: 'Country code' + hint: '' + area_code: + label: 'Area code' + hint: '' + subscriber_number: + label: 'Subscriber number' + hint: '' + extension: + label: 'Extension' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/phone_sip_accounts/de.yml b/config/locales/views/phone_sip_accounts/de.yml new file mode 100644 index 0000000..98c7bc5 --- /dev/null +++ b/config/locales/views/phone_sip_accounts/de.yml @@ -0,0 +1,37 @@ +de: + phone_sip_accounts: + name: "SIP-Konten auf einem Telefon" + controller: + successfuly_created: 'Das SIP-Konto wurde auf dem Telefon eingetragen.' + successfuly_destroyed: 'Das SIP-Konto wurde von dem Telefon entfernt.' + no_existing_sip_accounts_warning: 'Aktuell gibt es keine SIP-Konten, die einem Telefon eingetragen werden können. Bitte legen Sie ein neues SIP-Konto an.' + index: + page_title: "SIP-Konten auf einem Telefon" + phone_id: 'Telefon' + sip_account_id: 'SIP-Konto' + position: 'Position' + actions: + confirm: 'Sind Sie sicher, dass Sie dieses SIP-Konto von dem Telefon entfernen möchten?' + destroy: 'Entfernen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Auf dem Telefon %{resource} ein neues SIP-Konto eintragen' + show: + page_title: 'SIP-Konten eines Telefons anzeigen' + phone_id: 'Telefon' + sip_account_id: 'SIP-Konto' + position: 'Position' + actions: + confirm: 'Sind Sie sicher, dass Sie dieses SIP-Konto von dem Telefon entfernen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + new: + page_title: 'SIP-Konto eintragen' + actions: + form: + sip_account_id: + label: 'SIP-Konto' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/phone_sip_accounts/en.yml b/config/locales/views/phone_sip_accounts/en.yml new file mode 100644 index 0000000..9577241 --- /dev/null +++ b/config/locales/views/phone_sip_accounts/en.yml @@ -0,0 +1,37 @@ +en: + phone_sip_accounts: + name: "SIP accounts of a phone" + controller: + successfuly_created: 'Successfully connected a new SIP account to a phone.' + successfuly_destroyed: 'Successfully deleted a SIP account on a phone.' + no_existing_sip_accounts_warning: 'There are no SIP accounts to connect to. Please create a new SIP account first.' + index: + page_title: "SIP accounts of a phone" + phone_id: 'Phone' + sip_account_id: 'SIP account' + position: 'Position' + actions: + confirm: 'Are you sure you want to delete this SIP account on this phone?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New SIP account connection for %{resource}' + show: + page_title: 'Show SIP account of a phone' + phone_id: 'Phone' + sip_account_id: 'SIP account' + position: 'Position' + actions: + confirm: 'Are you sure you want to delete this SIP account on this phone?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View All' + new: + page_title: 'New SIP account on a phone' + actions: + form: + sip_account_id: + label: 'SIP account' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/phones/de.yml b/config/locales/views/phones/de.yml new file mode 100644 index 0000000..3a0adea --- /dev/null +++ b/config/locales/views/phones/de.yml @@ -0,0 +1,77 @@ +de: + phones: + name: 'Telefon' + controller: + successfuly_created: 'Eine neues Telefon wurde in der Datenbank eingetragen.' + successfuly_updated: 'Die Daten zum Telefon wurden aktualisiert.' + successfuly_destroyed: 'Das Telefon wurde aus dem System entfernt.' + index: + page_title: 'Telefone' + mac_address: 'MAC-Adresse' + phone_model_id: 'Telefonmodell' + hot_deskable: 'Hot-Desk fähig' + ip_address: 'IP-Adresse' + last_ip_address: 'Letzte IP-Adresse' + http_user: 'http user' + http_password: 'http password' + nightly_reboot: 'Nachts automatischer Reboot' + provisioning_key_active: 'Provisioning Schlüssel aktiv' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Telefon aus Gemeinschaft entfernen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neues Telefon für %{resource} anlegen' + show: + page_title: 'Telefon anzeigen' + mac_address: 'MAC-Adresse' + phone_model_id: 'Telefonmodell' + hot_deskable: 'Hot-Desk fähig' + ip_address: 'IP-Adresse' + last_ip_address: 'Letzte IP-Adresse' + http_user: 'http user' + http_password: 'http password' + nightly_reboot: 'Nachts automatischer Reboot' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Telefon aus Gemeinschaft entfernen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle Telefone anzeigen' + new: + page_title: 'Neues Telefon' + actions: + edit: + page_title: 'Telefondaten bearbeiten' + form: + mac_address: + label: 'MAC-Adresse' + hint: '' + phone_model_id: + label: 'Telefonmodell' + hint: '' + hot_deskable: + label: 'Hot-Desk fähig' + hint: '' + nightly_reboot: + label: 'Nachts automatischer Reboot' + hint: '' + ip_address: + label: 'IP-Adresse' + hint: '' + last_ip_address: + label: 'Letzte IP-Adresse' + hint: '' + http_user: + label: 'http user' + hint: '' + http_password: + label: 'http password' + hint: '' + provisioning_key_active: + label: 'Provisioning Schlüssel aktiv' + hint: '' + button: 'Absenden' + sip_accounts: + title: 'Auf dem Telefon eingetragene SIP-Konten' + connect_to_sip_account: 'Neues SIP-Konto auf das Telefon eintragen'
\ No newline at end of file diff --git a/config/locales/views/phones/en.yml b/config/locales/views/phones/en.yml new file mode 100644 index 0000000..afcf9b5 --- /dev/null +++ b/config/locales/views/phones/en.yml @@ -0,0 +1,77 @@ +en: + phones: + name: 'Phone' + controller: + successfuly_created: 'Successfully created phone.' + successfuly_updated: 'Successfully updated phone.' + successfuly_destroyed: 'Successfully destroyed phone.' + index: + page_title: 'Phones' + mac_address: 'Mac address' + phone_model_id: 'Phone model' + hot_deskable: 'Hot-deskable' + ip_address: 'IP address' + last_ip_address: 'Last IP address' + http_user: 'http user' + http_password: 'http password' + nightly_reboot: 'Nightly reboot' + actions: + confirm: 'Are you sure you want to delete this phone?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New phone for %{resource}' + show: + page_title: 'Show phone' + mac_address: 'Mac address' + phone_model_id: 'Phone model' + hot_deskable: 'Hot-deskable' + ip_address: 'IP address' + last_ip_address: 'Last IP address' + http_user: 'http user' + http_password: 'http password' + nightly_reboot: 'Nightly reboot' + provisioning_key_active: 'Provisioning key active' + actions: + confirm: 'Are you sure you want to delete this phone?' + destroy: 'Delete phone' + edit: 'Edit phone' + view_all: 'View all phones' + new: + page_title: 'New phone' + actions: + edit: + page_title: 'Editing Phone' + form: + mac_address: + label: 'Mac address' + hint: '' + phone_model_id: + label: 'Phone model' + hint: '' + hot_deskable: + label: 'Hot-deskable' + hint: '' + nightly_reboot: + label: 'Nightly reboot' + hint: '' + ip_address: + label: 'IP address' + hint: '' + last_ip_address: + label: 'Last IP address' + hint: '' + http_user: + label: 'GUI user' + hint: 'Phone GUI user name' + http_password: + label: 'GUI password' + hint: 'Phone GUI password' + provisioning_key_active: + label: 'Provisioning key active' + hint: '' + button: 'Submit' + sip_accounts: + title: 'Listing of connected SIP accounts' + connect_to_sip_account: 'Connect to an existing SIP account'
\ No newline at end of file diff --git a/config/locales/views/ringtones/de.yml b/config/locales/views/ringtones/de.yml new file mode 100644 index 0000000..6bb3683 --- /dev/null +++ b/config/locales/views/ringtones/de.yml @@ -0,0 +1,39 @@ +de: + ringtones: + name: 'Klingelton' + controller: + successfuly_created: 'Ein neuer Klingelton wurde eingetragen.' + successfuly_updated: 'Der Klingelton wurde aktualisiert.' + successfuly_destroyed: 'Der Klingelton wurde gelöscht.' + index: + page_title: 'Klingeltöne' + audio: 'Audio' + bellcore_id: 'Bellcore-ID' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Klingelton löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + show: + page_title: 'Klingelton' + audio: 'Audio' + bellcore_id: 'Bellcore-ID' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Klingelton löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + new: + page_title: 'Neuer Klingelton' + edit: + page_title: 'Klingelton bearbeiten' + form: + audio: + label: 'Audio' + hint: '' + bellcore_id: + label: 'Bellcore-ID' + hint: 'Im Telefon eingebaute Klingeltöne. "0" bedeutet lautlos.' + button: 'Absenden' + set_a_ringtone: 'Klingelton einstellen'
\ No newline at end of file diff --git a/config/locales/views/ringtones/en.yml b/config/locales/views/ringtones/en.yml new file mode 100644 index 0000000..e681d96 --- /dev/null +++ b/config/locales/views/ringtones/en.yml @@ -0,0 +1,39 @@ +en: + ringtones: + name: 'Ringtone' + controller: + successfuly_created: 'Successfully created ringtone.' + successfuly_updated: 'Successfully updated ringtone.' + successfuly_destroyed: 'Successfully destroyed ringtone.' + index: + page_title: 'Ringtones' + audio: 'Audio' + bellcore_id: 'Bellcore ID' + actions: + confirm: 'Are you sure you want to delete this ringtone?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + show: + page_title: 'Show ringtone' + audio: 'Audio' + bellcore_id: 'Bellcore ID' + actions: + confirm: 'Are you sure you want to delete this ringtone?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View All' + new: + page_title: 'New ringtone' + edit: + page_title: 'Editing ringtone' + form: + audio: + label: 'Audio' + hint: '' + bellcore_id: + label: 'Bellcore ID' + hint: 'Build in ringtone ID. A "0" means silence.' + button: 'Submit' + set_a_ringtone: 'Set a ringtone'
\ No newline at end of file diff --git a/config/locales/views/sessions/de.yml b/config/locales/views/sessions/de.yml new file mode 100644 index 0000000..4bc80e8 --- /dev/null +++ b/config/locales/views/sessions/de.yml @@ -0,0 +1,15 @@ +de: + sessions: + controller: + successfully_created: 'Sie sind jetzt als Benutzer "%{resource}" angemeldet.' + successfully_destroyed: 'Abgemeldet.' + new: + page_title: 'Anmeldung' + form: + email: 'E-Mail oder Benutzer-Name' + password: 'Passwort' + reset_password: 'Ich habe mein Passwort vergessen' + flash_messages: + invalid_email_or_password: 'Ungültige Anmeldedaten. Bitte versuchen Sie es noch einmal und achten Sie beim Passwort auf Groß- und Kleinschreibung.' + password_recovery_successful: 'Ein neues Passwort wurde an Ihre registrierte E-Mail-Addresse gesendet.' + password_recovery_failed: 'Fehler beim setzten des Passwortes.' diff --git a/config/locales/views/sessions/en.yml b/config/locales/views/sessions/en.yml new file mode 100644 index 0000000..51dcde7 --- /dev/null +++ b/config/locales/views/sessions/en.yml @@ -0,0 +1,15 @@ +en: + sessions: + controller: + successfully_created: 'Logged in as %{resource}.' + successfully_destroyed: 'Logged out.' + new: + page_title: 'Log In' + form: + email: 'E-Mail or Username' + password: 'Password' + reset_password: 'I forgot my password' + flash_messages: + invalid_email_or_password: 'Invalid e-mail or password. Please try again.' + password_recovery_successful: 'A new password has been sent to your registered email address' + password_recovery_failed: 'There was an error updating your password.' diff --git a/config/locales/views/sip_accounts/de.yml b/config/locales/views/sip_accounts/de.yml new file mode 100644 index 0000000..ad78a93 --- /dev/null +++ b/config/locales/views/sip_accounts/de.yml @@ -0,0 +1,83 @@ +de: + sip_accounts: + name: 'SIP-Konto' + controller: + successfuly_created: 'Ein SIP-Konto für %{resource} wurde erstellt.' + successfuly_updated: 'Das SIP-Konto wurde aktualisiert.' + successfuly_destroyed: 'Das SIP-Konto wurde gelöscht.' + index: + page_title: 'SIP-Konten' + auth_name: 'Auth Name' + caller_name: 'Caller Name' + password: 'Passwort' + voicemail_pin: 'Anrufbeantworter PIN' + phone_numbers: 'Telefonnummern' + call_waiting: 'Anklopfen' + clir: 'Rufnummernunterdrückung (CLIR)' + clip: 'Rufnummernanzeige (CLIP)' + hotdeskable: 'Hot-Desk fähig' + clip_no_screening: 'Spezifische Rufnummernanzeige (CLIP -no screening-)' + callforward_rules_act_per_sip_account: 'Rufweiterleitungen gelten für das gesamte SIP-Konto' + online: 'Online' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen SIP-Konto löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neues SIP-Konto für %{resource} anlegen' + show: + page_title: 'SIP-Konto anzeigen' + auth_name: 'Auth Name' + caller_name: 'Caller Name' + password: 'Passwort' + voicemail_pin: 'Anrufbeantworter PIN' + call_waiting: 'Anklopfen' + clir: 'Rufnummernunterdrückung (CLIR)' + clip: 'Rufnummernanzeige (CLIP)' + hotdeskable: 'Hot-Desk fähig' + clip_no_screening: 'Spezifische Rufnummernanzeige (CLIP -no screening-)' + callforward_rules_act_per_sip_account: 'Rufweiterleitungen gelten für das gesamte SIP-Konto' + registration: 'Registrierung' + expires: 'Läuft ab' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen SIP-Konto löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle SIP-Konten anzeigen' + new: + page_title: 'Neues SIP-Konto' + edit: + page_title: 'SIP-Konto bearbeiten' + form: + auth_name: + label: 'Auth Name' + hint: '' + caller_name: + label: 'Caller Name' + hint: '' + password: + label: 'Passwort' + hint: '' + voicemail_pin: + label: 'Anrufbeantworter PIN' + hint: '' + call_waiting: + label: 'Anklopfen' + hint: '' + clir: + label: 'Rufnummernunterdrückung (CLIR)' + hint: '' + clip: + label: 'Rufnummernanzeige (CLIP)' + hint: '' + clip_no_screening: + label: 'Spezifische Rufnummernanzeige (CLIP -no screening-)' + hint: '' + hotdeskable: + label: 'Hot-Desk fähig' + hint: '' + callforward_rules_act_per_sip_account: + label: 'Rufweiterleitungen gelten für das gesamte SIP-Konto' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/sip_accounts/en.yml b/config/locales/views/sip_accounts/en.yml new file mode 100644 index 0000000..c1d8613 --- /dev/null +++ b/config/locales/views/sip_accounts/en.yml @@ -0,0 +1,83 @@ +en: + sip_accounts: + name: 'SIP account' + controller: + successfuly_created: 'Successfully created a new SIP account for %{resource}.' + successfuly_updated: 'Successfully updated a SIP account.' + successfuly_destroyed: 'Successfully destroyed a SIP account.' + index: + page_title: 'SIP accounts' + auth_name: 'Auth name' + caller_name: 'Caller name' + password: 'Password' + voicemail_pin: 'Voicemail PIN' + phone_numbers: 'Phone numbers' + call_waiting: 'Call waiting' + clir: 'CLIR' + clip: 'CLIP' + hotdeskable: 'Hotdeskable' + clip_no_screening: 'CLIP -no screening-' + callforward_rules_act_per_sip_account: 'Callforwards work for the whole sip account' + online: 'Online' + actions: + confirm: 'Are you sure you want to delete this SIP account?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New SIP account for %{resource}' + show: + page_title: 'Show SIP account' + auth_name: 'Auth name' + caller_name: 'Caller name' + password: 'Password' + voicemail_pin: 'Voicemail PIN' + call_waiting: 'Call waiting' + clir: 'CLIR' + clip: 'CLIP' + hotdeskable: 'Hotdeskable' + clip_no_screening: 'CLIP -no screening-' + callforward_rules_act_per_sip_account: 'Callforwards work for the whole sip account' + registration: 'Registration' + expires: 'Expires' + actions: + confirm: 'Are you sure you want to delete this SIP account?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View all SIP accounts' + new: + page_title: 'New SIP account' + edit: + page_title: 'Editing SIP account' + form: + auth_name: + label: 'Auth name' + hint: '' + caller_name: + label: 'Caller name' + hint: '' + password: + label: 'Password' + hint: '' + voicemail_pin: + label: 'Voicemail PIN' + hint: '' + call_waiting: + label: 'Call waiting' + hint: '' + clir: + label: 'CLIR' + hint: 'Calling Line Identification Restriction. If set true no number will be transmitted.' + clip: + label: 'CLIP' + hint: 'Calling Line Identification Presentation.' + clip_no_screening: + label: 'CLIP -no screening-' + hint: 'Calling Line Identification Presentation. Leave blank if you are not sure.' + callforward_rules_act_per_sip_account: + label: 'Callforwards work for the whole sip account' + hint: '' + hotdeskable: + label: 'Hotdeskable' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/sip_domains/de.yml b/config/locales/views/sip_domains/de.yml new file mode 100644 index 0000000..447caa9 --- /dev/null +++ b/config/locales/views/sip_domains/de.yml @@ -0,0 +1,38 @@ +de: + sip_domains: + name: 'SIP-Domain' + controller: + successfuly_created: 'Eine SIP-Domain wurde im System gespeichert.' + successfuly_updated: 'Die SIP-Domain wurde aktualisiert.' + successfuly_destroyed: 'Die SIP-Domain wurde gelöscht.' + index: + page_title: 'SIP-Domains' + host: 'Host' + realm: 'Realm' + actions: + confirm: 'Sind Sie sicher, dass Sie diese SIP-Domain löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + show: + page_title: 'SIP-Domain bearbeiten' + host: 'Host' + realm: 'Realm' + actions: + confirm: 'Sind Sie sicher, dass Sie diese SIP-Domain löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle SIP-Domains anzeigen' + new: + page_title: 'Neue SIP-Domain' + edit: + page_title: 'SIP-Domain von %{resource} bearbeiten' + form: + host: + label: 'Host' + hint: '' + realm: + label: 'Realm' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/sip_domains/en.yml b/config/locales/views/sip_domains/en.yml new file mode 100644 index 0000000..bf62a60 --- /dev/null +++ b/config/locales/views/sip_domains/en.yml @@ -0,0 +1,38 @@ +en: + sip_domains: + name: 'SIP domain' + controller: + successfuly_created: 'Successfully created SIP domain.' + successfuly_updated: 'Successfully updated SIP domain.' + successfuly_destroyed: 'Successfully destroyed SIP domain.' + index: + page_title: 'SIP domains' + host: 'Host' + realm: 'Realm' + actions: + confirm: 'Are you sure you want to delete this SIP domain?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + show: + page_title: 'Editing SIP domain' + host: 'Host' + realm: 'Realm' + actions: + confirm: 'Are you sure you want to delete this SIP domain?' + destroy: 'Delete SIP domain' + edit: 'Edit SIP domain' + view_all: 'View all SIP domains' + new: + page_title: 'New SIP domain' + edit: + page_title: 'Editing SIP domain %{resource}' + form: + host: + label: 'Host' + hint: '' + realm: + label: 'Realm' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/softkeys/de.yml b/config/locales/views/softkeys/de.yml new file mode 100644 index 0000000..f5f8f26 --- /dev/null +++ b/config/locales/views/softkeys/de.yml @@ -0,0 +1,68 @@ +de: + softkeys: + functions: + speed_dial: 'Kurzwahl' + blf: 'Besetzt-Lampe' + dtmf: 'DTMF' + log_in: 'Anmelden' + log_out: 'Abmelden' + conference: 'Konferenz' + call_forwarding: 'Rufumleitung ein/aus' + hold: 'Halten' + hunt_group_membership: 'Rufgruppen-Mitgliedschaft' + deactivated: 'Deaktiviert' + acd_membership: 'Warteschleife' + name: 'Softkey' + controller: + successfuly_created: 'Ein neuer Softkey wurde erstellt.' + successfuly_updated: 'Der Softkey wurde aktualisiert.' + successfuly_destroyed: 'Der Softkey wurde gelöscht.' + index: + page_title: 'Tastenbelegungen' + phone_id: 'Telefon' + function: 'Funktion' + number: 'Nummer' + label: 'Beschriftung' + position: 'Position' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Softkey löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neuer Softkey' + create_for: 'Neuer Softkey für %{resource}' + show: + page_title: 'Softkey anzeigen' + phone_id: 'Telefon' + function: 'Funktion' + number: 'Nummer' + label: 'Beschriftung' + position: 'Position' + call_forward: 'Rufumleitung' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Softkey löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + new: + page_title: 'Neuer Softkey' + actions: + edit: + page_title: 'Softkey bearbeiten' + actions: + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + form: + function: + label: 'Funktion' + hint: '' + call_forward: + label: 'Rufumleitung' + hint: '' + number: + label: 'Nummer' + hint: '' + label: + label: 'Beschriftung' + hint: 'Je nach Telefonmodell gibt es die Möglichkeit eine Beschriftung bzw. eine Beschreibung auf dem Telefon anzuzeigen.' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/softkeys/en.yml b/config/locales/views/softkeys/en.yml new file mode 100644 index 0000000..e29e4c6 --- /dev/null +++ b/config/locales/views/softkeys/en.yml @@ -0,0 +1,64 @@ +en: + softkeys: + functions: + speed_dial: 'Speed Dial' + blf: 'BLF' + dtmf: 'DTMF' + log_in: 'Log in' + log_out: 'Log out' + conference: 'Conference' + call_forwarding: 'Toggle Call Forwarding' + hold: 'Hold' + hunt_group_membership: 'Hunt Group' + deactivated: 'Deactivated' + acd_membership: 'ACD' + name: 'Softkey' + controller: + successfuly_created: 'Successfully created softkeys.' + successfuly_updated: 'Successfully updated softkeys.' + successfuly_destroyed: 'Successfully destroyed softkeys.' + index: + page_title: 'Softkeys' + phone_id: 'Phone' + function: 'Function' + number: 'Number' + label: 'Label' + position: 'Position' + actions: + confirm: 'Are you sure you want to delete this softkey?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New softkey' + create_for: 'New softkey for %{resource}' + show: + page_title: 'Show softkey' + phone_id: 'Phone' + function: 'Function' + number: 'Number' + label: 'Label' + position: 'Position' + call_forward: 'Call forward' + actions: + confirm: 'Are you sure you want to delete this softkey?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View All' + new: + page_title: 'New softkey' + edit: + page_title: 'Editing softkey' + form: + function: + label: 'Function' + hint: '' + call_forward: + label: 'Call forward' + hint: '' + number: + label: 'Number' + hint: '' + label: + label: 'Label' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/system_messages/de.yml b/config/locales/views/system_messages/de.yml new file mode 100644 index 0000000..9bdce48 --- /dev/null +++ b/config/locales/views/system_messages/de.yml @@ -0,0 +1,38 @@ +de: + system_messages: + name: 'Systemnachricht' + controller: + successfuly_created: 'Eine Systemnachricht wurde erstellt.' + successfuly_updated: 'Eine Systemnachricht wurde aktualisiert.' + successfuly_destroyed: 'Eine Systemnachricht wurde gelöscht.' + index: + page_title: 'Systemnachrichten' + user_id: 'Benutzer' + content: 'Nachricht' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Systemnachricht löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + show: + page_title: 'Systemnachricht anzeigen' + user_id: 'Benutzer' + content: 'Nachricht' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Systemnachricht löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + new: + page_title: 'Neue Systemnachricht' + edit: + page_title: 'Systemnachricht bezüglich %{resource} bearbeiten' + form: + user_id: + label: 'Benutzer' + hint: '' + content: + label: 'Nachricht' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/system_messages/en.yml b/config/locales/views/system_messages/en.yml new file mode 100644 index 0000000..05e0836 --- /dev/null +++ b/config/locales/views/system_messages/en.yml @@ -0,0 +1,38 @@ +en: + system_messages: + name: 'System message' + controller: + successfuly_created: 'Successfully created System message.' + successfuly_updated: 'Successfully updated System message.' + successfuly_destroyed: 'Successfully destroyed System message.' + index: + page_title: 'System messages' + user_id: 'User' + content: 'Content' + actions: + confirm: 'Are you sure you want to delete this element?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + show: + page_title: 'Editing System message' + user_id: 'User' + content: 'Content' + actions: + confirm: 'Are you sure you want to delete this element?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View All' + new: + page_title: 'New System message' + edit: + page_title: 'Editing System message %{resource}' + form: + user_id: + label: 'User' + hint: '' + content: + label: 'Content' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/tenants/de.yml b/config/locales/views/tenants/de.yml new file mode 100644 index 0000000..66d9d0a --- /dev/null +++ b/config/locales/views/tenants/de.yml @@ -0,0 +1,73 @@ +de: + tenants: + name: "Mandant" + new_name_scaffold: 'Beispiel Firma Nr. %{counter} GmbH' + controller: + successfuly_created: 'Der Mandant "%{resource}" wurde angelegt.' + successfuly_created_plus_delayed_jobs: 'Der Mandant "%{resource}" wurde angelegt. %{amount_of_numbers} Durchwahlen und Telefonnummern werden jetzt im Hintergrund ins System gespielt. Dies kann je nach benutzter Hardware ein paar Minuten dauern. ' + successfuly_updated: 'Der Mandant wurde aktualisiert.' + successfuly_destroyed: 'Der Mandant wurde gelöscht.' + index: + page_title: 'Mandanten' + name: 'Name' + description: 'Beschreibung' + state: 'Status' + position: 'Position' + from_field_voicemail_email: 'Anrufbeantworter Absender' + from_field_pin_change_email: 'Neue PIN Absender' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Mandant löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neuen Mandanten anlegen' + show: + page_title: 'Mandant anzeigen' + name: 'Name' + description: 'Beschreibung' + state: 'Status' + position: 'Position' + from_field_voicemail_email: 'Anrufbeantworter Absender' + from_field_pin_change_email: 'Neue PIN Absender' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Mandant löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle Mandanten anzeigen' + new: + page_title: 'Neuen Mandant anlegen' + edit: + page_title: 'Mandant %{resource} bearbeiten' + form: + phone_numbers: 'Telefonnummern' + intro: 'Gemeinschaft verwaltet alle Durchwahlen und kann so sicher stellen, dass Nummern nicht doppelt vergeben werden. Dazu müssen Sie an dieser Stelle die möglichen Rufnummern definieren.' + name: + label: 'Name' + hint: '' + description: + label: 'Beschreibung' + hint: '' + country_id: + label: 'Land' + hint: '' + language_id: + label: 'Sprache' + hint: '' + sip_domain: + label: 'SIP-Domain' + hint: '' + from_field_voicemail_email: + label: 'Absender Sprachnachrichten' + hint: 'Absender für E-Mails mit Sprachnachrichten.' + from_field_pin_change_email: + label: 'Absender PIN Änderung' + hint: 'Absender für E-Mails bezüglich PIN Änderungen.' + internal_extension_ranges: + label: 'Interne Durchwahlen' + hint: 'Hier können Sie Rufnummernbereiche für die von Ihnen gewünschten internen Durchwahlen definieren (z.B. "10-99" für 2-stellige Durchwahlen, "10-20,50-85" oder "150-299").' + did_list: + label: 'Externe Rufnummern' + hint: 'Hier können Sie die Rufnummern eintragen, mit denen Sie vom Festnetz aus erreichbar sind. Sollten Sie nur eine Nummer haben, dann bitte diese eintragen. Sie können das x als Wildcard für die oben angegebenen Durchwahlen benutzen. Beispiele: "0228-12345x, 0228-123450" (ein typisches Beispiel für einen Anlagenanschluss mit 2-stelligen Durchwahlen), "0228-123456", "0228-123456,0228-123999"' + button: 'Absenden' + + switch_to_tenant: "Mandant wechseln" diff --git a/config/locales/views/tenants/en.yml b/config/locales/views/tenants/en.yml new file mode 100644 index 0000000..adbf45f --- /dev/null +++ b/config/locales/views/tenants/en.yml @@ -0,0 +1,73 @@ +en: + tenants: + name: "Tenant" + new_name_scaffold: 'Snake Oil #%{counter}, Ltd.' + controller: + successfuly_created: 'Successfully created the tenant "%{resource}".' + successfuly_created_plus_delayed_jobs: 'Successfully created the tenant "%{resource}". A total of %{amount_of_numbers} extensions and external numbers is being proccessed in the background. Depending on your hardware that can take a couple of minutes.' + successfuly_updated: 'Successfully updated tenant.' + successfuly_destroyed: 'Successfully destroyed tenant.' + index: + page_title: 'Tenants' + name: 'Name' + description: 'Description' + state: 'State' + position: 'Position' + from_field_voicemail_email: 'Voicemail sender' + from_field_pin_change_email: 'PIN change sender' + actions: + confirm: 'Are you sure you want to delete this tenant?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + show: + page_title: 'Show Tenant' + name: 'Name' + description: 'Description' + state: 'State' + position: 'Position' + from_field_voicemail_email: 'Voicemail sender' + from_field_pin_change_email: 'PIN change sender' + actions: + confirm: 'Are you sure you want to delete this tenant?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View all tenants' + new: + page_title: 'New Tenant' + edit: + page_title: 'Editing Tenant %{resource}' + form: + phone_numbers: 'Phone numbers' + intro: 'Gemeinschaft is strict with phone numbers. You need to define the range of available numbers.' + name: + label: 'Name' + hint: '' + description: + label: 'Description' + hint: '' + country_id: + label: 'Country' + hint: '' + language_id: + label: 'Language' + hint: '' + sip_domain: + label: 'SIP domain' + hint: '' + from_field_voicemail_email: + label: 'Voicemail sender' + hint: 'From field for e-mails with voicemails.' + from_field_pin_change_email: + label: 'PIN change sender' + hint: 'From field for e-mails about PIN changes.' + internal_extension_ranges: + label: 'Internal extensions' + hint: 'Define ranges (e.g. "10-99", "10-20,50-85" or "150-299").' + did_list: + label: 'External numbers' + hint: 'These are the phone numbers which can be called from the outside (seperated by commas). The x represents all internal extensions (can be added to a trunk number). You can mix MSNs and DIDs. Examples: "0228-12345x, 0228-123450" (this would be the solution for most companies), "0228-123456", "0228-123456,0228-123999"' + button: 'Submit' + + switch_to_tenant: "Switch to tenant" diff --git a/config/locales/views/user_group_memberships/de.yml b/config/locales/views/user_group_memberships/de.yml new file mode 100644 index 0000000..35dbac8 --- /dev/null +++ b/config/locales/views/user_group_memberships/de.yml @@ -0,0 +1,35 @@ +de: + user_group_memberships: + name: "Gruppenzugehörigkeit" + controller: + successfuly_created: 'Ein Benutzer wurde einer Gruppe zugeordnet.' + successfuly_updated: 'Die Gruppenzugehörigkeit wurde aktualisiert.' + successfuly_destroyed: 'Die Gruppenzugehörigkeit wurde gelöscht.' + no_more_user_to_add: 'Es gibt keine weiteren Benutzer, die dieser Gruppe zugeordnet werden können.' + index: + page_title: 'Gruppenzugehörigkeiten' + user: 'Benutzer' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Gruppenzugehörigkeit löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neue Gruppenzugehörigkeiten für %{resource}' + show: + page_title: 'Gruppenzugehörigkeiten anzeigen' + user: 'Benutzer' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Gruppenzugehörigkeit löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle Gruppenzugehörigkeitenen anzeigen' + new: + page_title: 'Neue Gruppenzugehörigkeit' + edit: + page_title: 'Gruppenzugehörigkeiten von "%{resource}" bearbeiten' + form: + name: + user: 'Benutzer' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/user_group_memberships/en.yml b/config/locales/views/user_group_memberships/en.yml new file mode 100644 index 0000000..d31a87a --- /dev/null +++ b/config/locales/views/user_group_memberships/en.yml @@ -0,0 +1,35 @@ +en: + user_group_memberships: + name: "User group membership" + controller: + successfuly_created: 'Successfully created user group membership.' + successfuly_updated: 'Successfully updated user group membership.' + successfuly_destroyed: 'Successfully destroyed user group membership.' + no_more_user_to_add: 'There are no more users to add to this user group.' + index: + page_title: 'User group memberships' + user: 'User' + actions: + confirm: 'Are you sure you want to delete this user group membership?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New user group membership for %{resource}' + show: + page_title: 'Show user group membership' + user: 'User' + actions: + confirm: 'Are you sure you want to delete this user group membership?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View all user group memberships' + new: + page_title: 'New user group membership' + edit: + page_title: 'Editing user group membership "%{resource}"' + form: + name: + user: 'User' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/user_groups/de.yml b/config/locales/views/user_groups/de.yml new file mode 100644 index 0000000..40b3da8 --- /dev/null +++ b/config/locales/views/user_groups/de.yml @@ -0,0 +1,42 @@ +de: + user_groups: + name: "Gruppe" + controller: + successfuly_created: 'Eine Gruppe wurde angelegt.' + successfuly_updated: 'Die Gruppe wurde aktualisiert.' + successfuly_destroyed: 'Die Gruppe wurde gelöscht.' + index: + page_title: 'Gruppen' + name: 'Name' + description: 'Beschreibung' + tenant_id: 'Mandant' + number_of_members: 'Benutzerzahl' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Gruppe löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neue Gruppe für %{resource} anlegen' + show: + page_title: 'Gruppe anzeigen' + name: 'Name' + description: 'Beschreibung' + tenant_id: 'Mandant' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Gruppe löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle Gruppen anzeigen' + new: + page_title: 'Neue Gruppe' + edit: + page_title: 'Gruppe "%{resource}" bearbeiten' + form: + name: + label: 'Name' + hint: '' + description: + label: 'Beschreibung' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/user_groups/en.yml b/config/locales/views/user_groups/en.yml new file mode 100644 index 0000000..a2db293 --- /dev/null +++ b/config/locales/views/user_groups/en.yml @@ -0,0 +1,42 @@ +en: + user_groups: + name: "User groups" + controller: + successfuly_created: 'Successfully created user group.' + successfuly_updated: 'Successfully updated user group.' + successfuly_destroyed: 'Successfully destroyed user group.' + index: + page_title: 'User groups' + name: 'Name' + description: 'Description' + tenant_id: 'Tenant' + number_of_members: 'Number of members' + actions: + confirm: 'Are you sure you want to delete this user group?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New user group for %{resource}' + show: + page_title: 'Show user group' + name: 'Name' + description: 'Description' + tenant_id: 'Tenant' + actions: + confirm: 'Are you sure you want to delete this user group?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View all user groups' + new: + page_title: 'New user group' + edit: + page_title: 'Editing user group "%{resource}"' + form: + name: + label: 'Name' + hint: '' + description: + label: 'Description' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/users/de.yml b/config/locales/views/users/de.yml new file mode 100644 index 0000000..951600d --- /dev/null +++ b/config/locales/views/users/de.yml @@ -0,0 +1,95 @@ +de: + users: + name: "Benutzer" + controller: + successfuly_created: 'Der Benutzer %{resource} wurde erstellt.' + successfuly_created_and_login: 'Der Benutzer %{resource} wurde erstellt. Sie sind jetzt als dieser Benutzer angemeldet.' + successfuly_updated: 'Benutzer-Daten aktualisiert.' + successfuly_destroyed: 'Der Benutzer wurde gelöscht.' + avatar_destroyed: 'Der Avatar wurde gelöscht.' + index: + page_title: 'Benutzer' + user_name: 'Benutzer-Name' + email: 'E-Mail Adresse' + first_name: 'Vorname' + middle_name: 'Zweiter Vorname' + last_name: 'Nachname' + male: 'männlich' + gemeinschaft_unique_id: 'Eindeutige Bezeichnung' + state: 'Status' + language_id: 'Sprache' + send_voicemail_as_email_attachment: 'Sprachnachrichten per E-Mail' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Benutzer löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neuen Benutzer anlegen' + create_for: 'Neuen Benutzer für %{resource} anlegen' + show: + page_title: 'Benutzer anzeigen' + user_name: 'Benutzer-Name' + email: 'E-Mail Adresse' + first_name: 'Vorname' + middle_name: 'Zweiter Vorname' + last_name: 'Nachname' + male: 'männlich' + gemeinschaft_unique_id: 'Eindeutige Bezeichnung' + state: 'Status' + language_id: 'Sprache' + send_voicemail_as_email_attachment: 'Sprachnachrichten per E-Mail' + actions: + confirm: 'Sind Sie sicher, dass Sie diesen Benutzer löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle Benutzer anzeigen' + new: + page_title: 'Neuen Benutzer anlegen' + edit: + page_title: 'Benutzer %{resource} bearbeiten' + form: + user_name: + label: 'Benutzer-Name' + hint: '' + email: + label: 'E-Mail Adresse' + hint: '' + password: + label: 'Passwort' + hint: 'Mit diesem Passwort können Sie sich später in dieser Web-Oberfläche anmelden.' + password_confirmation: + label: 'Wiederholung des Passwortes' + hint: '' + new_pin: + label: 'PIN' + hint: 'Mit dieser PIN können Sie sich in Hot-Desk Umgebungen am Telefon anmelden.' + new_pin_confirmation: + label: 'Wiederholung der PIN' + hint: '' + first_name: + label: 'Vorname' + hint: '' + middle_name: + label: 'Zweiter Vorname' + hint: '' + last_name: + label: 'Nachname' + hint: '' + gender: + label: 'Geschlecht' + hint: '' + male: "Männlich" + female: "Weiblich" + gemeinschaft_unique_id: + label: 'Eindeutige Bezeichnung' + hint: '' + image: + label: 'Avatar Foto' + hint: '' + language_id: + label: 'Sprache' + hint: '' + send_voicemail_as_email_attachment: + label: 'Sprachnachrichten per E-Mail verschicken' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/users/en.yml b/config/locales/views/users/en.yml new file mode 100644 index 0000000..317e616 --- /dev/null +++ b/config/locales/views/users/en.yml @@ -0,0 +1,95 @@ +en: + users: + name: "Users" + controller: + successfuly_created: 'Successfully created user %{resource}.' + successfuly_created_and_login: 'Successfully created user %{resource}. You are now logged in.' + successfuly_updated: 'Successfully updated user.' + successfuly_destroyed: 'Successfully destroyed user.' + avatar_destroyed: 'The avatar was deleted.' + index: + page_title: 'Users' + user_name: 'Username' + email: 'E-mail address' + first_name: 'First name' + middle_name: 'Middle name' + last_name: 'Last name' + male: 'Male' + gemeinschaft_unique_id: 'Unique identifier' + state: 'State' + language_id: 'Language' + send_voicemail_as_email_attachment: 'Send voicemails by e-mail' + actions: + confirm: 'Are you sure you want to delete this user?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'Create a new user' + create_for: 'Create a new user for %{resource}' + show: + page_title: 'Show User' + user_name: 'Username' + email: 'E-mail address' + first_name: 'First name' + middle_name: 'Middle name' + last_name: 'Last name' + male: 'Male' + gemeinschaft_unique_id: 'Unique identifier' + state: 'State' + language_id: 'Language' + send_voicemail_as_email_attachment: 'Send voicemails by e-mail' + actions: + confirm: 'Are you sure you want to delete this user?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View all users' + new: + page_title: 'New User' + edit: + page_title: 'Editing user %{resource}' + form: + user_name: + label: 'Username' + hint: '' + email: + label: 'E-mail address' + hint: '' + password: + label: 'Password' + hint: 'This password needs to be entered in the WebGUI.' + password_confirmation: + label: 'Password (confirmation)' + hint: '' + new_pin: + label: 'PIN' + hint: 'This PIN is needed in hotdesking environments to log into a phone.' + new_pin_confirmation: + label: 'PIN (confirmation)' + hint: '' + first_name: + label: 'First name' + hint: '' + middle_name: + label: 'Middle name' + hint: '' + last_name: + label: 'Last name' + hint: '' + gender: + label: 'Gender' + hint: '' + male: "Male" + female: "Female" + gemeinschaft_unique_id: + label: 'Unique identifier' + hint: '' + image: + label: 'Avatar photo' + hint: '' + language_id: + label: 'Language' + hint: '' + send_voicemail_as_email_attachment: + label: 'Send voicemails as e-mail attachments' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/locales/views/voicemail_messages/de.yml b/config/locales/views/voicemail_messages/de.yml new file mode 100644 index 0000000..61ee837 --- /dev/null +++ b/config/locales/views/voicemail_messages/de.yml @@ -0,0 +1,34 @@ +de: + voicemail_messages: + name: 'Sprachnachrichten' + controller: + successfuly_destroyed: 'Die Nachricht wurde gelöscht.' + index: + page_title: 'Nachrichten' + page_title_read: 'Gelesene Nachrichten' + page_title_unread: 'Ungelesene Nachrichten' + date_format: '%d.%m.%Y %H:%M' + date_today_format: '%H:%M' + navigation: + read: 'Gelesen: %{count}' + unread: 'Ungelesen: %{count}' + all: 'Alle: %{count}' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Nachricht löschen möchten?' + confirm_selected: 'Sind Sie sicher, dass Sie die markierten Nachrichten löschen möchten?' + destroy: 'Löschen' + destroy_multiple: 'Markierte Nachrichten löschen' + call: 'Anrufen' + mark_read: 'gelesen' + mark_unread: 'ungelesen' + mailbox: + inbox: 'Eingang' + flags: + save: 'Gespeichert' + call_results: + UNSPECIFIED: '' + ORIGINATOR_CANCEL: 'Abgebrochen' + UNALLOCATED_NUMBER: 'Rufnummer nicht bekannt' + NO_USER_RESPONSE: 'Keine Antwort' + NOANSWER: 'Keine Antwort' + USER_NOT_REGISTERED: 'Offline' diff --git a/config/locales/views/voicemail_messages/en.yml b/config/locales/views/voicemail_messages/en.yml new file mode 100644 index 0000000..3c02b6d --- /dev/null +++ b/config/locales/views/voicemail_messages/en.yml @@ -0,0 +1,35 @@ +en: + voicemail_messages: + name: 'Voicemail Messages' + controller: + successfuly_destroyed: 'Successfully destroyed voicemail message.' + index: + page_title: 'Messages' + page_title_read: 'Read Messages' + page_title_unread: 'Unread Messages' + date_format: '%m/%d/%Y %H:%M' + date_today_format: '%H:%M' + navigation: + read: 'Read: %{count}' + unread: 'Unread: %{count}' + all: 'All: %{count}' + actions: + confirm: 'Are you sure you want to delete this message?' + confirm_selected: 'Are you sure you want to delete all selected messages?' + destroy: 'Delete' + destroy_multiple: 'Delete selected messages' + call: 'Call' + mark_read: 'Mark read' + mark_unread: 'Mark unread' + mailbox: + inbox: 'Inbox' + flags: + save: 'Saved' + call_results: + UNSPECIFIED: '' + ORIGINATOR_CANCEL: 'Cancelled' + UNALLOCATED_NUMBER: 'Unallocated number' + NO_USER_RESPONSE: 'No user response' + NOANSWER: 'No answer' + USER_NOT_REGISTERED: 'Offline' +
\ No newline at end of file diff --git a/config/locales/views/voicemail_settings/de.yml b/config/locales/views/voicemail_settings/de.yml new file mode 100644 index 0000000..184fcac --- /dev/null +++ b/config/locales/views/voicemail_settings/de.yml @@ -0,0 +1,52 @@ +de: + voicemail_settings: + name: 'Anrufbeantworter Einstellungen' + controller: + successfuly_destroyed: 'Einstellungen wurden gelöscht.' + successfuly_created: 'Einstellungen wurden erstellt.' + successfuly_updated: 'Einstellungen wurden aktuallisiert.' + index: + page_title: 'Anrufbeantworter Einstellungen' + actions: + confirm: 'Sind Sie sicher, dass Sie die Einstellungen löschen möchten?' + destroy: 'Löschen' + show: + page_title: 'Anrufbeantworter Einstellungen' + greeting_path: 'Begrüßung' + name_path: 'Name' + flags: 'Flags' + notify: 'Benachrichtigung' + attachment: 'Datei anfügen' + mark_read: 'Als gelesen markieren' + purge: 'Löschen' + actions: + confirm: 'Sind Sie sicher, dass Sie die Einstellungen löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + new: + page_title: 'Anrufbeantworter Einstellungen' + edit: + page_title: 'Anrufbeantworter Einstellungen' + form: + greeting: + label: 'Begrüßung' + hint: '' + name: + label: 'Name' + hint: '' + pin: + label: 'Anrufbeantworter PIN' + hint: '' + notify: + label: 'Benachrichtigung' + hint: 'Benachrichtigung per E-Mail' + attachment: + label: 'Datei anfügen' + hint: 'Datei an E-Mail anfügen' + mark_read: + label: 'Als gelesen markieren' + hint: 'Markieren als gelesen' + purge: + label: 'Löschen' + hint: 'Nachricht löschen' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/voicemail_settings/en.yml b/config/locales/views/voicemail_settings/en.yml new file mode 100644 index 0000000..ddade59 --- /dev/null +++ b/config/locales/views/voicemail_settings/en.yml @@ -0,0 +1,52 @@ +en: + voicemail_settings: + name: 'Voicemail Settings' + controller: + successfuly_destroyed: 'Successfully destroyed voicemail settings.' + successfuly_created: 'Successfully created voicemail settings.' + successfuly_updated: 'Successfully updated voicemail settings.' + index: + page_title: 'Voicemail Settings' + actions: + confirm: 'Are you sure you want to delete voicemail settings?' + destroy: 'Delete' + show: + page_title: 'Voicemail Settings' + greeting_path: 'Greeting' + name_path: 'Name' + flags: 'Flags' + notify: 'Notify by email' + attachment: 'Attach file' + mark_read: 'Mark read' + purge: 'Delete' + actions: + confirm: 'Are you sure you want to delete voicemail settings?' + destroy: 'Delete' + edit: 'Edit' + new: + page_title: 'Voicemail Settings' + edit: + page_title: 'Voicemail Settings' + form: + greeting: + label: 'Greeting' + hint: 'Voicemail greeting' + name: + label: 'Name' + hint: 'Voicemail name' + pin: + label: 'Voicemail PIN' + hint: 'Voicemail PIN' + notify: + label: 'Notify by email' + hint: 'Notify user by email' + attachment: + label: 'Attach file' + hint: 'Attach file to notification email' + mark_read: + label: 'Mark read' + hint: 'Mark message read after notification is sent' + purge: + label: 'Delete' + hint: 'Delete message after notification is sent' + button: 'Submit' diff --git a/config/locales/views/whitelists/de.yml b/config/locales/views/whitelists/de.yml new file mode 100644 index 0000000..7710de6 --- /dev/null +++ b/config/locales/views/whitelists/de.yml @@ -0,0 +1,38 @@ +de: + whitelists: + name: 'Positivliste' + controller: + successfuly_created: 'Eine Positivliste wurde erstellt.' + successfuly_updated: 'Die Positivliste wurde aktualisiert.' + successfuly_destroyed: 'Die Positivliste wurde gelöscht.' + index: + page_title: 'Positivlisten' + name: 'Name' + phone_numbers: 'Telefonnummern' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Positivliste löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: 'Neue Positivliste für %{resource} anlegen' + show: + page_title: 'Positivliste anzeigen' + name: 'Name' + actions: + confirm: 'Sind Sie sicher, dass Sie diese Positivliste löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + new: + page_title: 'Neue Positivliste' + edit: + page_title: 'Positivliste bearbeiten' + form: + name: + label: 'Name' + hint: '' + phone_numbers: + label: 'Telefonnummern' + hint: '' + button: 'Absenden'
\ No newline at end of file diff --git a/config/locales/views/whitelists/en.yml b/config/locales/views/whitelists/en.yml new file mode 100644 index 0000000..d681153 --- /dev/null +++ b/config/locales/views/whitelists/en.yml @@ -0,0 +1,38 @@ +en: + whitelists: + name: 'Whitelist' + controller: + successfuly_created: 'Successfully created whitelist.' + successfuly_updated: 'Successfully updated whitelist.' + successfuly_destroyed: 'Successfully destroyed whitelist.' + index: + page_title: 'Whitelists' + name: 'Name' + phone_numbers: 'Phone numbers' + actions: + confirm: 'Are you sure you want to delete this whitelist?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New whitelist for %{resource}' + show: + page_title: 'Show whitelist' + name: 'Name' + actions: + confirm: 'Are you sure you want to delete this whitelist?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View All' + new: + page_title: 'New whitelist' + edit: + page_title: 'Editing whitelist' + form: + name: + label: 'Name' + hint: '' + phone_numbers: + label: 'Phone numbers' + hint: '' + button: 'Submit'
\ No newline at end of file diff --git a/config/private_pub.yml b/config/private_pub.yml new file mode 100644 index 0000000..19a7e9e --- /dev/null +++ b/config/private_pub.yml @@ -0,0 +1,10 @@ +development: + server: "http://localhost:9292/faye" + secret_token: "secret" +test: + server: "http://localhost:9292/faye" + secret_token: "secret" +production: + server: "http://example.com/faye" + secret_token: "ade2c51226bf26e7fbbce1e0d8848082b750d23516b46dc5bc12e910e0e64558" + signature_expiration: 3600 # one hour diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 0000000..64d64cf --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,375 @@ +Gemeinschaft42c::Application.routes.draw do + resources :automatic_call_distributors + + resources :gs_cluster_sync_log_entries + + resources :gs_nodes do + member do + get 'sync' + end + end + + resources :gui_functions + + namespace :api do + resources :rows + end + + resources :acd_agents, :only => [] do + resources :phone_numbers do + member do + put 'move_higher' + put 'move_lower' + end + end + end + + resources :automatic_call_distributors, :only => [] do + resources :acd_agents + resources :phone_numbers do + member do + put 'move_higher' + put 'move_lower' + end + end + end + + resources :hunt_group_members, :only => [] do + resources :phone_numbers do + member do + put 'move_higher' + put 'move_lower' + end + end + end + + resources :hunt_groups, :only => [] do + resources :hunt_group_members + resources :phone_numbers do + member do + put 'move_higher' + put 'move_lower' + end + end + end + + if CALLTHROUGH_HAS_WHITELISTS == true + resources :whitelists, :only => [] do + resources :phone_numbers do + member do + put 'move_higher' + put 'move_lower' + end + end + end + end + + resources :access_authorizations, :only => [] do + resources :phone_numbers do + member do + put 'move_higher' + put 'move_lower' + end + end + end + + resources :fax_documents + + resources :fax_accounts, :only => [] do + resources :fax_documents + resources :phone_numbers do + member do + put 'move_higher' + put 'move_lower' + end + end + end + + resources :gemeinschaft_setups, :only => [:new, :create] + + resources :phone_number_ranges, :only => [] do + resources :phone_numbers do + member do + put 'move_higher' + put 'move_lower' + end + end + end + + resources :conferences, :only => [] do + resources :conference_invitees + resources :phone_numbers do + member do + put 'move_higher' + put 'move_lower' + end + end + end + + resources :phone_numbers, :only => [] do + resources :call_forwards + resources :ringtones + end + + resources :addresses + + resources :sip_domains + + resources :manufacturers do + resources :phone_models + end + + # Log-in / log-out / sign-up: + get "log_in" => "sessions#new" , :as => "log_in" + get "log_out" => "sessions#destroy" , :as => "log_out" + get "sign_up" => "users#new" , :as => "sign_up" + get "login" => "sessions#new" , :as => "log_in" + get "logout" => "sessions#destroy" , :as => "log_out" + get "signup" => "users#new" , :as => "sign_up" + + # Provisioning: + # Snom + get "config_snom/:phone/:sip_account/idle_screen" => "config_snom#idle_screen" + get "config_snom/:phone/:sip_account/log_in" => "config_snom#log_in" + get "config_snom/:phone/:sip_account/phone_book" => "config_snom#phone_book" + get "config_snom/:phone/:sip_account/call_history" => "config_snom#call_history" + get "config_snom/:phone/:sip_account/call_history_:type" => "config_snom#call_history" + get "config_snom/:phone/:sip_account/call_forwarding" => "config_snom#call_forwarding" + get "config_snom/exit" => "config_snom#exit" + get "config_snom/:phone/exit" => "config_snom#exit" + get "config_snom/:phone/:sip_account/exit" => "config_snom#exit" + get "config_snom/:phone/:sip_account/hunt_group" => "config_snom#hunt_group" + get "config_snom/:phone/state_settings" => "config_snom#state_settings" + get "config_snom/:phone/log_out" => "config_snom#log_out" + get "config_snom/:phone/:sip_account/log_out" => "config_snom#log_out" + get "config_snom/:phone/log_in" => "config_snom#log_in" + get "config_snom/:phone/:sip_account/log_in" => "config_snom#log_in" + get "config_snom/:phone/:sip_account/acd" => "config_snom#acd" + + # Siemens + get "config_siemens/:phone/call_history" => "config_siemens#call_history" + get "config_siemens/:phone/:sip_account/call_history" => "config_siemens#call_history" + get "config_siemens/:phone/:sip_account/call_forwarding" => "config_siemens#call_forwarding" + get "config_siemens/:phone/hunt_group" => "config_siemens#hunt_group" + get "config_siemens/:phone/:sip_account/hunt_group" => "config_siemens#hunt_group" + get "config_siemens/:phone/menu" => "config_siemens#menu" + get "config_siemens/:phone/:sip_account/menu" => "config_siemens#menu" + + #Polycom + get "config_polycom/:phone/:sip_account/phone_book" => "config_polycom#phone_book" + get "config_polycom/:phone/:sip_account/call_history" => "config_polycom#call_history" + get "config_polycom/:phone/:sip_account/idle_screen" => "config_polycom#idle_screen" + + # Unified path for Snom phones. + # Enter e.g. "http://192.168.1.105:3000/settings" + # as the Setting URL (Advanced -> Update). + match 'snom-:provisioning_key' => 'config_snom#show', + :via => [:get], + :format => 'xml' + match 'settings-:mac_address' => 'config_snom#show', + :constraints => { :mac_address => /000413[0-9A-F]{6}/i }, + :via => [:get], + :format => 'xml' + match "/DeploymentService/LoginService" => 'config_siemens#index', + :via => [:post], + :format => 'xml' + match ':mac_address.cfg' => 'config_polycom#config_files', + :constraints => { :mac_address => /0004f2[0-9A-F]{6}/i }, + :via => [:get], + :format => 'xml' + match 'settings-:mac_address.cfg' => 'config_polycom#settings', + :constraints => { :mac_address => /0004f2[0-9A-F]{6}/i }, + :via => [:get], + :format => 'xml' + match ':mac_address-directory' => 'config_polycom#settings_directory', + :constraints => { :mac_address => /0004f2[0-9A-F]{6}/i }, + :via => [:get], + :format => 'xml' + + # Snom default path. + # e.g. "/snom360-000413000000.htm" + # Enter e.g. "http://192.168.1.105:3000" + # as the Setting URL (Advanced -> Update). + match 'snom:model-:mac_address' => 'config_snom#show', + :constraints => { :mac_address => /000413[0-9A-F]{6}/i, :model => /[0-9]{3}/ }, + :via => [:get], + :format => 'xml' + + resources :sessions + + get "page/index" + get "page/conference" + + root :to => "page#index" + + get "wizards/new_initial_setup" + post "wizards/create_initial_setup" + + resources :users do + # Display all phone books that the current user owns: + resources :phone_books + resources :user_groups, :only => [ :index, :show ] + resources :sip_accounts + resources :phones + resources :conferences + resources :fax_accounts + resources :system_messages, :except => [ :edit, :update, :destroy ] + end + + resources :user_groups do + # Display all phone books that the group of the current user owns: + resources :phone_books + resources :sip_accounts + resources :fax_accounts + resources :user_group_memberships + end + + resources :tenants do + # Display all phone books that the tenant of the current user owns: + resources :phone_books + resources :users do + get "destroy_avatar" + end + resources :user_groups + resources :sip_accounts + resources :phones + resources :conferences + resources :phone_number_ranges + resources :callthroughs + if CALLTHROUGH_HAS_WHITELISTS == true + resources :whitelists + end + resources :hunt_groups + resources :automatic_call_distributors + end + + resources :callthroughs, :only => [] do + resources :access_authorizations + resources :phone_numbers do + member do + put 'move_higher' + put 'move_lower' + end + end + if CALLTHROUGH_HAS_WHITELISTS == true + resources :whitelists + end + end + + resources :sip_accounts, :only => [] do + resources :phones_sip_accounts + resources :phone_numbers do + member do + put 'move_higher' + put 'move_lower' + end + end + resources :softkeys do + member do + put 'move_higher' + put 'move_lower' + end + end + resources :call_histories do + collection do + delete 'destroy_multiple' + end + member do + put 'call' + end + end + resources :voicemail_messages do + collection do + delete 'destroy_multiple' + end + member do + put 'call' + put 'mark_read' + put 'mark_unread' + end + end + resources :voicemail_settings + end + + resources :phones, :only => [] do + resources :phone_sip_accounts + end + + # Display all phone book entries that the current user can access: + resources :phone_book_entries, :only => [ :index, :show ] do + resources :phone_numbers do + member do + put 'move_higher' + put 'move_lower' + end + end + end + + # Display all phone books that the current user can access: + resources :phone_books, :only => [ :index, :show ] do + resources :phone_book_entries + end + + # Search + post "search" => "phone_book_entries#index", :as => 'search' + + # http://0.0.0.0:3000/phone_books/3?name=Wintermeyer + + + # The priority is based upon order of creation: + # first created -> highest priority. + + # Sample of regular route: + # match 'products/:id' => 'catalog#view' + # Keep in mind you can assign values other than :controller and :action + + # Sample of named route: + # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase + # This route can be invoked with purchase_url(:id => product.id) + + # Sample resource route (maps HTTP verbs to controller actions automatically): + # resources :products + + # Sample resource route with options: + # resources :products do + # member do + # get 'short' + # post 'toggle' + # end + # + # collection do + # get 'sold' + # end + # end + + # Sample resource route with sub-resources: + # resources :products do + # resources :comments, :sales + # resource :seller + # end + + # Sample resource route with more complex sub-resources + # resources :products do + # resources :comments + # resources :sales do + # get 'recent', :on => :collection + # end + # end + + # Sample resource route within a namespace: + # namespace :admin do + # # Directs /admin/products/* to Admin::ProductsController + # # (app/controllers/admin/products_controller.rb) + # resources :products + # end + + # You can have the root of your site routed with "root" + # just remember to delete public/index.html. + # root :to => 'welcome#index' + + # See how all your routes lay out with "rake routes" + + # This is a legacy wild controller route that's not recommended for RESTful applications. + # Note: This route will make all actions in every controller accessible via GET requests. + # match ':controller(/:action(/:id(.:format)))' +end diff --git a/db/migrate/20111006073436_create_tenants.rb b/db/migrate/20111006073436_create_tenants.rb new file mode 100644 index 0000000..290379c --- /dev/null +++ b/db/migrate/20111006073436_create_tenants.rb @@ -0,0 +1,15 @@ +class CreateTenants < ActiveRecord::Migration + def self.up + create_table :tenants do |t| + t.string :name + t.text :description + t.string :state + t.integer :position + t.timestamps + end + end + + def self.down + drop_table :tenants + end +end diff --git a/db/migrate/20111006074623_create_users.rb b/db/migrate/20111006074623_create_users.rb new file mode 100644 index 0000000..dab37c5 --- /dev/null +++ b/db/migrate/20111006074623_create_users.rb @@ -0,0 +1,20 @@ +class CreateUsers < ActiveRecord::Migration + def self.up + create_table :users do |t| + t.string :user_name + t.string :email + t.string :password_digest + t.string :first_name + t.string :middle_name + t.string :last_name + t.boolean :male + t.string :gemeinschaft_unique_id + t.string :state + t.timestamps + end + end + + def self.down + drop_table :users + end +end diff --git a/db/migrate/20111006081257_create_tenant_memberships.rb b/db/migrate/20111006081257_create_tenant_memberships.rb new file mode 100644 index 0000000..d65d82b --- /dev/null +++ b/db/migrate/20111006081257_create_tenant_memberships.rb @@ -0,0 +1,12 @@ +class CreateTenantMemberships < ActiveRecord::Migration + def change + create_table :tenant_memberships do |t| + t.integer :tenant_id + t.integer :user_id + t.string :state + t.integer :position + + t.timestamps + end + end +end diff --git a/db/migrate/20111006081936_create_user_groups.rb b/db/migrate/20111006081936_create_user_groups.rb new file mode 100644 index 0000000..611102d --- /dev/null +++ b/db/migrate/20111006081936_create_user_groups.rb @@ -0,0 +1,15 @@ +class CreateUserGroups < ActiveRecord::Migration + def self.up + create_table :user_groups do |t| + t.string :name + t.text :description + t.integer :tenant_id + t.integer :position + t.timestamps + end + end + + def self.down + drop_table :user_groups + end +end diff --git a/db/migrate/20111006082944_create_user_group_memberships.rb b/db/migrate/20111006082944_create_user_group_memberships.rb new file mode 100644 index 0000000..de4d207 --- /dev/null +++ b/db/migrate/20111006082944_create_user_group_memberships.rb @@ -0,0 +1,11 @@ +class CreateUserGroupMemberships < ActiveRecord::Migration + def change + create_table :user_group_memberships do |t| + t.integer :user_group_id + t.integer :user_id + t.string :state + + t.timestamps + end + end +end diff --git a/db/migrate/20111006082945_create_phone_books.rb b/db/migrate/20111006082945_create_phone_books.rb new file mode 100644 index 0000000..7a4e967 --- /dev/null +++ b/db/migrate/20111006082945_create_phone_books.rb @@ -0,0 +1,17 @@ +class CreatePhoneBooks < ActiveRecord::Migration + def self.up + create_table :phone_books do |t| + t.string :name + t.string :description + t.integer :phone_bookable_id + t.string :phone_bookable_type + t.string :state + t.integer :position + t.timestamps + end + end + + def self.down + drop_table :phone_books + end +end diff --git a/db/migrate/20111006101543_create_phone_book_entries.rb b/db/migrate/20111006101543_create_phone_book_entries.rb new file mode 100644 index 0000000..fab48b3 --- /dev/null +++ b/db/migrate/20111006101543_create_phone_book_entries.rb @@ -0,0 +1,35 @@ +class CreatePhoneBookEntries < ActiveRecord::Migration + def self.up + create_table :phone_book_entries do |t| + t.integer :phone_book_id + t.string :first_name + t.string :middle_name + t.string :last_name + t.string :title + t.string :nickname + t.string :organization + t.boolean :is_organization + t.string :department + t.string :job_title + t.boolean :is_male + t.date :birthday + t.string :birth_name + t.string :state + t.text :description + t.integer :position + t.string :homepage_personal + t.string :homepage_organization + t.string :twitter_account + t.string :facebook_account + t.string :google_plus_account + t.string :xing_account + t.string :linkedin_account + t.string :mobileme_account + t.timestamps + end + end + + def self.down + drop_table :phone_book_entries + end +end diff --git a/db/migrate/20111007084820_add_image_to_user.rb b/db/migrate/20111007084820_add_image_to_user.rb new file mode 100644 index 0000000..ca32b2b --- /dev/null +++ b/db/migrate/20111007084820_add_image_to_user.rb @@ -0,0 +1,5 @@ +class AddImageToUser < ActiveRecord::Migration + def change + add_column :users, :image, :string + end +end diff --git a/db/migrate/20111007092546_add_image_to_phone_book_entry.rb b/db/migrate/20111007092546_add_image_to_phone_book_entry.rb new file mode 100644 index 0000000..f4ae09e --- /dev/null +++ b/db/migrate/20111007092546_add_image_to_phone_book_entry.rb @@ -0,0 +1,5 @@ +class AddImageToPhoneBookEntry < ActiveRecord::Migration + def change + add_column :phone_book_entries, :image, :string + end +end diff --git a/db/migrate/20111007110029_remove_position_from_phone_book.rb b/db/migrate/20111007110029_remove_position_from_phone_book.rb new file mode 100644 index 0000000..401a273 --- /dev/null +++ b/db/migrate/20111007110029_remove_position_from_phone_book.rb @@ -0,0 +1,9 @@ +class RemovePositionFromPhoneBook < ActiveRecord::Migration + def up + remove_column :phone_books, :position + end + + def down + add_column :phone_books, :position, :integer + end +end diff --git a/db/migrate/20111007110258_remove_position_from_tenant.rb b/db/migrate/20111007110258_remove_position_from_tenant.rb new file mode 100644 index 0000000..1415114 --- /dev/null +++ b/db/migrate/20111007110258_remove_position_from_tenant.rb @@ -0,0 +1,9 @@ +class RemovePositionFromTenant < ActiveRecord::Migration + def up + remove_column :tenants, :position + end + + def down + add_column :tenants, :position, :integer + end +end diff --git a/db/migrate/20111007123901_remove_position_from_tenant_membership.rb b/db/migrate/20111007123901_remove_position_from_tenant_membership.rb new file mode 100644 index 0000000..03e380a --- /dev/null +++ b/db/migrate/20111007123901_remove_position_from_tenant_membership.rb @@ -0,0 +1,9 @@ +class RemovePositionFromTenantMembership < ActiveRecord::Migration + def up + remove_column :tenant_memberships, :position + end + + def down + add_column :tenant_memberships, :position, :integer + end +end diff --git a/db/migrate/20111008112922_add_current_tenant_id_to_users.rb b/db/migrate/20111008112922_add_current_tenant_id_to_users.rb new file mode 100644 index 0000000..ff0d78b --- /dev/null +++ b/db/migrate/20111008112922_add_current_tenant_id_to_users.rb @@ -0,0 +1,5 @@ +class AddCurrentTenantIdToUsers < ActiveRecord::Migration + def change + add_column :users, :current_tenant_id, :integer + end +end diff --git a/db/migrate/20111009184013_add_sessions_table.rb b/db/migrate/20111009184013_add_sessions_table.rb new file mode 100644 index 0000000..fd942cc --- /dev/null +++ b/db/migrate/20111009184013_add_sessions_table.rb @@ -0,0 +1,16 @@ +class AddSessionsTable < ActiveRecord::Migration + def up + create_table :sessions do |t| + t.string :session_id, :null => false + t.text :data + t.timestamps + end + + add_index :sessions, :session_id + add_index :sessions, :updated_at + end + + def down + drop_table :sessions + end +end diff --git a/db/migrate/20111009202027_create_phone_numbers.rb b/db/migrate/20111009202027_create_phone_numbers.rb new file mode 100644 index 0000000..a7d15b4 --- /dev/null +++ b/db/migrate/20111009202027_create_phone_numbers.rb @@ -0,0 +1,19 @@ +class CreatePhoneNumbers < ActiveRecord::Migration + def self.up + create_table :phone_numbers do |t| + t.integer :phone_book_id + t.string :name + t.string :number + t.string :country_code + t.string :area_code + t.string :subscriber_number + t.string :extension + t.integer :position + t.timestamps + end + end + + def self.down + drop_table :phone_numbers + end +end diff --git a/db/migrate/20111009202500_create_countries.rb b/db/migrate/20111009202500_create_countries.rb new file mode 100644 index 0000000..d22fb42 --- /dev/null +++ b/db/migrate/20111009202500_create_countries.rb @@ -0,0 +1,12 @@ +class CreateCountries < ActiveRecord::Migration + def change + create_table :countries do |t| + t.string :name + t.string :country_code + t.string :international_call_prefix + t.string :trunk_prefix + + t.timestamps + end + end +end diff --git a/db/migrate/20111009202910_create_area_codes.rb b/db/migrate/20111009202910_create_area_codes.rb new file mode 100644 index 0000000..7719bd9 --- /dev/null +++ b/db/migrate/20111009202910_create_area_codes.rb @@ -0,0 +1,11 @@ +class CreateAreaCodes < ActiveRecord::Migration + def change + create_table :area_codes do |t| + t.integer :country_id + t.string :name + t.string :area_code + + t.timestamps + end + end +end diff --git a/db/migrate/20111009203233_add_country_id_to_tenant.rb b/db/migrate/20111009203233_add_country_id_to_tenant.rb new file mode 100644 index 0000000..9ba0288 --- /dev/null +++ b/db/migrate/20111009203233_add_country_id_to_tenant.rb @@ -0,0 +1,5 @@ +class AddCountryIdToTenant < ActiveRecord::Migration + def change + add_column :tenants, :country_id, :integer + end +end diff --git a/db/migrate/20111010110232_add_phone_book_entry_id_to_phone_number.rb b/db/migrate/20111010110232_add_phone_book_entry_id_to_phone_number.rb new file mode 100644 index 0000000..f9efda7 --- /dev/null +++ b/db/migrate/20111010110232_add_phone_book_entry_id_to_phone_number.rb @@ -0,0 +1,6 @@ +class AddPhoneBookEntryIdToPhoneNumber < ActiveRecord::Migration + def change + remove_column :phone_numbers, :phone_book_id + add_column :phone_numbers, :phone_book_entry_id, :integer + end +end diff --git a/db/migrate/20111011080014_add_central_office_code_to_phone_number.rb b/db/migrate/20111011080014_add_central_office_code_to_phone_number.rb new file mode 100644 index 0000000..4a15b82 --- /dev/null +++ b/db/migrate/20111011080014_add_central_office_code_to_phone_number.rb @@ -0,0 +1,5 @@ +class AddCentralOfficeCodeToPhoneNumber < ActiveRecord::Migration + def change + add_column :phone_numbers, :central_office_code, :string + end +end diff --git a/db/migrate/20111011181421_add_central_office_code_to_area_code.rb b/db/migrate/20111011181421_add_central_office_code_to_area_code.rb new file mode 100644 index 0000000..b5d0d12 --- /dev/null +++ b/db/migrate/20111011181421_add_central_office_code_to_area_code.rb @@ -0,0 +1,5 @@ +class AddCentralOfficeCodeToAreaCode < ActiveRecord::Migration + def change + add_column :area_codes, :central_office_code, :string + end +end diff --git a/db/migrate/20111012113547_create_ouis.rb b/db/migrate/20111012113547_create_ouis.rb new file mode 100644 index 0000000..ad81508 --- /dev/null +++ b/db/migrate/20111012113547_create_ouis.rb @@ -0,0 +1,11 @@ +class CreateOuis < ActiveRecord::Migration + def change + create_table :ouis do |t| + t.integer :manufacturer_id + t.string :value + t.string :state + + t.timestamps + end + end +end diff --git a/db/migrate/20111012121851_create_manufacturers.rb b/db/migrate/20111012121851_create_manufacturers.rb new file mode 100644 index 0000000..0f8b132 --- /dev/null +++ b/db/migrate/20111012121851_create_manufacturers.rb @@ -0,0 +1,15 @@ +class CreateManufacturers < ActiveRecord::Migration + def self.up + create_table :manufacturers do |t| + t.string :name + t.string :ieee_name + t.string :homepage_url + t.string :state + t.timestamps + end + end + + def self.down + drop_table :manufacturers + end +end diff --git a/db/migrate/20111012131652_create_phones.rb b/db/migrate/20111012131652_create_phones.rb new file mode 100644 index 0000000..96d88f7 --- /dev/null +++ b/db/migrate/20111012131652_create_phones.rb @@ -0,0 +1,17 @@ +class CreatePhones < ActiveRecord::Migration + def self.up + create_table :phones do |t| + t.string :mac_address + t.integer :phone_model_id + t.string :ip_address + t.string :last_ip_address + t.string :http_user + t.string :http_password + t.timestamps + end + end + + def self.down + drop_table :phones + end +end diff --git a/db/migrate/20111012142952_add_state_to_phone.rb b/db/migrate/20111012142952_add_state_to_phone.rb new file mode 100644 index 0000000..c5601d9 --- /dev/null +++ b/db/migrate/20111012142952_add_state_to_phone.rb @@ -0,0 +1,5 @@ +class AddStateToPhone < ActiveRecord::Migration + def change + add_column :phones, :state, :string + end +end diff --git a/db/migrate/20111012143922_create_phone_models.rb b/db/migrate/20111012143922_create_phone_models.rb new file mode 100644 index 0000000..50221c0 --- /dev/null +++ b/db/migrate/20111012143922_create_phone_models.rb @@ -0,0 +1,15 @@ +class CreatePhoneModels < ActiveRecord::Migration + def self.up + create_table :phone_models do |t| + t.string :name + t.string :manufacturer_id + t.string :product_manual_homepage_url + t.string :product_homepage_url + t.timestamps + end + end + + def self.down + drop_table :phone_models + end +end diff --git a/db/migrate/20111013110804_add_state_to_phone_models.rb b/db/migrate/20111013110804_add_state_to_phone_models.rb new file mode 100644 index 0000000..3820372 --- /dev/null +++ b/db/migrate/20111013110804_add_state_to_phone_models.rb @@ -0,0 +1,5 @@ +class AddStateToPhoneModels < ActiveRecord::Migration + def change + add_column :phone_models, :state, :string + end +end diff --git a/db/migrate/20111013173838_create_sip_domains.rb b/db/migrate/20111013173838_create_sip_domains.rb new file mode 100644 index 0000000..1977c5c --- /dev/null +++ b/db/migrate/20111013173838_create_sip_domains.rb @@ -0,0 +1,13 @@ +class CreateSipDomains < ActiveRecord::Migration + def self.up + create_table :sip_domains do |t| + t.string :host + t.string :realm + t.timestamps + end + end + + def self.down + drop_table :sip_domains + end +end diff --git a/db/migrate/20111013173845_add_sip_domain_id_to_tenant.rb b/db/migrate/20111013173845_add_sip_domain_id_to_tenant.rb new file mode 100644 index 0000000..562059b --- /dev/null +++ b/db/migrate/20111013173845_add_sip_domain_id_to_tenant.rb @@ -0,0 +1,5 @@ +class AddSipDomainIdToTenant < ActiveRecord::Migration + def change + add_column :tenants, :sip_domain_id, :integer + end +end diff --git a/db/migrate/20111013180505_create_sip_accounts.rb b/db/migrate/20111013180505_create_sip_accounts.rb new file mode 100644 index 0000000..eca8db2 --- /dev/null +++ b/db/migrate/20111013180505_create_sip_accounts.rb @@ -0,0 +1,18 @@ +class CreateSipAccounts < ActiveRecord::Migration + def self.up + create_table :sip_accounts do |t| + t.string :sip_accountable_type + t.integer :sip_accountable_id + t.string :auth_name + t.string :caller_name + t.string :password + t.string :voicemail_pin + t.string :state + t.timestamps + end + end + + def self.down + drop_table :sip_accounts + end +end diff --git a/db/migrate/20111031125955_create_addresses.rb b/db/migrate/20111031125955_create_addresses.rb new file mode 100644 index 0000000..6f31593 --- /dev/null +++ b/db/migrate/20111031125955_create_addresses.rb @@ -0,0 +1,19 @@ +class CreateAddresses < ActiveRecord::Migration + def self.up + create_table :addresses do |t| + t.integer :phone_book_entry_id + t.string :line1 + t.string :line2 + t.string :street + t.string :zip_code + t.string :city + t.integer :country_id + t.integer :position + t.timestamps + end + end + + def self.down + drop_table :addresses + end +end diff --git a/db/migrate/20111101155754_add_phonetics_to_phone_book_entry.rb b/db/migrate/20111101155754_add_phonetics_to_phone_book_entry.rb new file mode 100644 index 0000000..a8e42c2 --- /dev/null +++ b/db/migrate/20111101155754_add_phonetics_to_phone_book_entry.rb @@ -0,0 +1,7 @@ +class AddPhoneticsToPhoneBookEntry < ActiveRecord::Migration + def change + add_column :phone_book_entries, :first_name_phonetic, :string + add_column :phone_book_entries, :last_name_phonetic, :string + add_column :phone_book_entries, :organization_phonetic, :string + end +end diff --git a/db/migrate/20111101155952_add_index_to_phone_book_entry.rb b/db/migrate/20111101155952_add_index_to_phone_book_entry.rb new file mode 100644 index 0000000..1627e0f --- /dev/null +++ b/db/migrate/20111101155952_add_index_to_phone_book_entry.rb @@ -0,0 +1,10 @@ +class AddIndexToPhoneBookEntry < ActiveRecord::Migration + def change + add_index :phone_book_entries, :first_name + add_index :phone_book_entries, :last_name + add_index :phone_book_entries, :organization + add_index :phone_book_entries, :first_name_phonetic + add_index :phone_book_entries, :last_name_phonetic + add_index :phone_book_entries, :organization_phonetic + end +end diff --git a/db/migrate/20111104114634_add_polymorphic_to_phone_number.rb b/db/migrate/20111104114634_add_polymorphic_to_phone_number.rb new file mode 100644 index 0000000..29400cf --- /dev/null +++ b/db/migrate/20111104114634_add_polymorphic_to_phone_number.rb @@ -0,0 +1,8 @@ +class AddPolymorphicToPhoneNumber < ActiveRecord::Migration + def change + add_column :phone_numbers, :phone_numberable_type, :string + add_column :phone_numbers, :phone_numberable_id, :integer + + remove_column :phone_numbers, :phone_book_entry_id + end +end diff --git a/db/migrate/20111104140800_create_calls.rb b/db/migrate/20111104140800_create_calls.rb new file mode 100644 index 0000000..1b66a76 --- /dev/null +++ b/db/migrate/20111104140800_create_calls.rb @@ -0,0 +1,28 @@ +class CreateCalls < ActiveRecord::Migration + def self.up + create_table :calls, :id => false do |t| + t.string :call_uuid, :limit=>'255', :primary => true + t.string :call_created, :limit=>'128' + t.integer :call_created_epoch + t.string :function, :limit=>'1024' + t.string :caller_cid_name, :limit=>'1024' + t.string :caller_cid_num, :limit=>'256' + t.string :caller_dest_num, :limit=>'256' + t.string :caller_chan_name, :limit=>'1024' + t.string :caller_uuid, :limit=>'256' + t.string :callee_cid_name, :limit=>'1024' + t.string :callee_cid_numcallee_dest_num, :limit=>'256' + t.string :callee_chan_name, :limit=>'1024' + t.string :callee_uuid, :limit=>'256' + t.string :hostname, :limit=>'256' + end + add_index :calls, [ :hostname ], :unique => false, :name => 'calls1' + add_index :calls, [ :callee_uuid, :hostname ], :unique => false, :name => 'eeuuindex' + add_index :calls, [ :call_uuid, :hostname ], :unique => false, :name => 'eeuuindex2' + add_index :calls, [ :caller_uuid, :hostname ], :unique => false, :name => 'eruuindex' + end + + def self.down + drop_table :calls + end +end diff --git a/db/migrate/20111104140900_create_channels.rb b/db/migrate/20111104140900_create_channels.rb new file mode 100644 index 0000000..eb81844 --- /dev/null +++ b/db/migrate/20111104140900_create_channels.rb @@ -0,0 +1,42 @@ +class CreateChannels < ActiveRecord::Migration + def self.up + create_table :channels, :id => false do |t| + t.string :uuid, :limit=>'256', :primary => true + t.string :direction, :limit=>'32' + t.string :created, :limit=>'128' + t.integer :created_epoch + t.string :name, :limit=>'1024' + t.string :state, :limit=>'64' + t.string :cid_name, :limit=>'1024' + t.string :cid_num, :limit=>'256' + t.string :ip_addr, :limit=>'256' + t.string :dest, :limit=>'1024' + t.string :application, :limit=>'128' + t.string :application_data, :limit=>'4096' + t.string :dialplan, :limit=>'128' + t.string :context, :limit=>'128' + t.string :read_codec, :limit=>'128' + t.string :read_rate, :limit=>'32' + t.string :read_bit_rate, :limit=>'32' + t.string :write_codec, :limit=>'128' + t.string :write_rate, :limit=>'32' + t.string :write_bit_rate, :limit=>'32' + t.string :secure, :limit=>'32' + t.string :hostname, :limit=>'256' + t.string :presence_id, :limit=>'4096' + t.string :presence_data, :limit=>'4096' + t.string :callstate, :limit=>'64' + t.string :callee_name, :limit=>'1024' + t.string :callee_num, :limit=>'256' + t.string :callee_direction, :limit=>'5' + t.string :call_uuid, :limit=>'256' + end + add_index :channels, [ :hostname ], :unique => false, :name => 'channels1' + add_index :channels, [ :uuid, :hostname ], :unique => true, :name => 'uuindex' + add_index :channels, [ :call_uuid, :hostname ], :unique => false, :name => 'uuindex2' + end + + def self.down + drop_table :channels + end +end diff --git a/db/migrate/20111104140901_create_interfaces.rb b/db/migrate/20111104140901_create_interfaces.rb new file mode 100644 index 0000000..eede6a3 --- /dev/null +++ b/db/migrate/20111104140901_create_interfaces.rb @@ -0,0 +1,17 @@ +class CreateInterfaces < ActiveRecord::Migration + def self.up + create_table :interfaces, :id => false do |t| + t.string :type, :limit=>'128' + t.string :name, :limit=>'1024' + t.string :description, :limit=>'4096' + t.string :ikey, :limit=>'1024' + t.string :filename, :limit=>'4096' + t.string :syntax, :limit=>'4096' + t.string :hostname, :limit=>'256' + end + end + + def self.down + drop_table :interfaces + end +end diff --git a/db/migrate/20111104141000_create_aliases.rb b/db/migrate/20111104141000_create_aliases.rb new file mode 100644 index 0000000..e645ef5 --- /dev/null +++ b/db/migrate/20111104141000_create_aliases.rb @@ -0,0 +1,15 @@ +class CreateAliases < ActiveRecord::Migration + def self.up + create_table :aliases, :id => false do |t| + t.integer :sticky + t.string :alias, :limit=>'128' + t.string :command, :limit=>'4096' + t.string :hostname, :limit=>'256' + end + add_index :aliases, [ :alias ], :unique => false, :name => 'alias1' + end + + def self.down + drop_table :aliases + end +end diff --git a/db/migrate/20111104141001_create_complete.rb b/db/migrate/20111104141001_create_complete.rb new file mode 100644 index 0000000..e5f0a63 --- /dev/null +++ b/db/migrate/20111104141001_create_complete.rb @@ -0,0 +1,33 @@ +class CreateComplete < ActiveRecord::Migration + def self.up + create_table :complete, :id => false do |t| + t.integer :sticky + t.string :a1, :limit=>'128' + t.string :a2, :limit=>'128' + t.string :a3, :limit=>'128' + t.string :a4, :limit=>'128' + t.string :a5, :limit=>'128' + t.string :a6, :limit=>'128' + t.string :a7, :limit=>'128' + t.string :a8, :limit=>'128' + t.string :a9, :limit=>'128' + t.string :a10, :limit=>'128' + t.string :hostname, :limit=>'256' + end + add_index :complete, [ :a1, :hostname ], :unique => false, :name => 'complete1' + add_index :complete, [ :a2, :hostname ], :unique => false, :name => 'complete2' + add_index :complete, [ :a3, :hostname ], :unique => false, :name => 'complete3' + add_index :complete, [ :a4, :hostname ], :unique => false, :name => 'complete4' + add_index :complete, [ :a5, :hostname ], :unique => false, :name => 'complete5' + add_index :complete, [ :a6, :hostname ], :unique => false, :name => 'complete6' + add_index :complete, [ :a7, :hostname ], :unique => false, :name => 'complete7' + add_index :complete, [ :a8, :hostname ], :unique => false, :name => 'complete8' + add_index :complete, [ :a9, :hostname ], :unique => false, :name => 'complete9' + add_index :complete, [ :a10, :hostname ], :unique => false, :name => 'complete10' + add_index :complete, [ :a1, :a2, :a3, :a4, :a5, :a6, :a7, :a8, :a9, :a10, :hostname ], :unique => false, :name => 'complete11' + end + + def self.down + drop_table :complete + end +end diff --git a/db/migrate/20111104141002_create_tasks.rb b/db/migrate/20111104141002_create_tasks.rb new file mode 100644 index 0000000..d7539bc --- /dev/null +++ b/db/migrate/20111104141002_create_tasks.rb @@ -0,0 +1,16 @@ +class CreateTasks < ActiveRecord::Migration + def self.up + create_table :tasks, :id => false do |t| + t.integer :task_id, :primary => true + t.string :task_desc, :limit=>'4096' + t.string :task_group, :limit=>'1024' + t.integer :task_sql_manager + t.string :hostname, :limit=>'256' + end + add_index :tasks, [ :hostname, :task_id ], :unique => true, :name => 'tasks1' + end + + def self.down + drop_table :tasks + end +end diff --git a/db/migrate/20111104141100_create_nat.rb b/db/migrate/20111104141100_create_nat.rb new file mode 100644 index 0000000..7c5cde4 --- /dev/null +++ b/db/migrate/20111104141100_create_nat.rb @@ -0,0 +1,15 @@ +class CreateNat < ActiveRecord::Migration + def self.up + create_table :nat, :id => false do |t| + t.integer :sticky + t.integer :port + t.integer :proto + t.string :hostname, :limit => '256' + end + add_index :nat, [ :port, :proto, :hostname ], :unique => false, :name => 'nat_map_port_proto' + end + + def self.down + drop_table :nat + end +end diff --git a/db/migrate/20111104141101_create_registrations.rb b/db/migrate/20111104141101_create_registrations.rb new file mode 100644 index 0000000..e2683d7 --- /dev/null +++ b/db/migrate/20111104141101_create_registrations.rb @@ -0,0 +1,20 @@ +class CreateRegistrations < ActiveRecord::Migration + def self.up + create_table :registrations, :id => false do |t| + t.string :reg_user + t.string :realm, :limit => '256' + t.string :token, :limit => '256' + t.text :url + t.integer :expires + t.string :network_ip, :limit => '256' + t.string :network_port, :limit => '256' + t.string :network_proto, :limit => '256' + t.string :hostname, :limit => '256' + end + add_index :registrations, [ :reg_user, :realm, :hostname ], :unique => false, :name => 'regindex1' + end + + def self.down + drop_table :registrations + end +end diff --git a/db/migrate/20111106081300_create_fifo_bridge.rb b/db/migrate/20111106081300_create_fifo_bridge.rb new file mode 100644 index 0000000..090d0d7 --- /dev/null +++ b/db/migrate/20111106081300_create_fifo_bridge.rb @@ -0,0 +1,17 @@ +class CreateFifoBridge < ActiveRecord::Migration + def self.up + create_table :fifo_bridge, :id => false do |t| + t.string :fifo_name, :limit => '1024', :null => false + t.string :caller_uuid, :limit => '255', :null => false + t.string :caller_caller_id_name, :limit => '255', :null => false + t.string :caller_caller_id_number, :limit => '255', :null => false + t.string :consumer_uuid, :limit => '255', :null => false + t.string :consumer_outgoing_uuid, :limit => '255' + t.integer :bridge_start + end + end + + def self.down + drop_table :fifo_bridge + end +end diff --git a/db/migrate/20111106082100_create_fifo_callers.rb b/db/migrate/20111106082100_create_fifo_callers.rb new file mode 100644 index 0000000..084e811 --- /dev/null +++ b/db/migrate/20111106082100_create_fifo_callers.rb @@ -0,0 +1,15 @@ +class CreateFifoCallers < ActiveRecord::Migration + def self.up + create_table :fifo_callers, :id => false do |t| + t.string :fifo_name, :limit => '255', :null => false + t.string :uuid, :limit => '255', :null => false + t.string :caller_caller_id_name, :limit => '255' + t.string :caller_caller_id_number, :limit => '255' + t.integer :timestamp + end + end + + def self.down + drop_table :fifo_callers + end +end diff --git a/db/migrate/20111106082500_create_fifo_outbound.rb b/db/migrate/20111106082500_create_fifo_outbound.rb new file mode 100644 index 0000000..0f06855 --- /dev/null +++ b/db/migrate/20111106082500_create_fifo_outbound.rb @@ -0,0 +1,36 @@ +class CreateFifoOutbound < ActiveRecord::Migration + def self.up + create_table :fifo_outbound, :id => false do |t| + t.string :uuid, :limit => '255' + t.string :fifo_name, :limit => '255' + t.string :originate_string, :limit => '255' + t.integer :simo_count + t.integer :use_count + t.integer :timeout + t.integer :lag + t.integer :next_avail, :null => false, :default => 0 + t.integer :expires, :null => false, :default => 0 + t.integer :static, :null => false, :default => 0 + t.integer :outbound_call_count, :null => false, :default => 0 + t.integer :outbound_fail_count, :null => false, :default => 0 + t.string :hostname, :limit => '255' + t.integer :taking_calls, :null => false, :default => 1 + t.string :status, :limit => '255' + t.integer :outbound_call_total_count, :null => false, :default => 0 + t.integer :outbound_fail_total_count, :null => false, :default => 0 + t.integer :active_time, :null => false, :default => 0 + t.integer :inactive_time, :null => false, :default => 0 + t.integer :manual_calls_out_count, :null => false, :default => 0 + t.integer :manual_calls_in_count, :null => false, :default => 0 + t.integer :manual_calls_out_total_count, :null => false, :default => 0 + t.integer :manual_calls_in_total_count, :null => false, :default => 0 + t.integer :ring_count, :null => false, :default => 0 + t.integer :start_time, :null => false, :default => 0 + t.integer :stop_time, :null => false, :default => 0 + end + end + + def self.down + drop_table :fifo_outbound + end +end diff --git a/db/migrate/20111106083400_create_voicemail_msgs.rb b/db/migrate/20111106083400_create_voicemail_msgs.rb new file mode 100644 index 0000000..9b78430 --- /dev/null +++ b/db/migrate/20111106083400_create_voicemail_msgs.rb @@ -0,0 +1,30 @@ +class CreateVoicemailMsgs < ActiveRecord::Migration + def self.up + create_table :voicemail_msgs, :id => false do |t| + t.integer :created_epoch + t.integer :read_epoch + t.string :username, :limit => '255' + t.string :domain, :limit => '255' + t.string :uuid, :limit => '255' + t.string :cid_name, :limit => '255' + t.string :cid_number, :limit => '255' + t.string :in_folder, :limit => '255' + t.string :file_path, :limit => '255' + t.integer :message_len + t.string :flags, :limit => '255' + t.string :read_flags, :limit => '255' + t.string :forwarded_by, :limit => '255' + end + add_index :voicemail_msgs, [ :created_epoch ], :unique => false, :name => 'voicemail_msgs_idx1' + add_index :voicemail_msgs, [ :username ], :unique => false, :name => 'voicemail_msgs_idx2' + add_index :voicemail_msgs, [ :domain ], :unique => false, :name => 'voicemail_msgs_idx3' + add_index :voicemail_msgs, [ :uuid ], :unique => false, :name => 'voicemail_msgs_idx4' + add_index :voicemail_msgs, [ :in_folder ], :unique => false, :name => 'voicemail_msgs_idx5' + add_index :voicemail_msgs, [ :read_flags ], :unique => false, :name => 'voicemail_msgs_idx6' + add_index :voicemail_msgs, [ :forwarded_by ], :unique => false, :name => 'voicemail_msgs_idx7' + end + + def self.down + drop_table :voicemail_msgs + end +end diff --git a/db/migrate/20111106084200_create_voicemail_prefs.rb b/db/migrate/20111106084200_create_voicemail_prefs.rb new file mode 100644 index 0000000..162fa8a --- /dev/null +++ b/db/migrate/20111106084200_create_voicemail_prefs.rb @@ -0,0 +1,17 @@ +class CreateVoicemailPrefs < ActiveRecord::Migration + def self.up + create_table :voicemail_prefs, :id => false do |t| + t.string :username, :limit => '255' + t.string :domain, :limit => '255' + t.string :name_path, :limit => '255' + t.string :greeting_path, :limit => '255' + t.string :password, :limit => '255' + end + add_index :voicemail_prefs, [ :username ], :unique => false, :name => 'voicemail_prefs_idx1' + add_index :voicemail_prefs, [ :domain ], :unique => false, :name => 'voicemail_prefs_idx2' + end + + def self.down + drop_table :voicemail_prefs + end +end diff --git a/db/migrate/20111106162141_add_phoneable_to_phone.rb b/db/migrate/20111106162141_add_phoneable_to_phone.rb new file mode 100644 index 0000000..7b60ac6 --- /dev/null +++ b/db/migrate/20111106162141_add_phoneable_to_phone.rb @@ -0,0 +1,6 @@ +class AddPhoneableToPhone < ActiveRecord::Migration + def change + add_column :phones, :phoneable_type, :string + add_column :phones, :phoneable_id, :integer + end +end diff --git a/db/migrate/20111119124303_add_hot_deskable_to_phone.rb b/db/migrate/20111119124303_add_hot_deskable_to_phone.rb new file mode 100644 index 0000000..620fb1b --- /dev/null +++ b/db/migrate/20111119124303_add_hot_deskable_to_phone.rb @@ -0,0 +1,5 @@ +class AddHotDeskableToPhone < ActiveRecord::Migration + def change + add_column :phones, :hot_deskable, :boolean + end +end diff --git a/db/migrate/20111119153939_add_state_to_phone_number.rb b/db/migrate/20111119153939_add_state_to_phone_number.rb new file mode 100644 index 0000000..69e650a --- /dev/null +++ b/db/migrate/20111119153939_add_state_to_phone_number.rb @@ -0,0 +1,5 @@ +class AddStateToPhoneNumber < ActiveRecord::Migration + def change + add_column :phone_numbers, :state, :string + end +end diff --git a/db/migrate/20111120110056_add_value_of_to_s_to_phone_number.rb b/db/migrate/20111120110056_add_value_of_to_s_to_phone_number.rb new file mode 100644 index 0000000..39b13fc --- /dev/null +++ b/db/migrate/20111120110056_add_value_of_to_s_to_phone_number.rb @@ -0,0 +1,6 @@ +class AddValueOfToSToPhoneNumber < ActiveRecord::Migration + def change + add_column :phone_numbers, :value_of_to_s, :string + add_column :sip_accounts, :value_of_to_s, :string + end +end diff --git a/db/migrate/20111120112448_add_value_of_to_s_to_phone_book_entry.rb b/db/migrate/20111120112448_add_value_of_to_s_to_phone_book_entry.rb new file mode 100644 index 0000000..8658453 --- /dev/null +++ b/db/migrate/20111120112448_add_value_of_to_s_to_phone_book_entry.rb @@ -0,0 +1,5 @@ +class AddValueOfToSToPhoneBookEntry < ActiveRecord::Migration + def change + add_column :phone_book_entries, :value_of_to_s, :string + end +end diff --git a/db/migrate/20111120180010_create_call_forward_cases.rb b/db/migrate/20111120180010_create_call_forward_cases.rb new file mode 100644 index 0000000..d29c246 --- /dev/null +++ b/db/migrate/20111120180010_create_call_forward_cases.rb @@ -0,0 +1,21 @@ +class CreateCallForwardCases < ActiveRecord::Migration + + def self.up + create_table :call_forward_cases do |t| + t.string :value + t.timestamps + end + add_index( :call_forward_cases, :value, { + :name => "call_forward_cases_value_index", + :unique => true, + }) + end + + def self.down + remove_index( :call_forward_cases, { + :name => "call_forward_cases_value_index", + }) + drop_table :call_forward_cases + end + +end diff --git a/db/migrate/20111120180020_create_call_forwards.rb b/db/migrate/20111120180020_create_call_forwards.rb new file mode 100644 index 0000000..e8795eb --- /dev/null +++ b/db/migrate/20111120180020_create_call_forwards.rb @@ -0,0 +1,25 @@ +class CreateCallForwards < ActiveRecord::Migration + + def self.up + create_table :call_forwards do |t| + t.integer :sip_account_id + t.integer :call_forward_case_id + t.integer :timeout + t.string :destination + t.string :source + t.boolean :active + t.timestamps + end + add_index( :call_forwards, :sip_account_id, { + :name => "call_forwards_sip_account_index", + }) + end + + def self.down + remove_index( :call_forwards, { + :name => "call_forwards_sip_account_index", + }) + drop_table :call_forwards + end + +end diff --git a/db/migrate/20111121105916_add_sip_domain_id_to_sip_accounts.rb b/db/migrate/20111121105916_add_sip_domain_id_to_sip_accounts.rb new file mode 100644 index 0000000..86f7743 --- /dev/null +++ b/db/migrate/20111121105916_add_sip_domain_id_to_sip_accounts.rb @@ -0,0 +1,5 @@ +class AddSipDomainIdToSipAccounts < ActiveRecord::Migration + def change + add_column :sip_accounts, :sip_domain_id, :integer + end +end diff --git a/db/migrate/20111121152220_create_conferences.rb b/db/migrate/20111121152220_create_conferences.rb new file mode 100644 index 0000000..fe32ee8 --- /dev/null +++ b/db/migrate/20111121152220_create_conferences.rb @@ -0,0 +1,20 @@ +class CreateConferences < ActiveRecord::Migration + def self.up + create_table :conferences do |t| + t.string :name + t.datetime :start + t.datetime :end + t.text :description + t.string :pin + t.text :state + t.boolean :open_for_anybody + t.string :conferenceable_type + t.integer :conferenceable_id + t.timestamps + end + end + + def self.down + drop_table :conferences + end +end diff --git a/db/migrate/20111122172513_create_conference_invitees.rb b/db/migrate/20111122172513_create_conference_invitees.rb new file mode 100644 index 0000000..3ec293e --- /dev/null +++ b/db/migrate/20111122172513_create_conference_invitees.rb @@ -0,0 +1,16 @@ +class CreateConferenceInvitees < ActiveRecord::Migration + def self.up + create_table :conference_invitees do |t| + t.integer :conference_id + t.integer :phone_book_entry_id + t.string :pin + t.boolean :speaker + t.boolean :moderator + t.timestamps + end + end + + def self.down + drop_table :conference_invitees + end +end diff --git a/db/migrate/20111123124113_create_phone_number_ranges.rb b/db/migrate/20111123124113_create_phone_number_ranges.rb new file mode 100644 index 0000000..e20014b --- /dev/null +++ b/db/migrate/20111123124113_create_phone_number_ranges.rb @@ -0,0 +1,14 @@ +class CreatePhoneNumberRanges < ActiveRecord::Migration + def self.up + create_table :phone_number_ranges do |t| + t.integer :tenant_id + t.string :name + t.text :description + t.timestamps + end + end + + def self.down + drop_table :phone_number_ranges + end +end diff --git a/db/migrate/20111123132658_create_languages.rb b/db/migrate/20111123132658_create_languages.rb new file mode 100644 index 0000000..48a5da5 --- /dev/null +++ b/db/migrate/20111123132658_create_languages.rb @@ -0,0 +1,10 @@ +class CreateLanguages < ActiveRecord::Migration + def change + create_table :languages do |t| + t.string :name + t.string :code + + t.timestamps + end + end +end diff --git a/db/migrate/20111123141120_create_gemeinschaft_setups.rb b/db/migrate/20111123141120_create_gemeinschaft_setups.rb new file mode 100644 index 0000000..cc17e7a --- /dev/null +++ b/db/migrate/20111123141120_create_gemeinschaft_setups.rb @@ -0,0 +1,19 @@ +class CreateGemeinschaftSetups < ActiveRecord::Migration + def self.up + create_table :gemeinschaft_setups do |t| + t.integer :tenant_id + t.integer :user_id + t.integer :sip_domain_id + t.integer :default_extension_length + t.integer :country_id + t.integer :language_id + t.string :human_area_code + t.integer :area_code_id + t.timestamps + end + end + + def self.down + drop_table :gemeinschaft_setups + end +end diff --git a/db/migrate/20111123211631_rename_columns_of_phone_number_range.rb b/db/migrate/20111123211631_rename_columns_of_phone_number_range.rb new file mode 100644 index 0000000..f7e129a --- /dev/null +++ b/db/migrate/20111123211631_rename_columns_of_phone_number_range.rb @@ -0,0 +1,8 @@ +class RenameColumnsOfPhoneNumberRange < ActiveRecord::Migration + def change + # I'm not going to revert all existing entries here. + add_column :phone_number_ranges, :phone_number_rangeable_type, :string + add_column :phone_number_ranges, :phone_number_rangeable_id, :integer + remove_column :phone_number_ranges, :tenant_id + end +end diff --git a/db/migrate/20111125124957_add_phone_number_id_to_call_forward.rb b/db/migrate/20111125124957_add_phone_number_id_to_call_forward.rb new file mode 100644 index 0000000..8d5f02a --- /dev/null +++ b/db/migrate/20111125124957_add_phone_number_id_to_call_forward.rb @@ -0,0 +1,9 @@ +class AddPhoneNumberIdToCallForward < ActiveRecord::Migration + + def change + remove_column :call_forwards, :sip_account_id + add_column :call_forwards, :phone_number_id, :integer + add_index( :call_forwards, :phone_number_id ) + end + +end diff --git a/db/migrate/20111125150925_add_hops_to_call_forward.rb b/db/migrate/20111125150925_add_hops_to_call_forward.rb new file mode 100644 index 0000000..d7d580c --- /dev/null +++ b/db/migrate/20111125150925_add_hops_to_call_forward.rb @@ -0,0 +1,5 @@ +class AddHopsToCallForward < ActiveRecord::Migration + def change + add_column :call_forwards, :hops, :integer + end +end diff --git a/db/migrate/20111126180826_add_depth_to_call_forward.rb b/db/migrate/20111126180826_add_depth_to_call_forward.rb new file mode 100644 index 0000000..4cb44b2 --- /dev/null +++ b/db/migrate/20111126180826_add_depth_to_call_forward.rb @@ -0,0 +1,5 @@ +class AddDepthToCallForward < ActiveRecord::Migration + def change + rename_column :call_forwards, :hops, :depth + end +end diff --git a/db/migrate/20111127112235_add_internal_extension_ranges_to_gemeinschaft_setup.rb b/db/migrate/20111127112235_add_internal_extension_ranges_to_gemeinschaft_setup.rb new file mode 100644 index 0000000..8f4643c --- /dev/null +++ b/db/migrate/20111127112235_add_internal_extension_ranges_to_gemeinschaft_setup.rb @@ -0,0 +1,6 @@ +class AddInternalExtensionRangesToGemeinschaftSetup < ActiveRecord::Migration + def change + add_column :gemeinschaft_setups, :internal_extension_ranges, :string + remove_column :gemeinschaft_setups, :default_extension_length + end +end diff --git a/db/migrate/20111128100949_add_pin_to_user.rb b/db/migrate/20111128100949_add_pin_to_user.rb new file mode 100644 index 0000000..d193452 --- /dev/null +++ b/db/migrate/20111128100949_add_pin_to_user.rb @@ -0,0 +1,6 @@ +class AddPinToUser < ActiveRecord::Migration + def change + add_column :users, :pin_salt, :string + add_column :users, :pin_hash, :string + end +end diff --git a/db/migrate/20111203164007_add_tenant_id_to_sip_account.rb b/db/migrate/20111203164007_add_tenant_id_to_sip_account.rb new file mode 100644 index 0000000..2efea2b --- /dev/null +++ b/db/migrate/20111203164007_add_tenant_id_to_sip_account.rb @@ -0,0 +1,5 @@ +class AddTenantIdToSipAccount < ActiveRecord::Migration + def change + add_column :sip_accounts, :tenant_id, :integer + end +end diff --git a/db/migrate/20111210124447_remove_sip_domain_id_from_sip_account.rb b/db/migrate/20111210124447_remove_sip_domain_id_from_sip_account.rb new file mode 100644 index 0000000..eeb5afe --- /dev/null +++ b/db/migrate/20111210124447_remove_sip_domain_id_from_sip_account.rb @@ -0,0 +1,9 @@ +class RemoveSipDomainIdFromSipAccount < ActiveRecord::Migration + def up + remove_column :sip_accounts, :sip_domain_id + end + + def down + add_column :sip_accounts, :sip_domain_id, :integer + end +end diff --git a/db/migrate/20111211184853_remove_state_from_sip_account.rb b/db/migrate/20111211184853_remove_state_from_sip_account.rb new file mode 100644 index 0000000..e0ed8ed --- /dev/null +++ b/db/migrate/20111211184853_remove_state_from_sip_account.rb @@ -0,0 +1,9 @@ +class RemoveStateFromSipAccount < ActiveRecord::Migration + def up + remove_column :sip_accounts, :state + end + + def down + add_column :sip_accounts, :state, :string + end +end diff --git a/db/migrate/20111211192350_add_sip_domain_id_to_sip_account.rb b/db/migrate/20111211192350_add_sip_domain_id_to_sip_account.rb new file mode 100644 index 0000000..5344247 --- /dev/null +++ b/db/migrate/20111211192350_add_sip_domain_id_to_sip_account.rb @@ -0,0 +1,5 @@ +class AddSipDomainIdToSipAccount < ActiveRecord::Migration + def change + add_column :sip_accounts, :sip_domain_id, :integer + end +end diff --git a/db/migrate/20111212193532_create_faxes.rb b/db/migrate/20111212193532_create_faxes.rb new file mode 100644 index 0000000..d713f2d --- /dev/null +++ b/db/migrate/20111212193532_create_faxes.rb @@ -0,0 +1,19 @@ +class CreateFaxes < ActiveRecord::Migration + def self.up + create_table :faxes do |t| + t.boolean :inbound + t.string :pdf + t.integer :faxable_id + t.string :faxable_type + t.string :state + t.integer :number_of_pages + t.integer :transmission_time + t.datetime :sent_at + t.timestamps + end + end + + def self.down + drop_table :faxes + end +end diff --git a/db/migrate/20111212201847_create_fax_pages.rb b/db/migrate/20111212201847_create_fax_pages.rb new file mode 100644 index 0000000..de8c708 --- /dev/null +++ b/db/migrate/20111212201847_create_fax_pages.rb @@ -0,0 +1,13 @@ +class CreateFaxPages < ActiveRecord::Migration + def self.up + create_table :fax_pages do |t| + t.string :page + t.integer :position + t.timestamps + end + end + + def self.down + drop_table :fax_pages + end +end diff --git a/db/migrate/20111213122843_add_external_numbers_to_gemeinschaft_setup.rb b/db/migrate/20111213122843_add_external_numbers_to_gemeinschaft_setup.rb new file mode 100644 index 0000000..056f532 --- /dev/null +++ b/db/migrate/20111213122843_add_external_numbers_to_gemeinschaft_setup.rb @@ -0,0 +1,5 @@ +class AddExternalNumbersToGemeinschaftSetup < ActiveRecord::Migration + def change + add_column :gemeinschaft_setups, :external_numbers, :string + end +end diff --git a/db/migrate/20111213170837_add_language_id_to_tenant.rb b/db/migrate/20111213170837_add_language_id_to_tenant.rb new file mode 100644 index 0000000..a2bf018 --- /dev/null +++ b/db/migrate/20111213170837_add_language_id_to_tenant.rb @@ -0,0 +1,7 @@ +class AddLanguageIdToTenant < ActiveRecord::Migration + def change + add_column :tenants, :language_id, :integer + add_column :tenants, :internal_extension_ranges, :string + add_column :tenants, :did_list, :string + end +end diff --git a/db/migrate/20111213182546_remove_tenant_id_from_gemeinschaft_setup.rb b/db/migrate/20111213182546_remove_tenant_id_from_gemeinschaft_setup.rb new file mode 100644 index 0000000..d91b195 --- /dev/null +++ b/db/migrate/20111213182546_remove_tenant_id_from_gemeinschaft_setup.rb @@ -0,0 +1,17 @@ +class RemoveTenantIdFromGemeinschaftSetup < ActiveRecord::Migration + def up + remove_column :gemeinschaft_setups, :tenant_id + remove_column :gemeinschaft_setups, :human_area_code + remove_column :gemeinschaft_setups, :area_code_id + remove_column :gemeinschaft_setups, :internal_extension_ranges + remove_column :gemeinschaft_setups, :external_numbers + end + + def down + add_column :gemeinschaft_setups, :external_numbers, :string + add_column :gemeinschaft_setups, :internal_extension_ranges, :string + add_column :gemeinschaft_setups, :area_code_id, :integer + add_column :gemeinschaft_setups, :human_area_code, :string + add_column :gemeinschaft_setups, :tenant_id, :integer + end +end diff --git a/db/migrate/20111214182903_add_max_members_to_conference.rb b/db/migrate/20111214182903_add_max_members_to_conference.rb new file mode 100644 index 0000000..c7f1013 --- /dev/null +++ b/db/migrate/20111214182903_add_max_members_to_conference.rb @@ -0,0 +1,5 @@ +class AddMaxMembersToConference < ActiveRecord::Migration + def change + add_column :conferences, :max_members, :integer + end +end diff --git a/db/migrate/20111215172021_create_fax_accounts.rb b/db/migrate/20111215172021_create_fax_accounts.rb new file mode 100644 index 0000000..1039585 --- /dev/null +++ b/db/migrate/20111215172021_create_fax_accounts.rb @@ -0,0 +1,16 @@ +class CreateFaxAccounts < ActiveRecord::Migration + def self.up + create_table :fax_accounts do |t| + t.string :fax_accountable_type + t.integer :fax_accountable_id + t.string :name + t.string :email + t.boolean :delete_after_email + t.timestamps + end + end + + def self.down + drop_table :fax_accounts + end +end diff --git a/db/migrate/20111216112614_add_information_to_fax.rb b/db/migrate/20111216112614_add_information_to_fax.rb new file mode 100644 index 0000000..d5c03da --- /dev/null +++ b/db/migrate/20111216112614_add_information_to_fax.rb @@ -0,0 +1,19 @@ +class AddInformationToFax < ActiveRecord::Migration + def change + remove_column :faxes, :number_of_pages + add_column :faxes, :document_total_pages, :integer + add_column :faxes, :document_transferred_pages, :integer + add_column :faxes, :ecm_requested, :boolean + add_column :faxes, :ecm_used, :boolean + add_column :faxes, :image_resolution, :string + add_column :faxes, :image_size, :string + add_column :faxes, :local_station_id, :string + add_column :faxes, :result_code, :integer + add_column :faxes, :result_text, :string + add_column :faxes, :remote_station_id, :string + add_column :faxes, :success, :boolean + add_column :faxes, :transfer_rate, :integer + add_column :faxes, :t38_gateway_format, :string + add_column :faxes, :t38_peer, :string + end +end diff --git a/db/migrate/20111217162506_add_fax_to_faxes.rb b/db/migrate/20111217162506_add_fax_to_faxes.rb new file mode 100644 index 0000000..fe5d7b6 --- /dev/null +++ b/db/migrate/20111217162506_add_fax_to_faxes.rb @@ -0,0 +1,8 @@ +class AddFaxToFaxes < ActiveRecord::Migration + def change + remove_column :faxes, :pdf + remove_column :fax_pages, :page + add_column :faxes, :fax, :string + add_column :fax_pages, :fax_page, :string + end +end diff --git a/db/migrate/20111218085222_create_fax_documents.rb b/db/migrate/20111218085222_create_fax_documents.rb new file mode 100644 index 0000000..00bf73f --- /dev/null +++ b/db/migrate/20111218085222_create_fax_documents.rb @@ -0,0 +1,32 @@ +class CreateFaxDocuments < ActiveRecord::Migration + def self.up + create_table :fax_documents do |t| + t.string :fax_documentable_type + t.integer :fax_documentable_id + t.boolean :inbound + t.string :state + t.integer :transmission_time + t.datetime :sent_at + t.integer :document_total_pages + t.integer :document_transferred_pages + t.boolean :ecm_requested + t.boolean :ecm_used + t.string :image_resolution + t.string :image_size + t.string :local_station_id + t.integer :result_code + t.string :result_text + t.string :remote_station_id + t.boolean :success + t.integer :transfer_rate + t.string :t38_gateway_format + t.string :t38_peer + t.string :document + t.timestamps + end + end + + def self.down + drop_table :fax_documents + end +end diff --git a/db/migrate/20111218100048_add_fax_account_id_to_fax_documents.rb b/db/migrate/20111218100048_add_fax_account_id_to_fax_documents.rb new file mode 100644 index 0000000..d7fb7a0 --- /dev/null +++ b/db/migrate/20111218100048_add_fax_account_id_to_fax_documents.rb @@ -0,0 +1,5 @@ +class AddFaxAccountIdToFaxDocuments < ActiveRecord::Migration + def change + add_column :fax_documents, :fax_account_id, :integer + end +end diff --git a/db/migrate/20111218143152_add_tenant_id_to_fax_account.rb b/db/migrate/20111218143152_add_tenant_id_to_fax_account.rb new file mode 100644 index 0000000..1c352b8 --- /dev/null +++ b/db/migrate/20111218143152_add_tenant_id_to_fax_account.rb @@ -0,0 +1,6 @@ +class AddTenantIdToFaxAccount < ActiveRecord::Migration + def change + add_column :fax_accounts, :tenant_id, :integer + add_column :fax_accounts, :station_id, :string + end +end diff --git a/db/migrate/20111218145713_add_days_till_auto_delete_to_fax_accounts.rb b/db/migrate/20111218145713_add_days_till_auto_delete_to_fax_accounts.rb new file mode 100644 index 0000000..0df0965 --- /dev/null +++ b/db/migrate/20111218145713_add_days_till_auto_delete_to_fax_accounts.rb @@ -0,0 +1,6 @@ +class AddDaysTillAutoDeleteToFaxAccounts < ActiveRecord::Migration + def change + add_column :fax_accounts, :days_till_auto_delete, :integer + remove_column :fax_accounts, :delete_after_email + end +end diff --git a/db/migrate/20111219131952_create_fax_thumbnails.rb b/db/migrate/20111219131952_create_fax_thumbnails.rb new file mode 100644 index 0000000..3474e0a --- /dev/null +++ b/db/migrate/20111219131952_create_fax_thumbnails.rb @@ -0,0 +1,11 @@ +class CreateFaxThumbnails < ActiveRecord::Migration + def change + create_table :fax_thumbnails do |t| + t.integer :fax_document_id + t.integer :position + t.string :thumbnail + + t.timestamps + end + end +end diff --git a/db/migrate/20111219163426_remove_fax_documentable_from_fax_documents.rb b/db/migrate/20111219163426_remove_fax_documentable_from_fax_documents.rb new file mode 100644 index 0000000..f8a55ec --- /dev/null +++ b/db/migrate/20111219163426_remove_fax_documentable_from_fax_documents.rb @@ -0,0 +1,11 @@ +class RemoveFaxDocumentableFromFaxDocuments < ActiveRecord::Migration + def up + remove_column :fax_documents, :fax_documentable_type + remove_column :fax_documents, :fax_documentable_id + end + + def down + add_column :fax_documents, :fax_documentable_id, :integer + add_column :fax_documents, :fax_documentable_type, :string + end +end diff --git a/db/migrate/20111220151633_add_caller_id_number_to_fax_documents.rb b/db/migrate/20111220151633_add_caller_id_number_to_fax_documents.rb new file mode 100644 index 0000000..fe1461e --- /dev/null +++ b/db/migrate/20111220151633_add_caller_id_number_to_fax_documents.rb @@ -0,0 +1,8 @@ +class AddCallerIdNumberToFaxDocuments < ActiveRecord::Migration + def change + add_column :fax_documents, :caller_id_number, :string + add_column :fax_documents, :caller_id_name, :string + remove_column :fax_documents, :t38_gateway_format + remove_column :fax_documents, :t38_peer + end +end diff --git a/db/migrate/20111222173947_remove_result_text_from_fax_documents.rb b/db/migrate/20111222173947_remove_result_text_from_fax_documents.rb new file mode 100644 index 0000000..46acafd --- /dev/null +++ b/db/migrate/20111222173947_remove_result_text_from_fax_documents.rb @@ -0,0 +1,13 @@ +class RemoveResultTextFromFaxDocuments < ActiveRecord::Migration + def up + remove_column :fax_documents, :result_text + add_column :fax_documents, :retry_counter, :integer + add_column :fax_accounts, :retries, :integer + end + + def down + add_column :fax_documents, :result_text, :string + remove_column :fax_documents, :retry_counter + remove_column :fax_accounts, :retries + end +end diff --git a/db/migrate/20111222184203_add_tiff_to_fax_documents.rb b/db/migrate/20111222184203_add_tiff_to_fax_documents.rb new file mode 100644 index 0000000..523c1c0 --- /dev/null +++ b/db/migrate/20111222184203_add_tiff_to_fax_documents.rb @@ -0,0 +1,5 @@ +class AddTiffToFaxDocuments < ActiveRecord::Migration + def change + add_column :fax_documents, :tiff, :string + end +end diff --git a/db/migrate/20111222184912_add_resolution_to_fax_documents.rb b/db/migrate/20111222184912_add_resolution_to_fax_documents.rb new file mode 100644 index 0000000..48631aa --- /dev/null +++ b/db/migrate/20111222184912_add_resolution_to_fax_documents.rb @@ -0,0 +1,5 @@ +class AddResolutionToFaxDocuments < ActiveRecord::Migration + def change + add_column :fax_documents, :fax_resolution_id, :integer + end +end diff --git a/db/migrate/20111222185426_create_fax_resolutions.rb b/db/migrate/20111222185426_create_fax_resolutions.rb new file mode 100644 index 0000000..6782021 --- /dev/null +++ b/db/migrate/20111222185426_create_fax_resolutions.rb @@ -0,0 +1,11 @@ +class CreateFaxResolutions < ActiveRecord::Migration + def change + create_table :fax_resolutions do |t| + t.string :name + t.string :resolution_value + t.integer :position + + t.timestamps + end + end +end diff --git a/db/migrate/20111226180221_create_delayed_jobs.rb b/db/migrate/20111226180221_create_delayed_jobs.rb new file mode 100644 index 0000000..ac579df --- /dev/null +++ b/db/migrate/20111226180221_create_delayed_jobs.rb @@ -0,0 +1,21 @@ +class CreateDelayedJobs < ActiveRecord::Migration + def self.up + create_table :delayed_jobs, :force => true do |table| + table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue + table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually. + table.text :handler # YAML-encoded string of the object that will do work + table.text :last_error # reason for last failure (See Note below) + table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future. + table.datetime :locked_at # Set when a client is working on this object + table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead) + table.string :locked_by # Who is working on this object (if locked) + table.timestamps + end + + add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority' + end + + def self.down + drop_table :delayed_jobs + end +end
\ No newline at end of file diff --git a/db/migrate/20111228194037_create_phone_sip_accounts.rb b/db/migrate/20111228194037_create_phone_sip_accounts.rb new file mode 100644 index 0000000..7fd5e39 --- /dev/null +++ b/db/migrate/20111228194037_create_phone_sip_accounts.rb @@ -0,0 +1,14 @@ +class CreatePhoneSipAccounts < ActiveRecord::Migration + def self.up + create_table :phone_sip_accounts do |t| + t.integer :phone_id + t.integer :sip_account_id + t.integer :position + t.timestamps + end + end + + def self.down + drop_table :phone_sip_accounts + end +end diff --git a/db/migrate/20120104142556_create_ringtones.rb b/db/migrate/20120104142556_create_ringtones.rb new file mode 100644 index 0000000..b671375 --- /dev/null +++ b/db/migrate/20120104142556_create_ringtones.rb @@ -0,0 +1,15 @@ +class CreateRingtones < ActiveRecord::Migration + def self.up + create_table :ringtones do |t| + t.string :ringtoneable_type + t.integer :ringtoneable_id + t.string :audio + t.integer :bellcore_id + t.timestamps + end + end + + def self.down + drop_table :ringtones + end +end diff --git a/db/migrate/20120112094631_add_announcement_of_a_new_member_to_conference.rb b/db/migrate/20120112094631_add_announcement_of_a_new_member_to_conference.rb new file mode 100644 index 0000000..104f419 --- /dev/null +++ b/db/migrate/20120112094631_add_announcement_of_a_new_member_to_conference.rb @@ -0,0 +1,6 @@ +class AddAnnouncementOfANewMemberToConference < ActiveRecord::Migration + def change + add_column :conferences, :announce_new_member_by_name, :boolean + add_column :conferences, :announce_left_member_by_name, :boolean + end +end diff --git a/db/migrate/20120118195740_create_softkeys.rb b/db/migrate/20120118195740_create_softkeys.rb new file mode 100644 index 0000000..a8d7d64 --- /dev/null +++ b/db/migrate/20120118195740_create_softkeys.rb @@ -0,0 +1,17 @@ +class CreateSoftkeys < ActiveRecord::Migration + def self.up + create_table :softkeys do |t| + t.integer :phone_id + t.string :name + t.string :function + t.string :number + t.string :label + t.integer :position + t.timestamps + end + end + + def self.down + drop_table :softkeys + end +end diff --git a/db/migrate/20120119103852_add_sip_account_to_softkeys.rb b/db/migrate/20120119103852_add_sip_account_to_softkeys.rb new file mode 100644 index 0000000..74bfaee --- /dev/null +++ b/db/migrate/20120119103852_add_sip_account_to_softkeys.rb @@ -0,0 +1,6 @@ +class AddSipAccountToSoftkeys < ActiveRecord::Migration + def change + add_column :softkeys, :sip_account_id, :integer + remove_column :softkeys, :phone_id + end +end diff --git a/db/migrate/20120119111944_add_call_waiting_to_sip_accounts.rb b/db/migrate/20120119111944_add_call_waiting_to_sip_accounts.rb new file mode 100644 index 0000000..f23e9a0 --- /dev/null +++ b/db/migrate/20120119111944_add_call_waiting_to_sip_accounts.rb @@ -0,0 +1,5 @@ +class AddCallWaitingToSipAccounts < ActiveRecord::Migration + def change + add_column :sip_accounts, :call_waiting, :boolean + end +end diff --git a/db/migrate/20120119114329_add_clir_and_clip_phone_number_to_sip_accounts.rb b/db/migrate/20120119114329_add_clir_and_clip_phone_number_to_sip_accounts.rb new file mode 100644 index 0000000..d21916c --- /dev/null +++ b/db/migrate/20120119114329_add_clir_and_clip_phone_number_to_sip_accounts.rb @@ -0,0 +1,6 @@ +class AddClirAndClipPhoneNumberToSipAccounts < ActiveRecord::Migration + def change + add_column :sip_accounts, :clir, :boolean + add_column :sip_accounts, :clip_phone_number_id, :integer + end +end diff --git a/db/migrate/20120119154422_fax_defaults.rb b/db/migrate/20120119154422_fax_defaults.rb new file mode 100644 index 0000000..76ccb47 --- /dev/null +++ b/db/migrate/20120119154422_fax_defaults.rb @@ -0,0 +1,15 @@ +# ruby encoding: utf-8 + +class FaxDefaults < ActiveRecord::Migration + def up + ################################################################ + # Fax resolutions + ################################################################ + FaxResolution.create(:name => 'Standard', :resolution_value => '204x98') + FaxResolution.create(:name => 'Fine', :resolution_value => '204x196') + end + + def down + FaxResolution.destroy_all + end +end diff --git a/db/migrate/20120119154633_language_defaults.rb b/db/migrate/20120119154633_language_defaults.rb new file mode 100644 index 0000000..a67b641 --- /dev/null +++ b/db/migrate/20120119154633_language_defaults.rb @@ -0,0 +1,15 @@ +# ruby encoding: utf-8 + +class LanguageDefaults < ActiveRecord::Migration + def up + ################################################################ + # Languages + ################################################################ + Language.create(:name => 'Deutsch', :code => 'de') + Language.create(:name => 'English', :code => 'en') + end + + def down + Language.destroy_all + end +end diff --git a/db/migrate/20120119154759_country_defaults.rb b/db/migrate/20120119154759_country_defaults.rb new file mode 100644 index 0000000..dd8fd43 --- /dev/null +++ b/db/migrate/20120119154759_country_defaults.rb @@ -0,0 +1,254 @@ +# ruby encoding: utf-8 + +class CountryDefaults < ActiveRecord::Migration + def up + ################################################################ + # Countries + ################################################################ + + ActiveRecord::Base.transaction do + Country.create(:name => "American Samoa", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Anguilla", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Antigua and Barbuda", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Bahamas", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Barbados", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Bermuda", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "British Virgin Islands", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Canada", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Cayman Islands", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Dominica", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Dominican Republic", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Grenada", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Guam", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Jamaica", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Montserrat", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Northern Mariana Islands", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Puerto Rico", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Saint Kitts and Nevis", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Saint Lucia", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Saint Vincent and the Grenadines", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Trinidad and Tobago", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Turks and Caicos Islands", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "United States of America", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "United States Virgin Islands", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Egypt", :country_code => "20", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Morocco", :country_code => "212", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Algeria", :country_code => "213", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Tunisia", :country_code => "216", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Libya", :country_code => "218", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Gambia", :country_code => "220", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Senegal", :country_code => "221", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Mauritania", :country_code => "222", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Mali", :country_code => "223", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Guinea", :country_code => "224", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Côte d'Ivoire", :country_code => "225", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Burkina Faso", :country_code => "226", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Niger", :country_code => "227", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Togolese Republic", :country_code => "228", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Benin", :country_code => "229", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Mauritius", :country_code => "230", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Liberia", :country_code => "231", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Sierra Leone", :country_code => "232", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Ghana", :country_code => "233", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Nigeria", :country_code => "234", :international_call_prefix => "009", :trunk_prefix => "" ) + Country.create(:name => "Chad", :country_code => "235", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Central African Republic", :country_code => "236", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Cameroon", :country_code => "237", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Cape Verde", :country_code => "238", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Sao Tome and Principe", :country_code => "239", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Equatorial Guinea", :country_code => "240", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Gabonese Republic", :country_code => "241", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Congo", :country_code => "242", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Democratic Republic of the Congo", :country_code => "243", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Angola", :country_code => "244", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Guinea-Bissau", :country_code => "245", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Diego Garcia", :country_code => "246", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Ascension", :country_code => "247", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Seychelles", :country_code => "248", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Sudan", :country_code => "249", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Rwanda", :country_code => "250", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Ethiopia", :country_code => "251", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Somali Democratic Republic", :country_code => "252", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Djibouti", :country_code => "253", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Kenya", :country_code => "254", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Tanzania", :country_code => "255", :international_call_prefix => "000", :trunk_prefix => "" ) + Country.create(:name => "Uganda", :country_code => "256", :international_call_prefix => "000", :trunk_prefix => "" ) + Country.create(:name => "Burundi", :country_code => "257", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Mozambique", :country_code => "258", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Zambia", :country_code => "260", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Madagascar", :country_code => "261", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Reunion", :country_code => "262", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Zimbabwe", :country_code => "263", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Namibia", :country_code => "264", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Malawi", :country_code => "265", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Lesotho", :country_code => "266", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Botswana", :country_code => "267", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Swaziland", :country_code => "268", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Comoros", :country_code => "269", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Mayotte", :country_code => "269", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "South Africa", :country_code => "27", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Saint Helena", :country_code => "290", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Eritrea", :country_code => "291", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Aruba", :country_code => "297", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Faroe Islands", :country_code => "298", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Greenland", :country_code => "299", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Greece", :country_code => "30", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Netherlands", :country_code => "31", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Belgium", :country_code => "32", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "France", :country_code => "33", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Spain", :country_code => "34", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Gibraltar", :country_code => "350", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Portugal", :country_code => "351", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Luxembourg", :country_code => "352", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Ireland", :country_code => "353", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Iceland", :country_code => "354", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Albania", :country_code => "355", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Malta", :country_code => "356", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Cyprus", :country_code => "357", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Finland", :country_code => "358", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Bulgaria", :country_code => "359", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Hungary", :country_code => "36", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Lithuania", :country_code => "370", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Latvia", :country_code => "371", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Estonia", :country_code => "372", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Moldova", :country_code => "373", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Armenia", :country_code => "374", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Belarus", :country_code => "375", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Andorra", :country_code => "376", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Monaco", :country_code => "377", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "San Marino", :country_code => "378", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Vatican City State", :country_code => "379", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Ukraine", :country_code => "380", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Serbia and Montenegro", :country_code => "381", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Croatia", :country_code => "385", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Slovenia", :country_code => "386", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Bosnia and Herzegovina", :country_code => "387", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Group of countries, shared code", :country_code => "388", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "The Former Yugoslav Republic of Macedonia", :country_code => "389", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Italy", :country_code => "39", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Vatican City State", :country_code => "39", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Romania", :country_code => "40", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Switzerland", :country_code => "41", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Czech Republic", :country_code => "420", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Slovak Republic", :country_code => "421", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Liechtenstein", :country_code => "423", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Austria", :country_code => "43", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "United Kingdom of Great Britain and Northern Ireland", :country_code => "44", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Denmark", :country_code => "45", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Sweden", :country_code => "46", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Norway", :country_code => "47", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Poland", :country_code => "48", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Germany", :country_code => "49", :international_call_prefix => "00", :trunk_prefix => "0" ) + Country.create(:name => "Falkland Islands", :country_code => "500", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Belize", :country_code => "501", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Guatemala", :country_code => "502", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "El Salvador", :country_code => "503", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Honduras", :country_code => "504", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Nicaragua", :country_code => "505", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Costa Rica", :country_code => "506", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Panama", :country_code => "507", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Saint Pierre and Miquelon", :country_code => "508", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Haiti", :country_code => "509", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Peru", :country_code => "51", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Mexico", :country_code => "52", :international_call_prefix => "00", :trunk_prefix => "01" ) + Country.create(:name => "Cuba", :country_code => "53", :international_call_prefix => "119", :trunk_prefix => "" ) + Country.create(:name => "Argentine Republic", :country_code => "54", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Brazil", :country_code => "55", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Chile", :country_code => "56", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Colombia", :country_code => "57", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Venezuela", :country_code => "58", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Guadeloupe", :country_code => "590", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Bolivia", :country_code => "591", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Guyana", :country_code => "592", :international_call_prefix => "001", :trunk_prefix => "" ) + Country.create(:name => "Ecuador", :country_code => "593", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "French Guiana", :country_code => "594", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Paraguay", :country_code => "595", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Martinique", :country_code => "596", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Suriname", :country_code => "597", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Uruguay", :country_code => "598", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Netherlands Antilles", :country_code => "599", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Malaysia", :country_code => "60", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Australia", :country_code => "61", :international_call_prefix => "0011", :trunk_prefix => "" ) + Country.create(:name => "Indonesia", :country_code => "62", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Philippines", :country_code => "63", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "New Zealand", :country_code => "64", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Singapore", :country_code => "65", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Thailand", :country_code => "66", :international_call_prefix => "001", :trunk_prefix => "" ) + Country.create(:name => "Democratic Republic of Timor-Leste", :country_code => "670", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Australian External Territories", :country_code => "672", :international_call_prefix => "0011", :trunk_prefix => "" ) + Country.create(:name => "Brunei Darussalam", :country_code => "673", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Nauru", :country_code => "674", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Papua New Guinea", :country_code => "675", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Tonga", :country_code => "676", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Solomon Islands", :country_code => "677", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Vanuatu", :country_code => "678", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Fiji", :country_code => "679", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Palau", :country_code => "680", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Wallis and Futuna", :country_code => "681", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Cook Islands", :country_code => "682", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Niue", :country_code => "683", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Samoa", :country_code => "685", :international_call_prefix => "0", :trunk_prefix => "" ) + Country.create(:name => "Kiribati", :country_code => "686", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "New Caledonia", :country_code => "687", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Tuvalu", :country_code => "688", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "French Polynesia", :country_code => "689", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Tokelau", :country_code => "690", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Micronesia", :country_code => "691", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Marshall Islands", :country_code => "692", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Kazakhstan", :country_code => "7", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Russian Federation", :country_code => "7", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "International Freephone Service", :country_code => "800", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "International Shared Cost Service", :country_code => "808", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Japan", :country_code => "81", :international_call_prefix => "010", :trunk_prefix => "" ) + Country.create(:name => "Korea", :country_code => "82", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Viet Nam", :country_code => "84", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Democratic People's Republic of Korea", :country_code => "850", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Hong Kong, China", :country_code => "852", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Macao, China", :country_code => "853", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Cambodia", :country_code => "855", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Lao People's Democratic Republic", :country_code => "856", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "China", :country_code => "86", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Inmarsat SNAC", :country_code => "870", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Inmarsat (871)", :country_code => "871", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Inmarsat (872)", :country_code => "872", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Inmarsat (873)", :country_code => "873", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Inmarsat (874)", :country_code => "874", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Universal Personal Telecommunication Service", :country_code => "878", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Bangladesh", :country_code => "880", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Turkey", :country_code => "90", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "India", :country_code => "91", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Pakistan", :country_code => "92", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Afghanistan", :country_code => "93", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Sri Lanka", :country_code => "94", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Myanmar", :country_code => "95", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Maldives", :country_code => "960", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Lebanon", :country_code => "961", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Jordan", :country_code => "962", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Syrian Arab Republic", :country_code => "963", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Iraq", :country_code => "964", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Kuwait", :country_code => "965", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Saudi Arabia", :country_code => "966", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Yemen", :country_code => "967", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Oman", :country_code => "968", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "United Arab Emirates", :country_code => "971", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Israel", :country_code => "972", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Bahrain", :country_code => "973", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Qatar", :country_code => "974", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Bhutan", :country_code => "975", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Mongolia", :country_code => "976", :international_call_prefix => "001", :trunk_prefix => "" ) + Country.create(:name => "Nepal", :country_code => "977", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "International Premium Rate Service", :country_code => "979", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Iran", :country_code => "98", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Tajikistan", :country_code => "992", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Turkmenistan", :country_code => "993", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Azerbaijani Republic", :country_code => "994", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Georgia", :country_code => "995", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Kyrgyz Republic", :country_code => "996", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Uzbekistan", :country_code => "998", :international_call_prefix => "00", :trunk_prefix => "" ) + end + end + + def down + Country.destroy_all + end +end diff --git a/db/migrate/20120119154952_area_codes_germany.rb b/db/migrate/20120119154952_area_codes_germany.rb new file mode 100644 index 0000000..6a4ce56 --- /dev/null +++ b/db/migrate/20120119154952_area_codes_germany.rb @@ -0,0 +1,5245 @@ +# ruby encoding: utf-8 + +class AreaCodesGermany < ActiveRecord::Migration + def up + germany = Country.find_by_name('Germany') + + ################################################################ + # AreaCodes Germany + ################################################################ + + ActiveRecord::Base.transaction do + AreaCode.create(:country => germany, :name => "Essen", :area_code => "201") + AreaCode.create(:country => germany, :name => "Wuppertal", :area_code => "202") + AreaCode.create(:country => germany, :name => "Duisburg", :area_code => "203") + AreaCode.create(:country => germany, :name => "Bottrop", :area_code => "2041") + AreaCode.create(:country => germany, :name => "Gladbeck", :area_code => "2043") + AreaCode.create(:country => germany, :name => "Bottrop-Kirchhellen", :area_code => "2045") + AreaCode.create(:country => germany, :name => "Velbert", :area_code => "2051") + AreaCode.create(:country => germany, :name => "Velbert-Langenberg", :area_code => "2052") + AreaCode.create(:country => germany, :name => "Velbert-Neviges", :area_code => "2053") + AreaCode.create(:country => germany, :name => "Essen-Kettwig", :area_code => "2054") + AreaCode.create(:country => germany, :name => "Heiligenhaus", :area_code => "2056") + AreaCode.create(:country => germany, :name => "Wülfrath", :area_code => "2058") + AreaCode.create(:country => germany, :name => "Dinslaken", :area_code => "2064") + AreaCode.create(:country => germany, :name => "Duisburg-Rheinhausen", :area_code => "2065") + AreaCode.create(:country => germany, :name => "Duisburg-Homberg", :area_code => "2066") + AreaCode.create(:country => germany, :name => "Oberhausen Rheinl", :area_code => "208") + AreaCode.create(:country => germany, :name => "Gelsenkirchen", :area_code => "209") + AreaCode.create(:country => germany, :name => "Ratingen", :area_code => "2102") + AreaCode.create(:country => germany, :name => "Hilden", :area_code => "2103") + AreaCode.create(:country => germany, :name => "Mettmann", :area_code => "2104") + AreaCode.create(:country => germany, :name => "Düsseldorf", :area_code => "211") + AreaCode.create(:country => germany, :name => "Solingen", :area_code => "212") + AreaCode.create(:country => germany, :name => "Haan Rheinl", :area_code => "2129") + AreaCode.create(:country => germany, :name => "Neuss", :area_code => "2131") + AreaCode.create(:country => germany, :name => "Meerbusch-Büderich", :area_code => "2132") + AreaCode.create(:country => germany, :name => "Dormagen", :area_code => "2133") + AreaCode.create(:country => germany, :name => "Neuss-Norf", :area_code => "2137") + AreaCode.create(:country => germany, :name => "Leverkusen", :area_code => "214") + AreaCode.create(:country => germany, :name => "Meerbusch-Lank", :area_code => "2150") + AreaCode.create(:country => germany, :name => "Krefeld", :area_code => "2151") + AreaCode.create(:country => germany, :name => "Kempen", :area_code => "2152") + AreaCode.create(:country => germany, :name => "Nettetal-Lobberich", :area_code => "2153") + AreaCode.create(:country => germany, :name => "Willich", :area_code => "2154") + AreaCode.create(:country => germany, :name => "Willich-Anrath", :area_code => "2156") + AreaCode.create(:country => germany, :name => "Nettetal-Kaldenkirchen", :area_code => "2157") + AreaCode.create(:country => germany, :name => "Grefrath b Krefeld", :area_code => "2158") + AreaCode.create(:country => germany, :name => "Meerbusch-Osterath", :area_code => "2159") + AreaCode.create(:country => germany, :name => "Mönchengladbach", :area_code => "2161") + AreaCode.create(:country => germany, :name => "Viersen", :area_code => "2162") + AreaCode.create(:country => germany, :name => "Schwalmtal Niederrhein", :area_code => "2163") + AreaCode.create(:country => germany, :name => "Jüchen-Otzenrath", :area_code => "2164") + AreaCode.create(:country => germany, :name => "Jüchen", :area_code => "2165") + AreaCode.create(:country => germany, :name => "Mönchengladbach-Rheydt", :area_code => "2166") + AreaCode.create(:country => germany, :name => "Leverkusen-Opladen", :area_code => "2171") + AreaCode.create(:country => germany, :name => "Langenfeld Rheinland", :area_code => "2173") + AreaCode.create(:country => germany, :name => "Burscheid Rheinl", :area_code => "2174") + AreaCode.create(:country => germany, :name => "Leichlingen Rheinland", :area_code => "2175") + AreaCode.create(:country => germany, :name => "Grevenbroich", :area_code => "2181") + AreaCode.create(:country => germany, :name => "Grevenbroich-Kapellen", :area_code => "2182") + AreaCode.create(:country => germany, :name => "Rommerskirchen", :area_code => "2183") + AreaCode.create(:country => germany, :name => "Remscheid", :area_code => "2191") + AreaCode.create(:country => germany, :name => "Hückeswagen", :area_code => "2192") + AreaCode.create(:country => germany, :name => "Dabringhausen", :area_code => "2193") + AreaCode.create(:country => germany, :name => "Radevormwald", :area_code => "2195") + AreaCode.create(:country => germany, :name => "Wermelskirchen", :area_code => "2196") + AreaCode.create(:country => germany, :name => "Bergisch Gladbach", :area_code => "2202") + AreaCode.create(:country => germany, :name => "Köln-Porz", :area_code => "2203") + AreaCode.create(:country => germany, :name => "Bensberg", :area_code => "2204") + AreaCode.create(:country => germany, :name => "Rösrath", :area_code => "2205") + AreaCode.create(:country => germany, :name => "Overath", :area_code => "2206") + AreaCode.create(:country => germany, :name => "Kürten-Dürscheid", :area_code => "2207") + AreaCode.create(:country => germany, :name => "Niederkassel", :area_code => "2208") + AreaCode.create(:country => germany, :name => "Köln", :area_code => "221") + AreaCode.create(:country => germany, :name => "Bornheim Rheinl", :area_code => "2222") + AreaCode.create(:country => germany, :name => "Königswinter", :area_code => "2223") + AreaCode.create(:country => germany, :name => "Bad Honnef", :area_code => "2224") + AreaCode.create(:country => germany, :name => "Meckenheim Rheinl", :area_code => "2225") + AreaCode.create(:country => germany, :name => "Rheinbach", :area_code => "2226") + AreaCode.create(:country => germany, :name => "Bornheim-Merten", :area_code => "2227") + AreaCode.create(:country => germany, :name => "Remagen-Rolandseck", :area_code => "2228") + AreaCode.create(:country => germany, :name => "Brühl Rheinl", :area_code => "2232") + AreaCode.create(:country => germany, :name => "Hürth Rheinl", :area_code => "2233") + AreaCode.create(:country => germany, :name => "Frechen", :area_code => "2234") + AreaCode.create(:country => germany, :name => "Erftstadt", :area_code => "2235") + AreaCode.create(:country => germany, :name => "Wesseling Rheinl", :area_code => "2236") + AreaCode.create(:country => germany, :name => "Kerpen Rheinl-Türnich", :area_code => "2237") + AreaCode.create(:country => germany, :name => "Pulheim", :area_code => "2238") + AreaCode.create(:country => germany, :name => "Siegburg", :area_code => "2241") + AreaCode.create(:country => germany, :name => "Hennef Sieg", :area_code => "2242") + AreaCode.create(:country => germany, :name => "Eitorf", :area_code => "2243") + AreaCode.create(:country => germany, :name => "Königswinter-Oberpleis", :area_code => "2244") + AreaCode.create(:country => germany, :name => "Much", :area_code => "2245") + AreaCode.create(:country => germany, :name => "Lohmar", :area_code => "2246") + AreaCode.create(:country => germany, :name => "Neunkirchen-Seelscheid", :area_code => "2247") + AreaCode.create(:country => germany, :name => "Hennef-Uckerath", :area_code => "2248") + AreaCode.create(:country => germany, :name => "Euskirchen", :area_code => "2251") + AreaCode.create(:country => germany, :name => "Zülpich", :area_code => "2252") + AreaCode.create(:country => germany, :name => "Bad Münstereifel", :area_code => "2253") + AreaCode.create(:country => germany, :name => "Weilerswist", :area_code => "2254") + AreaCode.create(:country => germany, :name => "Euskirchen-Flamersheim", :area_code => "2255") + AreaCode.create(:country => germany, :name => "Mechernich-Satzvey", :area_code => "2256") + AreaCode.create(:country => germany, :name => "Reckerscheid", :area_code => "2257") + AreaCode.create(:country => germany, :name => "Gummersbach", :area_code => "2261") + AreaCode.create(:country => germany, :name => "Wiehl", :area_code => "2262") + AreaCode.create(:country => germany, :name => "Engelskirchen", :area_code => "2263") + AreaCode.create(:country => germany, :name => "Marienheide", :area_code => "2264") + AreaCode.create(:country => germany, :name => "Reichshof-Eckenhagen", :area_code => "2265") + AreaCode.create(:country => germany, :name => "Lindlar", :area_code => "2266") + AreaCode.create(:country => germany, :name => "Wipperfürth", :area_code => "2267") + AreaCode.create(:country => germany, :name => "Kürten", :area_code => "2268") + AreaCode.create(:country => germany, :name => "Kierspe-Rönsahl", :area_code => "2269") + AreaCode.create(:country => germany, :name => "Bergheim Erft", :area_code => "2271") + AreaCode.create(:country => germany, :name => "Bedburg Erft", :area_code => "2272") + AreaCode.create(:country => germany, :name => "Kerpen-Horrem", :area_code => "2273") + AreaCode.create(:country => germany, :name => "Elsdorf Rheinl", :area_code => "2274") + AreaCode.create(:country => germany, :name => "Kerpen-Buir", :area_code => "2275") + AreaCode.create(:country => germany, :name => "Bonn", :area_code => "228") + AreaCode.create(:country => germany, :name => "Waldbröl", :area_code => "2291") + AreaCode.create(:country => germany, :name => "Windeck Sieg", :area_code => "2292") + AreaCode.create(:country => germany, :name => "Nümbrecht", :area_code => "2293") + AreaCode.create(:country => germany, :name => "Morsbach Sieg", :area_code => "2294") + AreaCode.create(:country => germany, :name => "Ruppichteroth", :area_code => "2295") + AreaCode.create(:country => germany, :name => "Reichshof-Brüchermühle", :area_code => "2296") + AreaCode.create(:country => germany, :name => "Wildbergerhütte", :area_code => "2297") + AreaCode.create(:country => germany, :name => "Holzwickede", :area_code => "2301") + AreaCode.create(:country => germany, :name => "Witten", :area_code => "2302") + AreaCode.create(:country => germany, :name => "Unna", :area_code => "2303") + AreaCode.create(:country => germany, :name => "Schwerte", :area_code => "2304") + AreaCode.create(:country => germany, :name => "Castrop-Rauxel", :area_code => "2305") + AreaCode.create(:country => germany, :name => "Lünen", :area_code => "2306") + AreaCode.create(:country => germany, :name => "Kamen", :area_code => "2307") + AreaCode.create(:country => germany, :name => "Unna-Hemmerde", :area_code => "2308") + AreaCode.create(:country => germany, :name => "Waltrop", :area_code => "2309") + AreaCode.create(:country => germany, :name => "Dortmund", :area_code => "231") + AreaCode.create(:country => germany, :name => "Herne", :area_code => "2323") + AreaCode.create(:country => germany, :name => "Hattingen Ruhr", :area_code => "2324") + AreaCode.create(:country => germany, :name => "Wanne-Eickel", :area_code => "2325") + AreaCode.create(:country => germany, :name => "Bochum-Wattenscheid", :area_code => "2327") + AreaCode.create(:country => germany, :name => "Herdecke", :area_code => "2330") + AreaCode.create(:country => germany, :name => "Hagen Westf", :area_code => "2331") + AreaCode.create(:country => germany, :name => "Gevelsberg", :area_code => "2332") + AreaCode.create(:country => germany, :name => "Ennepetal", :area_code => "2333") + AreaCode.create(:country => germany, :name => "Hagen-Hohenlimburg", :area_code => "2334") + AreaCode.create(:country => germany, :name => "Wetter Ruhr", :area_code => "2335") + AreaCode.create(:country => germany, :name => "Schwelm", :area_code => "2336") + AreaCode.create(:country => germany, :name => "Hagen-Dahl", :area_code => "2337") + AreaCode.create(:country => germany, :name => "Breckerfeld", :area_code => "2338") + AreaCode.create(:country => germany, :name => "Sprockhövel-Haßlinghausen", :area_code => "2339") + AreaCode.create(:country => germany, :name => "Bochum", :area_code => "234") + AreaCode.create(:country => germany, :name => "Lüdenscheid", :area_code => "2351") + AreaCode.create(:country => germany, :name => "Altena Westf", :area_code => "2352") + AreaCode.create(:country => germany, :name => "Halver", :area_code => "2353") + AreaCode.create(:country => germany, :name => "Meinerzhagen", :area_code => "2354") + AreaCode.create(:country => germany, :name => "Schalksmühle", :area_code => "2355") + AreaCode.create(:country => germany, :name => "Herscheid Westf", :area_code => "2357") + AreaCode.create(:country => germany, :name => "Meinerzhagen-Valbert", :area_code => "2358") + AreaCode.create(:country => germany, :name => "Kierspe", :area_code => "2359") + AreaCode.create(:country => germany, :name => "Haltern-Lippramsdorf", :area_code => "2360") + AreaCode.create(:country => germany, :name => "Recklinghausen", :area_code => "2361") + AreaCode.create(:country => germany, :name => "Dorsten", :area_code => "2362") + AreaCode.create(:country => germany, :name => "Datteln", :area_code => "2363") + AreaCode.create(:country => germany, :name => "Haltern Westf", :area_code => "2364") + AreaCode.create(:country => germany, :name => "Marl", :area_code => "2365") + AreaCode.create(:country => germany, :name => "Herten Westf", :area_code => "2366") + AreaCode.create(:country => germany, :name => "Henrichenburg", :area_code => "2367") + AreaCode.create(:country => germany, :name => "Oer-Erkenschwick", :area_code => "2368") + AreaCode.create(:country => germany, :name => "Dorsten-Wulfen", :area_code => "2369") + AreaCode.create(:country => germany, :name => "Iserlohn", :area_code => "2371") + AreaCode.create(:country => germany, :name => "Hemer", :area_code => "2372") + AreaCode.create(:country => germany, :name => "Menden Sauerland", :area_code => "2373") + AreaCode.create(:country => germany, :name => "Iserlohn-Letmathe", :area_code => "2374") + AreaCode.create(:country => germany, :name => "Balve", :area_code => "2375") + AreaCode.create(:country => germany, :name => "Wickede Ruhr", :area_code => "2377") + AreaCode.create(:country => germany, :name => "Fröndenberg-Langschede", :area_code => "2378") + AreaCode.create(:country => germany, :name => "Menden-Asbeck", :area_code => "2379") + AreaCode.create(:country => germany, :name => "Hamm Westf", :area_code => "2381") + AreaCode.create(:country => germany, :name => "Ahlen Westf", :area_code => "2382") + AreaCode.create(:country => germany, :name => "Bönen", :area_code => "2383") + AreaCode.create(:country => germany, :name => "Welver", :area_code => "2384") + AreaCode.create(:country => germany, :name => "Hamm-Rhynern", :area_code => "2385") + AreaCode.create(:country => germany, :name => "Drensteinfurt-Walstedde", :area_code => "2387") + AreaCode.create(:country => germany, :name => "Hamm-Uentrop", :area_code => "2388") + AreaCode.create(:country => germany, :name => "Werne", :area_code => "2389") + AreaCode.create(:country => germany, :name => "Plettenberg", :area_code => "2391") + AreaCode.create(:country => germany, :name => "Werdohl", :area_code => "2392") + AreaCode.create(:country => germany, :name => "Sundern-Allendorf", :area_code => "2393") + AreaCode.create(:country => germany, :name => "Neuenrade-Affeln", :area_code => "2394") + AreaCode.create(:country => germany, :name => "Finnentrop-Rönkhausen", :area_code => "2395") + AreaCode.create(:country => germany, :name => "Baesweiler", :area_code => "2401") + AreaCode.create(:country => germany, :name => "Stolberg Rheinl", :area_code => "2402") + AreaCode.create(:country => germany, :name => "Eschweiler Rheinl", :area_code => "2403") + AreaCode.create(:country => germany, :name => "Alsdorf Rheinl", :area_code => "2404") + AreaCode.create(:country => germany, :name => "Würselen", :area_code => "2405") + AreaCode.create(:country => germany, :name => "Herzogenrath", :area_code => "2406") + AreaCode.create(:country => germany, :name => "Herzogenrath-Kohlscheid", :area_code => "2407") + AreaCode.create(:country => germany, :name => "Aachen-Kornelimünster", :area_code => "2408") + AreaCode.create(:country => germany, :name => "Stolberg-Gressenich", :area_code => "2409") + AreaCode.create(:country => germany, :name => "Aachen", :area_code => "241") + AreaCode.create(:country => germany, :name => "Düren", :area_code => "2421") + AreaCode.create(:country => germany, :name => "Kreuzau", :area_code => "2422") + AreaCode.create(:country => germany, :name => "Langerwehe", :area_code => "2423") + AreaCode.create(:country => germany, :name => "Vettweiss", :area_code => "2424") + AreaCode.create(:country => germany, :name => "Nideggen-Embken", :area_code => "2425") + AreaCode.create(:country => germany, :name => "Nörvenich", :area_code => "2426") + AreaCode.create(:country => germany, :name => "Nideggen", :area_code => "2427") + AreaCode.create(:country => germany, :name => "Niederzier", :area_code => "2428") + AreaCode.create(:country => germany, :name => "Hürtgenwald", :area_code => "2429") + AreaCode.create(:country => germany, :name => "Erkelenz", :area_code => "2431") + AreaCode.create(:country => germany, :name => "Wassenberg", :area_code => "2432") + AreaCode.create(:country => germany, :name => "Hückelhoven", :area_code => "2433") + AreaCode.create(:country => germany, :name => "Wegberg", :area_code => "2434") + AreaCode.create(:country => germany, :name => "Erkelenz-Lövenich", :area_code => "2435") + AreaCode.create(:country => germany, :name => "Wegberg-Rödgen", :area_code => "2436") + AreaCode.create(:country => germany, :name => "Nettersheim-Tondorf", :area_code => "2440") + AreaCode.create(:country => germany, :name => "Kall", :area_code => "2441") + AreaCode.create(:country => germany, :name => "Mechernich", :area_code => "2443") + AreaCode.create(:country => germany, :name => "Schleiden-Gemünd", :area_code => "2444") + AreaCode.create(:country => germany, :name => "Schleiden Eifel", :area_code => "2445") + AreaCode.create(:country => germany, :name => "Heimbach Eifel", :area_code => "2446") + AreaCode.create(:country => germany, :name => "Dahlem b Kall", :area_code => "2447") + AreaCode.create(:country => germany, :name => "Hellenthal-Rescheid", :area_code => "2448") + AreaCode.create(:country => germany, :name => "Blankenheim Ahr", :area_code => "2449") + AreaCode.create(:country => germany, :name => "Geilenkirchen", :area_code => "2451") + AreaCode.create(:country => germany, :name => "Heinsberg Rheinl", :area_code => "2452") + AreaCode.create(:country => germany, :name => "Heinsberg-Randerath", :area_code => "2453") + AreaCode.create(:country => germany, :name => "Gangelt", :area_code => "2454") + AreaCode.create(:country => germany, :name => "Waldfeucht", :area_code => "2455") + AreaCode.create(:country => germany, :name => "Selfkant", :area_code => "2456") + AreaCode.create(:country => germany, :name => "Jülich", :area_code => "2461") + AreaCode.create(:country => germany, :name => "Linnich", :area_code => "2462") + AreaCode.create(:country => germany, :name => "Titz", :area_code => "2463") + AreaCode.create(:country => germany, :name => "Aldenhoven b Jülich", :area_code => "2464") + AreaCode.create(:country => germany, :name => "Inden", :area_code => "2465") + AreaCode.create(:country => germany, :name => "Roetgen Eifel", :area_code => "2471") + AreaCode.create(:country => germany, :name => "Monschau", :area_code => "2472") + AreaCode.create(:country => germany, :name => "Simmerath", :area_code => "2473") + AreaCode.create(:country => germany, :name => "Nideggen-Schmidt", :area_code => "2474") + AreaCode.create(:country => germany, :name => "Hellenthal", :area_code => "2482") + AreaCode.create(:country => germany, :name => "Mechernich-Eiserfey", :area_code => "2484") + AreaCode.create(:country => germany, :name => "Schleiden-Dreiborn", :area_code => "2485") + AreaCode.create(:country => germany, :name => "Nettersheim", :area_code => "2486") + AreaCode.create(:country => germany, :name => "Münster-Hiltrup", :area_code => "2501") + AreaCode.create(:country => germany, :name => "Nottuln", :area_code => "2502") + AreaCode.create(:country => germany, :name => "Telgte", :area_code => "2504") + AreaCode.create(:country => germany, :name => "Altenberge Westf", :area_code => "2505") + AreaCode.create(:country => germany, :name => "Münster-Wolbeck", :area_code => "2506") + AreaCode.create(:country => germany, :name => "Havixbeck", :area_code => "2507") + AreaCode.create(:country => germany, :name => "Drensteinfurt", :area_code => "2508") + AreaCode.create(:country => germany, :name => "Nottuln-Appelhülsen", :area_code => "2509") + AreaCode.create(:country => germany, :name => "Münster", :area_code => "251") + AreaCode.create(:country => germany, :name => "Wadersloh-Diestedde", :area_code => "2520") + AreaCode.create(:country => germany, :name => "Beckum", :area_code => "2521") + AreaCode.create(:country => germany, :name => "Oelde", :area_code => "2522") + AreaCode.create(:country => germany, :name => "Wadersloh", :area_code => "2523") + AreaCode.create(:country => germany, :name => "Ennigerloh", :area_code => "2524") + AreaCode.create(:country => germany, :name => "Beckum-Neubeckum", :area_code => "2525") + AreaCode.create(:country => germany, :name => "Sendenhorst", :area_code => "2526") + AreaCode.create(:country => germany, :name => "Lippetal-Lippborg", :area_code => "2527") + AreaCode.create(:country => germany, :name => "Ennigerloh-Enniger", :area_code => "2528") + AreaCode.create(:country => germany, :name => "Oelde-Stromberg", :area_code => "2529") + AreaCode.create(:country => germany, :name => "Ostbevern", :area_code => "2532") + AreaCode.create(:country => germany, :name => "Münster-Nienberge", :area_code => "2533") + AreaCode.create(:country => germany, :name => "Münster-Roxel", :area_code => "2534") + AreaCode.create(:country => germany, :name => "Sendenhorst-Albersloh", :area_code => "2535") + AreaCode.create(:country => germany, :name => "Münster-Albachten", :area_code => "2536") + AreaCode.create(:country => germany, :name => "Drensteinfurt-Rinkerode", :area_code => "2538") + AreaCode.create(:country => germany, :name => "Coesfeld", :area_code => "2541") + AreaCode.create(:country => germany, :name => "Gescher", :area_code => "2542") + AreaCode.create(:country => germany, :name => "Billerbeck Westf", :area_code => "2543") + AreaCode.create(:country => germany, :name => "Rosendahl-Darfeld", :area_code => "2545") + AreaCode.create(:country => germany, :name => "Coesfeld-Lette", :area_code => "2546") + AreaCode.create(:country => germany, :name => "Rosendahl-Osterwick", :area_code => "2547") + AreaCode.create(:country => germany, :name => "Dülmen-Rorup", :area_code => "2548") + AreaCode.create(:country => germany, :name => "Steinfurt-Burgsteinfurt", :area_code => "2551") + AreaCode.create(:country => germany, :name => "Steinfurt-Borghorst", :area_code => "2552") + AreaCode.create(:country => germany, :name => "Ochtrup", :area_code => "2553") + AreaCode.create(:country => germany, :name => "Laer Kr Steinfurt", :area_code => "2554") + AreaCode.create(:country => germany, :name => "Schöppingen", :area_code => "2555") + AreaCode.create(:country => germany, :name => "Metelen", :area_code => "2556") + AreaCode.create(:country => germany, :name => "Wettringen Kr Steinfurt", :area_code => "2557") + AreaCode.create(:country => germany, :name => "Horstmar", :area_code => "2558") + AreaCode.create(:country => germany, :name => "Ahaus", :area_code => "2561") + AreaCode.create(:country => germany, :name => "Gronau Westfalen", :area_code => "2562") + AreaCode.create(:country => germany, :name => "Stadtlohn", :area_code => "2563") + AreaCode.create(:country => germany, :name => "Vreden", :area_code => "2564") + AreaCode.create(:country => germany, :name => "Gronau-Epe", :area_code => "2565") + AreaCode.create(:country => germany, :name => "Legden", :area_code => "2566") + AreaCode.create(:country => germany, :name => "Ahaus-Alstätte", :area_code => "2567") + AreaCode.create(:country => germany, :name => "Heek", :area_code => "2568") + AreaCode.create(:country => germany, :name => "Greven Westf", :area_code => "2571") + AreaCode.create(:country => germany, :name => "Emsdetten", :area_code => "2572") + AreaCode.create(:country => germany, :name => "Nordwalde", :area_code => "2573") + AreaCode.create(:country => germany, :name => "Saerbeck", :area_code => "2574") + AreaCode.create(:country => germany, :name => "Greven-Reckenfeld", :area_code => "2575") + AreaCode.create(:country => germany, :name => "Warendorf", :area_code => "2581") + AreaCode.create(:country => germany, :name => "Everswinkel", :area_code => "2582") + AreaCode.create(:country => germany, :name => "Sassenberg", :area_code => "2583") + AreaCode.create(:country => germany, :name => "Warendorf-Milte", :area_code => "2584") + AreaCode.create(:country => germany, :name => "Warendorf-Hoetmar", :area_code => "2585") + AreaCode.create(:country => germany, :name => "Beelen", :area_code => "2586") + AreaCode.create(:country => germany, :name => "Ennigerloh-Westkirchen", :area_code => "2587") + AreaCode.create(:country => germany, :name => "Harsewinkel-Greffen", :area_code => "2588") + AreaCode.create(:country => germany, :name => "Dülmen-Buldern", :area_code => "2590") + AreaCode.create(:country => germany, :name => "Lüdinghausen", :area_code => "2591") + AreaCode.create(:country => germany, :name => "Selm", :area_code => "2592") + AreaCode.create(:country => germany, :name => "Ascheberg Westf", :area_code => "2593") + AreaCode.create(:country => germany, :name => "Dülmen", :area_code => "2594") + AreaCode.create(:country => germany, :name => "Olfen", :area_code => "2595") + AreaCode.create(:country => germany, :name => "Nordkirchen", :area_code => "2596") + AreaCode.create(:country => germany, :name => "Senden Westf", :area_code => "2597") + AreaCode.create(:country => germany, :name => "Senden-Ottmarsbocholt", :area_code => "2598") + AreaCode.create(:country => germany, :name => "Ascheberg-Herbern", :area_code => "2599") + AreaCode.create(:country => germany, :name => "Nauort", :area_code => "2601") + AreaCode.create(:country => germany, :name => "Montabaur", :area_code => "2602") + AreaCode.create(:country => germany, :name => "Bad Ems", :area_code => "2603") + AreaCode.create(:country => germany, :name => "Nassau Lahn", :area_code => "2604") + AreaCode.create(:country => germany, :name => "Löf", :area_code => "2605") + AreaCode.create(:country => germany, :name => "Winningen Mosel", :area_code => "2606") + AreaCode.create(:country => germany, :name => "Kobern-Gondorf", :area_code => "2607") + AreaCode.create(:country => germany, :name => "Welschneudorf", :area_code => "2608") + AreaCode.create(:country => germany, :name => "Koblenz am Rhein", :area_code => "261") + AreaCode.create(:country => germany, :name => "Neuhäusel Westerw", :area_code => "2620") + AreaCode.create(:country => germany, :name => "Lahnstein", :area_code => "2621") + AreaCode.create(:country => germany, :name => "Bendorf", :area_code => "2622") + AreaCode.create(:country => germany, :name => "Ransbach-Baumbach", :area_code => "2623") + AreaCode.create(:country => germany, :name => "Höhr-Grenzhausen", :area_code => "2624") + AreaCode.create(:country => germany, :name => "Ochtendung", :area_code => "2625") + AreaCode.create(:country => germany, :name => "Selters Westerwald", :area_code => "2626") + AreaCode.create(:country => germany, :name => "Braubach", :area_code => "2627") + AreaCode.create(:country => germany, :name => "Rhens", :area_code => "2628") + AreaCode.create(:country => germany, :name => "Mülheim-Kärlich", :area_code => "2630") + AreaCode.create(:country => germany, :name => "Neuwied", :area_code => "2631") + AreaCode.create(:country => germany, :name => "Andernach", :area_code => "2632") + AreaCode.create(:country => germany, :name => "Brohl-Lützing", :area_code => "2633") + AreaCode.create(:country => germany, :name => "Rengsdorf", :area_code => "2634") + AreaCode.create(:country => germany, :name => "Rheinbrohl", :area_code => "2635") + AreaCode.create(:country => germany, :name => "Burgbrohl", :area_code => "2636") + AreaCode.create(:country => germany, :name => "Weissenthurm", :area_code => "2637") + AreaCode.create(:country => germany, :name => "Waldbreitbach", :area_code => "2638") + AreaCode.create(:country => germany, :name => "Anhausen Kr Neuwied", :area_code => "2639") + AreaCode.create(:country => germany, :name => "Bad Neuenahr-Ahrweiler", :area_code => "2641") + AreaCode.create(:country => germany, :name => "Remagen", :area_code => "2642") + AreaCode.create(:country => germany, :name => "Altenahr", :area_code => "2643") + AreaCode.create(:country => germany, :name => "Linz am Rhein", :area_code => "2644") + AreaCode.create(:country => germany, :name => "Vettelschoss", :area_code => "2645") + AreaCode.create(:country => germany, :name => "Königsfeld Eifel", :area_code => "2646") + AreaCode.create(:country => germany, :name => "Kesseling", :area_code => "2647") + AreaCode.create(:country => germany, :name => "Mayen", :area_code => "2651") + AreaCode.create(:country => germany, :name => "Mendig", :area_code => "2652") + AreaCode.create(:country => germany, :name => "Kaisersesch", :area_code => "2653") + AreaCode.create(:country => germany, :name => "Polch", :area_code => "2654") + AreaCode.create(:country => germany, :name => "Weibern", :area_code => "2655") + AreaCode.create(:country => germany, :name => "Virneburg", :area_code => "2656") + AreaCode.create(:country => germany, :name => "Uersfeld", :area_code => "2657") + AreaCode.create(:country => germany, :name => "Bad Marienberg Westerwald", :area_code => "2661") + AreaCode.create(:country => germany, :name => "Hachenburg", :area_code => "2662") + AreaCode.create(:country => germany, :name => "Westerburg Westerw", :area_code => "2663") + AreaCode.create(:country => germany, :name => "Rennerod", :area_code => "2664") + AreaCode.create(:country => germany, :name => "Freilingen Westerw", :area_code => "2666") + AreaCode.create(:country => germany, :name => "Stein-Neukirch", :area_code => "2667") + AreaCode.create(:country => germany, :name => "Cochem", :area_code => "2671") + AreaCode.create(:country => germany, :name => "Treis-Karden", :area_code => "2672") + AreaCode.create(:country => germany, :name => "Ellenz-Poltersdorf", :area_code => "2673") + AreaCode.create(:country => germany, :name => "Bad Bertrich", :area_code => "2674") + AreaCode.create(:country => germany, :name => "Ediger-Eller", :area_code => "2675") + AreaCode.create(:country => germany, :name => "Ulmen", :area_code => "2676") + AreaCode.create(:country => germany, :name => "Lutzerath", :area_code => "2677") + AreaCode.create(:country => germany, :name => "Büchel b Cochem", :area_code => "2678") + AreaCode.create(:country => germany, :name => "Mündersbach", :area_code => "2680") + AreaCode.create(:country => germany, :name => "Altenkirchen Westerwald", :area_code => "2681") + AreaCode.create(:country => germany, :name => "Hamm Sieg", :area_code => "2682") + AreaCode.create(:country => germany, :name => "Asbach Westerw", :area_code => "2683") + AreaCode.create(:country => germany, :name => "Puderbach Westerw", :area_code => "2684") + AreaCode.create(:country => germany, :name => "Flammersfeld", :area_code => "2685") + AreaCode.create(:country => germany, :name => "Weyerbusch", :area_code => "2686") + AreaCode.create(:country => germany, :name => "Horhausen Westerwald", :area_code => "2687") + AreaCode.create(:country => germany, :name => "Kroppach", :area_code => "2688") + AreaCode.create(:country => germany, :name => "Dierdorf", :area_code => "2689") + AreaCode.create(:country => germany, :name => "Adenau", :area_code => "2691") + AreaCode.create(:country => germany, :name => "Kelberg", :area_code => "2692") + AreaCode.create(:country => germany, :name => "Antweiler", :area_code => "2693") + AreaCode.create(:country => germany, :name => "Wershofen", :area_code => "2694") + AreaCode.create(:country => germany, :name => "Insul", :area_code => "2695") + AreaCode.create(:country => germany, :name => "Nohn Eifel", :area_code => "2696") + AreaCode.create(:country => germany, :name => "Blankenheim-Ahrhütte", :area_code => "2697") + AreaCode.create(:country => germany, :name => "Siegen", :area_code => "271") + AreaCode.create(:country => germany, :name => "Lennestadt", :area_code => "2721") + AreaCode.create(:country => germany, :name => "Attendorn", :area_code => "2722") + AreaCode.create(:country => germany, :name => "Kirchhundem", :area_code => "2723") + AreaCode.create(:country => germany, :name => "Finnentrop-Serkenrode", :area_code => "2724") + AreaCode.create(:country => germany, :name => "Lennestadt-Oedingen", :area_code => "2725") + AreaCode.create(:country => germany, :name => "Kreuztal", :area_code => "2732") + AreaCode.create(:country => germany, :name => "Hilchenbach", :area_code => "2733") + AreaCode.create(:country => germany, :name => "Freudenberg Westf", :area_code => "2734") + AreaCode.create(:country => germany, :name => "Neunkirchen Siegerl", :area_code => "2735") + AreaCode.create(:country => germany, :name => "Burbach Siegerl", :area_code => "2736") + AreaCode.create(:country => germany, :name => "Netphen-Deuz", :area_code => "2737") + AreaCode.create(:country => germany, :name => "Netphen", :area_code => "2738") + AreaCode.create(:country => germany, :name => "Wilnsdorf", :area_code => "2739") + AreaCode.create(:country => germany, :name => "Betzdorf", :area_code => "2741") + AreaCode.create(:country => germany, :name => "Wissen", :area_code => "2742") + AreaCode.create(:country => germany, :name => "Daaden", :area_code => "2743") + AreaCode.create(:country => germany, :name => "Herdorf", :area_code => "2744") + AreaCode.create(:country => germany, :name => "Brachbach Sieg", :area_code => "2745") + AreaCode.create(:country => germany, :name => "Molzhain", :area_code => "2747") + AreaCode.create(:country => germany, :name => "Diedenshausen", :area_code => "2750") + AreaCode.create(:country => germany, :name => "Bad Berleburg", :area_code => "2751") + AreaCode.create(:country => germany, :name => "Bad Laasphe", :area_code => "2752") + AreaCode.create(:country => germany, :name => "Erndtebrück", :area_code => "2753") + AreaCode.create(:country => germany, :name => "Bad Laasphe-Feudingen", :area_code => "2754") + AreaCode.create(:country => germany, :name => "Bad Berleburg-Schwarzenau", :area_code => "2755") + AreaCode.create(:country => germany, :name => "Bad Berleburg-Girkhausen", :area_code => "2758") + AreaCode.create(:country => germany, :name => "Bad Berleburg-Aue", :area_code => "2759") + AreaCode.create(:country => germany, :name => "Olpe Biggesee", :area_code => "2761") + AreaCode.create(:country => germany, :name => "Wenden Südsauerland", :area_code => "2762") + AreaCode.create(:country => germany, :name => "Drolshagen-Bleche", :area_code => "2763") + AreaCode.create(:country => germany, :name => "Welschen Ennest", :area_code => "2764") + AreaCode.create(:country => germany, :name => "Eschenburg", :area_code => "2770") + AreaCode.create(:country => germany, :name => "Dillenburg", :area_code => "2771") + AreaCode.create(:country => germany, :name => "Herborn Hess", :area_code => "2772") + AreaCode.create(:country => germany, :name => "Haiger", :area_code => "2773") + AreaCode.create(:country => germany, :name => "Dietzhölztal", :area_code => "2774") + AreaCode.create(:country => germany, :name => "Driedorf", :area_code => "2775") + AreaCode.create(:country => germany, :name => "Bad Endbach-Hartenrod", :area_code => "2776") + AreaCode.create(:country => germany, :name => "Breitscheid Hess", :area_code => "2777") + AreaCode.create(:country => germany, :name => "Siegbach", :area_code => "2778") + AreaCode.create(:country => germany, :name => "Greifenstein-Beilstein", :area_code => "2779") + AreaCode.create(:country => germany, :name => "Xanten", :area_code => "2801") + AreaCode.create(:country => germany, :name => "Alpen", :area_code => "2802") + AreaCode.create(:country => germany, :name => "Wesel-Büderich", :area_code => "2803") + AreaCode.create(:country => germany, :name => "Xanten-Marienbaum", :area_code => "2804") + AreaCode.create(:country => germany, :name => "Wesel", :area_code => "281") + AreaCode.create(:country => germany, :name => "Kleve Niederrhein", :area_code => "2821") + AreaCode.create(:country => germany, :name => "Emmerich", :area_code => "2822") + AreaCode.create(:country => germany, :name => "Goch", :area_code => "2823") + AreaCode.create(:country => germany, :name => "Kalkar", :area_code => "2824") + AreaCode.create(:country => germany, :name => "Uedem", :area_code => "2825") + AreaCode.create(:country => germany, :name => "Kranenburg Niederrhein", :area_code => "2826") + AreaCode.create(:country => germany, :name => "Goch-Hassum", :area_code => "2827") + AreaCode.create(:country => germany, :name => "Emmerich-Elten", :area_code => "2828") + AreaCode.create(:country => germany, :name => "Geldern", :area_code => "2831") + AreaCode.create(:country => germany, :name => "Kevelaer", :area_code => "2832") + AreaCode.create(:country => germany, :name => "Kerken", :area_code => "2833") + AreaCode.create(:country => germany, :name => "Straelen", :area_code => "2834") + AreaCode.create(:country => germany, :name => "Issum", :area_code => "2835") + AreaCode.create(:country => germany, :name => "Wachtendonk", :area_code => "2836") + AreaCode.create(:country => germany, :name => "Weeze", :area_code => "2837") + AreaCode.create(:country => germany, :name => "Sonsbeck", :area_code => "2838") + AreaCode.create(:country => germany, :name => "Straelen-Herongen", :area_code => "2839") + AreaCode.create(:country => germany, :name => "Moers", :area_code => "2841") + AreaCode.create(:country => germany, :name => "Kamp-Lintfort", :area_code => "2842") + AreaCode.create(:country => germany, :name => "Rheinberg", :area_code => "2843") + AreaCode.create(:country => germany, :name => "Rheinberg-Orsoy", :area_code => "2844") + AreaCode.create(:country => germany, :name => "Neukirchen-Vluyn", :area_code => "2845") + AreaCode.create(:country => germany, :name => "Rees-Haldern", :area_code => "2850") + AreaCode.create(:country => germany, :name => "Rees", :area_code => "2851") + AreaCode.create(:country => germany, :name => "Hamminkeln", :area_code => "2852") + AreaCode.create(:country => germany, :name => "Schermbeck", :area_code => "2853") + AreaCode.create(:country => germany, :name => "Voerde Niederrhein", :area_code => "2855") + AreaCode.create(:country => germany, :name => "Hamminkeln-Brünen", :area_code => "2856") + AreaCode.create(:country => germany, :name => "Rees-Mehr", :area_code => "2857") + AreaCode.create(:country => germany, :name => "Hünxe", :area_code => "2858") + AreaCode.create(:country => germany, :name => "Wesel-Bislich", :area_code => "2859") + AreaCode.create(:country => germany, :name => "Borken Westf", :area_code => "2861") + AreaCode.create(:country => germany, :name => "Südlohn", :area_code => "2862") + AreaCode.create(:country => germany, :name => "Velen", :area_code => "2863") + AreaCode.create(:country => germany, :name => "Reken", :area_code => "2864") + AreaCode.create(:country => germany, :name => "Raesfeld", :area_code => "2865") + AreaCode.create(:country => germany, :name => "Dorsten-Rhade", :area_code => "2866") + AreaCode.create(:country => germany, :name => "Heiden Kr Borken", :area_code => "2867") + AreaCode.create(:country => germany, :name => "Bocholt", :area_code => "2871") + AreaCode.create(:country => germany, :name => "Rhede Westf", :area_code => "2872") + AreaCode.create(:country => germany, :name => "Isselburg-Werth", :area_code => "2873") + AreaCode.create(:country => germany, :name => "Isselburg", :area_code => "2874") + AreaCode.create(:country => germany, :name => "Warstein", :area_code => "2902") + AreaCode.create(:country => germany, :name => "Meschede-Freienohl", :area_code => "2903") + AreaCode.create(:country => germany, :name => "Bestwig", :area_code => "2904") + AreaCode.create(:country => germany, :name => "Bestwig-Ramsbeck", :area_code => "2905") + AreaCode.create(:country => germany, :name => "Meschede", :area_code => "291") + AreaCode.create(:country => germany, :name => "Soest", :area_code => "2921") + AreaCode.create(:country => germany, :name => "Werl", :area_code => "2922") + AreaCode.create(:country => germany, :name => "Lippetal-Herzfeld", :area_code => "2923") + AreaCode.create(:country => germany, :name => "Möhnesee", :area_code => "2924") + AreaCode.create(:country => germany, :name => "Warstein-Allagen", :area_code => "2925") + AreaCode.create(:country => germany, :name => "Neuengeseke", :area_code => "2927") + AreaCode.create(:country => germany, :name => "Soest-Ostönnen", :area_code => "2928") + AreaCode.create(:country => germany, :name => "Arnsberg", :area_code => "2931") + AreaCode.create(:country => germany, :name => "Neheim-Hüsten", :area_code => "2932") + AreaCode.create(:country => germany, :name => "Sundern Sauerland", :area_code => "2933") + AreaCode.create(:country => germany, :name => "Sundern-Altenhellefeld", :area_code => "2934") + AreaCode.create(:country => germany, :name => "Sundern-Hachen", :area_code => "2935") + AreaCode.create(:country => germany, :name => "Arnsberg-Oeventrop", :area_code => "2937") + AreaCode.create(:country => germany, :name => "Ense", :area_code => "2938") + AreaCode.create(:country => germany, :name => "Lippstadt", :area_code => "2941") + AreaCode.create(:country => germany, :name => "Geseke", :area_code => "2942") + AreaCode.create(:country => germany, :name => "Erwitte", :area_code => "2943") + AreaCode.create(:country => germany, :name => "Rietberg-Mastholte", :area_code => "2944") + AreaCode.create(:country => germany, :name => "Lippstadt-Benninghausen", :area_code => "2945") + AreaCode.create(:country => germany, :name => "Anröchte", :area_code => "2947") + AreaCode.create(:country => germany, :name => "Lippstadt-Rebbeke", :area_code => "2948") + AreaCode.create(:country => germany, :name => "Büren", :area_code => "2951") + AreaCode.create(:country => germany, :name => "Rüthen", :area_code => "2952") + AreaCode.create(:country => germany, :name => "Wünnenberg", :area_code => "2953") + AreaCode.create(:country => germany, :name => "Rüthen-Oestereiden", :area_code => "2954") + AreaCode.create(:country => germany, :name => "Büren-Wewelsburg", :area_code => "2955") + AreaCode.create(:country => germany, :name => "Wünnenberg-Haaren", :area_code => "2957") + AreaCode.create(:country => germany, :name => "Büren-Harth", :area_code => "2958") + AreaCode.create(:country => germany, :name => "Brilon", :area_code => "2961") + AreaCode.create(:country => germany, :name => "Olsberg", :area_code => "2962") + AreaCode.create(:country => germany, :name => "Brilon-Messinghausen", :area_code => "2963") + AreaCode.create(:country => germany, :name => "Brilon-Alme", :area_code => "2964") + AreaCode.create(:country => germany, :name => "Schmallenberg-Dorlar", :area_code => "2971") + AreaCode.create(:country => germany, :name => "Schmallenberg", :area_code => "2972") + AreaCode.create(:country => germany, :name => "Eslohe Sauerland", :area_code => "2973") + AreaCode.create(:country => germany, :name => "Schmallenberg-Fredeburg", :area_code => "2974") + AreaCode.create(:country => germany, :name => "Schmallenberg-Oberkirchen", :area_code => "2975") + AreaCode.create(:country => germany, :name => "Schmallenberg-Bödefeld", :area_code => "2977") + AreaCode.create(:country => germany, :name => "Winterberg Westf", :area_code => "2981") + AreaCode.create(:country => germany, :name => "Medebach", :area_code => "2982") + AreaCode.create(:country => germany, :name => "Winterberg-Siedlinghausen", :area_code => "2983") + AreaCode.create(:country => germany, :name => "Hallenberg", :area_code => "2984") + AreaCode.create(:country => germany, :name => "Winterberg-Niedersfeld", :area_code => "2985") + AreaCode.create(:country => germany, :name => "Marsberg-Bredelar", :area_code => "2991") + AreaCode.create(:country => germany, :name => "Marsberg", :area_code => "2992") + AreaCode.create(:country => germany, :name => "Marsberg-Canstein", :area_code => "2993") + AreaCode.create(:country => germany, :name => "Marsberg-Westheim", :area_code => "2994") + AreaCode.create(:country => germany, :name => "Berlin", :area_code => "30") + AreaCode.create(:country => germany, :name => "Oranienburg", :area_code => "3301") + AreaCode.create(:country => germany, :name => "Hennigsdorf", :area_code => "3302") + AreaCode.create(:country => germany, :name => "Birkenwerder", :area_code => "3303") + AreaCode.create(:country => germany, :name => "Velten", :area_code => "3304") + AreaCode.create(:country => germany, :name => "Nassenheide", :area_code => "33051") + AreaCode.create(:country => germany, :name => "Zehlendorf Kr Oberhavel", :area_code => "33053") + AreaCode.create(:country => germany, :name => "Liebenwalde", :area_code => "33054") + AreaCode.create(:country => germany, :name => "Kremmen", :area_code => "33055") + AreaCode.create(:country => germany, :name => "Mühlenbeck Kr Oberhavel", :area_code => "33056") + AreaCode.create(:country => germany, :name => "Gransee", :area_code => "3306") + AreaCode.create(:country => germany, :name => "Zehdenick", :area_code => "3307") + AreaCode.create(:country => germany, :name => "Marienthal Kr Oberhavel", :area_code => "33080") + AreaCode.create(:country => germany, :name => "Menz Kr Oberhavel", :area_code => "33082") + AreaCode.create(:country => germany, :name => "Schulzendorf Kr Oberhavel", :area_code => "33083") + AreaCode.create(:country => germany, :name => "Gutengermendorf", :area_code => "33084") + AreaCode.create(:country => germany, :name => "Seilershof", :area_code => "33085") + AreaCode.create(:country => germany, :name => "Grieben Kr Oberhavel", :area_code => "33086") + AreaCode.create(:country => germany, :name => "Bredereiche", :area_code => "33087") + AreaCode.create(:country => germany, :name => "Falkenthal", :area_code => "33088") + AreaCode.create(:country => germany, :name => "Himmelpfort", :area_code => "33089") + AreaCode.create(:country => germany, :name => "Fürstenberg Havel", :area_code => "33093") + AreaCode.create(:country => germany, :name => "Löwenberg", :area_code => "33094") + AreaCode.create(:country => germany, :name => "Potsdam", :area_code => "331") + AreaCode.create(:country => germany, :name => "Bergholz-Rehbrücke", :area_code => "33200") + AreaCode.create(:country => germany, :name => "Gross Glienicke", :area_code => "33201") + AreaCode.create(:country => germany, :name => "Töplitz", :area_code => "33202") + AreaCode.create(:country => germany, :name => "Kleinmachnow", :area_code => "33203") + AreaCode.create(:country => germany, :name => "Beelitz Mark", :area_code => "33204") + AreaCode.create(:country => germany, :name => "Michendorf", :area_code => "33205") + AreaCode.create(:country => germany, :name => "Fichtenwalde", :area_code => "33206") + AreaCode.create(:country => germany, :name => "Gross Kreutz", :area_code => "33207") + AreaCode.create(:country => germany, :name => "Fahrland", :area_code => "33208") + AreaCode.create(:country => germany, :name => "Caputh", :area_code => "33209") + AreaCode.create(:country => germany, :name => "Nauen Brandenb", :area_code => "3321") + AreaCode.create(:country => germany, :name => "Falkensee", :area_code => "3322") + AreaCode.create(:country => germany, :name => "Börnicke Kr Havelland", :area_code => "33230") + AreaCode.create(:country => germany, :name => "Pausin", :area_code => "33231") + AreaCode.create(:country => germany, :name => "Brieselang", :area_code => "33232") + AreaCode.create(:country => germany, :name => "Ketzin", :area_code => "33233") + AreaCode.create(:country => germany, :name => "Wustermark", :area_code => "33234") + AreaCode.create(:country => germany, :name => "Friesack", :area_code => "33235") + AreaCode.create(:country => germany, :name => "Paulinenaue", :area_code => "33237") + AreaCode.create(:country => germany, :name => "Senzke", :area_code => "33238") + AreaCode.create(:country => germany, :name => "Gross Behnitz", :area_code => "33239") + AreaCode.create(:country => germany, :name => "Werder Havel", :area_code => "3327") + AreaCode.create(:country => germany, :name => "Teltow", :area_code => "3328") + AreaCode.create(:country => germany, :name => "Stahnsdorf", :area_code => "3329") + AreaCode.create(:country => germany, :name => "Angermünde", :area_code => "3331") + AreaCode.create(:country => germany, :name => "Schwedt/Oder", :area_code => "3332") + AreaCode.create(:country => germany, :name => "Casekow", :area_code => "33331") + AreaCode.create(:country => germany, :name => "Gartz Oder", :area_code => "33332") + AreaCode.create(:country => germany, :name => "Tantow", :area_code => "33333") + AreaCode.create(:country => germany, :name => "Greiffenberg", :area_code => "33334") + AreaCode.create(:country => germany, :name => "Pinnow Kr Uckermark", :area_code => "33335") + AreaCode.create(:country => germany, :name => "Passow Kr Uckermark", :area_code => "33336") + AreaCode.create(:country => germany, :name => "Altkünkendorf", :area_code => "33337") + AreaCode.create(:country => germany, :name => "Stolpe/Oder", :area_code => "33338") + AreaCode.create(:country => germany, :name => "Eberswalde", :area_code => "3334") + AreaCode.create(:country => germany, :name => "Finowfurt", :area_code => "3335") + AreaCode.create(:country => germany, :name => "Joachimsthal", :area_code => "33361") + AreaCode.create(:country => germany, :name => "Liepe Kr Barnim", :area_code => "33362") + AreaCode.create(:country => germany, :name => "Altenhof Kr Barnim", :area_code => "33363") + AreaCode.create(:country => germany, :name => "Gross Ziethen Kr Barnim", :area_code => "33364") + AreaCode.create(:country => germany, :name => "Lüdersdorf Kr Barnim", :area_code => "33365") + AreaCode.create(:country => germany, :name => "Chorin", :area_code => "33366") + AreaCode.create(:country => germany, :name => "Friedrichswalde Brandenb", :area_code => "33367") + AreaCode.create(:country => germany, :name => "Hohensaaten", :area_code => "33368") + AreaCode.create(:country => germany, :name => "Oderberg", :area_code => "33369") + AreaCode.create(:country => germany, :name => "Biesenthal Brandenb", :area_code => "3337") + AreaCode.create(:country => germany, :name => "Bernau Brandenb", :area_code => "3338") + AreaCode.create(:country => germany, :name => "Gross Schönebeck Kr Barnim", :area_code => "33393") + AreaCode.create(:country => germany, :name => "Blumberg Kr Barnim", :area_code => "33394") + AreaCode.create(:country => germany, :name => "Zerpenschleuse", :area_code => "33395") + AreaCode.create(:country => germany, :name => "Klosterfelde", :area_code => "33396") + AreaCode.create(:country => germany, :name => "Wandlitz", :area_code => "33397") + AreaCode.create(:country => germany, :name => "Werneuchen", :area_code => "33398") + AreaCode.create(:country => germany, :name => "Strausberg", :area_code => "3341") + AreaCode.create(:country => germany, :name => "Neuenhagen b Berlin", :area_code => "3342") + AreaCode.create(:country => germany, :name => "Müncheberg", :area_code => "33432") + AreaCode.create(:country => germany, :name => "Buckow Märk Schweiz", :area_code => "33433") + AreaCode.create(:country => germany, :name => "Herzfelde b Strausberg", :area_code => "33434") + AreaCode.create(:country => germany, :name => "Rehfelde", :area_code => "33435") + AreaCode.create(:country => germany, :name => "Prötzel", :area_code => "33436") + AreaCode.create(:country => germany, :name => "Reichenberg b Strausberg", :area_code => "33437") + AreaCode.create(:country => germany, :name => "Altlandsberg", :area_code => "33438") + AreaCode.create(:country => germany, :name => "Fredersdorf-Vogelsdorf", :area_code => "33439") + AreaCode.create(:country => germany, :name => "Bad Freienwalde", :area_code => "3344") + AreaCode.create(:country => germany, :name => "Heckelberg", :area_code => "33451") + AreaCode.create(:country => germany, :name => "Neulewin", :area_code => "33452") + AreaCode.create(:country => germany, :name => "Wölsickendorf/Wollenberg", :area_code => "33454") + AreaCode.create(:country => germany, :name => "Wriezen", :area_code => "33456") + AreaCode.create(:country => germany, :name => "Altreetz", :area_code => "33457") + AreaCode.create(:country => germany, :name => "Falkenberg Mark", :area_code => "33458") + AreaCode.create(:country => germany, :name => "Seelow", :area_code => "3346") + AreaCode.create(:country => germany, :name => "Lietzen", :area_code => "33470") + AreaCode.create(:country => germany, :name => "Golzow b Seelow", :area_code => "33472") + AreaCode.create(:country => germany, :name => "Zechin", :area_code => "33473") + AreaCode.create(:country => germany, :name => "Neutrebbin", :area_code => "33474") + AreaCode.create(:country => germany, :name => "Letschin", :area_code => "33475") + AreaCode.create(:country => germany, :name => "Neuhardenberg", :area_code => "33476") + AreaCode.create(:country => germany, :name => "Trebnitz b Müncheberg", :area_code => "33477") + AreaCode.create(:country => germany, :name => "Gross Neuendorf", :area_code => "33478") + AreaCode.create(:country => germany, :name => "Küstrin-Kietz", :area_code => "33479") + AreaCode.create(:country => germany, :name => "Frankfurt (Oder)", :area_code => "335") + AreaCode.create(:country => germany, :name => "Podelzig", :area_code => "33601") + AreaCode.create(:country => germany, :name => "Alt Zeschdorf", :area_code => "33602") + AreaCode.create(:country => germany, :name => "Falkenhagen b Seelow", :area_code => "33603") + AreaCode.create(:country => germany, :name => "Lebus", :area_code => "33604") + AreaCode.create(:country => germany, :name => "Boossen", :area_code => "33605") + AreaCode.create(:country => germany, :name => "Müllrose", :area_code => "33606") + AreaCode.create(:country => germany, :name => "Briesen Mark", :area_code => "33607") + AreaCode.create(:country => germany, :name => "Jacobsdorf Mark", :area_code => "33608") + AreaCode.create(:country => germany, :name => "Brieskow-Finkenheerd", :area_code => "33609") + AreaCode.create(:country => germany, :name => "Fürstenwalde Spree", :area_code => "3361") + AreaCode.create(:country => germany, :name => "Erkner", :area_code => "3362") + AreaCode.create(:country => germany, :name => "Bad Saarow-Pieskow", :area_code => "33631") + AreaCode.create(:country => germany, :name => "Hangelsberg", :area_code => "33632") + AreaCode.create(:country => germany, :name => "Spreenhagen", :area_code => "33633") + AreaCode.create(:country => germany, :name => "Berkenbrück Kr Oder-Spree", :area_code => "33634") + AreaCode.create(:country => germany, :name => "Arensdorf Kr Oder-Spree", :area_code => "33635") + AreaCode.create(:country => germany, :name => "Steinhöfel Kr Oder-Spree", :area_code => "33636") + AreaCode.create(:country => germany, :name => "Beerfelde", :area_code => "33637") + AreaCode.create(:country => germany, :name => "Rüdersdorf b Berlin", :area_code => "33638") + AreaCode.create(:country => germany, :name => "Eisenhüttenstadt", :area_code => "3364") + AreaCode.create(:country => germany, :name => "Neuzelle", :area_code => "33652") + AreaCode.create(:country => germany, :name => "Ziltendorf", :area_code => "33653") + AreaCode.create(:country => germany, :name => "Fünfeichen", :area_code => "33654") + AreaCode.create(:country => germany, :name => "Grunow Kr Oder-Spree", :area_code => "33655") + AreaCode.create(:country => germany, :name => "Bahro", :area_code => "33656") + AreaCode.create(:country => germany, :name => "Steinsdorf Brandenb", :area_code => "33657") + AreaCode.create(:country => germany, :name => "Beeskow", :area_code => "3366") + AreaCode.create(:country => germany, :name => "Lieberose", :area_code => "33671") + AreaCode.create(:country => germany, :name => "Pfaffendorf b Beeskow", :area_code => "33672") + AreaCode.create(:country => germany, :name => "Weichensdorf", :area_code => "33673") + AreaCode.create(:country => germany, :name => "Trebatsch", :area_code => "33674") + AreaCode.create(:country => germany, :name => "Tauche", :area_code => "33675") + AreaCode.create(:country => germany, :name => "Friedland b Beeskow", :area_code => "33676") + AreaCode.create(:country => germany, :name => "Glienicke b Beeskow", :area_code => "33677") + AreaCode.create(:country => germany, :name => "Storkow Mark", :area_code => "33678") + AreaCode.create(:country => germany, :name => "Wendisch Rietz", :area_code => "33679") + AreaCode.create(:country => germany, :name => "Grossbeeren", :area_code => "33701") + AreaCode.create(:country => germany, :name => "Wünsdorf", :area_code => "33702") + AreaCode.create(:country => germany, :name => "Sperenberg", :area_code => "33703") + AreaCode.create(:country => germany, :name => "Baruth Mark", :area_code => "33704") + AreaCode.create(:country => germany, :name => "Rangsdorf", :area_code => "33708") + AreaCode.create(:country => germany, :name => "Luckenwalde", :area_code => "3371") + AreaCode.create(:country => germany, :name => "Jüterbog", :area_code => "3372") + AreaCode.create(:country => germany, :name => "Trebbin", :area_code => "33731") + AreaCode.create(:country => germany, :name => "Hennickendorf b Luckenwalde", :area_code => "33732") + AreaCode.create(:country => germany, :name => "Stülpe", :area_code => "33733") + AreaCode.create(:country => germany, :name => "Felgentreu", :area_code => "33734") + AreaCode.create(:country => germany, :name => "Niedergörsdorf", :area_code => "33741") + AreaCode.create(:country => germany, :name => "Oehna Brandenb", :area_code => "33742") + AreaCode.create(:country => germany, :name => "Blönsdorf", :area_code => "33743") + AreaCode.create(:country => germany, :name => "Hohenseefeld", :area_code => "33744") + AreaCode.create(:country => germany, :name => "Petkus", :area_code => "33745") + AreaCode.create(:country => germany, :name => "Werbig b Jüterbog", :area_code => "33746") + AreaCode.create(:country => germany, :name => "Marzahna", :area_code => "33747") + AreaCode.create(:country => germany, :name => "Treuenbrietzen", :area_code => "33748") + AreaCode.create(:country => germany, :name => "Königs Wusterhausen", :area_code => "3375") + AreaCode.create(:country => germany, :name => "Münchehofe Kr Dahme-Spreewald", :area_code => "33760") + AreaCode.create(:country => germany, :name => "Zeuthen", :area_code => "33762") + AreaCode.create(:country => germany, :name => "Bestensee", :area_code => "33763") + AreaCode.create(:country => germany, :name => "Mittenwalde Mark", :area_code => "33764") + AreaCode.create(:country => germany, :name => "Märkisch Buchholz", :area_code => "33765") + AreaCode.create(:country => germany, :name => "Teupitz", :area_code => "33766") + AreaCode.create(:country => germany, :name => "Friedersdorf b Berlin", :area_code => "33767") + AreaCode.create(:country => germany, :name => "Prieros", :area_code => "33768") + AreaCode.create(:country => germany, :name => "Töpchin", :area_code => "33769") + AreaCode.create(:country => germany, :name => "Zossen Brandenb", :area_code => "3377") + AreaCode.create(:country => germany, :name => "Ludwigsfelde", :area_code => "3378") + AreaCode.create(:country => germany, :name => "Mahlow", :area_code => "3379") + AreaCode.create(:country => germany, :name => "Brandenburg an der Havel", :area_code => "3381") + AreaCode.create(:country => germany, :name => "Lehnin", :area_code => "3382") + AreaCode.create(:country => germany, :name => "Ziesar", :area_code => "33830") + AreaCode.create(:country => germany, :name => "Weseram", :area_code => "33831") + AreaCode.create(:country => germany, :name => "Rogäsen", :area_code => "33832") + AreaCode.create(:country => germany, :name => "Wollin b Brandenburg", :area_code => "33833") + AreaCode.create(:country => germany, :name => "Pritzerbe", :area_code => "33834") + AreaCode.create(:country => germany, :name => "Golzow b Brandenburg", :area_code => "33835") + AreaCode.create(:country => germany, :name => "Butzow b Brandenburg", :area_code => "33836") + AreaCode.create(:country => germany, :name => "Brielow", :area_code => "33837") + AreaCode.create(:country => germany, :name => "Päwesin", :area_code => "33838") + AreaCode.create(:country => germany, :name => "Wusterwitz", :area_code => "33839") + AreaCode.create(:country => germany, :name => "Belzig", :area_code => "33841") + AreaCode.create(:country => germany, :name => "Niemegk", :area_code => "33843") + AreaCode.create(:country => germany, :name => "Brück Brandenb", :area_code => "33844") + AreaCode.create(:country => germany, :name => "Borkheide", :area_code => "33845") + AreaCode.create(:country => germany, :name => "Dippmannsdorf", :area_code => "33846") + AreaCode.create(:country => germany, :name => "Görzke", :area_code => "33847") + AreaCode.create(:country => germany, :name => "Raben", :area_code => "33848") + AreaCode.create(:country => germany, :name => "Wiesenburg Mark", :area_code => "33849") + AreaCode.create(:country => germany, :name => "Rathenow", :area_code => "3385") + AreaCode.create(:country => germany, :name => "Premnitz", :area_code => "3386") + AreaCode.create(:country => germany, :name => "Zollchow b Rathenow", :area_code => "33870") + AreaCode.create(:country => germany, :name => "Hohennauen", :area_code => "33872") + AreaCode.create(:country => germany, :name => "Grosswudicke", :area_code => "33873") + AreaCode.create(:country => germany, :name => "Stechow Brandenb", :area_code => "33874") + AreaCode.create(:country => germany, :name => "Rhinow", :area_code => "33875") + AreaCode.create(:country => germany, :name => "Buschow", :area_code => "33876") + AreaCode.create(:country => germany, :name => "Nitzahn", :area_code => "33877") + AreaCode.create(:country => germany, :name => "Nennhausen", :area_code => "33878") + AreaCode.create(:country => germany, :name => "Neuruppin", :area_code => "3391") + AreaCode.create(:country => germany, :name => "Walsleben b Neuruppin", :area_code => "33920") + AreaCode.create(:country => germany, :name => "Zechlinerhütte", :area_code => "33921") + AreaCode.create(:country => germany, :name => "Karwesee", :area_code => "33922") + AreaCode.create(:country => germany, :name => "Flecken Zechlin", :area_code => "33923") + AreaCode.create(:country => germany, :name => "Rägelin", :area_code => "33924") + AreaCode.create(:country => germany, :name => "Wustrau-Altfriesack", :area_code => "33925") + AreaCode.create(:country => germany, :name => "Herzberg Mark", :area_code => "33926") + AreaCode.create(:country => germany, :name => "Wildberg Brandenb", :area_code => "33928") + AreaCode.create(:country => germany, :name => "Gühlen-Glienicke", :area_code => "33929") + AreaCode.create(:country => germany, :name => "Rheinsberg Mark", :area_code => "33931") + AreaCode.create(:country => germany, :name => "Fehrbellin", :area_code => "33932") + AreaCode.create(:country => germany, :name => "Lindow Mark", :area_code => "33933") + AreaCode.create(:country => germany, :name => "Wittstock Dosse", :area_code => "3394") + AreaCode.create(:country => germany, :name => "Pritzwalk", :area_code => "3395") + AreaCode.create(:country => germany, :name => "Heiligengrabe", :area_code => "33962") + AreaCode.create(:country => germany, :name => "Wulfersdorf b Wittstock", :area_code => "33963") + AreaCode.create(:country => germany, :name => "Fretzdorf", :area_code => "33964") + AreaCode.create(:country => germany, :name => "Herzsprung b Wittstock", :area_code => "33965") + AreaCode.create(:country => germany, :name => "Dranse", :area_code => "33966") + AreaCode.create(:country => germany, :name => "Freyenstein", :area_code => "33967") + AreaCode.create(:country => germany, :name => "Meyenburg Kr Prignitz", :area_code => "33968") + AreaCode.create(:country => germany, :name => "Stepenitz", :area_code => "33969") + AreaCode.create(:country => germany, :name => "Neustadt Dosse", :area_code => "33970") + AreaCode.create(:country => germany, :name => "Kyritz Brandenb", :area_code => "33971") + AreaCode.create(:country => germany, :name => "Breddin", :area_code => "33972") + AreaCode.create(:country => germany, :name => "Zernitz b Neustadt Dosse", :area_code => "33973") + AreaCode.create(:country => germany, :name => "Dessow", :area_code => "33974") + AreaCode.create(:country => germany, :name => "Dannenwalde Kr Prignitz", :area_code => "33975") + AreaCode.create(:country => germany, :name => "Wutike", :area_code => "33976") + AreaCode.create(:country => germany, :name => "Gumtow", :area_code => "33977") + AreaCode.create(:country => germany, :name => "Segeletz", :area_code => "33978") + AreaCode.create(:country => germany, :name => "Wusterhausen Dosse", :area_code => "33979") + AreaCode.create(:country => germany, :name => "Putlitz", :area_code => "33981") + AreaCode.create(:country => germany, :name => "Hoppenrade Kr Prignitz", :area_code => "33982") + AreaCode.create(:country => germany, :name => "Gross Pankow Kr Prignitz", :area_code => "33983") + AreaCode.create(:country => germany, :name => "Blumenthal b Pritzwalk", :area_code => "33984") + AreaCode.create(:country => germany, :name => "Falkenhagen Kr Prignitz", :area_code => "33986") + AreaCode.create(:country => germany, :name => "Sadenbeck", :area_code => "33989") + AreaCode.create(:country => germany, :name => "Dessau Anh", :area_code => "340") + AreaCode.create(:country => germany, :name => "Leipzig", :area_code => "341") + AreaCode.create(:country => germany, :name => "Delitzsch", :area_code => "34202") + AreaCode.create(:country => germany, :name => "Zwenkau", :area_code => "34203") + AreaCode.create(:country => germany, :name => "Schkeuditz", :area_code => "34204") + AreaCode.create(:country => germany, :name => "Markranstädt", :area_code => "34205") + AreaCode.create(:country => germany, :name => "Rötha", :area_code => "34206") + AreaCode.create(:country => germany, :name => "Zwochau", :area_code => "34207") + AreaCode.create(:country => germany, :name => "Löbnitz b Delitzsch", :area_code => "34208") + AreaCode.create(:country => germany, :name => "Torgau", :area_code => "3421") + AreaCode.create(:country => germany, :name => "Schildau Gneisenaustadt", :area_code => "34221") + AreaCode.create(:country => germany, :name => "Arzberg b Torgau", :area_code => "34222") + AreaCode.create(:country => germany, :name => "Dommitzsch", :area_code => "34223") + AreaCode.create(:country => germany, :name => "Belgern Sachs", :area_code => "34224") + AreaCode.create(:country => germany, :name => "Eilenburg", :area_code => "3423") + AreaCode.create(:country => germany, :name => "Jesewitz", :area_code => "34241") + AreaCode.create(:country => germany, :name => "Hohenpriessnitz", :area_code => "34242") + AreaCode.create(:country => germany, :name => "Bad Düben", :area_code => "34243") + AreaCode.create(:country => germany, :name => "Mockrehna", :area_code => "34244") + AreaCode.create(:country => germany, :name => "Wurzen", :area_code => "3425") + AreaCode.create(:country => germany, :name => "Kühren b Wurzen", :area_code => "34261") + AreaCode.create(:country => germany, :name => "Falkenhain b Wurzen", :area_code => "34262") + AreaCode.create(:country => germany, :name => "Hohburg", :area_code => "34263") + AreaCode.create(:country => germany, :name => "Borsdorf", :area_code => "34291") + AreaCode.create(:country => germany, :name => "Brandis b Wurzen", :area_code => "34292") + AreaCode.create(:country => germany, :name => "Naunhof b Grimma", :area_code => "34293") + AreaCode.create(:country => germany, :name => "Rackwitz", :area_code => "34294") + AreaCode.create(:country => germany, :name => "Krensitz", :area_code => "34295") + AreaCode.create(:country => germany, :name => "Groitzsch b Pegau", :area_code => "34296") + AreaCode.create(:country => germany, :name => "Liebertwolkwitz", :area_code => "34297") + AreaCode.create(:country => germany, :name => "Taucha b Leipzig", :area_code => "34298") + AreaCode.create(:country => germany, :name => "Gaschwitz", :area_code => "34299") + AreaCode.create(:country => germany, :name => "Döbeln", :area_code => "3431") + AreaCode.create(:country => germany, :name => "Leisnig", :area_code => "34321") + AreaCode.create(:country => germany, :name => "Rosswein", :area_code => "34322") + AreaCode.create(:country => germany, :name => "Ostrau Sachs", :area_code => "34324") + AreaCode.create(:country => germany, :name => "Mochau-Lüttewitz", :area_code => "34325") + AreaCode.create(:country => germany, :name => "Waldheim Sachs", :area_code => "34327") + AreaCode.create(:country => germany, :name => "Hartha b Döbeln", :area_code => "34328") + AreaCode.create(:country => germany, :name => "Borna Stadt", :area_code => "3433") + AreaCode.create(:country => germany, :name => "Geithain", :area_code => "34341") + AreaCode.create(:country => germany, :name => "Neukieritzsch", :area_code => "34342") + AreaCode.create(:country => germany, :name => "Regis-Breitingen", :area_code => "34343") + AreaCode.create(:country => germany, :name => "Kohren-Sahlis", :area_code => "34344") + AreaCode.create(:country => germany, :name => "Bad Lausick", :area_code => "34345") + AreaCode.create(:country => germany, :name => "Narsdorf", :area_code => "34346") + AreaCode.create(:country => germany, :name => "Oelzschau b Borna", :area_code => "34347") + AreaCode.create(:country => germany, :name => "Frohburg", :area_code => "34348") + AreaCode.create(:country => germany, :name => "Oschatz", :area_code => "3435") + AreaCode.create(:country => germany, :name => "Dahlen Sachs", :area_code => "34361") + AreaCode.create(:country => germany, :name => "Mügeln b Oschatz", :area_code => "34362") + AreaCode.create(:country => germany, :name => "Cavertitz", :area_code => "34363") + AreaCode.create(:country => germany, :name => "Wermsdorf", :area_code => "34364") + AreaCode.create(:country => germany, :name => "Grimma", :area_code => "3437") + AreaCode.create(:country => germany, :name => "Colditz", :area_code => "34381") + AreaCode.create(:country => germany, :name => "Nerchau", :area_code => "34382") + AreaCode.create(:country => germany, :name => "Trebsen Mulde", :area_code => "34383") + AreaCode.create(:country => germany, :name => "Grossbothen", :area_code => "34384") + AreaCode.create(:country => germany, :name => "Mutzschen", :area_code => "34385") + AreaCode.create(:country => germany, :name => "Dürrweitzschen b Grimma", :area_code => "34386") + AreaCode.create(:country => germany, :name => "Zeitz", :area_code => "3441") + AreaCode.create(:country => germany, :name => "Osterfeld", :area_code => "34422") + AreaCode.create(:country => germany, :name => "Heuckewalde", :area_code => "34423") + AreaCode.create(:country => germany, :name => "Reuden b Zeitz", :area_code => "34424") + AreaCode.create(:country => germany, :name => "Droyssig", :area_code => "34425") + AreaCode.create(:country => germany, :name => "Kayna", :area_code => "34426") + AreaCode.create(:country => germany, :name => "Weissenfels Sachs-Anh", :area_code => "3443") + AreaCode.create(:country => germany, :name => "Hohenmölsen", :area_code => "34441") + AreaCode.create(:country => germany, :name => "Teuchern", :area_code => "34443") + AreaCode.create(:country => germany, :name => "Lützen", :area_code => "34444") + AreaCode.create(:country => germany, :name => "Stößen", :area_code => "34445") + AreaCode.create(:country => germany, :name => "Grosskorbetha", :area_code => "34446") + AreaCode.create(:country => germany, :name => "Naumburg Saale", :area_code => "3445") + AreaCode.create(:country => germany, :name => "Nebra Unstrut", :area_code => "34461") + AreaCode.create(:country => germany, :name => "Laucha Unstrut", :area_code => "34462") + AreaCode.create(:country => germany, :name => "Bad Kösen", :area_code => "34463") + AreaCode.create(:country => germany, :name => "Freyburg Unstrut", :area_code => "34464") + AreaCode.create(:country => germany, :name => "Bad Bibra", :area_code => "34465") + AreaCode.create(:country => germany, :name => "Janisroda", :area_code => "34466") + AreaCode.create(:country => germany, :name => "Eckartsberga", :area_code => "34467") + AreaCode.create(:country => germany, :name => "Altenburg Thür", :area_code => "3447") + AreaCode.create(:country => germany, :name => "Meuselwitz Thür", :area_code => "3448") + AreaCode.create(:country => germany, :name => "Schmölln Thür", :area_code => "34491") + AreaCode.create(:country => germany, :name => "Lucka", :area_code => "34492") + AreaCode.create(:country => germany, :name => "Gößnitz Thür", :area_code => "34493") + AreaCode.create(:country => germany, :name => "Ehrenhain", :area_code => "34494") + AreaCode.create(:country => germany, :name => "Dobitschen", :area_code => "34495") + AreaCode.create(:country => germany, :name => "Nöbdenitz", :area_code => "34496") + AreaCode.create(:country => germany, :name => "Langenleuba-Niederhain", :area_code => "34497") + AreaCode.create(:country => germany, :name => "Rositz", :area_code => "34498") + AreaCode.create(:country => germany, :name => "Halle Saale", :area_code => "345") + AreaCode.create(:country => germany, :name => "Ostrau Saalkreis", :area_code => "34600") + AreaCode.create(:country => germany, :name => "Teutschenthal", :area_code => "34601") + AreaCode.create(:country => germany, :name => "Landsberg Sachs-Anh", :area_code => "34602") + AreaCode.create(:country => germany, :name => "Nauendorf Sachs-Anh", :area_code => "34603") + AreaCode.create(:country => germany, :name => "Niemberg", :area_code => "34604") + AreaCode.create(:country => germany, :name => "Gröbers", :area_code => "34605") + AreaCode.create(:country => germany, :name => "Teicha Sachs-Anh", :area_code => "34606") + AreaCode.create(:country => germany, :name => "Wettin", :area_code => "34607") + AreaCode.create(:country => germany, :name => "Salzmünde", :area_code => "34609") + AreaCode.create(:country => germany, :name => "Merseburg Saale", :area_code => "3461") + AreaCode.create(:country => germany, :name => "Bad Dürrenberg", :area_code => "3462") + AreaCode.create(:country => germany, :name => "Mücheln Geiseltal", :area_code => "34632") + AreaCode.create(:country => germany, :name => "Braunsbedra", :area_code => "34633") + AreaCode.create(:country => germany, :name => "Bad Lauchstädt", :area_code => "34635") + AreaCode.create(:country => germany, :name => "Schafstädt", :area_code => "34636") + AreaCode.create(:country => germany, :name => "Frankleben", :area_code => "34637") + AreaCode.create(:country => germany, :name => "Zöschen", :area_code => "34638") + AreaCode.create(:country => germany, :name => "Wallendorf Luppe", :area_code => "34639") + AreaCode.create(:country => germany, :name => "Sangerhausen", :area_code => "3464") + AreaCode.create(:country => germany, :name => "Rossla", :area_code => "34651") + AreaCode.create(:country => germany, :name => "Allstedt", :area_code => "34652") + AreaCode.create(:country => germany, :name => "Rottleberode", :area_code => "34653") + AreaCode.create(:country => germany, :name => "Stolberg Harz", :area_code => "34654") + AreaCode.create(:country => germany, :name => "Wallhausen Sachs-Anh", :area_code => "34656") + AreaCode.create(:country => germany, :name => "Hayn Harz", :area_code => "34658") + AreaCode.create(:country => germany, :name => "Blankenheim b Sangerhausen", :area_code => "34659") + AreaCode.create(:country => germany, :name => "Artern Unstrut", :area_code => "3466") + AreaCode.create(:country => germany, :name => "Bad Frankenhausen Kyffhäuser", :area_code => "34671") + AreaCode.create(:country => germany, :name => "Rossleben", :area_code => "34672") + AreaCode.create(:country => germany, :name => "Heldrungen", :area_code => "34673") + AreaCode.create(:country => germany, :name => "Könnern", :area_code => "34691") + AreaCode.create(:country => germany, :name => "Alsleben Saale", :area_code => "34692") + AreaCode.create(:country => germany, :name => "Bernburg Saale", :area_code => "3471") + AreaCode.create(:country => germany, :name => "Nienburg Saale", :area_code => "34721") + AreaCode.create(:country => germany, :name => "Preusslitz", :area_code => "34722") + AreaCode.create(:country => germany, :name => "Aschersleben Sachs-Anh", :area_code => "3473") + AreaCode.create(:country => germany, :name => "Frose", :area_code => "34741") + AreaCode.create(:country => germany, :name => "Sylda", :area_code => "34742") + AreaCode.create(:country => germany, :name => "Ermsleben", :area_code => "34743") + AreaCode.create(:country => germany, :name => "Winningen Sachs-Anh", :area_code => "34745") + AreaCode.create(:country => germany, :name => "Giersleben", :area_code => "34746") + AreaCode.create(:country => germany, :name => "Lutherstadt Eisleben", :area_code => "3475") + AreaCode.create(:country => germany, :name => "Hettstedt Sachs-Anh", :area_code => "3476") + AreaCode.create(:country => germany, :name => "Querfurt", :area_code => "34771") + AreaCode.create(:country => germany, :name => "Helbra", :area_code => "34772") + AreaCode.create(:country => germany, :name => "Schwittersdorf", :area_code => "34773") + AreaCode.create(:country => germany, :name => "Röblingen am See", :area_code => "34774") + AreaCode.create(:country => germany, :name => "Wippra", :area_code => "34775") + AreaCode.create(:country => germany, :name => "Rothenschirmbach", :area_code => "34776") + AreaCode.create(:country => germany, :name => "Abberode", :area_code => "34779") + AreaCode.create(:country => germany, :name => "Greifenhagen", :area_code => "34781") + AreaCode.create(:country => germany, :name => "Mansfeld Südharz", :area_code => "34782") + AreaCode.create(:country => germany, :name => "Gerbstedt", :area_code => "34783") + AreaCode.create(:country => germany, :name => "Sandersleben", :area_code => "34785") + AreaCode.create(:country => germany, :name => "Roßlau Elbe", :area_code => "34901") + AreaCode.create(:country => germany, :name => "Coswig Anhalt", :area_code => "34903") + AreaCode.create(:country => germany, :name => "Oranienbaum", :area_code => "34904") + AreaCode.create(:country => germany, :name => "Wörlitz", :area_code => "34905") + AreaCode.create(:country => germany, :name => "Raguhn", :area_code => "34906") + AreaCode.create(:country => germany, :name => "Jeber-Bergfrieden", :area_code => "34907") + AreaCode.create(:country => germany, :name => "Aken Elbe", :area_code => "34909") + AreaCode.create(:country => germany, :name => "Lutherstadt Wittenberg", :area_code => "3491") + AreaCode.create(:country => germany, :name => "Kropstädt", :area_code => "34920") + AreaCode.create(:country => germany, :name => "Kemberg", :area_code => "34921") + AreaCode.create(:country => germany, :name => "Mühlanger", :area_code => "34922") + AreaCode.create(:country => germany, :name => "Cobbelsdorf", :area_code => "34923") + AreaCode.create(:country => germany, :name => "Zahna", :area_code => "34924") + AreaCode.create(:country => germany, :name => "Bad Schmiedeberg", :area_code => "34925") + AreaCode.create(:country => germany, :name => "Pretzsch Elbe", :area_code => "34926") + AreaCode.create(:country => germany, :name => "Globig-Bleddin", :area_code => "34927") + AreaCode.create(:country => germany, :name => "Seegrehna", :area_code => "34928") + AreaCode.create(:country => germany, :name => "Straach", :area_code => "34929") + AreaCode.create(:country => germany, :name => "Bitterfeld", :area_code => "3493") + AreaCode.create(:country => germany, :name => "Wolfen", :area_code => "3494") + AreaCode.create(:country => germany, :name => "Gräfenhainichen", :area_code => "34953") + AreaCode.create(:country => germany, :name => "Roitzsch b Bitterfeld", :area_code => "34954") + AreaCode.create(:country => germany, :name => "Gossa", :area_code => "34955") + AreaCode.create(:country => germany, :name => "Zörbig", :area_code => "34956") + AreaCode.create(:country => germany, :name => "Köthen Anhalt", :area_code => "3496") + AreaCode.create(:country => germany, :name => "Osternienburg", :area_code => "34973") + AreaCode.create(:country => germany, :name => "Görzig Kr Köthen", :area_code => "34975") + AreaCode.create(:country => germany, :name => "Gröbzig", :area_code => "34976") + AreaCode.create(:country => germany, :name => "Quellendorf", :area_code => "34977") + AreaCode.create(:country => germany, :name => "Radegast Kr Köthen", :area_code => "34978") + AreaCode.create(:country => germany, :name => "Wulfen Sachs-Anh", :area_code => "34979") + AreaCode.create(:country => germany, :name => "Pirna", :area_code => "3501") + AreaCode.create(:country => germany, :name => "Struppen", :area_code => "35020") + AreaCode.create(:country => germany, :name => "Königstein Sächs Schweiz", :area_code => "35021") + AreaCode.create(:country => germany, :name => "Bad Schandau", :area_code => "35022") + AreaCode.create(:country => germany, :name => "Bad Gottleuba", :area_code => "35023") + AreaCode.create(:country => germany, :name => "Stadt Wehlen", :area_code => "35024") + AreaCode.create(:country => germany, :name => "Liebstadt", :area_code => "35025") + AreaCode.create(:country => germany, :name => "Dürrröhrsdorf-Dittersbach", :area_code => "35026") + AreaCode.create(:country => germany, :name => "Weesenstein", :area_code => "35027") + AreaCode.create(:country => germany, :name => "Krippen", :area_code => "35028") + AreaCode.create(:country => germany, :name => "Langenhennersdorf", :area_code => "35032") + AreaCode.create(:country => germany, :name => "Rosenthal Sächs Schweiz", :area_code => "35033") + AreaCode.create(:country => germany, :name => "Dippoldiswalde", :area_code => "3504") + AreaCode.create(:country => germany, :name => "Kipsdorf Kurort", :area_code => "35052") + AreaCode.create(:country => germany, :name => "Glashütte Sachs", :area_code => "35053") + AreaCode.create(:country => germany, :name => "Lauenstein Sachs", :area_code => "35054") + AreaCode.create(:country => germany, :name => "Höckendorf b Dippoldiswalde", :area_code => "35055") + AreaCode.create(:country => germany, :name => "Altenberg Sachs", :area_code => "35056") + AreaCode.create(:country => germany, :name => "Hermsdorf Erzgeb", :area_code => "35057") + AreaCode.create(:country => germany, :name => "Pretzschendorf", :area_code => "35058") + AreaCode.create(:country => germany, :name => "Dresden", :area_code => "351") + AreaCode.create(:country => germany, :name => "Arnsdorf b Dresden", :area_code => "35200") + AreaCode.create(:country => germany, :name => "Langebrück", :area_code => "35201") + AreaCode.create(:country => germany, :name => "Klingenberg Sachs", :area_code => "35202") + AreaCode.create(:country => germany, :name => "Tharandt", :area_code => "35203") + AreaCode.create(:country => germany, :name => "Wilsdruff", :area_code => "35204") + AreaCode.create(:country => germany, :name => "Ottendorf-Okrilla", :area_code => "35205") + AreaCode.create(:country => germany, :name => "Kreischa b Dresden", :area_code => "35206") + AreaCode.create(:country => germany, :name => "Moritzburg", :area_code => "35207") + AreaCode.create(:country => germany, :name => "Radeburg", :area_code => "35208") + AreaCode.create(:country => germany, :name => "Mohorn", :area_code => "35209") + AreaCode.create(:country => germany, :name => "Meissen", :area_code => "3521") + AreaCode.create(:country => germany, :name => "Grossenhain Sachs", :area_code => "3522") + AreaCode.create(:country => germany, :name => "Coswig b Dresden", :area_code => "3523") + AreaCode.create(:country => germany, :name => "Tauscha b Großenhain", :area_code => "35240") + AreaCode.create(:country => germany, :name => "Lommatzsch", :area_code => "35241") + AreaCode.create(:country => germany, :name => "Nossen", :area_code => "35242") + AreaCode.create(:country => germany, :name => "Weinböhla", :area_code => "35243") + AreaCode.create(:country => germany, :name => "Krögis", :area_code => "35244") + AreaCode.create(:country => germany, :name => "Burkhardswalde-Munzig", :area_code => "35245") + AreaCode.create(:country => germany, :name => "Ziegenhain Sachs", :area_code => "35246") + AreaCode.create(:country => germany, :name => "Zehren Sachs", :area_code => "35247") + AreaCode.create(:country => germany, :name => "Schönfeld b Großenhain", :area_code => "35248") + AreaCode.create(:country => germany, :name => "Basslitz", :area_code => "35249") + AreaCode.create(:country => germany, :name => "Riesa", :area_code => "3525") + AreaCode.create(:country => germany, :name => "Gröditz b Riesa", :area_code => "35263") + AreaCode.create(:country => germany, :name => "Strehla", :area_code => "35264") + AreaCode.create(:country => germany, :name => "Glaubitz", :area_code => "35265") + AreaCode.create(:country => germany, :name => "Heyda b Riesa", :area_code => "35266") + AreaCode.create(:country => germany, :name => "Diesbar-Seusslitz", :area_code => "35267") + AreaCode.create(:country => germany, :name => "Stauchitz", :area_code => "35268") + AreaCode.create(:country => germany, :name => "Radeberg", :area_code => "3528") + AreaCode.create(:country => germany, :name => "Heidenau Sachs", :area_code => "3529") + AreaCode.create(:country => germany, :name => "Finsterwalde", :area_code => "3531") + AreaCode.create(:country => germany, :name => "Doberlug-Kirchhain", :area_code => "35322") + AreaCode.create(:country => germany, :name => "Sonnewalde", :area_code => "35323") + AreaCode.create(:country => germany, :name => "Crinitz", :area_code => "35324") + AreaCode.create(:country => germany, :name => "Rückersdorf b Finsterwalde", :area_code => "35325") + AreaCode.create(:country => germany, :name => "Schönborn Kr Elbe-Elster", :area_code => "35326") + AreaCode.create(:country => germany, :name => "Priessen", :area_code => "35327") + AreaCode.create(:country => germany, :name => "Dollenchen", :area_code => "35329") + AreaCode.create(:country => germany, :name => "Elsterwerda", :area_code => "3533") + AreaCode.create(:country => germany, :name => "Bad Liebenwerda", :area_code => "35341") + AreaCode.create(:country => germany, :name => "Mühlberg Elbe", :area_code => "35342") + AreaCode.create(:country => germany, :name => "Hirschfeld b Elsterwerda", :area_code => "35343") + AreaCode.create(:country => germany, :name => "Herzberg Elster", :area_code => "3535") + AreaCode.create(:country => germany, :name => "Schlieben", :area_code => "35361") + AreaCode.create(:country => germany, :name => "Schönewalde b Herzberg", :area_code => "35362") + AreaCode.create(:country => germany, :name => "Fermerswalde", :area_code => "35363") + AreaCode.create(:country => germany, :name => "Lebusa", :area_code => "35364") + AreaCode.create(:country => germany, :name => "Falkenberg Elster", :area_code => "35365") + AreaCode.create(:country => germany, :name => "Jessen Elster", :area_code => "3537") + AreaCode.create(:country => germany, :name => "Elster Elbe", :area_code => "35383") + AreaCode.create(:country => germany, :name => "Steinsdorf b Jessen", :area_code => "35384") + AreaCode.create(:country => germany, :name => "Annaburg", :area_code => "35385") + AreaCode.create(:country => germany, :name => "Prettin", :area_code => "35386") + AreaCode.create(:country => germany, :name => "Seyda", :area_code => "35387") + AreaCode.create(:country => germany, :name => "Klöden", :area_code => "35388") + AreaCode.create(:country => germany, :name => "Holzdorf Elster", :area_code => "35389") + AreaCode.create(:country => germany, :name => "Calau", :area_code => "3541") + AreaCode.create(:country => germany, :name => "Lübbenau Spreewald", :area_code => "3542") + AreaCode.create(:country => germany, :name => "Vetschau", :area_code => "35433") + AreaCode.create(:country => germany, :name => "Altdöbern", :area_code => "35434") + AreaCode.create(:country => germany, :name => "Gollmitz b Calau", :area_code => "35435") + AreaCode.create(:country => germany, :name => "Laasow b Calau", :area_code => "35436") + AreaCode.create(:country => germany, :name => "Zinnitz", :area_code => "35439") + AreaCode.create(:country => germany, :name => "Luckau Brandenb", :area_code => "3544") + AreaCode.create(:country => germany, :name => "Dahme Brandenb", :area_code => "35451") + AreaCode.create(:country => germany, :name => "Golssen", :area_code => "35452") + AreaCode.create(:country => germany, :name => "Drahnsdorf", :area_code => "35453") + AreaCode.create(:country => germany, :name => "Uckro", :area_code => "35454") + AreaCode.create(:country => germany, :name => "Walddrehna", :area_code => "35455") + AreaCode.create(:country => germany, :name => "Terpt", :area_code => "35456") + AreaCode.create(:country => germany, :name => "Lübben Spreewald", :area_code => "3546") + AreaCode.create(:country => germany, :name => "Birkenhainchen", :area_code => "35471") + AreaCode.create(:country => germany, :name => "Schlepzig", :area_code => "35472") + AreaCode.create(:country => germany, :name => "Neu Lübbenau", :area_code => "35473") + AreaCode.create(:country => germany, :name => "Schönwalde b Lübben", :area_code => "35474") + AreaCode.create(:country => germany, :name => "Straupitz", :area_code => "35475") + AreaCode.create(:country => germany, :name => "Wittmannsdorf-Bückchen", :area_code => "35476") + AreaCode.create(:country => germany, :name => "Rietzneuendorf-Friedrichshof", :area_code => "35477") + AreaCode.create(:country => germany, :name => "Goyatz", :area_code => "35478") + AreaCode.create(:country => germany, :name => "Cottbus", :area_code => "355") + AreaCode.create(:country => germany, :name => "Döbern NL", :area_code => "35600") + AreaCode.create(:country => germany, :name => "Peitz", :area_code => "35601") + AreaCode.create(:country => germany, :name => "Drebkau", :area_code => "35602") + AreaCode.create(:country => germany, :name => "Burg Spreewald", :area_code => "35603") + AreaCode.create(:country => germany, :name => "Krieschow", :area_code => "35604") + AreaCode.create(:country => germany, :name => "Komptendorf", :area_code => "35605") + AreaCode.create(:country => germany, :name => "Briesen b Cottbus", :area_code => "35606") + AreaCode.create(:country => germany, :name => "Jänschwalde", :area_code => "35607") + AreaCode.create(:country => germany, :name => "Gross Ossnig", :area_code => "35608") + AreaCode.create(:country => germany, :name => "Drachhausen", :area_code => "35609") + AreaCode.create(:country => germany, :name => "Guben", :area_code => "3561") + AreaCode.create(:country => germany, :name => "Forst Lausitz", :area_code => "3562") + AreaCode.create(:country => germany, :name => "Spremberg", :area_code => "3563") + AreaCode.create(:country => germany, :name => "Schwarze Pumpe", :area_code => "3564") + AreaCode.create(:country => germany, :name => "Bärenklau NL", :area_code => "35691") + AreaCode.create(:country => germany, :name => "Kerkwitz", :area_code => "35692") + AreaCode.create(:country => germany, :name => "Lauschütz", :area_code => "35693") + AreaCode.create(:country => germany, :name => "Gosda b Klinge", :area_code => "35694") + AreaCode.create(:country => germany, :name => "Simmersdorf", :area_code => "35695") + AreaCode.create(:country => germany, :name => "Briesnig", :area_code => "35696") + AreaCode.create(:country => germany, :name => "Bagenz", :area_code => "35697") + AreaCode.create(:country => germany, :name => "Hornow", :area_code => "35698") + AreaCode.create(:country => germany, :name => "Hoyerswerda", :area_code => "3571") + AreaCode.create(:country => germany, :name => "Lauta b Hoyerswerda", :area_code => "35722") + AreaCode.create(:country => germany, :name => "Bernsdorf OL", :area_code => "35723") + AreaCode.create(:country => germany, :name => "Lohsa", :area_code => "35724") + AreaCode.create(:country => germany, :name => "Wittichenau", :area_code => "35725") + AreaCode.create(:country => germany, :name => "Groß Särchen", :area_code => "35726") + AreaCode.create(:country => germany, :name => "Burghammer", :area_code => "35727") + AreaCode.create(:country => germany, :name => "Uhyst Spree", :area_code => "35728") + AreaCode.create(:country => germany, :name => "Senftenberg", :area_code => "3573") + AreaCode.create(:country => germany, :name => "Lauchhammer", :area_code => "3574") + AreaCode.create(:country => germany, :name => "Welzow", :area_code => "35751") + AreaCode.create(:country => germany, :name => "Ruhland", :area_code => "35752") + AreaCode.create(:country => germany, :name => "Großräschen", :area_code => "35753") + AreaCode.create(:country => germany, :name => "Klettwitz", :area_code => "35754") + AreaCode.create(:country => germany, :name => "Ortrand", :area_code => "35755") + AreaCode.create(:country => germany, :name => "Hosena", :area_code => "35756") + AreaCode.create(:country => germany, :name => "Weisswasser", :area_code => "3576") + AreaCode.create(:country => germany, :name => "Bad Muskau", :area_code => "35771") + AreaCode.create(:country => germany, :name => "Rietschen", :area_code => "35772") + AreaCode.create(:country => germany, :name => "Schleife", :area_code => "35773") + AreaCode.create(:country => germany, :name => "Boxberg Sachs", :area_code => "35774") + AreaCode.create(:country => germany, :name => "Pechern", :area_code => "35775") + AreaCode.create(:country => germany, :name => "Kamenz", :area_code => "3578") + AreaCode.create(:country => germany, :name => "Ossling", :area_code => "35792") + AreaCode.create(:country => germany, :name => "Elstra", :area_code => "35793") + AreaCode.create(:country => germany, :name => "Königsbrück", :area_code => "35795") + AreaCode.create(:country => germany, :name => "Panschwitz-Kuckau", :area_code => "35796") + AreaCode.create(:country => germany, :name => "Schwepnitz", :area_code => "35797") + AreaCode.create(:country => germany, :name => "Görlitz", :area_code => "3581") + AreaCode.create(:country => germany, :name => "Zodel", :area_code => "35820") + AreaCode.create(:country => germany, :name => "Hagenwerder", :area_code => "35822") + AreaCode.create(:country => germany, :name => "Ostritz", :area_code => "35823") + AreaCode.create(:country => germany, :name => "Kodersdorf", :area_code => "35825") + AreaCode.create(:country => germany, :name => "Königshain b Görlitz", :area_code => "35826") + AreaCode.create(:country => germany, :name => "Nieder-Seifersdorf", :area_code => "35827") + AreaCode.create(:country => germany, :name => "Reichenbach OL", :area_code => "35828") + AreaCode.create(:country => germany, :name => "Gersdorf b Görlitz", :area_code => "35829") + AreaCode.create(:country => germany, :name => "Zittau", :area_code => "3583") + AreaCode.create(:country => germany, :name => "Großschönau Sachs", :area_code => "35841") + AreaCode.create(:country => germany, :name => "Oderwitz", :area_code => "35842") + AreaCode.create(:country => germany, :name => "Hirschfelde b Zittau", :area_code => "35843") + AreaCode.create(:country => germany, :name => "Oybin Kurort", :area_code => "35844") + AreaCode.create(:country => germany, :name => "Löbau", :area_code => "3585") + AreaCode.create(:country => germany, :name => "Neugersdorf ,Sachs", :area_code => "3586") + AreaCode.create(:country => germany, :name => "Neusalza-Spremberg", :area_code => "35872") + AreaCode.create(:country => germany, :name => "Herrnhut", :area_code => "35873") + AreaCode.create(:country => germany, :name => "Bernstadt a d Eigen", :area_code => "35874") + AreaCode.create(:country => germany, :name => "Obercunnersdorf b Löbau", :area_code => "35875") + AreaCode.create(:country => germany, :name => "Weissenberg Sachs", :area_code => "35876") + AreaCode.create(:country => germany, :name => "Cunewalde", :area_code => "35877") + AreaCode.create(:country => germany, :name => "Niesky", :area_code => "3588") + AreaCode.create(:country => germany, :name => "Rothenburg OL", :area_code => "35891") + AreaCode.create(:country => germany, :name => "Horka OL", :area_code => "35892") + AreaCode.create(:country => germany, :name => "Mücka", :area_code => "35893") + AreaCode.create(:country => germany, :name => "Hähnichen", :area_code => "35894") + AreaCode.create(:country => germany, :name => "Klitten", :area_code => "35895") + AreaCode.create(:country => germany, :name => "Bautzen", :area_code => "3591") + AreaCode.create(:country => germany, :name => "Kirschau", :area_code => "3592") + AreaCode.create(:country => germany, :name => "Seitschen", :area_code => "35930") + AreaCode.create(:country => germany, :name => "Königswartha", :area_code => "35931") + AreaCode.create(:country => germany, :name => "Guttau", :area_code => "35932") + AreaCode.create(:country => germany, :name => "Neschwitz", :area_code => "35933") + AreaCode.create(:country => germany, :name => "Grossdubrau", :area_code => "35934") + AreaCode.create(:country => germany, :name => "Kleinwelka", :area_code => "35935") + AreaCode.create(:country => germany, :name => "Sohland Spree", :area_code => "35936") + AreaCode.create(:country => germany, :name => "Prischwitz", :area_code => "35937") + AreaCode.create(:country => germany, :name => "Großpostwitz OL", :area_code => "35938") + AreaCode.create(:country => germany, :name => "Hochkirch", :area_code => "35939") + AreaCode.create(:country => germany, :name => "Bischofswerda", :area_code => "3594") + AreaCode.create(:country => germany, :name => "Neukirch Lausitz", :area_code => "35951") + AreaCode.create(:country => germany, :name => "Großröhrsdorf OL", :area_code => "35952") + AreaCode.create(:country => germany, :name => "Burkau", :area_code => "35953") + AreaCode.create(:country => germany, :name => "Grossharthau", :area_code => "35954") + AreaCode.create(:country => germany, :name => "Pulsnitz", :area_code => "35955") + AreaCode.create(:country => germany, :name => "Neustadt i Sa", :area_code => "3596") + AreaCode.create(:country => germany, :name => "Sebnitz", :area_code => "35971") + AreaCode.create(:country => germany, :name => "Stolpen", :area_code => "35973") + AreaCode.create(:country => germany, :name => "Hinterhermsdorf", :area_code => "35974") + AreaCode.create(:country => germany, :name => "Hohnstein", :area_code => "35975") + AreaCode.create(:country => germany, :name => "Mühlhausen Thür", :area_code => "3601") + AreaCode.create(:country => germany, :name => "Ebeleben", :area_code => "36020") + AreaCode.create(:country => germany, :name => "Schlotheim", :area_code => "36021") + AreaCode.create(:country => germany, :name => "Grossengottern", :area_code => "36022") + AreaCode.create(:country => germany, :name => "Horsmar", :area_code => "36023") + AreaCode.create(:country => germany, :name => "Diedorf b Mühlhausen Thür", :area_code => "36024") + AreaCode.create(:country => germany, :name => "Körner", :area_code => "36025") + AreaCode.create(:country => germany, :name => "Struth b Mühlhausen Thür", :area_code => "36026") + AreaCode.create(:country => germany, :name => "Lengenfeld Unterm Stein", :area_code => "36027") + AreaCode.create(:country => germany, :name => "Kammerforst Thür", :area_code => "36028") + AreaCode.create(:country => germany, :name => "Menteroda", :area_code => "36029") + AreaCode.create(:country => germany, :name => "Bad Langensalza", :area_code => "3603") + AreaCode.create(:country => germany, :name => "Bad Tennstedt", :area_code => "36041") + AreaCode.create(:country => germany, :name => "Tonna", :area_code => "36042") + AreaCode.create(:country => germany, :name => "Kirchheilingen", :area_code => "36043") + AreaCode.create(:country => germany, :name => "Leinefelde", :area_code => "3605") + AreaCode.create(:country => germany, :name => "Heiligenstadt Heilbad", :area_code => "3606") + AreaCode.create(:country => germany, :name => "Teistungen", :area_code => "36071") + AreaCode.create(:country => germany, :name => "Weißenborn-Lüderode", :area_code => "36072") + AreaCode.create(:country => germany, :name => "Worbis", :area_code => "36074") + AreaCode.create(:country => germany, :name => "Dingelstädt Eichsfeld", :area_code => "36075") + AreaCode.create(:country => germany, :name => "Niederorschel", :area_code => "36076") + AreaCode.create(:country => germany, :name => "Grossbodungen", :area_code => "36077") + AreaCode.create(:country => germany, :name => "Arenshausen", :area_code => "36081") + AreaCode.create(:country => germany, :name => "Ershausen", :area_code => "36082") + AreaCode.create(:country => germany, :name => "Uder", :area_code => "36083") + AreaCode.create(:country => germany, :name => "Heuthen", :area_code => "36084") + AreaCode.create(:country => germany, :name => "Reinholterode", :area_code => "36085") + AreaCode.create(:country => germany, :name => "Wüstheuterode", :area_code => "36087") + AreaCode.create(:country => germany, :name => "Erfurt", :area_code => "361") + AreaCode.create(:country => germany, :name => "Elxleben b Arnstadt", :area_code => "36200") + AreaCode.create(:country => germany, :name => "Walschleben", :area_code => "36201") + AreaCode.create(:country => germany, :name => "Neudietendorf", :area_code => "36202") + AreaCode.create(:country => germany, :name => "Vieselbach", :area_code => "36203") + AreaCode.create(:country => germany, :name => "Stotternheim", :area_code => "36204") + AreaCode.create(:country => germany, :name => "Gräfenroda", :area_code => "36205") + AreaCode.create(:country => germany, :name => "Grossfahner", :area_code => "36206") + AreaCode.create(:country => germany, :name => "Plaue Thür", :area_code => "36207") + AreaCode.create(:country => germany, :name => "Ermstedt", :area_code => "36208") + AreaCode.create(:country => germany, :name => "Klettbach", :area_code => "36209") + AreaCode.create(:country => germany, :name => "Gotha Thür", :area_code => "3621") + AreaCode.create(:country => germany, :name => "Waltershausen Thür", :area_code => "3622") + AreaCode.create(:country => germany, :name => "Friedrichroda", :area_code => "3623") + AreaCode.create(:country => germany, :name => "Ohrdruf", :area_code => "3624") + AreaCode.create(:country => germany, :name => "Tambach-Dietharz Thür Wald", :area_code => "36252") + AreaCode.create(:country => germany, :name => "Georgenthal Thür Wald", :area_code => "36253") + AreaCode.create(:country => germany, :name => "Friedrichswerth", :area_code => "36254") + AreaCode.create(:country => germany, :name => "Goldbach b Gotha", :area_code => "36255") + AreaCode.create(:country => germany, :name => "Wechmar", :area_code => "36256") + AreaCode.create(:country => germany, :name => "Luisenthal Thür", :area_code => "36257") + AreaCode.create(:country => germany, :name => "Friemar", :area_code => "36258") + AreaCode.create(:country => germany, :name => "Tabarz Thür Wald", :area_code => "36259") + AreaCode.create(:country => germany, :name => "Arnstadt", :area_code => "3628") + AreaCode.create(:country => germany, :name => "Stadtilm", :area_code => "3629") + AreaCode.create(:country => germany, :name => "Nordhausen Thür", :area_code => "3631") + AreaCode.create(:country => germany, :name => "Sondershausen", :area_code => "3632") + AreaCode.create(:country => germany, :name => "Grossberndten", :area_code => "36330") + AreaCode.create(:country => germany, :name => "Ilfeld", :area_code => "36331") + AreaCode.create(:country => germany, :name => "Ellrich", :area_code => "36332") + AreaCode.create(:country => germany, :name => "Heringen Helme", :area_code => "36333") + AreaCode.create(:country => germany, :name => "Wolkramshausen", :area_code => "36334") + AreaCode.create(:country => germany, :name => "Grosswechsungen", :area_code => "36335") + AreaCode.create(:country => germany, :name => "Klettenberg", :area_code => "36336") + AreaCode.create(:country => germany, :name => "Schiedungen", :area_code => "36337") + AreaCode.create(:country => germany, :name => "Bleicherode", :area_code => "36338") + AreaCode.create(:country => germany, :name => "Sömmerda", :area_code => "3634") + AreaCode.create(:country => germany, :name => "Kölleda", :area_code => "3635") + AreaCode.create(:country => germany, :name => "Greussen", :area_code => "3636") + AreaCode.create(:country => germany, :name => "Grossenehrich", :area_code => "36370") + AreaCode.create(:country => germany, :name => "Schlossvippach", :area_code => "36371") + AreaCode.create(:country => germany, :name => "Kleinneuhausen", :area_code => "36372") + AreaCode.create(:country => germany, :name => "Buttstädt", :area_code => "36373") + AreaCode.create(:country => germany, :name => "Weissensee", :area_code => "36374") + AreaCode.create(:country => germany, :name => "Kindelbrück", :area_code => "36375") + AreaCode.create(:country => germany, :name => "Straussfurt", :area_code => "36376") + AreaCode.create(:country => germany, :name => "Rastenberg", :area_code => "36377") + AreaCode.create(:country => germany, :name => "Ostramondra", :area_code => "36378") + AreaCode.create(:country => germany, :name => "Holzengel", :area_code => "36379") + AreaCode.create(:country => germany, :name => "Jena", :area_code => "3641") + AreaCode.create(:country => germany, :name => "Camburg", :area_code => "36421") + AreaCode.create(:country => germany, :name => "Reinstädt Thür", :area_code => "36422") + AreaCode.create(:country => germany, :name => "Orlamünde", :area_code => "36423") + AreaCode.create(:country => germany, :name => "Kahla Thür", :area_code => "36424") + AreaCode.create(:country => germany, :name => "Isserstedt", :area_code => "36425") + AreaCode.create(:country => germany, :name => "Ottendorf b Stadtroda", :area_code => "36426") + AreaCode.create(:country => germany, :name => "Dornburg Saale", :area_code => "36427") + AreaCode.create(:country => germany, :name => "Stadtroda", :area_code => "36428") + AreaCode.create(:country => germany, :name => "Weimar Thür", :area_code => "3643") + AreaCode.create(:country => germany, :name => "Apolda", :area_code => "3644") + AreaCode.create(:country => germany, :name => "Kranichfeld", :area_code => "36450") + AreaCode.create(:country => germany, :name => "Buttelstedt", :area_code => "36451") + AreaCode.create(:country => germany, :name => "Berlstedt", :area_code => "36452") + AreaCode.create(:country => germany, :name => "Mellingen", :area_code => "36453") + AreaCode.create(:country => germany, :name => "Magdala", :area_code => "36454") + AreaCode.create(:country => germany, :name => "Bad Berka", :area_code => "36458") + AreaCode.create(:country => germany, :name => "Blankenhain Thür", :area_code => "36459") + AreaCode.create(:country => germany, :name => "Bad Sulza", :area_code => "36461") + AreaCode.create(:country => germany, :name => "Ossmannstedt", :area_code => "36462") + AreaCode.create(:country => germany, :name => "Gebstedt", :area_code => "36463") + AreaCode.create(:country => germany, :name => "Wormstedt", :area_code => "36464") + AreaCode.create(:country => germany, :name => "Oberndorf b Apolda", :area_code => "36465") + AreaCode.create(:country => germany, :name => "Pößneck", :area_code => "3647") + AreaCode.create(:country => germany, :name => "Neustadt an der Orla", :area_code => "36481") + AreaCode.create(:country => germany, :name => "Triptis", :area_code => "36482") + AreaCode.create(:country => germany, :name => "Ziegenrück", :area_code => "36483") + AreaCode.create(:country => germany, :name => "Knau b Pößneck", :area_code => "36484") + AreaCode.create(:country => germany, :name => "Gera", :area_code => "365") + AreaCode.create(:country => germany, :name => "Hermsdorf Thür", :area_code => "36601") + AreaCode.create(:country => germany, :name => "Ronneburg Thür", :area_code => "36602") + AreaCode.create(:country => germany, :name => "Weida", :area_code => "36603") + AreaCode.create(:country => germany, :name => "Münchenbernsdorf", :area_code => "36604") + AreaCode.create(:country => germany, :name => "Bad Köstritz", :area_code => "36605") + AreaCode.create(:country => germany, :name => "Kraftsdorf", :area_code => "36606") + AreaCode.create(:country => germany, :name => "Niederpöllnitz", :area_code => "36607") + AreaCode.create(:country => germany, :name => "Seelingstädt b Gera", :area_code => "36608") + AreaCode.create(:country => germany, :name => "Greiz", :area_code => "3661") + AreaCode.create(:country => germany, :name => "Elsterberg b Plauen", :area_code => "36621") + AreaCode.create(:country => germany, :name => "Triebes", :area_code => "36622") + AreaCode.create(:country => germany, :name => "Berga Elster", :area_code => "36623") + AreaCode.create(:country => germany, :name => "Teichwolframsdorf", :area_code => "36624") + AreaCode.create(:country => germany, :name => "Langenwetzendorf", :area_code => "36625") + AreaCode.create(:country => germany, :name => "Auma", :area_code => "36626") + AreaCode.create(:country => germany, :name => "Zeulenroda", :area_code => "36628") + AreaCode.create(:country => germany, :name => "Schleiz", :area_code => "3663") + AreaCode.create(:country => germany, :name => "Remptendorf", :area_code => "36640") + AreaCode.create(:country => germany, :name => "Harra", :area_code => "36642") + AreaCode.create(:country => germany, :name => "Thimmendorf", :area_code => "36643") + AreaCode.create(:country => germany, :name => "Hirschberg Saale", :area_code => "36644") + AreaCode.create(:country => germany, :name => "Mühltroff", :area_code => "36645") + AreaCode.create(:country => germany, :name => "Tanna b Schleiz", :area_code => "36646") + AreaCode.create(:country => germany, :name => "Saalburg Thür", :area_code => "36647") + AreaCode.create(:country => germany, :name => "Dittersdorf b Schleiz", :area_code => "36648") + AreaCode.create(:country => germany, :name => "Gefell b Schleiz", :area_code => "36649") + AreaCode.create(:country => germany, :name => "Lobenstein", :area_code => "36651") + AreaCode.create(:country => germany, :name => "Wurzbach", :area_code => "36652") + AreaCode.create(:country => germany, :name => "Lehesten Thür Wald", :area_code => "36653") + AreaCode.create(:country => germany, :name => "Eisenberg Thür", :area_code => "36691") + AreaCode.create(:country => germany, :name => "Bürgel", :area_code => "36692") + AreaCode.create(:country => germany, :name => "Crossen an der Elster", :area_code => "36693") + AreaCode.create(:country => germany, :name => "Schkölen Thür", :area_code => "36694") + AreaCode.create(:country => germany, :name => "Söllmnitz", :area_code => "36695") + AreaCode.create(:country => germany, :name => "Lichte", :area_code => "36701") + AreaCode.create(:country => germany, :name => "Lauscha", :area_code => "36702") + AreaCode.create(:country => germany, :name => "Gräfenthal", :area_code => "36703") + AreaCode.create(:country => germany, :name => "Steinheid", :area_code => "36704") + AreaCode.create(:country => germany, :name => "Oberweißbach Thür Wald", :area_code => "36705") + AreaCode.create(:country => germany, :name => "Saalfeld Saale", :area_code => "3671") + AreaCode.create(:country => germany, :name => "Rudolstadt", :area_code => "3672") + AreaCode.create(:country => germany, :name => "Sitzendorf", :area_code => "36730") + AreaCode.create(:country => germany, :name => "Unterloquitz", :area_code => "36731") + AreaCode.create(:country => germany, :name => "Könitz", :area_code => "36732") + AreaCode.create(:country => germany, :name => "Kaulsdorf", :area_code => "36733") + AreaCode.create(:country => germany, :name => "Leutenberg", :area_code => "36734") + AreaCode.create(:country => germany, :name => "Probstzella", :area_code => "36735") + AreaCode.create(:country => germany, :name => "Arnsgereuth", :area_code => "36736") + AreaCode.create(:country => germany, :name => "Drognitz", :area_code => "36737") + AreaCode.create(:country => germany, :name => "Königsee", :area_code => "36738") + AreaCode.create(:country => germany, :name => "Rottenbach", :area_code => "36739") + AreaCode.create(:country => germany, :name => "Bad Blankenburg", :area_code => "36741") + AreaCode.create(:country => germany, :name => "Uhlstädt", :area_code => "36742") + AreaCode.create(:country => germany, :name => "Teichel", :area_code => "36743") + AreaCode.create(:country => germany, :name => "Remda", :area_code => "36744") + AreaCode.create(:country => germany, :name => "Sonneberg Thür", :area_code => "3675") + AreaCode.create(:country => germany, :name => "Heubisch", :area_code => "36761") + AreaCode.create(:country => germany, :name => "Steinach Thür", :area_code => "36762") + AreaCode.create(:country => germany, :name => "Neuhaus-Schierschnitz", :area_code => "36764") + AreaCode.create(:country => germany, :name => "Schalkau", :area_code => "36766") + AreaCode.create(:country => germany, :name => "Ilmenau Thür", :area_code => "3677") + AreaCode.create(:country => germany, :name => "Grossbreitenbach", :area_code => "36781") + AreaCode.create(:country => germany, :name => "Schmiedefeld a Rennsteig", :area_code => "36782") + AreaCode.create(:country => germany, :name => "Gehren Thür", :area_code => "36783") + AreaCode.create(:country => germany, :name => "Stützerbach", :area_code => "36784") + AreaCode.create(:country => germany, :name => "Gräfinau-Angstedt", :area_code => "36785") + AreaCode.create(:country => germany, :name => "Neuhaus a Rennweg", :area_code => "3679") + AreaCode.create(:country => germany, :name => "Suhl", :area_code => "3681") + AreaCode.create(:country => germany, :name => "Zella-Mehlis", :area_code => "3682") + AreaCode.create(:country => germany, :name => "Schmalkalden", :area_code => "3683") + AreaCode.create(:country => germany, :name => "Trusetal", :area_code => "36840") + AreaCode.create(:country => germany, :name => "Schleusingen", :area_code => "36841") + AreaCode.create(:country => germany, :name => "Oberhof Thür", :area_code => "36842") + AreaCode.create(:country => germany, :name => "Benshausen", :area_code => "36843") + AreaCode.create(:country => germany, :name => "Rohr Thür", :area_code => "36844") + AreaCode.create(:country => germany, :name => "Gehlberg", :area_code => "36845") + AreaCode.create(:country => germany, :name => "Suhl-Dietzhausen", :area_code => "36846") + AreaCode.create(:country => germany, :name => "Steinbach-Hallenberg", :area_code => "36847") + AreaCode.create(:country => germany, :name => "Wernshausen", :area_code => "36848") + AreaCode.create(:country => germany, :name => "Kleinschmalkalden", :area_code => "36849") + AreaCode.create(:country => germany, :name => "Hildburghausen", :area_code => "3685") + AreaCode.create(:country => germany, :name => "Eisfeld", :area_code => "3686") + AreaCode.create(:country => germany, :name => "Masserberg", :area_code => "36870") + AreaCode.create(:country => germany, :name => "Bad Colberg-Heldburg", :area_code => "36871") + AreaCode.create(:country => germany, :name => "Themar", :area_code => "36873") + AreaCode.create(:country => germany, :name => "Schönbrunn b Hildburghaus", :area_code => "36874") + AreaCode.create(:country => germany, :name => "Straufhain-Streufdorf", :area_code => "36875") + AreaCode.create(:country => germany, :name => "Oberland", :area_code => "36878") + AreaCode.create(:country => germany, :name => "Eisenach Thür", :area_code => "3691") + AreaCode.create(:country => germany, :name => "Grossenlupnitz", :area_code => "36920") + AreaCode.create(:country => germany, :name => "Wutha-Farnroda", :area_code => "36921") + AreaCode.create(:country => germany, :name => "Gerstungen", :area_code => "36922") + AreaCode.create(:country => germany, :name => "Treffurt", :area_code => "36923") + AreaCode.create(:country => germany, :name => "Mihla", :area_code => "36924") + AreaCode.create(:country => germany, :name => "Marksuhl", :area_code => "36925") + AreaCode.create(:country => germany, :name => "Creuzburg", :area_code => "36926") + AreaCode.create(:country => germany, :name => "Unterellen", :area_code => "36927") + AreaCode.create(:country => germany, :name => "Neuenhof Thür", :area_code => "36928") + AreaCode.create(:country => germany, :name => "Ruhla", :area_code => "36929") + AreaCode.create(:country => germany, :name => "Meiningen", :area_code => "3693") + AreaCode.create(:country => germany, :name => "Oepfershausen", :area_code => "36940") + AreaCode.create(:country => germany, :name => "Wasungen", :area_code => "36941") + AreaCode.create(:country => germany, :name => "Bettenhausen Thür", :area_code => "36943") + AreaCode.create(:country => germany, :name => "Rentwertshausen", :area_code => "36944") + AreaCode.create(:country => germany, :name => "Henneberg", :area_code => "36945") + AreaCode.create(:country => germany, :name => "Erbenhausen Thür", :area_code => "36946") + AreaCode.create(:country => germany, :name => "Jüchsen", :area_code => "36947") + AreaCode.create(:country => germany, :name => "Römhild", :area_code => "36948") + AreaCode.create(:country => germany, :name => "Obermaßfeld-Grimmenthal", :area_code => "36949") + AreaCode.create(:country => germany, :name => "Bad Salzungen", :area_code => "3695") + AreaCode.create(:country => germany, :name => "Bad Liebenstein", :area_code => "36961") + AreaCode.create(:country => germany, :name => "Vacha", :area_code => "36962") + AreaCode.create(:country => germany, :name => "Dorndorf Rhön", :area_code => "36963") + AreaCode.create(:country => germany, :name => "Dermbach Rhön", :area_code => "36964") + AreaCode.create(:country => germany, :name => "Stadtlengsfeld", :area_code => "36965") + AreaCode.create(:country => germany, :name => "Kaltennordheim", :area_code => "36966") + AreaCode.create(:country => germany, :name => "Geisa", :area_code => "36967") + AreaCode.create(:country => germany, :name => "Rossdorf Rhön", :area_code => "36968") + AreaCode.create(:country => germany, :name => "Merkers", :area_code => "36969") + AreaCode.create(:country => germany, :name => "Chemnitz Sachs", :area_code => "371") + AreaCode.create(:country => germany, :name => "Wittgensdorf b Chemnitz", :area_code => "37200") + AreaCode.create(:country => germany, :name => "Claussnitz b Chemnitz", :area_code => "37202") + AreaCode.create(:country => germany, :name => "Gersdorf b Chemnitz", :area_code => "37203") + AreaCode.create(:country => germany, :name => "Lichtenstein Sachs", :area_code => "37204") + AreaCode.create(:country => germany, :name => "Frankenberg", :area_code => "37206") + AreaCode.create(:country => germany, :name => "Hainichen", :area_code => "37207") + AreaCode.create(:country => germany, :name => "Auerswalde", :area_code => "37208") + AreaCode.create(:country => germany, :name => "Einsiedel b Chemnitz", :area_code => "37209") + AreaCode.create(:country => germany, :name => "Meinersdorf", :area_code => "3721") + AreaCode.create(:country => germany, :name => "Limbach-Oberfrohna", :area_code => "3722") + AreaCode.create(:country => germany, :name => "Hohenstein-Ernstthal", :area_code => "3723") + AreaCode.create(:country => germany, :name => "Burgstädt", :area_code => "3724") + AreaCode.create(:country => germany, :name => "Zschopau", :area_code => "3725") + AreaCode.create(:country => germany, :name => "Flöha", :area_code => "3726") + AreaCode.create(:country => germany, :name => "Mittweida", :area_code => "3727") + AreaCode.create(:country => germany, :name => "Augustusburg", :area_code => "37291") + AreaCode.create(:country => germany, :name => "Oederan", :area_code => "37292") + AreaCode.create(:country => germany, :name => "Eppendorf Sachs", :area_code => "37293") + AreaCode.create(:country => germany, :name => "Grünhainichen", :area_code => "37294") + AreaCode.create(:country => germany, :name => "Lugau Erzgeb", :area_code => "37295") + AreaCode.create(:country => germany, :name => "Stollberg Erzgeb", :area_code => "37296") + AreaCode.create(:country => germany, :name => "Thum Sachs", :area_code => "37297") + AreaCode.create(:country => germany, :name => "Oelsnitz Erzgeb", :area_code => "37298") + AreaCode.create(:country => germany, :name => "Freiberg Sachs", :area_code => "3731") + AreaCode.create(:country => germany, :name => "Mulda Sachs", :area_code => "37320") + AreaCode.create(:country => germany, :name => "Frankenstein Sachs", :area_code => "37321") + AreaCode.create(:country => germany, :name => "Brand-Erbisdorf", :area_code => "37322") + AreaCode.create(:country => germany, :name => "Lichtenberg Erzgeb", :area_code => "37323") + AreaCode.create(:country => germany, :name => "Reinsberg Sachs", :area_code => "37324") + AreaCode.create(:country => germany, :name => "Niederbobritzsch", :area_code => "37325") + AreaCode.create(:country => germany, :name => "Frauenstein Sachs", :area_code => "37326") + AreaCode.create(:country => germany, :name => "Rechenberg-Bienenmühle", :area_code => "37327") + AreaCode.create(:country => germany, :name => "Grossschirma", :area_code => "37328") + AreaCode.create(:country => germany, :name => "Grosshartmannsdorf", :area_code => "37329") + AreaCode.create(:country => germany, :name => "Annaberg-Buchholz", :area_code => "3733") + AreaCode.create(:country => germany, :name => "Ehrenfriedersdorf", :area_code => "37341") + AreaCode.create(:country => germany, :name => "Cranzahl", :area_code => "37342") + AreaCode.create(:country => germany, :name => "Jöhstadt", :area_code => "37343") + AreaCode.create(:country => germany, :name => "Crottendorf Sachs", :area_code => "37344") + AreaCode.create(:country => germany, :name => "Geyer", :area_code => "37346") + AreaCode.create(:country => germany, :name => "Bärenstein Kr Annaberg", :area_code => "37347") + AreaCode.create(:country => germany, :name => "Oberwiesenthal Kurort", :area_code => "37348") + AreaCode.create(:country => germany, :name => "Scheibenberg", :area_code => "37349") + AreaCode.create(:country => germany, :name => "Marienberg Sachs", :area_code => "3735") + AreaCode.create(:country => germany, :name => "Olbernhau", :area_code => "37360") + AreaCode.create(:country => germany, :name => "Neuhausen Erzgeb", :area_code => "37361") + AreaCode.create(:country => germany, :name => "Seiffen Erzgeb", :area_code => "37362") + AreaCode.create(:country => germany, :name => "Zöblitz", :area_code => "37363") + AreaCode.create(:country => germany, :name => "Reitzenhain Erzgeb", :area_code => "37364") + AreaCode.create(:country => germany, :name => "Sayda", :area_code => "37365") + AreaCode.create(:country => germany, :name => "Rübenau", :area_code => "37366") + AreaCode.create(:country => germany, :name => "Lengefeld Erzgeb", :area_code => "37367") + AreaCode.create(:country => germany, :name => "Deutschneudorf", :area_code => "37368") + AreaCode.create(:country => germany, :name => "Wolkenstein", :area_code => "37369") + AreaCode.create(:country => germany, :name => "Rochlitz", :area_code => "3737") + AreaCode.create(:country => germany, :name => "Penig", :area_code => "37381") + AreaCode.create(:country => germany, :name => "Geringswalde", :area_code => "37382") + AreaCode.create(:country => germany, :name => "Lunzenau", :area_code => "37383") + AreaCode.create(:country => germany, :name => "Wechselburg", :area_code => "37384") + AreaCode.create(:country => germany, :name => "Plauen", :area_code => "3741") + AreaCode.create(:country => germany, :name => "Oelsnitz Vogtl", :area_code => "37421") + AreaCode.create(:country => germany, :name => "Markneukirchen", :area_code => "37422") + AreaCode.create(:country => germany, :name => "Adorf Vogtl", :area_code => "37423") + AreaCode.create(:country => germany, :name => "Eichigt", :area_code => "37430") + AreaCode.create(:country => germany, :name => "Mehltheuer Vogtl", :area_code => "37431") + AreaCode.create(:country => germany, :name => "Pausa Vogtl", :area_code => "37432") + AreaCode.create(:country => germany, :name => "Gutenfürst", :area_code => "37433") + AreaCode.create(:country => germany, :name => "Bobenneukirchen", :area_code => "37434") + AreaCode.create(:country => germany, :name => "Reuth b Plauen", :area_code => "37435") + AreaCode.create(:country => germany, :name => "Weischlitz", :area_code => "37436") + AreaCode.create(:country => germany, :name => "Bad Elster", :area_code => "37437") + AreaCode.create(:country => germany, :name => "Bad Brambach", :area_code => "37438") + AreaCode.create(:country => germany, :name => "Jocketa", :area_code => "37439") + AreaCode.create(:country => germany, :name => "Auerbach Vogtl.", :area_code => "3744") + AreaCode.create(:country => germany, :name => "Falkenstein Vogtl", :area_code => "3745") + AreaCode.create(:country => germany, :name => "Rothenkirchen Vogtl", :area_code => "37462") + AreaCode.create(:country => germany, :name => "Bergen Vogtl", :area_code => "37463") + AreaCode.create(:country => germany, :name => "Schöneck Vogtl", :area_code => "37464") + AreaCode.create(:country => germany, :name => "Tannenbergsthal Vogtl", :area_code => "37465") + AreaCode.create(:country => germany, :name => "Klingenthal Sachs", :area_code => "37467") + AreaCode.create(:country => germany, :name => "Treuen Vogtl", :area_code => "37468") + AreaCode.create(:country => germany, :name => "Zwickau", :area_code => "375") + AreaCode.create(:country => germany, :name => "Neumark Sachs", :area_code => "37600") + AreaCode.create(:country => germany, :name => "Mülsen Skt Jacob", :area_code => "37601") + AreaCode.create(:country => germany, :name => "Kirchberg Sachs", :area_code => "37602") + AreaCode.create(:country => germany, :name => "Wildenfels", :area_code => "37603") + AreaCode.create(:country => germany, :name => "Mosel", :area_code => "37604") + AreaCode.create(:country => germany, :name => "Hartenstein Sachs", :area_code => "37605") + AreaCode.create(:country => germany, :name => "Lengenfeld Vogtl", :area_code => "37606") + AreaCode.create(:country => germany, :name => "Ebersbrunn Sachs", :area_code => "37607") + AreaCode.create(:country => germany, :name => "Waldenburg Sachs", :area_code => "37608") + AreaCode.create(:country => germany, :name => "Wolkenburg Mulde", :area_code => "37609") + AreaCode.create(:country => germany, :name => "Werdau Sachs", :area_code => "3761") + AreaCode.create(:country => germany, :name => "Crimmitschau", :area_code => "3762") + AreaCode.create(:country => germany, :name => "Glauchau", :area_code => "3763") + AreaCode.create(:country => germany, :name => "Meerane", :area_code => "3764") + AreaCode.create(:country => germany, :name => "Reichenbach Vogtl", :area_code => "3765") + AreaCode.create(:country => germany, :name => "Aue Sachs", :area_code => "3771") + AreaCode.create(:country => germany, :name => "Schneeberg Erzgeb", :area_code => "3772") + AreaCode.create(:country => germany, :name => "Johanngeorgenstadt", :area_code => "3773") + AreaCode.create(:country => germany, :name => "Schwarzenberg", :area_code => "3774") + AreaCode.create(:country => germany, :name => "Eibenstock", :area_code => "37752") + AreaCode.create(:country => germany, :name => "Zwönitz", :area_code => "37754") + AreaCode.create(:country => germany, :name => "Schönheide Erzgeb", :area_code => "37755") + AreaCode.create(:country => germany, :name => "Breitenbrunn Erzgeb", :area_code => "37756") + AreaCode.create(:country => germany, :name => "Rittersgrün", :area_code => "37757") + AreaCode.create(:country => germany, :name => "Rostock", :area_code => "381") + AreaCode.create(:country => germany, :name => "Gelbensande", :area_code => "38201") + AreaCode.create(:country => germany, :name => "Volkenshagen", :area_code => "38202") + AreaCode.create(:country => germany, :name => "Bad Doberan", :area_code => "38203") + AreaCode.create(:country => germany, :name => "Broderstorf", :area_code => "38204") + AreaCode.create(:country => germany, :name => "Tessin b Rostock", :area_code => "38205") + AreaCode.create(:country => germany, :name => "Graal-Müritz Seeheilbad", :area_code => "38206") + AreaCode.create(:country => germany, :name => "Stäbelow", :area_code => "38207") + AreaCode.create(:country => germany, :name => "Kavelstorf", :area_code => "38208") + AreaCode.create(:country => germany, :name => "Sanitz b Rostock", :area_code => "38209") + AreaCode.create(:country => germany, :name => "Ribnitz-Damgarten", :area_code => "3821") + AreaCode.create(:country => germany, :name => "Wustrow Ostseebad", :area_code => "38220") + AreaCode.create(:country => germany, :name => "Marlow", :area_code => "38221") + AreaCode.create(:country => germany, :name => "Semlow", :area_code => "38222") + AreaCode.create(:country => germany, :name => "Saal Vorpom", :area_code => "38223") + AreaCode.create(:country => germany, :name => "Gresenhorst", :area_code => "38224") + AreaCode.create(:country => germany, :name => "Trinwillershagen", :area_code => "38225") + AreaCode.create(:country => germany, :name => "Dierhagen Ostseebad", :area_code => "38226") + AreaCode.create(:country => germany, :name => "Lüdershagen b Barth", :area_code => "38227") + AreaCode.create(:country => germany, :name => "Dettmannsdorf-Kölzow", :area_code => "38228") + AreaCode.create(:country => germany, :name => "Bad Sülze", :area_code => "38229") + AreaCode.create(:country => germany, :name => "Barth", :area_code => "38231") + AreaCode.create(:country => germany, :name => "Zingst Ostseebad", :area_code => "38232") + AreaCode.create(:country => germany, :name => "Prerow Ostseebad", :area_code => "38233") + AreaCode.create(:country => germany, :name => "Born a Darß", :area_code => "38234") + AreaCode.create(:country => germany, :name => "Kröpelin", :area_code => "38292") + AreaCode.create(:country => germany, :name => "Kühlungsborn Ostseebad", :area_code => "38293") + AreaCode.create(:country => germany, :name => "Neubukow", :area_code => "38294") + AreaCode.create(:country => germany, :name => "Satow b Bad Doberan", :area_code => "38295") + AreaCode.create(:country => germany, :name => "Rerik Ostseebad", :area_code => "38296") + AreaCode.create(:country => germany, :name => "Moitin", :area_code => "38297") + AreaCode.create(:country => germany, :name => "Insel Hiddensee", :area_code => "38300") + AreaCode.create(:country => germany, :name => "Putbus", :area_code => "38301") + AreaCode.create(:country => germany, :name => "Sagard", :area_code => "38302") + AreaCode.create(:country => germany, :name => "Sellin Ostseebad", :area_code => "38303") + AreaCode.create(:country => germany, :name => "Garz Rügen", :area_code => "38304") + AreaCode.create(:country => germany, :name => "Gingst", :area_code => "38305") + AreaCode.create(:country => germany, :name => "Samtens", :area_code => "38306") + AreaCode.create(:country => germany, :name => "Poseritz", :area_code => "38307") + AreaCode.create(:country => germany, :name => "Göhren Rügen", :area_code => "38308") + AreaCode.create(:country => germany, :name => "Trent", :area_code => "38309") + AreaCode.create(:country => germany, :name => "Stralsund", :area_code => "3831") + AreaCode.create(:country => germany, :name => "Tribsees", :area_code => "38320") + AreaCode.create(:country => germany, :name => "Martensdorf b Stralsund", :area_code => "38321") + AreaCode.create(:country => germany, :name => "Richtenberg", :area_code => "38322") + AreaCode.create(:country => germany, :name => "Prohn", :area_code => "38323") + AreaCode.create(:country => germany, :name => "Velgast", :area_code => "38324") + AreaCode.create(:country => germany, :name => "Rolofshagen", :area_code => "38325") + AreaCode.create(:country => germany, :name => "Grimmen", :area_code => "38326") + AreaCode.create(:country => germany, :name => "Elmenhorst Vorpom", :area_code => "38327") + AreaCode.create(:country => germany, :name => "Miltzow", :area_code => "38328") + AreaCode.create(:country => germany, :name => "Rakow Vorpom", :area_code => "38331") + AreaCode.create(:country => germany, :name => "Gross Bisdorf", :area_code => "38332") + AreaCode.create(:country => germany, :name => "Horst b Grimmen", :area_code => "38333") + AreaCode.create(:country => germany, :name => "Grammendorf", :area_code => "38334") + AreaCode.create(:country => germany, :name => "Greifswald", :area_code => "3834") + AreaCode.create(:country => germany, :name => "Mesekenhagen", :area_code => "38351") + AreaCode.create(:country => germany, :name => "Kemnitz b Greifswald", :area_code => "38352") + AreaCode.create(:country => germany, :name => "Gützkow b Greifswald", :area_code => "38353") + AreaCode.create(:country => germany, :name => "Wusterhusen", :area_code => "38354") + AreaCode.create(:country => germany, :name => "Züssow", :area_code => "38355") + AreaCode.create(:country => germany, :name => "Behrenhoff", :area_code => "38356") + AreaCode.create(:country => germany, :name => "Wolgast", :area_code => "3836") + AreaCode.create(:country => germany, :name => "Kröslin", :area_code => "38370") + AreaCode.create(:country => germany, :name => "Karlshagen", :area_code => "38371") + AreaCode.create(:country => germany, :name => "Usedom", :area_code => "38372") + AreaCode.create(:country => germany, :name => "Katzow", :area_code => "38373") + AreaCode.create(:country => germany, :name => "Lassan b Wolgast", :area_code => "38374") + AreaCode.create(:country => germany, :name => "Koserow", :area_code => "38375") + AreaCode.create(:country => germany, :name => "Zirchow", :area_code => "38376") + AreaCode.create(:country => germany, :name => "Zinnowitz", :area_code => "38377") + AreaCode.create(:country => germany, :name => "Heringsdorf Seebad", :area_code => "38378") + AreaCode.create(:country => germany, :name => "Benz Usedom", :area_code => "38379") + AreaCode.create(:country => germany, :name => "Bergen auf Rügen", :area_code => "3838") + AreaCode.create(:country => germany, :name => "Altenkirchen Rügen", :area_code => "38391") + AreaCode.create(:country => germany, :name => "Sassnitz", :area_code => "38392") + AreaCode.create(:country => germany, :name => "Binz Ostseebad", :area_code => "38393") + AreaCode.create(:country => germany, :name => "Wismar Meckl", :area_code => "3841") + AreaCode.create(:country => germany, :name => "Neukloster", :area_code => "38422") + AreaCode.create(:country => germany, :name => "Bad Kleinen", :area_code => "38423") + AreaCode.create(:country => germany, :name => "Bobitz", :area_code => "38424") + AreaCode.create(:country => germany, :name => "Kirchdorf Poel", :area_code => "38425") + AreaCode.create(:country => germany, :name => "Neuburg-Steinhausen", :area_code => "38426") + AreaCode.create(:country => germany, :name => "Blowatz", :area_code => "38427") + AreaCode.create(:country => germany, :name => "Hohenkirchen b Wismar", :area_code => "38428") + AreaCode.create(:country => germany, :name => "Glasin", :area_code => "38429") + AreaCode.create(:country => germany, :name => "Güstrow", :area_code => "3843") + AreaCode.create(:country => germany, :name => "Schwaan", :area_code => "3844") + AreaCode.create(:country => germany, :name => "Tarnow b Bützow", :area_code => "38450") + AreaCode.create(:country => germany, :name => "Hoppenrade b Güstrow", :area_code => "38451") + AreaCode.create(:country => germany, :name => "Lalendorf", :area_code => "38452") + AreaCode.create(:country => germany, :name => "Mistorf", :area_code => "38453") + AreaCode.create(:country => germany, :name => "Kritzkow", :area_code => "38454") + AreaCode.create(:country => germany, :name => "Plaaz", :area_code => "38455") + AreaCode.create(:country => germany, :name => "Langhagen b Güstrow", :area_code => "38456") + AreaCode.create(:country => germany, :name => "Krakow am See", :area_code => "38457") + AreaCode.create(:country => germany, :name => "Zehna", :area_code => "38458") + AreaCode.create(:country => germany, :name => "Laage", :area_code => "38459") + AreaCode.create(:country => germany, :name => "Bützow", :area_code => "38461") + AreaCode.create(:country => germany, :name => "Baumgarten Meckl", :area_code => "38462") + AreaCode.create(:country => germany, :name => "Bernitt", :area_code => "38464") + AreaCode.create(:country => germany, :name => "Jürgenshagen", :area_code => "38466") + AreaCode.create(:country => germany, :name => "Sternberg", :area_code => "3847") + AreaCode.create(:country => germany, :name => "Witzin", :area_code => "38481") + AreaCode.create(:country => germany, :name => "Warin", :area_code => "38482") + AreaCode.create(:country => germany, :name => "Brüel", :area_code => "38483") + AreaCode.create(:country => germany, :name => "Ventschow", :area_code => "38484") + AreaCode.create(:country => germany, :name => "Dabel", :area_code => "38485") + AreaCode.create(:country => germany, :name => "Gustävel", :area_code => "38486") + AreaCode.create(:country => germany, :name => "Demen", :area_code => "38488") + AreaCode.create(:country => germany, :name => "Schwerin Meckl", :area_code => "385") + AreaCode.create(:country => germany, :name => "Raben Steinfeld", :area_code => "3860") + AreaCode.create(:country => germany, :name => "Plate", :area_code => "3861") + AreaCode.create(:country => germany, :name => "Crivitz", :area_code => "3863") + AreaCode.create(:country => germany, :name => "Holthusen", :area_code => "3865") + AreaCode.create(:country => germany, :name => "Cambs", :area_code => "3866") + AreaCode.create(:country => germany, :name => "Lübstorf", :area_code => "3867") + AreaCode.create(:country => germany, :name => "Rastow", :area_code => "3868") + AreaCode.create(:country => germany, :name => "Dümmer", :area_code => "3869") + AreaCode.create(:country => germany, :name => "Parchim", :area_code => "3871") + AreaCode.create(:country => germany, :name => "Grebbin", :area_code => "38720") + AreaCode.create(:country => germany, :name => "Ziegendorf", :area_code => "38721") + AreaCode.create(:country => germany, :name => "Raduhn", :area_code => "38722") + AreaCode.create(:country => germany, :name => "Kladrum", :area_code => "38723") + AreaCode.create(:country => germany, :name => "Siggelkow", :area_code => "38724") + AreaCode.create(:country => germany, :name => "Gross Godems", :area_code => "38725") + AreaCode.create(:country => germany, :name => "Spornitz", :area_code => "38726") + AreaCode.create(:country => germany, :name => "Mestlin", :area_code => "38727") + AreaCode.create(:country => germany, :name => "Domsühl", :area_code => "38728") + AreaCode.create(:country => germany, :name => "Marnitz", :area_code => "38729") + AreaCode.create(:country => germany, :name => "Lübz", :area_code => "38731") + AreaCode.create(:country => germany, :name => "Gallin b Lübz", :area_code => "38732") + AreaCode.create(:country => germany, :name => "Karbow-Vietlübbe", :area_code => "38733") + AreaCode.create(:country => germany, :name => "Plau am See", :area_code => "38735") + AreaCode.create(:country => germany, :name => "Goldberg Meckl", :area_code => "38736") + AreaCode.create(:country => germany, :name => "Ganzlin", :area_code => "38737") + AreaCode.create(:country => germany, :name => "Karow b Lübz", :area_code => "38738") + AreaCode.create(:country => germany, :name => "Ludwigslust Meckl", :area_code => "3874") + AreaCode.create(:country => germany, :name => "Malliss", :area_code => "38750") + AreaCode.create(:country => germany, :name => "Picher", :area_code => "38751") + AreaCode.create(:country => germany, :name => "Zierzow b Ludwigslust", :area_code => "38752") + AreaCode.create(:country => germany, :name => "Wöbbelin", :area_code => "38753") + AreaCode.create(:country => germany, :name => "Leussow b Ludwigslust", :area_code => "38754") + AreaCode.create(:country => germany, :name => "Eldena", :area_code => "38755") + AreaCode.create(:country => germany, :name => "Grabow Meckl", :area_code => "38756") + AreaCode.create(:country => germany, :name => "Neustadt-Glewe", :area_code => "38757") + AreaCode.create(:country => germany, :name => "Dömitz", :area_code => "38758") + AreaCode.create(:country => germany, :name => "Tewswoos", :area_code => "38759") + AreaCode.create(:country => germany, :name => "Perleberg", :area_code => "3876") + AreaCode.create(:country => germany, :name => "Wittenberge", :area_code => "3877") + AreaCode.create(:country => germany, :name => "Lanz Brandenb", :area_code => "38780") + AreaCode.create(:country => germany, :name => "Mellen", :area_code => "38781") + AreaCode.create(:country => germany, :name => "Reetz b Perleberg", :area_code => "38782") + AreaCode.create(:country => germany, :name => "Dallmin", :area_code => "38783") + AreaCode.create(:country => germany, :name => "Kleinow Kr Prignitz", :area_code => "38784") + AreaCode.create(:country => germany, :name => "Berge b Perleberg", :area_code => "38785") + AreaCode.create(:country => germany, :name => "Glöwen", :area_code => "38787") + AreaCode.create(:country => germany, :name => "Gross Warnow", :area_code => "38788") + AreaCode.create(:country => germany, :name => "Wolfshagen b Perleberg", :area_code => "38789") + AreaCode.create(:country => germany, :name => "Bad Wilsnack", :area_code => "38791") + AreaCode.create(:country => germany, :name => "Lenzen (Elbe)", :area_code => "38792") + AreaCode.create(:country => germany, :name => "Dergenthin", :area_code => "38793") + AreaCode.create(:country => germany, :name => "Cumlosen", :area_code => "38794") + AreaCode.create(:country => germany, :name => "Viesecke", :area_code => "38796") + AreaCode.create(:country => germany, :name => "Karstädt Kr Prignitz", :area_code => "38797") + AreaCode.create(:country => germany, :name => "Grevesmühlen", :area_code => "3881") + AreaCode.create(:country => germany, :name => "Lüdersdorf Meckl", :area_code => "38821") + AreaCode.create(:country => germany, :name => "Diedrichshagen b Grevesmühlen", :area_code => "38822") + AreaCode.create(:country => germany, :name => "Selmsdorf", :area_code => "38823") + AreaCode.create(:country => germany, :name => "Mallentin", :area_code => "38824") + AreaCode.create(:country => germany, :name => "Klütz", :area_code => "38825") + AreaCode.create(:country => germany, :name => "Dassow", :area_code => "38826") + AreaCode.create(:country => germany, :name => "Kalkhorst", :area_code => "38827") + AreaCode.create(:country => germany, :name => "Schönberg Meckl", :area_code => "38828") + AreaCode.create(:country => germany, :name => "Hagenow", :area_code => "3883") + AreaCode.create(:country => germany, :name => "Neuhaus Elbe", :area_code => "38841") + AreaCode.create(:country => germany, :name => "Lüttenmark", :area_code => "38842") + AreaCode.create(:country => germany, :name => "Bennin", :area_code => "38843") + AreaCode.create(:country => germany, :name => "Gülze", :area_code => "38844") + AreaCode.create(:country => germany, :name => "Kaarssen", :area_code => "38845") + AreaCode.create(:country => germany, :name => "Boizenburg Elbe", :area_code => "38847") + AreaCode.create(:country => germany, :name => "Vellahn", :area_code => "38848") + AreaCode.create(:country => germany, :name => "Gammelin", :area_code => "38850") + AreaCode.create(:country => germany, :name => "Zarrentin Meckl", :area_code => "38851") + AreaCode.create(:country => germany, :name => "Wittenburg", :area_code => "38852") + AreaCode.create(:country => germany, :name => "Drönnewitz b Hagenow", :area_code => "38853") + AreaCode.create(:country => germany, :name => "Redefin", :area_code => "38854") + AreaCode.create(:country => germany, :name => "Lübtheen", :area_code => "38855") + AreaCode.create(:country => germany, :name => "Pritzier b Hagenow", :area_code => "38856") + AreaCode.create(:country => germany, :name => "Lassahn", :area_code => "38858") + AreaCode.create(:country => germany, :name => "Alt Zachun", :area_code => "38859") + AreaCode.create(:country => germany, :name => "Gadebusch", :area_code => "3886") + AreaCode.create(:country => germany, :name => "Mühlen Eichsen", :area_code => "38871") + AreaCode.create(:country => germany, :name => "Rehna", :area_code => "38872") + AreaCode.create(:country => germany, :name => "Carlow", :area_code => "38873") + AreaCode.create(:country => germany, :name => "Lützow", :area_code => "38874") + AreaCode.create(:country => germany, :name => "Schlagsdorf b Gadebusch", :area_code => "38875") + AreaCode.create(:country => germany, :name => "Roggendorf", :area_code => "38876") + AreaCode.create(:country => germany, :name => "Beetzendorf", :area_code => "39000") + AreaCode.create(:country => germany, :name => "Apenburg", :area_code => "39001") + AreaCode.create(:country => germany, :name => "Oebisfelde", :area_code => "39002") + AreaCode.create(:country => germany, :name => "Jübar", :area_code => "39003") + AreaCode.create(:country => germany, :name => "Köckte b Gardelegen", :area_code => "39004") + AreaCode.create(:country => germany, :name => "Kusey", :area_code => "39005") + AreaCode.create(:country => germany, :name => "Miesterhorst", :area_code => "39006") + AreaCode.create(:country => germany, :name => "Tangeln", :area_code => "39007") + AreaCode.create(:country => germany, :name => "Kunrau", :area_code => "39008") + AreaCode.create(:country => germany, :name => "Badel", :area_code => "39009") + AreaCode.create(:country => germany, :name => "Salzwedel", :area_code => "3901") + AreaCode.create(:country => germany, :name => "Diesdorf Altm", :area_code => "3902") + AreaCode.create(:country => germany, :name => "Brunau", :area_code => "39030") + AreaCode.create(:country => germany, :name => "Dähre", :area_code => "39031") + AreaCode.create(:country => germany, :name => "Mahlsdorf b Salzwedel", :area_code => "39032") + AreaCode.create(:country => germany, :name => "Wallstawe", :area_code => "39033") + AreaCode.create(:country => germany, :name => "Fleetmark", :area_code => "39034") + AreaCode.create(:country => germany, :name => "Kuhfelde", :area_code => "39035") + AreaCode.create(:country => germany, :name => "Binde", :area_code => "39036") + AreaCode.create(:country => germany, :name => "Pretzier", :area_code => "39037") + AreaCode.create(:country => germany, :name => "Henningen", :area_code => "39038") + AreaCode.create(:country => germany, :name => "Bonese", :area_code => "39039") + AreaCode.create(:country => germany, :name => "Haldensleben", :area_code => "3904") + AreaCode.create(:country => germany, :name => "Bartensleben", :area_code => "39050") + AreaCode.create(:country => germany, :name => "Calvörde", :area_code => "39051") + AreaCode.create(:country => germany, :name => "Erxleben b Haldensleben", :area_code => "39052") + AreaCode.create(:country => germany, :name => "Süplingen", :area_code => "39053") + AreaCode.create(:country => germany, :name => "Flechtingen", :area_code => "39054") + AreaCode.create(:country => germany, :name => "Hörsingen", :area_code => "39055") + AreaCode.create(:country => germany, :name => "Klüden", :area_code => "39056") + AreaCode.create(:country => germany, :name => "Rätzlingen Sachs-Anh", :area_code => "39057") + AreaCode.create(:country => germany, :name => "Uthmöden", :area_code => "39058") + AreaCode.create(:country => germany, :name => "Wegenstedt", :area_code => "39059") + AreaCode.create(:country => germany, :name => "Weferlingen", :area_code => "39061") + AreaCode.create(:country => germany, :name => "Bebertal", :area_code => "39062") + AreaCode.create(:country => germany, :name => "Gardelegen", :area_code => "3907") + AreaCode.create(:country => germany, :name => "Kalbe Milde", :area_code => "39080") + AreaCode.create(:country => germany, :name => "Kakerbeck Sachs-Anh", :area_code => "39081") + AreaCode.create(:country => germany, :name => "Mieste", :area_code => "39082") + AreaCode.create(:country => germany, :name => "Messdorf", :area_code => "39083") + AreaCode.create(:country => germany, :name => "Lindstedt", :area_code => "39084") + AreaCode.create(:country => germany, :name => "Zichtau", :area_code => "39085") + AreaCode.create(:country => germany, :name => "Jävenitz", :area_code => "39086") + AreaCode.create(:country => germany, :name => "Jerchel Altmark", :area_code => "39087") + AreaCode.create(:country => germany, :name => "Letzlingen", :area_code => "39088") + AreaCode.create(:country => germany, :name => "Bismark Altmark", :area_code => "39089") + AreaCode.create(:country => germany, :name => "Klötze Altmark", :area_code => "3909") + AreaCode.create(:country => germany, :name => "Magdeburg", :area_code => "391") + AreaCode.create(:country => germany, :name => "Gommern", :area_code => "39200") + AreaCode.create(:country => germany, :name => "Wolmirstedt", :area_code => "39201") + AreaCode.create(:country => germany, :name => "Gross Ammensleben", :area_code => "39202") + AreaCode.create(:country => germany, :name => "Barleben", :area_code => "39203") + AreaCode.create(:country => germany, :name => "Niederndodeleben", :area_code => "39204") + AreaCode.create(:country => germany, :name => "Langenweddingen", :area_code => "39205") + AreaCode.create(:country => germany, :name => "Eichenbarleben", :area_code => "39206") + AreaCode.create(:country => germany, :name => "Colbitz", :area_code => "39207") + AreaCode.create(:country => germany, :name => "Loitsche", :area_code => "39208") + AreaCode.create(:country => germany, :name => "Wanzleben", :area_code => "39209") + AreaCode.create(:country => germany, :name => "Burg b Magdeburg", :area_code => "3921") + AreaCode.create(:country => germany, :name => "Möckern b Magdeburg", :area_code => "39221") + AreaCode.create(:country => germany, :name => "Möser", :area_code => "39222") + AreaCode.create(:country => germany, :name => "Theessen", :area_code => "39223") + AreaCode.create(:country => germany, :name => "Büden", :area_code => "39224") + AreaCode.create(:country => germany, :name => "Altengrabow", :area_code => "39225") + AreaCode.create(:country => germany, :name => "Hohenziatz", :area_code => "39226") + AreaCode.create(:country => germany, :name => "Zerbst", :area_code => "3923") + AreaCode.create(:country => germany, :name => "Leitzkau", :area_code => "39241") + AreaCode.create(:country => germany, :name => "Prödel", :area_code => "39242") + AreaCode.create(:country => germany, :name => "Nedlitz b Zerbst", :area_code => "39243") + AreaCode.create(:country => germany, :name => "Steutz", :area_code => "39244") + AreaCode.create(:country => germany, :name => "Loburg", :area_code => "39245") + AreaCode.create(:country => germany, :name => "Lindau Anh", :area_code => "39246") + AreaCode.create(:country => germany, :name => "Güterglück", :area_code => "39247") + AreaCode.create(:country => germany, :name => "Dobritz", :area_code => "39248") + AreaCode.create(:country => germany, :name => "Stassfurt", :area_code => "3925") + AreaCode.create(:country => germany, :name => "Güsten Anh", :area_code => "39262") + AreaCode.create(:country => germany, :name => "Unseburg", :area_code => "39263") + AreaCode.create(:country => germany, :name => "Kroppenstedt", :area_code => "39264") + AreaCode.create(:country => germany, :name => "Löderburg", :area_code => "39265") + AreaCode.create(:country => germany, :name => "Förderstedt", :area_code => "39266") + AreaCode.create(:country => germany, :name => "Schneidlingen", :area_code => "39267") + AreaCode.create(:country => germany, :name => "Egeln", :area_code => "39268") + AreaCode.create(:country => germany, :name => "Schönebeck Elbe", :area_code => "3928") + AreaCode.create(:country => germany, :name => "Calbe Saale", :area_code => "39291") + AreaCode.create(:country => germany, :name => "Biederitz", :area_code => "39292") + AreaCode.create(:country => germany, :name => "Dreileben", :area_code => "39293") + AreaCode.create(:country => germany, :name => "Gross Rosenburg", :area_code => "39294") + AreaCode.create(:country => germany, :name => "Zuchau", :area_code => "39295") + AreaCode.create(:country => germany, :name => "Welsleben", :area_code => "39296") + AreaCode.create(:country => germany, :name => "Eickendorf Kr Schönebeck", :area_code => "39297") + AreaCode.create(:country => germany, :name => "Barby Elbe", :area_code => "39298") + AreaCode.create(:country => germany, :name => "Stendal", :area_code => "3931") + AreaCode.create(:country => germany, :name => "Schinne", :area_code => "39320") + AreaCode.create(:country => germany, :name => "Arneburg", :area_code => "39321") + AreaCode.create(:country => germany, :name => "Tangermünde", :area_code => "39322") + AreaCode.create(:country => germany, :name => "Schönhausen Elbe", :area_code => "39323") + AreaCode.create(:country => germany, :name => "Kläden b Stendal", :area_code => "39324") + AreaCode.create(:country => germany, :name => "Vinzelberg", :area_code => "39325") + AreaCode.create(:country => germany, :name => "Klietz", :area_code => "39327") + AreaCode.create(:country => germany, :name => "Rochau", :area_code => "39328") + AreaCode.create(:country => germany, :name => "Möringen", :area_code => "39329") + AreaCode.create(:country => germany, :name => "Genthin", :area_code => "3933") + AreaCode.create(:country => germany, :name => "Redekin", :area_code => "39341") + AreaCode.create(:country => germany, :name => "Gladau", :area_code => "39342") + AreaCode.create(:country => germany, :name => "Jerichow", :area_code => "39343") + AreaCode.create(:country => germany, :name => "Güsen", :area_code => "39344") + AreaCode.create(:country => germany, :name => "Parchen", :area_code => "39345") + AreaCode.create(:country => germany, :name => "Tucheim", :area_code => "39346") + AreaCode.create(:country => germany, :name => "Kade", :area_code => "39347") + AreaCode.create(:country => germany, :name => "Klitsche", :area_code => "39348") + AreaCode.create(:country => germany, :name => "Parey Elbe", :area_code => "39349") + AreaCode.create(:country => germany, :name => "Tangerhütte", :area_code => "3935") + AreaCode.create(:country => germany, :name => "Lüderitz", :area_code => "39361") + AreaCode.create(:country => germany, :name => "Grieben b Tangerhütte", :area_code => "39362") + AreaCode.create(:country => germany, :name => "Angern", :area_code => "39363") + AreaCode.create(:country => germany, :name => "Dolle", :area_code => "39364") + AreaCode.create(:country => germany, :name => "Bellingen b Stendal", :area_code => "39365") + AreaCode.create(:country => germany, :name => "Kehnert", :area_code => "39366") + AreaCode.create(:country => germany, :name => "Osterburg Altmark", :area_code => "3937") + AreaCode.create(:country => germany, :name => "Kamern", :area_code => "39382") + AreaCode.create(:country => germany, :name => "Sandau Elbe", :area_code => "39383") + AreaCode.create(:country => germany, :name => "Arendsee Altmark", :area_code => "39384") + AreaCode.create(:country => germany, :name => "Seehausen Altmark", :area_code => "39386") + AreaCode.create(:country => germany, :name => "Havelberg", :area_code => "39387") + AreaCode.create(:country => germany, :name => "Goldbeck Altm", :area_code => "39388") + AreaCode.create(:country => germany, :name => "Schollene", :area_code => "39389") + AreaCode.create(:country => germany, :name => "Iden", :area_code => "39390") + AreaCode.create(:country => germany, :name => "Lückstedt", :area_code => "39391") + AreaCode.create(:country => germany, :name => "Rönnebeck Sachs-Ahn", :area_code => "39392") + AreaCode.create(:country => germany, :name => "Werben Elbe", :area_code => "39393") + AreaCode.create(:country => germany, :name => "Hohenberg-Krusemark", :area_code => "39394") + AreaCode.create(:country => germany, :name => "Wanzer", :area_code => "39395") + AreaCode.create(:country => germany, :name => "Neukirchen Altmark", :area_code => "39396") + AreaCode.create(:country => germany, :name => "Geestgottberg", :area_code => "39397") + AreaCode.create(:country => germany, :name => "Gross Garz", :area_code => "39398") + AreaCode.create(:country => germany, :name => "Kleinau", :area_code => "39399") + AreaCode.create(:country => germany, :name => "Wefensleben", :area_code => "39400") + AreaCode.create(:country => germany, :name => "Neuwegersleben", :area_code => "39401") + AreaCode.create(:country => germany, :name => "Völpke", :area_code => "39402") + AreaCode.create(:country => germany, :name => "Gröningen Sachs-Ahn", :area_code => "39403") + AreaCode.create(:country => germany, :name => "Ausleben", :area_code => "39404") + AreaCode.create(:country => germany, :name => "Hötensleben", :area_code => "39405") + AreaCode.create(:country => germany, :name => "Harbke", :area_code => "39406") + AreaCode.create(:country => germany, :name => "Seehausen Börde", :area_code => "39407") + AreaCode.create(:country => germany, :name => "Hadmersleben", :area_code => "39408") + AreaCode.create(:country => germany, :name => "Eilsleben", :area_code => "39409") + AreaCode.create(:country => germany, :name => "Halberstadt", :area_code => "3941") + AreaCode.create(:country => germany, :name => "Osterwieck", :area_code => "39421") + AreaCode.create(:country => germany, :name => "Badersleben", :area_code => "39422") + AreaCode.create(:country => germany, :name => "Wegeleben", :area_code => "39423") + AreaCode.create(:country => germany, :name => "Schwanebeck Sachs-Anh", :area_code => "39424") + AreaCode.create(:country => germany, :name => "Dingelstedt a Huy", :area_code => "39425") + AreaCode.create(:country => germany, :name => "Hessen", :area_code => "39426") + AreaCode.create(:country => germany, :name => "Ströbeck", :area_code => "39427") + AreaCode.create(:country => germany, :name => "Pabstorf", :area_code => "39428") + AreaCode.create(:country => germany, :name => "Wernigerode", :area_code => "3943") + AreaCode.create(:country => germany, :name => "Blankenburg Harz", :area_code => "3944") + AreaCode.create(:country => germany, :name => "Wasserleben", :area_code => "39451") + AreaCode.create(:country => germany, :name => "Ilsenburg", :area_code => "39452") + AreaCode.create(:country => germany, :name => "Derenburg", :area_code => "39453") + AreaCode.create(:country => germany, :name => "Elbingerode Harz", :area_code => "39454") + AreaCode.create(:country => germany, :name => "Schierke", :area_code => "39455") + AreaCode.create(:country => germany, :name => "Altenbrak", :area_code => "39456") + AreaCode.create(:country => germany, :name => "Benneckenstein Harz", :area_code => "39457") + AreaCode.create(:country => germany, :name => "Heudeber", :area_code => "39458") + AreaCode.create(:country => germany, :name => "Hasselfelde", :area_code => "39459") + AreaCode.create(:country => germany, :name => "Quedlinburg", :area_code => "3946") + AreaCode.create(:country => germany, :name => "Thale", :area_code => "3947") + AreaCode.create(:country => germany, :name => "Hedersleben b Aschersleben", :area_code => "39481") + AreaCode.create(:country => germany, :name => "Gatersleben", :area_code => "39482") + AreaCode.create(:country => germany, :name => "Ballenstedt", :area_code => "39483") + AreaCode.create(:country => germany, :name => "Harzgerode", :area_code => "39484") + AreaCode.create(:country => germany, :name => "Gernrode Harz", :area_code => "39485") + AreaCode.create(:country => germany, :name => "Friedrichsbrunn", :area_code => "39487") + AreaCode.create(:country => germany, :name => "Güntersberge", :area_code => "39488") + AreaCode.create(:country => germany, :name => "Strassberg Harz", :area_code => "39489") + AreaCode.create(:country => germany, :name => "Oschersleben Bode", :area_code => "3949") + AreaCode.create(:country => germany, :name => "Neubrandenburg", :area_code => "395") + AreaCode.create(:country => germany, :name => "Zwiedorf", :area_code => "39600") + AreaCode.create(:country => germany, :name => "Friedland Meckl", :area_code => "39601") + AreaCode.create(:country => germany, :name => "Kleeth", :area_code => "39602") + AreaCode.create(:country => germany, :name => "Burg Stargard", :area_code => "39603") + AreaCode.create(:country => germany, :name => "Wildberg b Altentreptow", :area_code => "39604") + AreaCode.create(:country => germany, :name => "Gross Nemerow", :area_code => "39605") + AreaCode.create(:country => germany, :name => "Glienke", :area_code => "39606") + AreaCode.create(:country => germany, :name => "Kotelow", :area_code => "39607") + AreaCode.create(:country => germany, :name => "Staven", :area_code => "39608") + AreaCode.create(:country => germany, :name => "Altentreptow", :area_code => "3961") + AreaCode.create(:country => germany, :name => "Penzlin b Waren", :area_code => "3962") + AreaCode.create(:country => germany, :name => "Woldegk", :area_code => "3963") + AreaCode.create(:country => germany, :name => "Bredenfelde b Strasburg", :area_code => "3964") + AreaCode.create(:country => germany, :name => "Burow b Altentreptow", :area_code => "3965") + AreaCode.create(:country => germany, :name => "Cölpin", :area_code => "3966") + AreaCode.create(:country => germany, :name => "Oertzenhof b Strasburg", :area_code => "3967") + AreaCode.create(:country => germany, :name => "Schönbeck Meckl", :area_code => "3968") + AreaCode.create(:country => germany, :name => "Siedenbollentin", :area_code => "3969") + AreaCode.create(:country => germany, :name => "Anklam", :area_code => "3971") + AreaCode.create(:country => germany, :name => "Liepen b Anklam", :area_code => "39721") + AreaCode.create(:country => germany, :name => "Sarnow b Anklam", :area_code => "39722") + AreaCode.create(:country => germany, :name => "Krien", :area_code => "39723") + AreaCode.create(:country => germany, :name => "Klein Bünzow", :area_code => "39724") + AreaCode.create(:country => germany, :name => "Ducherow", :area_code => "39726") + AreaCode.create(:country => germany, :name => "Spantekow", :area_code => "39727") + AreaCode.create(:country => germany, :name => "Medow b Anklam", :area_code => "39728") + AreaCode.create(:country => germany, :name => "Pasewalk", :area_code => "3973") + AreaCode.create(:country => germany, :name => "Nechlin", :area_code => "39740") + AreaCode.create(:country => germany, :name => "Jatznick", :area_code => "39741") + AreaCode.create(:country => germany, :name => "Brüssow b Pasewalk", :area_code => "39742") + AreaCode.create(:country => germany, :name => "Zerrenthin", :area_code => "39743") + AreaCode.create(:country => germany, :name => "Rothenklempenow", :area_code => "39744") + AreaCode.create(:country => germany, :name => "Hetzdorf b Strasburg", :area_code => "39745") + AreaCode.create(:country => germany, :name => "Krackow", :area_code => "39746") + AreaCode.create(:country => germany, :name => "Züsedom", :area_code => "39747") + AreaCode.create(:country => germany, :name => "Viereck", :area_code => "39748") + AreaCode.create(:country => germany, :name => "Grambow b Pasewalk", :area_code => "39749") + AreaCode.create(:country => germany, :name => "Penkun", :area_code => "39751") + AreaCode.create(:country => germany, :name => "Blumenhagen b Strasburg", :area_code => "39752") + AreaCode.create(:country => germany, :name => "Strasburg", :area_code => "39753") + AreaCode.create(:country => germany, :name => "Löcknitz Vorpom", :area_code => "39754") + AreaCode.create(:country => germany, :name => "Torgelow b Ueckermünde", :area_code => "3976") + AreaCode.create(:country => germany, :name => "Ueckermünde", :area_code => "39771") + AreaCode.create(:country => germany, :name => "Rothemühl", :area_code => "39772") + AreaCode.create(:country => germany, :name => "Altwarp", :area_code => "39773") + AreaCode.create(:country => germany, :name => "Mönkebude", :area_code => "39774") + AreaCode.create(:country => germany, :name => "Ahlbeck b Torgelow", :area_code => "39775") + AreaCode.create(:country => germany, :name => "Hintersee", :area_code => "39776") + AreaCode.create(:country => germany, :name => "Borkenfriede", :area_code => "39777") + AreaCode.create(:country => germany, :name => "Ferdinandshof b Torgelow", :area_code => "39778") + AreaCode.create(:country => germany, :name => "Eggesin", :area_code => "39779") + AreaCode.create(:country => germany, :name => "Neustrelitz", :area_code => "3981") + AreaCode.create(:country => germany, :name => "Triepkendorf", :area_code => "39820") + AreaCode.create(:country => germany, :name => "Carpin", :area_code => "39821") + AreaCode.create(:country => germany, :name => "Kratzeburg", :area_code => "39822") + AreaCode.create(:country => germany, :name => "Rechlin", :area_code => "39823") + AreaCode.create(:country => germany, :name => "Hohenzieritz", :area_code => "39824") + AreaCode.create(:country => germany, :name => "Wokuhl", :area_code => "39825") + AreaCode.create(:country => germany, :name => "Blankensee b Neustrelitz", :area_code => "39826") + AreaCode.create(:country => germany, :name => "Schwarz b Neustrelitz", :area_code => "39827") + AreaCode.create(:country => germany, :name => "Wustrow Kr Mecklenburg-Strelitz", :area_code => "39828") + AreaCode.create(:country => germany, :name => "Blankenförde", :area_code => "39829") + AreaCode.create(:country => germany, :name => "Feldberg Meckl", :area_code => "39831") + AreaCode.create(:country => germany, :name => "Wesenberg Meckl", :area_code => "39832") + AreaCode.create(:country => germany, :name => "Mirow Kr Neustrelitz", :area_code => "39833") + AreaCode.create(:country => germany, :name => "Prenzlau", :area_code => "3984") + AreaCode.create(:country => germany, :name => "Göritz b Prenzlau", :area_code => "39851") + AreaCode.create(:country => germany, :name => "Schönermark b Prenzlau", :area_code => "39852") + AreaCode.create(:country => germany, :name => "Holzendorf b Prenzlau", :area_code => "39853") + AreaCode.create(:country => germany, :name => "Kleptow", :area_code => "39854") + AreaCode.create(:country => germany, :name => "Parmen-Weggun", :area_code => "39855") + AreaCode.create(:country => germany, :name => "Beenz b Prenzlau", :area_code => "39856") + AreaCode.create(:country => germany, :name => "Drense", :area_code => "39857") + AreaCode.create(:country => germany, :name => "Bietikow", :area_code => "39858") + AreaCode.create(:country => germany, :name => "Fürstenwerder", :area_code => "39859") + AreaCode.create(:country => germany, :name => "Gramzow b Prenzlau", :area_code => "39861") + AreaCode.create(:country => germany, :name => "Schmölln b Prenzlau", :area_code => "39862") + AreaCode.create(:country => germany, :name => "Seehausen b Prenzlau", :area_code => "39863") + AreaCode.create(:country => germany, :name => "Templin", :area_code => "3987") + AreaCode.create(:country => germany, :name => "Ringenwalde b Templin", :area_code => "39881") + AreaCode.create(:country => germany, :name => "Gollin", :area_code => "39882") + AreaCode.create(:country => germany, :name => "Groß Dölln", :area_code => "39883") + AreaCode.create(:country => germany, :name => "Hassleben b Prenzlau", :area_code => "39884") + AreaCode.create(:country => germany, :name => "Jakobshagen", :area_code => "39885") + AreaCode.create(:country => germany, :name => "Milmersdorf", :area_code => "39886") + AreaCode.create(:country => germany, :name => "Gerswalde", :area_code => "39887") + AreaCode.create(:country => germany, :name => "Lychen", :area_code => "39888") + AreaCode.create(:country => germany, :name => "Boitzenburg", :area_code => "39889") + AreaCode.create(:country => germany, :name => "Waren Müritz", :area_code => "3991") + AreaCode.create(:country => germany, :name => "Ankershagen", :area_code => "39921") + AreaCode.create(:country => germany, :name => "Dambeck b Röbel", :area_code => "39922") + AreaCode.create(:country => germany, :name => "Priborn", :area_code => "39923") + AreaCode.create(:country => germany, :name => "Stuer", :area_code => "39924") + AreaCode.create(:country => germany, :name => "Wredenhagen", :area_code => "39925") + AreaCode.create(:country => germany, :name => "Grabowhöfe", :area_code => "39926") + AreaCode.create(:country => germany, :name => "Nossentiner Hütte", :area_code => "39927") + AreaCode.create(:country => germany, :name => "Möllenhagen", :area_code => "39928") + AreaCode.create(:country => germany, :name => "Jabel b Waren", :area_code => "39929") + AreaCode.create(:country => germany, :name => "Röbel Müritz", :area_code => "39931") + AreaCode.create(:country => germany, :name => "Malchow b Waren", :area_code => "39932") + AreaCode.create(:country => germany, :name => "Vollrathsruhe", :area_code => "39933") + AreaCode.create(:country => germany, :name => "Groß Plasten", :area_code => "39934") + AreaCode.create(:country => germany, :name => "Malchin", :area_code => "3994") + AreaCode.create(:country => germany, :name => "Faulenrost", :area_code => "39951") + AreaCode.create(:country => germany, :name => "Grammentin", :area_code => "39952") + AreaCode.create(:country => germany, :name => "Schwinkendorf", :area_code => "39953") + AreaCode.create(:country => germany, :name => "Stavenhagen Reuterstadt", :area_code => "39954") + AreaCode.create(:country => germany, :name => "Jürgenstorf Meckl", :area_code => "39955") + AreaCode.create(:country => germany, :name => "Neukalen", :area_code => "39956") + AreaCode.create(:country => germany, :name => "Gielow", :area_code => "39957") + AreaCode.create(:country => germany, :name => "Dargun", :area_code => "39959") + AreaCode.create(:country => germany, :name => "Teterow", :area_code => "3996") + AreaCode.create(:country => germany, :name => "Gnoien", :area_code => "39971") + AreaCode.create(:country => germany, :name => "Walkendorf", :area_code => "39972") + AreaCode.create(:country => germany, :name => "Altkalen", :area_code => "39973") + AreaCode.create(:country => germany, :name => "Thürkow", :area_code => "39975") + AreaCode.create(:country => germany, :name => "Groß Bützin", :area_code => "39976") + AreaCode.create(:country => germany, :name => "Jördenstorf", :area_code => "39977") + AreaCode.create(:country => germany, :name => "Gross Roge", :area_code => "39978") + AreaCode.create(:country => germany, :name => "Demmin", :area_code => "3998") + AreaCode.create(:country => germany, :name => "Daberkow", :area_code => "39991") + AreaCode.create(:country => germany, :name => "Görmin", :area_code => "39992") + AreaCode.create(:country => germany, :name => "Hohenmocker", :area_code => "39993") + AreaCode.create(:country => germany, :name => "Metschow", :area_code => "39994") + AreaCode.create(:country => germany, :name => "Nossendorf", :area_code => "39995") + AreaCode.create(:country => germany, :name => "Törpin", :area_code => "39996") + AreaCode.create(:country => germany, :name => "Jarmen", :area_code => "39997") + AreaCode.create(:country => germany, :name => "Loitz b Demmin", :area_code => "39998") + AreaCode.create(:country => germany, :name => "Tutow", :area_code => "39999") + AreaCode.create(:country => germany, :name => "Hamburg", :area_code => "40") + AreaCode.create(:country => germany, :name => "Pinneberg", :area_code => "4101") + AreaCode.create(:country => germany, :name => "Ahrensburg", :area_code => "4102") + AreaCode.create(:country => germany, :name => "Wedel", :area_code => "4103") + AreaCode.create(:country => germany, :name => "Aumühle b Hamburg", :area_code => "4104") + AreaCode.create(:country => germany, :name => "Seevetal", :area_code => "4105") + AreaCode.create(:country => germany, :name => "Quickborn Kr Pinneberg", :area_code => "4106") + AreaCode.create(:country => germany, :name => "Siek Kr Stormarn", :area_code => "4107") + AreaCode.create(:country => germany, :name => "Rosengarten Kr Harburg", :area_code => "4108") + AreaCode.create(:country => germany, :name => "Tangstedt Bz Hamburg", :area_code => "4109") + AreaCode.create(:country => germany, :name => "Ellerhoop", :area_code => "4120") + AreaCode.create(:country => germany, :name => "Elmshorn", :area_code => "4121") + AreaCode.create(:country => germany, :name => "Uetersen", :area_code => "4122") + AreaCode.create(:country => germany, :name => "Barmstedt", :area_code => "4123") + AreaCode.create(:country => germany, :name => "Glückstadt", :area_code => "4124") + AreaCode.create(:country => germany, :name => "Seestermühe", :area_code => "4125") + AreaCode.create(:country => germany, :name => "Horst Holstein", :area_code => "4126") + AreaCode.create(:country => germany, :name => "Westerhorn", :area_code => "4127") + AreaCode.create(:country => germany, :name => "Kollmar", :area_code => "4128") + AreaCode.create(:country => germany, :name => "Haseldorf", :area_code => "4129") + AreaCode.create(:country => germany, :name => "Lüneburg", :area_code => "4131") + AreaCode.create(:country => germany, :name => "Amelinghausen", :area_code => "4132") + AreaCode.create(:country => germany, :name => "Wittorf Kr Lüneburg", :area_code => "4133") + AreaCode.create(:country => germany, :name => "Embsen Kr Lünebeburg", :area_code => "4134") + AreaCode.create(:country => germany, :name => "Kirchgellersen", :area_code => "4135") + AreaCode.create(:country => germany, :name => "Scharnebeck", :area_code => "4136") + AreaCode.create(:country => germany, :name => "Barendorf", :area_code => "4137") + AreaCode.create(:country => germany, :name => "Betzendorf Kr Lünebeburg", :area_code => "4138") + AreaCode.create(:country => germany, :name => "Hohnstorf Elbe", :area_code => "4139") + AreaCode.create(:country => germany, :name => "Estorf Kr Stade", :area_code => "4140") + AreaCode.create(:country => germany, :name => "Stade", :area_code => "4141") + AreaCode.create(:country => germany, :name => "Steinkirchen Kr Stade", :area_code => "4142") + AreaCode.create(:country => germany, :name => "Drochtersen", :area_code => "4143") + AreaCode.create(:country => germany, :name => "Himmelpforten", :area_code => "4144") + AreaCode.create(:country => germany, :name => "Stade-Bützfleth", :area_code => "4146") + AreaCode.create(:country => germany, :name => "Drochtersen-Assel", :area_code => "4148") + AreaCode.create(:country => germany, :name => "Fredenbeck", :area_code => "4149") + AreaCode.create(:country => germany, :name => "Schwarzenbek", :area_code => "4151") + AreaCode.create(:country => germany, :name => "Geesthacht", :area_code => "4152") + AreaCode.create(:country => germany, :name => "Lauenburg Elbe", :area_code => "4153") + AreaCode.create(:country => germany, :name => "Trittau", :area_code => "4154") + AreaCode.create(:country => germany, :name => "Büchen", :area_code => "4155") + AreaCode.create(:country => germany, :name => "Talkau", :area_code => "4156") + AreaCode.create(:country => germany, :name => "Roseburg", :area_code => "4158") + AreaCode.create(:country => germany, :name => "Basthorst", :area_code => "4159") + AreaCode.create(:country => germany, :name => "Buxtehude", :area_code => "4161") + AreaCode.create(:country => germany, :name => "Jork", :area_code => "4162") + AreaCode.create(:country => germany, :name => "Horneburg Niederelbe", :area_code => "4163") + AreaCode.create(:country => germany, :name => "Harsefeld", :area_code => "4164") + AreaCode.create(:country => germany, :name => "Hollenstedt Nordheide", :area_code => "4165") + AreaCode.create(:country => germany, :name => "Ahlerstedt", :area_code => "4166") + AreaCode.create(:country => germany, :name => "Apensen", :area_code => "4167") + AreaCode.create(:country => germany, :name => "Neu Wulmstorf-Elstorf", :area_code => "4168") + AreaCode.create(:country => germany, :name => "Sauensiek", :area_code => "4169") + AreaCode.create(:country => germany, :name => "Winsen Luhe", :area_code => "4171") + AreaCode.create(:country => germany, :name => "Salzhausen", :area_code => "4172") + AreaCode.create(:country => germany, :name => "Wulfsen", :area_code => "4173") + AreaCode.create(:country => germany, :name => "Stelle Kr Harburg", :area_code => "4174") + AreaCode.create(:country => germany, :name => "Egestorf Nordheide", :area_code => "4175") + AreaCode.create(:country => germany, :name => "Marschacht", :area_code => "4176") + AreaCode.create(:country => germany, :name => "Drage Elbe", :area_code => "4177") + AreaCode.create(:country => germany, :name => "Radbruch", :area_code => "4178") + AreaCode.create(:country => germany, :name => "Winsen-Tönnhausen", :area_code => "4179") + AreaCode.create(:country => germany, :name => "Königsmoor", :area_code => "4180") + AreaCode.create(:country => germany, :name => "Buchholz in der Nordheide", :area_code => "4181") + AreaCode.create(:country => germany, :name => "Tostedt", :area_code => "4182") + AreaCode.create(:country => germany, :name => "Jesteburg", :area_code => "4183") + AreaCode.create(:country => germany, :name => "Hanstedt Nordheide", :area_code => "4184") + AreaCode.create(:country => germany, :name => "Marxen Auetal", :area_code => "4185") + AreaCode.create(:country => germany, :name => "Buchholz-Trelde", :area_code => "4186") + AreaCode.create(:country => germany, :name => "Holm-Seppensen", :area_code => "4187") + AreaCode.create(:country => germany, :name => "Welle Nordheide", :area_code => "4188") + AreaCode.create(:country => germany, :name => "Undeloh", :area_code => "4189") + AreaCode.create(:country => germany, :name => "Kaltenkirchen Holst", :area_code => "4191") + AreaCode.create(:country => germany, :name => "Bad Bramstedt", :area_code => "4192") + AreaCode.create(:country => germany, :name => "Henstedt-Ulzburg", :area_code => "4193") + AreaCode.create(:country => germany, :name => "Sievershütten", :area_code => "4194") + AreaCode.create(:country => germany, :name => "Hartenholm", :area_code => "4195") + AreaCode.create(:country => germany, :name => "Achim b Bremen", :area_code => "4202") + AreaCode.create(:country => germany, :name => "Weyhe b Bremen", :area_code => "4203") + AreaCode.create(:country => germany, :name => "Thedinghausen", :area_code => "4204") + AreaCode.create(:country => germany, :name => "Ottersberg", :area_code => "4205") + AreaCode.create(:country => germany, :name => "Stuhr-Heiligenrode", :area_code => "4206") + AreaCode.create(:country => germany, :name => "Oyten", :area_code => "4207") + AreaCode.create(:country => germany, :name => "Grasberg", :area_code => "4208") + AreaCode.create(:country => germany, :name => "Schwanewede", :area_code => "4209") + AreaCode.create(:country => germany, :name => "Bremen", :area_code => "421") + AreaCode.create(:country => germany, :name => "Delmenhorst", :area_code => "4221") + AreaCode.create(:country => germany, :name => "Ganderkesee", :area_code => "4222") + AreaCode.create(:country => germany, :name => "Ganderkesee-Bookholzberg", :area_code => "4223") + AreaCode.create(:country => germany, :name => "Gross Ippener", :area_code => "4224") + AreaCode.create(:country => germany, :name => "Verden-Walle", :area_code => "4230") + AreaCode.create(:country => germany, :name => "Verden Aller", :area_code => "4231") + AreaCode.create(:country => germany, :name => "Langwedel Kr Verden", :area_code => "4232") + AreaCode.create(:country => germany, :name => "Blender", :area_code => "4233") + AreaCode.create(:country => germany, :name => "Dörverden", :area_code => "4234") + AreaCode.create(:country => germany, :name => "Langwedel-Etelsen", :area_code => "4235") + AreaCode.create(:country => germany, :name => "Kirchlinteln", :area_code => "4236") + AreaCode.create(:country => germany, :name => "Bendingbostel", :area_code => "4237") + AreaCode.create(:country => germany, :name => "Neddenaverbergen", :area_code => "4238") + AreaCode.create(:country => germany, :name => "Dörverden-Westen", :area_code => "4239") + AreaCode.create(:country => germany, :name => "Syke-Heiligenfelde", :area_code => "4240") + AreaCode.create(:country => germany, :name => "Bassum", :area_code => "4241") + AreaCode.create(:country => germany, :name => "Syke", :area_code => "4242") + AreaCode.create(:country => germany, :name => "Twistringen", :area_code => "4243") + AreaCode.create(:country => germany, :name => "Harpstedt", :area_code => "4244") + AreaCode.create(:country => germany, :name => "Neuenkirchen b Bassum", :area_code => "4245") + AreaCode.create(:country => germany, :name => "Twistringen-Heiligenloh", :area_code => "4246") + AreaCode.create(:country => germany, :name => "Affinghausen", :area_code => "4247") + AreaCode.create(:country => germany, :name => "Bassum-Neubruchhausen", :area_code => "4248") + AreaCode.create(:country => germany, :name => "Bassum-Nordwohlde", :area_code => "4249") + AreaCode.create(:country => germany, :name => "Hoya", :area_code => "4251") + AreaCode.create(:country => germany, :name => "Bruchhausen-Vilsen", :area_code => "4252") + AreaCode.create(:country => germany, :name => "Asendorf Kr Diepholz", :area_code => "4253") + AreaCode.create(:country => germany, :name => "Eystrup", :area_code => "4254") + AreaCode.create(:country => germany, :name => "Martfeld", :area_code => "4255") + AreaCode.create(:country => germany, :name => "Hilgermissen", :area_code => "4256") + AreaCode.create(:country => germany, :name => "Schweringen", :area_code => "4257") + AreaCode.create(:country => germany, :name => "Schwarme", :area_code => "4258") + AreaCode.create(:country => germany, :name => "Visselhövede-Wittorf", :area_code => "4260") + AreaCode.create(:country => germany, :name => "Rotenburg Wümme", :area_code => "4261") + AreaCode.create(:country => germany, :name => "Visselhövede", :area_code => "4262") + AreaCode.create(:country => germany, :name => "Scheessel", :area_code => "4263") + AreaCode.create(:country => germany, :name => "Sottrum Kr Rotenburg", :area_code => "4264") + AreaCode.create(:country => germany, :name => "Fintel", :area_code => "4265") + AreaCode.create(:country => germany, :name => "Brockel", :area_code => "4266") + AreaCode.create(:country => germany, :name => "Lauenbrück", :area_code => "4267") + AreaCode.create(:country => germany, :name => "Bötersen", :area_code => "4268") + AreaCode.create(:country => germany, :name => "Ahausen-Kirchwalsede", :area_code => "4269") + AreaCode.create(:country => germany, :name => "Sulingen", :area_code => "4271") + AreaCode.create(:country => germany, :name => "Siedenburg", :area_code => "4272") + AreaCode.create(:country => germany, :name => "Kirchdorf b Sulingen", :area_code => "4273") + AreaCode.create(:country => germany, :name => "Varrel b Sulingen", :area_code => "4274") + AreaCode.create(:country => germany, :name => "Ehrenburg", :area_code => "4275") + AreaCode.create(:country => germany, :name => "Borstel b Sulingen", :area_code => "4276") + AreaCode.create(:country => germany, :name => "Schwaförden", :area_code => "4277") + AreaCode.create(:country => germany, :name => "Zeven", :area_code => "4281") + AreaCode.create(:country => germany, :name => "Sittensen", :area_code => "4282") + AreaCode.create(:country => germany, :name => "Tarmstedt", :area_code => "4283") + AreaCode.create(:country => germany, :name => "Selsingen", :area_code => "4284") + AreaCode.create(:country => germany, :name => "Rhade b Zeven", :area_code => "4285") + AreaCode.create(:country => germany, :name => "Gyhum", :area_code => "4286") + AreaCode.create(:country => germany, :name => "Heeslingen-Boitzen", :area_code => "4287") + AreaCode.create(:country => germany, :name => "Horstedt Kr Rotenburg", :area_code => "4288") + AreaCode.create(:country => germany, :name => "Kirchtimke", :area_code => "4289") + AreaCode.create(:country => germany, :name => "Ritterhude", :area_code => "4292") + AreaCode.create(:country => germany, :name => "Ottersberg-Fischerhude", :area_code => "4293") + AreaCode.create(:country => germany, :name => "Riede Kr Verden", :area_code => "4294") + AreaCode.create(:country => germany, :name => "Emtinghausen", :area_code => "4295") + AreaCode.create(:country => germany, :name => "Schwanewede-Aschwarden", :area_code => "4296") + AreaCode.create(:country => germany, :name => "Ottersberg-Posthausen", :area_code => "4297") + AreaCode.create(:country => germany, :name => "Lilienthal", :area_code => "4298") + AreaCode.create(:country => germany, :name => "Kirchbarkau", :area_code => "4302") + AreaCode.create(:country => germany, :name => "Schlesen", :area_code => "4303") + AreaCode.create(:country => germany, :name => "Westensee", :area_code => "4305") + AreaCode.create(:country => germany, :name => "Raisdorf", :area_code => "4307") + AreaCode.create(:country => germany, :name => "Schwedeneck", :area_code => "4308") + AreaCode.create(:country => germany, :name => "Kiel", :area_code => "431") + AreaCode.create(:country => germany, :name => "Heidmühlen", :area_code => "4320") + AreaCode.create(:country => germany, :name => "Neumünster", :area_code => "4321") + AreaCode.create(:country => germany, :name => "Bordesholm", :area_code => "4322") + AreaCode.create(:country => germany, :name => "Bornhöved", :area_code => "4323") + AreaCode.create(:country => germany, :name => "Brokstedt", :area_code => "4324") + AreaCode.create(:country => germany, :name => "Wankendorf", :area_code => "4326") + AreaCode.create(:country => germany, :name => "Grossenaspe", :area_code => "4327") + AreaCode.create(:country => germany, :name => "Rickling", :area_code => "4328") + AreaCode.create(:country => germany, :name => "Langwedel Holst", :area_code => "4329") + AreaCode.create(:country => germany, :name => "Emkendorf", :area_code => "4330") + AreaCode.create(:country => germany, :name => "Rendsburg", :area_code => "4331") + AreaCode.create(:country => germany, :name => "Hamdorf b Rendsburg", :area_code => "4332") + AreaCode.create(:country => germany, :name => "Erfde", :area_code => "4333") + AreaCode.create(:country => germany, :name => "Bredenbek b Rendsburg", :area_code => "4334") + AreaCode.create(:country => germany, :name => "Hohn b Rendsburg", :area_code => "4335") + AreaCode.create(:country => germany, :name => "Owschlag", :area_code => "4336") + AreaCode.create(:country => germany, :name => "Jevenstedt", :area_code => "4337") + AreaCode.create(:country => germany, :name => "Alt Duvenstedt", :area_code => "4338") + AreaCode.create(:country => germany, :name => "Christiansholm", :area_code => "4339") + AreaCode.create(:country => germany, :name => "Achterwehr", :area_code => "4340") + AreaCode.create(:country => germany, :name => "Preetz Kr Plön", :area_code => "4342") + AreaCode.create(:country => germany, :name => "Laboe", :area_code => "4343") + AreaCode.create(:country => germany, :name => "Schönberg Holstein", :area_code => "4344") + AreaCode.create(:country => germany, :name => "Gettorf", :area_code => "4346") + AreaCode.create(:country => germany, :name => "Flintbek", :area_code => "4347") + AreaCode.create(:country => germany, :name => "Schönkirchen", :area_code => "4348") + AreaCode.create(:country => germany, :name => "Dänischenhagen", :area_code => "4349") + AreaCode.create(:country => germany, :name => "Eckernförde", :area_code => "4351") + AreaCode.create(:country => germany, :name => "Damp", :area_code => "4352") + AreaCode.create(:country => germany, :name => "Ascheffel", :area_code => "4353") + AreaCode.create(:country => germany, :name => "Fleckeby", :area_code => "4354") + AreaCode.create(:country => germany, :name => "Rieseby", :area_code => "4355") + AreaCode.create(:country => germany, :name => "Gross Wittensee", :area_code => "4356") + AreaCode.create(:country => germany, :name => "Sehestedt Eider", :area_code => "4357") + AreaCode.create(:country => germany, :name => "Loose b Eckernförde", :area_code => "4358") + AreaCode.create(:country => germany, :name => "Oldenburg in Holstein", :area_code => "4361") + AreaCode.create(:country => germany, :name => "Heiligenhafen", :area_code => "4362") + AreaCode.create(:country => germany, :name => "Lensahn", :area_code => "4363") + AreaCode.create(:country => germany, :name => "Dahme Kr Ostholstein", :area_code => "4364") + AreaCode.create(:country => germany, :name => "Heringsdorf Holst", :area_code => "4365") + AreaCode.create(:country => germany, :name => "Grömitz-Cismar", :area_code => "4366") + AreaCode.create(:country => germany, :name => "Grossenbrode", :area_code => "4367") + AreaCode.create(:country => germany, :name => "Burg auf Fehmarn", :area_code => "4371") + AreaCode.create(:country => germany, :name => "Westfehmarn", :area_code => "4372") + AreaCode.create(:country => germany, :name => "Lütjenburg", :area_code => "4381") + AreaCode.create(:country => germany, :name => "Wangels", :area_code => "4382") + AreaCode.create(:country => germany, :name => "Grebin", :area_code => "4383") + AreaCode.create(:country => germany, :name => "Selent", :area_code => "4384") + AreaCode.create(:country => germany, :name => "Hohenfelde b Kiel", :area_code => "4385") + AreaCode.create(:country => germany, :name => "Nortorf b Neumünster", :area_code => "4392") + AreaCode.create(:country => germany, :name => "Boostedt", :area_code => "4393") + AreaCode.create(:country => germany, :name => "Bokhorst", :area_code => "4394") + AreaCode.create(:country => germany, :name => "Brake Unterweser", :area_code => "4401") + AreaCode.create(:country => germany, :name => "Rastede", :area_code => "4402") + AreaCode.create(:country => germany, :name => "Bad Zwischenahn", :area_code => "4403") + AreaCode.create(:country => germany, :name => "Elsfleth", :area_code => "4404") + AreaCode.create(:country => germany, :name => "Edewecht", :area_code => "4405") + AreaCode.create(:country => germany, :name => "Berne", :area_code => "4406") + AreaCode.create(:country => germany, :name => "Wardenburg", :area_code => "4407") + AreaCode.create(:country => germany, :name => "Hude Oldenburg", :area_code => "4408") + AreaCode.create(:country => germany, :name => "Westerstede-Ocholt", :area_code => "4409") + AreaCode.create(:country => germany, :name => "Oldenburg (Oldb)", :area_code => "441") + AreaCode.create(:country => germany, :name => "Wilhelmshaven", :area_code => "4421") + AreaCode.create(:country => germany, :name => "Sande Kr Friesl", :area_code => "4422") + AreaCode.create(:country => germany, :name => "Fedderwarden", :area_code => "4423") + AreaCode.create(:country => germany, :name => "Wangerland-Hooksiel", :area_code => "4425") + AreaCode.create(:country => germany, :name => "Wangerland-Horumersiel", :area_code => "4426") + AreaCode.create(:country => germany, :name => "Wildeshausen", :area_code => "4431") + AreaCode.create(:country => germany, :name => "Dötlingen-Brettorf", :area_code => "4432") + AreaCode.create(:country => germany, :name => "Dötlingen", :area_code => "4433") + AreaCode.create(:country => germany, :name => "Colnrade", :area_code => "4434") + AreaCode.create(:country => germany, :name => "Grossenkneten", :area_code => "4435") + AreaCode.create(:country => germany, :name => "Vechta", :area_code => "4441") + AreaCode.create(:country => germany, :name => "Lohne Oldenburg", :area_code => "4442") + AreaCode.create(:country => germany, :name => "Dinklage", :area_code => "4443") + AreaCode.create(:country => germany, :name => "Goldenstedt", :area_code => "4444") + AreaCode.create(:country => germany, :name => "Visbek Kr Vechta", :area_code => "4445") + AreaCode.create(:country => germany, :name => "Bakum Kr Vechta", :area_code => "4446") + AreaCode.create(:country => germany, :name => "Vechta-Langförden", :area_code => "4447") + AreaCode.create(:country => germany, :name => "Varel Jadebusen", :area_code => "4451") + AreaCode.create(:country => germany, :name => "Zetel-Neuenburg", :area_code => "4452") + AreaCode.create(:country => germany, :name => "Zetel", :area_code => "4453") + AreaCode.create(:country => germany, :name => "Jade", :area_code => "4454") + AreaCode.create(:country => germany, :name => "Jade-Schweiburg", :area_code => "4455") + AreaCode.create(:country => germany, :name => "Varel-Altjührden", :area_code => "4456") + AreaCode.create(:country => germany, :name => "Wiefelstede-Spohle", :area_code => "4458") + AreaCode.create(:country => germany, :name => "Jever", :area_code => "4461") + AreaCode.create(:country => germany, :name => "Wittmund", :area_code => "4462") + AreaCode.create(:country => germany, :name => "Wangerland", :area_code => "4463") + AreaCode.create(:country => germany, :name => "Wittmund-Carolinensiel", :area_code => "4464") + AreaCode.create(:country => germany, :name => "Friedeburg Ostfriesl", :area_code => "4465") + AreaCode.create(:country => germany, :name => "Wittmund-Ardorf", :area_code => "4466") + AreaCode.create(:country => germany, :name => "Wittmund-Funnix", :area_code => "4467") + AreaCode.create(:country => germany, :name => "Friedeburg-Reepsholt", :area_code => "4468") + AreaCode.create(:country => germany, :name => "Wangerooge", :area_code => "4469") + AreaCode.create(:country => germany, :name => "Cloppenburg", :area_code => "4471") + AreaCode.create(:country => germany, :name => "Lastrup", :area_code => "4472") + AreaCode.create(:country => germany, :name => "Emstek", :area_code => "4473") + AreaCode.create(:country => germany, :name => "Garrel", :area_code => "4474") + AreaCode.create(:country => germany, :name => "Molbergen", :area_code => "4475") + AreaCode.create(:country => germany, :name => "Lastrup-Hemmelte", :area_code => "4477") + AreaCode.create(:country => germany, :name => "Cappeln Oldenburg", :area_code => "4478") + AreaCode.create(:country => germany, :name => "Molbergen-Peheim", :area_code => "4479") + AreaCode.create(:country => germany, :name => "Ovelgönne-Strückhausen", :area_code => "4480") + AreaCode.create(:country => germany, :name => "Hatten-Sandkrug", :area_code => "4481") + AreaCode.create(:country => germany, :name => "Hatten", :area_code => "4482") + AreaCode.create(:country => germany, :name => "Ovelgönne-Großenmeer", :area_code => "4483") + AreaCode.create(:country => germany, :name => "Hude-Wüsting", :area_code => "4484") + AreaCode.create(:country => germany, :name => "Elsfleth-Huntorf", :area_code => "4485") + AreaCode.create(:country => germany, :name => "Edewecht-Friedrichsfehn", :area_code => "4486") + AreaCode.create(:country => germany, :name => "Grossenkneten-Huntlosen", :area_code => "4487") + AreaCode.create(:country => germany, :name => "Westerstede", :area_code => "4488") + AreaCode.create(:country => germany, :name => "Apen", :area_code => "4489") + AreaCode.create(:country => germany, :name => "Friesoythe", :area_code => "4491") + AreaCode.create(:country => germany, :name => "Saterland", :area_code => "4492") + AreaCode.create(:country => germany, :name => "Friesoythe-Gehlenberg", :area_code => "4493") + AreaCode.create(:country => germany, :name => "Bösel Oldenburg", :area_code => "4494") + AreaCode.create(:country => germany, :name => "Friesoythe-Thüle", :area_code => "4495") + AreaCode.create(:country => germany, :name => "Friesoythe-Markhausen", :area_code => "4496") + AreaCode.create(:country => germany, :name => "Barßel-Harkebrügge", :area_code => "4497") + AreaCode.create(:country => germany, :name => "Saterland-Ramsloh", :area_code => "4498") + AreaCode.create(:country => germany, :name => "Barssel", :area_code => "4499") + AreaCode.create(:country => germany, :name => "Kastorf Holst", :area_code => "4501") + AreaCode.create(:country => germany, :name => "Lübeck-Travemünde", :area_code => "4502") + AreaCode.create(:country => germany, :name => "Timmendorfer Strand", :area_code => "4503") + AreaCode.create(:country => germany, :name => "Ratekau", :area_code => "4504") + AreaCode.create(:country => germany, :name => "Stockelsdorf-Curau", :area_code => "4505") + AreaCode.create(:country => germany, :name => "Stockelsdorf-Krumbeck", :area_code => "4506") + AreaCode.create(:country => germany, :name => "Krummesse", :area_code => "4508") + AreaCode.create(:country => germany, :name => "Groß Grönau", :area_code => "4509") + AreaCode.create(:country => germany, :name => "Lübeck", :area_code => "451") + AreaCode.create(:country => germany, :name => "Eutin", :area_code => "4521") + AreaCode.create(:country => germany, :name => "Plön", :area_code => "4522") + AreaCode.create(:country => germany, :name => "Malente", :area_code => "4523") + AreaCode.create(:country => germany, :name => "Scharbeutz-Pönitz", :area_code => "4524") + AreaCode.create(:country => germany, :name => "Ahrensbök", :area_code => "4525") + AreaCode.create(:country => germany, :name => "Ascheberg Holstein", :area_code => "4526") + AreaCode.create(:country => germany, :name => "Bosau", :area_code => "4527") + AreaCode.create(:country => germany, :name => "Schönwalde am Bungsberg", :area_code => "4528") + AreaCode.create(:country => germany, :name => "Süsel-Bujendorf", :area_code => "4529") + AreaCode.create(:country => germany, :name => "Bad Oldesloe", :area_code => "4531") + AreaCode.create(:country => germany, :name => "Bargteheide", :area_code => "4532") + AreaCode.create(:country => germany, :name => "Reinfeld Holstein", :area_code => "4533") + AreaCode.create(:country => germany, :name => "Steinburg Kr Storman", :area_code => "4534") + AreaCode.create(:country => germany, :name => "Nahe", :area_code => "4535") + AreaCode.create(:country => germany, :name => "Steinhorst Lauenb", :area_code => "4536") + AreaCode.create(:country => germany, :name => "Sülfeld Holst", :area_code => "4537") + AreaCode.create(:country => germany, :name => "Westerau", :area_code => "4539") + AreaCode.create(:country => germany, :name => "Ratzeburg", :area_code => "4541") + AreaCode.create(:country => germany, :name => "Mölln Lauenb", :area_code => "4542") + AreaCode.create(:country => germany, :name => "Nusse", :area_code => "4543") + AreaCode.create(:country => germany, :name => "Berkenthin", :area_code => "4544") + AreaCode.create(:country => germany, :name => "Seedorf Lauenb", :area_code => "4545") + AreaCode.create(:country => germany, :name => "Mustin Lauenburg", :area_code => "4546") + AreaCode.create(:country => germany, :name => "Gudow Lauenb", :area_code => "4547") + AreaCode.create(:country => germany, :name => "Bühnsdorf", :area_code => "4550") + AreaCode.create(:country => germany, :name => "Bad Segeberg", :area_code => "4551") + AreaCode.create(:country => germany, :name => "Leezen", :area_code => "4552") + AreaCode.create(:country => germany, :name => "Geschendorf", :area_code => "4553") + AreaCode.create(:country => germany, :name => "Wahlstedt", :area_code => "4554") + AreaCode.create(:country => germany, :name => "Seedorf b Bad Segeberg", :area_code => "4555") + AreaCode.create(:country => germany, :name => "Ahrensbök-Gnissau", :area_code => "4556") + AreaCode.create(:country => germany, :name => "Blunk", :area_code => "4557") + AreaCode.create(:country => germany, :name => "Todesfelde", :area_code => "4558") + AreaCode.create(:country => germany, :name => "Wensin", :area_code => "4559") + AreaCode.create(:country => germany, :name => "Neustadt in Holstein", :area_code => "4561") + AreaCode.create(:country => germany, :name => "Grömitz", :area_code => "4562") + AreaCode.create(:country => germany, :name => "Scharbeutz-Haffkrug", :area_code => "4563") + AreaCode.create(:country => germany, :name => "Schashagen", :area_code => "4564") + AreaCode.create(:country => germany, :name => "Freienwill", :area_code => "4602") + AreaCode.create(:country => germany, :name => "Havetoft", :area_code => "4603") + AreaCode.create(:country => germany, :name => "Grossenwiehe", :area_code => "4604") + AreaCode.create(:country => germany, :name => "Medelby", :area_code => "4605") + AreaCode.create(:country => germany, :name => "Wanderup", :area_code => "4606") + AreaCode.create(:country => germany, :name => "Janneby", :area_code => "4607") + AreaCode.create(:country => germany, :name => "Handewitt", :area_code => "4608") + AreaCode.create(:country => germany, :name => "Eggebek", :area_code => "4609") + AreaCode.create(:country => germany, :name => "Flensburg", :area_code => "461") + AreaCode.create(:country => germany, :name => "Schleswig", :area_code => "4621") + AreaCode.create(:country => germany, :name => "Taarstedt", :area_code => "4622") + AreaCode.create(:country => germany, :name => "Böklund", :area_code => "4623") + AreaCode.create(:country => germany, :name => "Kropp", :area_code => "4624") + AreaCode.create(:country => germany, :name => "Jübek", :area_code => "4625") + AreaCode.create(:country => germany, :name => "Treia", :area_code => "4626") + AreaCode.create(:country => germany, :name => "Dörpstedt", :area_code => "4627") + AreaCode.create(:country => germany, :name => "Barderup", :area_code => "4630") + AreaCode.create(:country => germany, :name => "Glücksburg Ostsee", :area_code => "4631") + AreaCode.create(:country => germany, :name => "Steinbergkirche", :area_code => "4632") + AreaCode.create(:country => germany, :name => "Satrup", :area_code => "4633") + AreaCode.create(:country => germany, :name => "Husby", :area_code => "4634") + AreaCode.create(:country => germany, :name => "Sörup", :area_code => "4635") + AreaCode.create(:country => germany, :name => "Langballig", :area_code => "4636") + AreaCode.create(:country => germany, :name => "Sterup", :area_code => "4637") + AreaCode.create(:country => germany, :name => "Tarp", :area_code => "4638") + AreaCode.create(:country => germany, :name => "Schafflund", :area_code => "4639") + AreaCode.create(:country => germany, :name => "Süderbrarup", :area_code => "4641") + AreaCode.create(:country => germany, :name => "Kappeln Schlei", :area_code => "4642") + AreaCode.create(:country => germany, :name => "Gelting Angeln", :area_code => "4643") + AreaCode.create(:country => germany, :name => "Karby", :area_code => "4644") + AreaCode.create(:country => germany, :name => "Mohrkirch", :area_code => "4646") + AreaCode.create(:country => germany, :name => "Sylt", :area_code => "4651") + AreaCode.create(:country => germany, :name => "Niebüll", :area_code => "4661") + AreaCode.create(:country => germany, :name => "Leck", :area_code => "4662") + AreaCode.create(:country => germany, :name => "Süderlügum", :area_code => "4663") + AreaCode.create(:country => germany, :name => "Neukirchen b Niebüll", :area_code => "4664") + AreaCode.create(:country => germany, :name => "Emmelsbüll-Horsbüll", :area_code => "4665") + AreaCode.create(:country => germany, :name => "Ladelund", :area_code => "4666") + AreaCode.create(:country => germany, :name => "Dagebüll", :area_code => "4667") + AreaCode.create(:country => germany, :name => "Klanxbüll", :area_code => "4668") + AreaCode.create(:country => germany, :name => "Bredstedt", :area_code => "4671") + AreaCode.create(:country => germany, :name => "Langenhorn", :area_code => "4672") + AreaCode.create(:country => germany, :name => "Joldelund", :area_code => "4673") + AreaCode.create(:country => germany, :name => "Ockholm", :area_code => "4674") + AreaCode.create(:country => germany, :name => "Wyk auf Föhr", :area_code => "4681") + AreaCode.create(:country => germany, :name => "Amrum", :area_code => "4682") + AreaCode.create(:country => germany, :name => "Oldsum", :area_code => "4683") + AreaCode.create(:country => germany, :name => "Langeneß Hallig", :area_code => "4684") + AreaCode.create(:country => germany, :name => "Sandstedt", :area_code => "4702") + AreaCode.create(:country => germany, :name => "Loxstedt-Donnern", :area_code => "4703") + AreaCode.create(:country => germany, :name => "Drangstedt", :area_code => "4704") + AreaCode.create(:country => germany, :name => "Wremen", :area_code => "4705") + AreaCode.create(:country => germany, :name => "Schiffdorf", :area_code => "4706") + AreaCode.create(:country => germany, :name => "Langen-Neuenwalde", :area_code => "4707") + AreaCode.create(:country => germany, :name => "Ringstedt", :area_code => "4708") + AreaCode.create(:country => germany, :name => "Bremerhaven", :area_code => "471") + AreaCode.create(:country => germany, :name => "Cuxhaven", :area_code => "4721") + AreaCode.create(:country => germany, :name => "Cuxhaven-Altenbruch", :area_code => "4722") + AreaCode.create(:country => germany, :name => "Cuxhaven-Altenwalde", :area_code => "4723") + AreaCode.create(:country => germany, :name => "Cuxhaven-Lüdingworth", :area_code => "4724") + AreaCode.create(:country => germany, :name => "Helgoland", :area_code => "4725") + AreaCode.create(:country => germany, :name => "Nordenham", :area_code => "4731") + AreaCode.create(:country => germany, :name => "Stadland-Rodenkirchen", :area_code => "4732") + AreaCode.create(:country => germany, :name => "Butjadingen-Burhave", :area_code => "4733") + AreaCode.create(:country => germany, :name => "Stadland-Seefeld", :area_code => "4734") + AreaCode.create(:country => germany, :name => "Butjadingen-Stollhamm", :area_code => "4735") + AreaCode.create(:country => germany, :name => "Butjadingen-Tossens", :area_code => "4736") + AreaCode.create(:country => germany, :name => "Stadland-Schwei", :area_code => "4737") + AreaCode.create(:country => germany, :name => "Loxstedt-Dedesdorf", :area_code => "4740") + AreaCode.create(:country => germany, :name => "Nordholz b Bremerhaven", :area_code => "4741") + AreaCode.create(:country => germany, :name => "Dorum", :area_code => "4742") + AreaCode.create(:country => germany, :name => "Langen b Bremerhaven", :area_code => "4743") + AreaCode.create(:country => germany, :name => "Loxstedt", :area_code => "4744") + AreaCode.create(:country => germany, :name => "Bad Bederkesa", :area_code => "4745") + AreaCode.create(:country => germany, :name => "Hagen b Bremerhaven", :area_code => "4746") + AreaCode.create(:country => germany, :name => "Beverstedt", :area_code => "4747") + AreaCode.create(:country => germany, :name => "Stubben b Bremerhaven", :area_code => "4748") + AreaCode.create(:country => germany, :name => "Schiffdorf-Geestenseth", :area_code => "4749") + AreaCode.create(:country => germany, :name => "Otterndorf", :area_code => "4751") + AreaCode.create(:country => germany, :name => "Neuhaus Oste", :area_code => "4752") + AreaCode.create(:country => germany, :name => "Balje", :area_code => "4753") + AreaCode.create(:country => germany, :name => "Bülkau", :area_code => "4754") + AreaCode.create(:country => germany, :name => "Ihlienworth", :area_code => "4755") + AreaCode.create(:country => germany, :name => "Odisheim", :area_code => "4756") + AreaCode.create(:country => germany, :name => "Wanna", :area_code => "4757") + AreaCode.create(:country => germany, :name => "Nordleda", :area_code => "4758") + AreaCode.create(:country => germany, :name => "Bremervörde", :area_code => "4761") + AreaCode.create(:country => germany, :name => "Kutenholz", :area_code => "4762") + AreaCode.create(:country => germany, :name => "Gnarrenburg", :area_code => "4763") + AreaCode.create(:country => germany, :name => "Gnarrenburg-Klenkendorf", :area_code => "4764") + AreaCode.create(:country => germany, :name => "Ebersdorf b Bremervörde", :area_code => "4765") + AreaCode.create(:country => germany, :name => "Basdahl", :area_code => "4766") + AreaCode.create(:country => germany, :name => "Bremervörde-Bevern", :area_code => "4767") + AreaCode.create(:country => germany, :name => "Hipstedt", :area_code => "4768") + AreaCode.create(:country => germany, :name => "Bremervörde-Iselersheim", :area_code => "4769") + AreaCode.create(:country => germany, :name => "Wischhafen", :area_code => "4770") + AreaCode.create(:country => germany, :name => "Hemmoor", :area_code => "4771") + AreaCode.create(:country => germany, :name => "Oberndorf Oste", :area_code => "4772") + AreaCode.create(:country => germany, :name => "Lamstedt", :area_code => "4773") + AreaCode.create(:country => germany, :name => "Hechthausen", :area_code => "4774") + AreaCode.create(:country => germany, :name => "Grossenwörden", :area_code => "4775") + AreaCode.create(:country => germany, :name => "Osten-Altendorf", :area_code => "4776") + AreaCode.create(:country => germany, :name => "Cadenberge", :area_code => "4777") + AreaCode.create(:country => germany, :name => "Wingst", :area_code => "4778") + AreaCode.create(:country => germany, :name => "Freiburg Elbe", :area_code => "4779") + AreaCode.create(:country => germany, :name => "Osterholz-Scharmbeck", :area_code => "4791") + AreaCode.create(:country => germany, :name => "Worpswede", :area_code => "4792") + AreaCode.create(:country => germany, :name => "Hambergen", :area_code => "4793") + AreaCode.create(:country => germany, :name => "Worpswede-Ostersode", :area_code => "4794") + AreaCode.create(:country => germany, :name => "Garlstedt", :area_code => "4795") + AreaCode.create(:country => germany, :name => "Teufelsmoor", :area_code => "4796") + AreaCode.create(:country => germany, :name => "Wrohm", :area_code => "4802") + AreaCode.create(:country => germany, :name => "Pahlen", :area_code => "4803") + AreaCode.create(:country => germany, :name => "Nordhastedt", :area_code => "4804") + AreaCode.create(:country => germany, :name => "Schafstedt", :area_code => "4805") + AreaCode.create(:country => germany, :name => "Sarzbüttel", :area_code => "4806") + AreaCode.create(:country => germany, :name => "Heide Holst", :area_code => "481") + AreaCode.create(:country => germany, :name => "Itzehoe", :area_code => "4821") + AreaCode.create(:country => germany, :name => "Kellinghusen", :area_code => "4822") + AreaCode.create(:country => germany, :name => "Wilster", :area_code => "4823") + AreaCode.create(:country => germany, :name => "Krempe", :area_code => "4824") + AreaCode.create(:country => germany, :name => "Burg Dithmarschen", :area_code => "4825") + AreaCode.create(:country => germany, :name => "Hohenlockstedt", :area_code => "4826") + AreaCode.create(:country => germany, :name => "Wacken", :area_code => "4827") + AreaCode.create(:country => germany, :name => "Lägerdorf", :area_code => "4828") + AreaCode.create(:country => germany, :name => "Wewelsfleth", :area_code => "4829") + AreaCode.create(:country => germany, :name => "Süderhastedt", :area_code => "4830") + AreaCode.create(:country => germany, :name => "Meldorf", :area_code => "4832") + AreaCode.create(:country => germany, :name => "Wesselburen", :area_code => "4833") + AreaCode.create(:country => germany, :name => "Büsum", :area_code => "4834") + AreaCode.create(:country => germany, :name => "Albersdorf Holst", :area_code => "4835") + AreaCode.create(:country => germany, :name => "Hennstedt Dithm", :area_code => "4836") + AreaCode.create(:country => germany, :name => "Neuenkirchen Dithm", :area_code => "4837") + AreaCode.create(:country => germany, :name => "Tellingstedt", :area_code => "4838") + AreaCode.create(:country => germany, :name => "Wöhrden Dithm", :area_code => "4839") + AreaCode.create(:country => germany, :name => "Husum Nordsee", :area_code => "4841") + AreaCode.create(:country => germany, :name => "Nordstrand", :area_code => "4842") + AreaCode.create(:country => germany, :name => "Viöl", :area_code => "4843") + AreaCode.create(:country => germany, :name => "Pellworm", :area_code => "4844") + AreaCode.create(:country => germany, :name => "Ostenfeld Husum", :area_code => "4845") + AreaCode.create(:country => germany, :name => "Hattstedt", :area_code => "4846") + AreaCode.create(:country => germany, :name => "Oster-Ohrstedt", :area_code => "4847") + AreaCode.create(:country => germany, :name => "Rantrum", :area_code => "4848") + AreaCode.create(:country => germany, :name => "Hooge", :area_code => "4849") + AreaCode.create(:country => germany, :name => "Marne", :area_code => "4851") + AreaCode.create(:country => germany, :name => "Brunsbüttel", :area_code => "4852") + AreaCode.create(:country => germany, :name => "Sankt Michaelisdonn", :area_code => "4853") + AreaCode.create(:country => germany, :name => "Friedrichskoog", :area_code => "4854") + AreaCode.create(:country => germany, :name => "Eddelak", :area_code => "4855") + AreaCode.create(:country => germany, :name => "Kronprinzenkoog", :area_code => "4856") + AreaCode.create(:country => germany, :name => "Barlt", :area_code => "4857") + AreaCode.create(:country => germany, :name => "Sankt Margarethen Holst", :area_code => "4858") + AreaCode.create(:country => germany, :name => "Windbergen", :area_code => "4859") + AreaCode.create(:country => germany, :name => "Tönning", :area_code => "4861") + AreaCode.create(:country => germany, :name => "Garding", :area_code => "4862") + AreaCode.create(:country => germany, :name => "Sankt Peter-Ording", :area_code => "4863") + AreaCode.create(:country => germany, :name => "Oldenswort", :area_code => "4864") + AreaCode.create(:country => germany, :name => "Osterhever", :area_code => "4865") + AreaCode.create(:country => germany, :name => "Hohenwestedt", :area_code => "4871") + AreaCode.create(:country => germany, :name => "Hanerau-Hademarschen", :area_code => "4872") + AreaCode.create(:country => germany, :name => "Aukrug", :area_code => "4873") + AreaCode.create(:country => germany, :name => "Todenbüttel", :area_code => "4874") + AreaCode.create(:country => germany, :name => "Stafstedt", :area_code => "4875") + AreaCode.create(:country => germany, :name => "Reher Holst", :area_code => "4876") + AreaCode.create(:country => germany, :name => "Hennstedt b Itzehoe", :area_code => "4877") + AreaCode.create(:country => germany, :name => "Friedrichstadt", :area_code => "4881") + AreaCode.create(:country => germany, :name => "Lunden", :area_code => "4882") + AreaCode.create(:country => germany, :name => "Süderstapel", :area_code => "4883") + AreaCode.create(:country => germany, :name => "Schwabstedt", :area_code => "4884") + AreaCode.create(:country => germany, :name => "Bergenhusen", :area_code => "4885") + AreaCode.create(:country => germany, :name => "Schenefeld Mittelholst", :area_code => "4892") + AreaCode.create(:country => germany, :name => "Hohenaspe", :area_code => "4893") + AreaCode.create(:country => germany, :name => "Jemgum-Ditzum", :area_code => "4902") + AreaCode.create(:country => germany, :name => "Wymeer", :area_code => "4903") + AreaCode.create(:country => germany, :name => "Leer Ostfriesland", :area_code => "491") + AreaCode.create(:country => germany, :name => "Wirdum", :area_code => "4920") + AreaCode.create(:country => germany, :name => "Emden Stadt", :area_code => "4921") + AreaCode.create(:country => germany, :name => "Borkum", :area_code => "4922") + AreaCode.create(:country => germany, :name => "Krummhörn-Pewsum", :area_code => "4923") + AreaCode.create(:country => germany, :name => "Moormerland-Oldersum", :area_code => "4924") + AreaCode.create(:country => germany, :name => "Hinte", :area_code => "4925") + AreaCode.create(:country => germany, :name => "Krummhörn-Greetsiel", :area_code => "4926") + AreaCode.create(:country => germany, :name => "Krummhörn-Loquard", :area_code => "4927") + AreaCode.create(:country => germany, :name => "Ihlow-Riepe", :area_code => "4928") + AreaCode.create(:country => germany, :name => "Ihlow Kr Aurich", :area_code => "4929") + AreaCode.create(:country => germany, :name => "Norden", :area_code => "4931") + AreaCode.create(:country => germany, :name => "Norderney", :area_code => "4932") + AreaCode.create(:country => germany, :name => "Dornum Ostfriesl", :area_code => "4933") + AreaCode.create(:country => germany, :name => "Marienhafe", :area_code => "4934") + AreaCode.create(:country => germany, :name => "Juist", :area_code => "4935") + AreaCode.create(:country => germany, :name => "Grossheide", :area_code => "4936") + AreaCode.create(:country => germany, :name => "Hagermarsch", :area_code => "4938") + AreaCode.create(:country => germany, :name => "Baltrum", :area_code => "4939") + AreaCode.create(:country => germany, :name => "Aurich", :area_code => "4941") + AreaCode.create(:country => germany, :name => "Südbrookmerland", :area_code => "4942") + AreaCode.create(:country => germany, :name => "Grossefehn", :area_code => "4943") + AreaCode.create(:country => germany, :name => "Wiesmoor", :area_code => "4944") + AreaCode.create(:country => germany, :name => "Grossefehn-Timmel", :area_code => "4945") + AreaCode.create(:country => germany, :name => "Grossefehn-Bagband", :area_code => "4946") + AreaCode.create(:country => germany, :name => "Aurich-Ogenbargen", :area_code => "4947") + AreaCode.create(:country => germany, :name => "Wiesmoor-Marcardsmoor", :area_code => "4948") + AreaCode.create(:country => germany, :name => "Holtland", :area_code => "4950") + AreaCode.create(:country => germany, :name => "Weener", :area_code => "4951") + AreaCode.create(:country => germany, :name => "Rhauderfehn", :area_code => "4952") + AreaCode.create(:country => germany, :name => "Bunde", :area_code => "4953") + AreaCode.create(:country => germany, :name => "Moormerland", :area_code => "4954") + AreaCode.create(:country => germany, :name => "Westoverledingen", :area_code => "4955") + AreaCode.create(:country => germany, :name => "Uplengen", :area_code => "4956") + AreaCode.create(:country => germany, :name => "Detern", :area_code => "4957") + AreaCode.create(:country => germany, :name => "Jemgum", :area_code => "4958") + AreaCode.create(:country => germany, :name => "Dollart", :area_code => "4959") + AreaCode.create(:country => germany, :name => "Papenburg", :area_code => "4961") + AreaCode.create(:country => germany, :name => "Papenburg-Aschendorf", :area_code => "4962") + AreaCode.create(:country => germany, :name => "Dörpen", :area_code => "4963") + AreaCode.create(:country => germany, :name => "Rhede Ems", :area_code => "4964") + AreaCode.create(:country => germany, :name => "Surwold", :area_code => "4965") + AreaCode.create(:country => germany, :name => "Neubörger", :area_code => "4966") + AreaCode.create(:country => germany, :name => "Rhauderfehn-Burlage", :area_code => "4967") + AreaCode.create(:country => germany, :name => "Neulehe", :area_code => "4968") + AreaCode.create(:country => germany, :name => "Esens", :area_code => "4971") + AreaCode.create(:country => germany, :name => "Langeoog", :area_code => "4972") + AreaCode.create(:country => germany, :name => "Wittmund-Burhafe", :area_code => "4973") + AreaCode.create(:country => germany, :name => "Neuharlingersiel", :area_code => "4974") + AreaCode.create(:country => germany, :name => "Westerholt Ostfriesl", :area_code => "4975") + AreaCode.create(:country => germany, :name => "Spiekeroog", :area_code => "4976") + AreaCode.create(:country => germany, :name => "Blomberg Ostfriesl", :area_code => "4977") + AreaCode.create(:country => germany, :name => "Nienburg Weser", :area_code => "5021") + AreaCode.create(:country => germany, :name => "Wietzen", :area_code => "5022") + AreaCode.create(:country => germany, :name => "Liebenau Kr Nienburg Weser", :area_code => "5023") + AreaCode.create(:country => germany, :name => "Rohrsen Kr Nienburg Weser", :area_code => "5024") + AreaCode.create(:country => germany, :name => "Estorf Weser", :area_code => "5025") + AreaCode.create(:country => germany, :name => "Steimbke", :area_code => "5026") + AreaCode.create(:country => germany, :name => "Linsburg", :area_code => "5027") + AreaCode.create(:country => germany, :name => "Pennigsehl", :area_code => "5028") + AreaCode.create(:country => germany, :name => "Wunstorf", :area_code => "5031") + AreaCode.create(:country => germany, :name => "Neustadt am Rübenberge", :area_code => "5032") + AreaCode.create(:country => germany, :name => "Wunstorf-Grossenheidorn", :area_code => "5033") + AreaCode.create(:country => germany, :name => "Neustadt-Hagen", :area_code => "5034") + AreaCode.create(:country => germany, :name => "Gross Munzel", :area_code => "5035") + AreaCode.create(:country => germany, :name => "Neustadt-Schneeren", :area_code => "5036") + AreaCode.create(:country => germany, :name => "Bad Rehburg", :area_code => "5037") + AreaCode.create(:country => germany, :name => "Springe Deister", :area_code => "5041") + AreaCode.create(:country => germany, :name => "Bad Münder am Deister", :area_code => "5042") + AreaCode.create(:country => germany, :name => "Lauenau", :area_code => "5043") + AreaCode.create(:country => germany, :name => "Springe-Eldagsen", :area_code => "5044") + AreaCode.create(:country => germany, :name => "Springe-Bennigsen", :area_code => "5045") + AreaCode.create(:country => germany, :name => "Bergen Kr Celle", :area_code => "5051") + AreaCode.create(:country => germany, :name => "Hermannsburg", :area_code => "5052") + AreaCode.create(:country => germany, :name => "Faßberg-Müden", :area_code => "5053") + AreaCode.create(:country => germany, :name => "Bergen-Sülze", :area_code => "5054") + AreaCode.create(:country => germany, :name => "Fassberg", :area_code => "5055") + AreaCode.create(:country => germany, :name => "Winsen-Meissendorf", :area_code => "5056") + AreaCode.create(:country => germany, :name => "Bodenburg", :area_code => "5060") + AreaCode.create(:country => germany, :name => "Holle b Hildesheim", :area_code => "5062") + AreaCode.create(:country => germany, :name => "Bad Salzdetfurth", :area_code => "5063") + AreaCode.create(:country => germany, :name => "Groß Düngen", :area_code => "5064") + AreaCode.create(:country => germany, :name => "Sibbesse", :area_code => "5065") + AreaCode.create(:country => germany, :name => "Sarstedt", :area_code => "5066") + AreaCode.create(:country => germany, :name => "Bockenem", :area_code => "5067") + AreaCode.create(:country => germany, :name => "Elze Leine", :area_code => "5068") + AreaCode.create(:country => germany, :name => "Nordstemmen", :area_code => "5069") + AreaCode.create(:country => germany, :name => "Schwarmstedt", :area_code => "5071") + AreaCode.create(:country => germany, :name => "Neustadt-Mandelsloh", :area_code => "5072") + AreaCode.create(:country => germany, :name => "Neustadt-Esperke", :area_code => "5073") + AreaCode.create(:country => germany, :name => "Rodewald", :area_code => "5074") + AreaCode.create(:country => germany, :name => "Langlingen", :area_code => "5082") + AreaCode.create(:country => germany, :name => "Hohne b Celle", :area_code => "5083") + AreaCode.create(:country => germany, :name => "Hambühren", :area_code => "5084") + AreaCode.create(:country => germany, :name => "Burgdorf-Ehlershausen", :area_code => "5085") + AreaCode.create(:country => germany, :name => "Celle-Scheuen", :area_code => "5086") + AreaCode.create(:country => germany, :name => "Pattensen", :area_code => "5101") + AreaCode.create(:country => germany, :name => "Laatzen", :area_code => "5102") + AreaCode.create(:country => germany, :name => "Wennigsen Deister", :area_code => "5103") + AreaCode.create(:country => germany, :name => "Barsinghausen", :area_code => "5105") + AreaCode.create(:country => germany, :name => "Gehrden Han", :area_code => "5108") + AreaCode.create(:country => germany, :name => "Ronnenberg", :area_code => "5109") + AreaCode.create(:country => germany, :name => "Hannover", :area_code => "511") + AreaCode.create(:country => germany, :name => "Hildesheim", :area_code => "5121") + AreaCode.create(:country => germany, :name => "Schellerten", :area_code => "5123") + AreaCode.create(:country => germany, :name => "Algermissen", :area_code => "5126") + AreaCode.create(:country => germany, :name => "Harsum", :area_code => "5127") + AreaCode.create(:country => germany, :name => "Hohenhameln", :area_code => "5128") + AreaCode.create(:country => germany, :name => "Söhlde", :area_code => "5129") + AreaCode.create(:country => germany, :name => "Wedemark", :area_code => "5130") + AreaCode.create(:country => germany, :name => "Garbsen", :area_code => "5131") + AreaCode.create(:country => germany, :name => "Lehrte", :area_code => "5132") + AreaCode.create(:country => germany, :name => "Burgwedel-Fuhrberg", :area_code => "5135") + AreaCode.create(:country => germany, :name => "Burgdorf Kr Hannover", :area_code => "5136") + AreaCode.create(:country => germany, :name => "Seelze", :area_code => "5137") + AreaCode.create(:country => germany, :name => "Sehnde", :area_code => "5138") + AreaCode.create(:country => germany, :name => "Burgwedel", :area_code => "5139") + AreaCode.create(:country => germany, :name => "Celle", :area_code => "5141") + AreaCode.create(:country => germany, :name => "Eschede", :area_code => "5142") + AreaCode.create(:country => germany, :name => "Winsen Aller", :area_code => "5143") + AreaCode.create(:country => germany, :name => "Wathlingen", :area_code => "5144") + AreaCode.create(:country => germany, :name => "Beedenbostel", :area_code => "5145") + AreaCode.create(:country => germany, :name => "Wietze", :area_code => "5146") + AreaCode.create(:country => germany, :name => "Uetze-Hänigsen", :area_code => "5147") + AreaCode.create(:country => germany, :name => "Steinhorst Niedersachs", :area_code => "5148") + AreaCode.create(:country => germany, :name => "Wienhausen", :area_code => "5149") + AreaCode.create(:country => germany, :name => "Hameln", :area_code => "5151") + AreaCode.create(:country => germany, :name => "Hessisch Oldendorf", :area_code => "5152") + AreaCode.create(:country => germany, :name => "Salzhemmendorf", :area_code => "5153") + AreaCode.create(:country => germany, :name => "Aerzen", :area_code => "5154") + AreaCode.create(:country => germany, :name => "Emmerthal", :area_code => "5155") + AreaCode.create(:country => germany, :name => "Coppenbrügge", :area_code => "5156") + AreaCode.create(:country => germany, :name => "Emmerthal-Börry", :area_code => "5157") + AreaCode.create(:country => germany, :name => "Hemeringen", :area_code => "5158") + AreaCode.create(:country => germany, :name => "Coppenbrügge-Bisperode", :area_code => "5159") + AreaCode.create(:country => germany, :name => "Walsrode", :area_code => "5161") + AreaCode.create(:country => germany, :name => "Fallingbostel", :area_code => "5162") + AreaCode.create(:country => germany, :name => "Fallingbostel-Dorfmark", :area_code => "5163") + AreaCode.create(:country => germany, :name => "Hodenhagen", :area_code => "5164") + AreaCode.create(:country => germany, :name => "Rethem Aller", :area_code => "5165") + AreaCode.create(:country => germany, :name => "Walsrode-Kirchboitzen", :area_code => "5166") + AreaCode.create(:country => germany, :name => "Walsrode-Westenholz", :area_code => "5167") + AreaCode.create(:country => germany, :name => "Walsrode-Stellichte", :area_code => "5168") + AreaCode.create(:country => germany, :name => "Peine", :area_code => "5171") + AreaCode.create(:country => germany, :name => "Ilsede", :area_code => "5172") + AreaCode.create(:country => germany, :name => "Uetze", :area_code => "5173") + AreaCode.create(:country => germany, :name => "Lahstedt", :area_code => "5174") + AreaCode.create(:country => germany, :name => "Lehrte-Arpke", :area_code => "5175") + AreaCode.create(:country => germany, :name => "Edemissen", :area_code => "5176") + AreaCode.create(:country => germany, :name => "Edemissen-Abbensen", :area_code => "5177") + AreaCode.create(:country => germany, :name => "Alfeld Leine", :area_code => "5181") + AreaCode.create(:country => germany, :name => "Gronau Leine", :area_code => "5182") + AreaCode.create(:country => germany, :name => "Lamspringe", :area_code => "5183") + AreaCode.create(:country => germany, :name => "Freden Leine", :area_code => "5184") + AreaCode.create(:country => germany, :name => "Duingen", :area_code => "5185") + AreaCode.create(:country => germany, :name => "Salzhemmendorf-Wallensen", :area_code => "5186") + AreaCode.create(:country => germany, :name => "Delligsen", :area_code => "5187") + AreaCode.create(:country => germany, :name => "Soltau-Emmingen", :area_code => "5190") + AreaCode.create(:country => germany, :name => "Soltau", :area_code => "5191") + AreaCode.create(:country => germany, :name => "Munster", :area_code => "5192") + AreaCode.create(:country => germany, :name => "Schneverdingen", :area_code => "5193") + AreaCode.create(:country => germany, :name => "Bispingen", :area_code => "5194") + AreaCode.create(:country => germany, :name => "Neuenkirchen b Soltau", :area_code => "5195") + AreaCode.create(:country => germany, :name => "Wietzendorf", :area_code => "5196") + AreaCode.create(:country => germany, :name => "Soltau-Frielingen", :area_code => "5197") + AreaCode.create(:country => germany, :name => "Schneverdingen-Wintermoor", :area_code => "5198") + AreaCode.create(:country => germany, :name => "Schneverdingen-Heber", :area_code => "5199") + AreaCode.create(:country => germany, :name => "Halle Westf", :area_code => "5201") + AreaCode.create(:country => germany, :name => "Oerlinghausen", :area_code => "5202") + AreaCode.create(:country => germany, :name => "Werther Westf", :area_code => "5203") + AreaCode.create(:country => germany, :name => "Steinhagen Westf", :area_code => "5204") + AreaCode.create(:country => germany, :name => "Bielefeld-Sennestadt", :area_code => "5205") + AreaCode.create(:country => germany, :name => "Bielefeld-Jöllenbeck", :area_code => "5206") + AreaCode.create(:country => germany, :name => "Schloss Holte-Stukenbrock", :area_code => "5207") + AreaCode.create(:country => germany, :name => "Leopoldshöhe", :area_code => "5208") + AreaCode.create(:country => germany, :name => "Gütersloh-Friedrichsdorf", :area_code => "5209") + AreaCode.create(:country => germany, :name => "Bielefeld", :area_code => "521") + AreaCode.create(:country => germany, :name => "Herford", :area_code => "5221") + AreaCode.create(:country => germany, :name => "Bad Salzuflen", :area_code => "5222") + AreaCode.create(:country => germany, :name => "Bünde", :area_code => "5223") + AreaCode.create(:country => germany, :name => "Enger Westf", :area_code => "5224") + AreaCode.create(:country => germany, :name => "Spenge", :area_code => "5225") + AreaCode.create(:country => germany, :name => "Bruchmühlen Westf", :area_code => "5226") + AreaCode.create(:country => germany, :name => "Vlotho-Exter", :area_code => "5228") + AreaCode.create(:country => germany, :name => "Detmold", :area_code => "5231") + AreaCode.create(:country => germany, :name => "Lage Lippe", :area_code => "5232") + AreaCode.create(:country => germany, :name => "Steinheim Westf", :area_code => "5233") + AreaCode.create(:country => germany, :name => "Horn-Bad Meinberg", :area_code => "5234") + AreaCode.create(:country => germany, :name => "Blomberg Lippe", :area_code => "5235") + AreaCode.create(:country => germany, :name => "Blomberg-Grossenmarpe", :area_code => "5236") + AreaCode.create(:country => germany, :name => "Augustdorf", :area_code => "5237") + AreaCode.create(:country => germany, :name => "Nieheim-Himmighausen", :area_code => "5238") + AreaCode.create(:country => germany, :name => "Gütersloh", :area_code => "5241") + AreaCode.create(:country => germany, :name => "Rheda-Wiedenbrück", :area_code => "5242") + AreaCode.create(:country => germany, :name => "Rietberg", :area_code => "5244") + AreaCode.create(:country => germany, :name => "Herzebrock-Clarholz", :area_code => "5245") + AreaCode.create(:country => germany, :name => "Verl", :area_code => "5246") + AreaCode.create(:country => germany, :name => "Harsewinkel", :area_code => "5247") + AreaCode.create(:country => germany, :name => "Langenberg Kr Gütersloh", :area_code => "5248") + AreaCode.create(:country => germany, :name => "Delbrück Westf", :area_code => "5250") + AreaCode.create(:country => germany, :name => "Paderborn", :area_code => "5251") + AreaCode.create(:country => germany, :name => "Bad Lippspringe", :area_code => "5252") + AreaCode.create(:country => germany, :name => "Bad Driburg", :area_code => "5253") + AreaCode.create(:country => germany, :name => "Paderborn-Schloss Neuhaus", :area_code => "5254") + AreaCode.create(:country => germany, :name => "Altenbeken", :area_code => "5255") + AreaCode.create(:country => germany, :name => "Hövelhof", :area_code => "5257") + AreaCode.create(:country => germany, :name => "Salzkotten", :area_code => "5258") + AreaCode.create(:country => germany, :name => "Bad Driburg-Neuenheerse", :area_code => "5259") + AreaCode.create(:country => germany, :name => "Lemgo", :area_code => "5261") + AreaCode.create(:country => germany, :name => "Extertal", :area_code => "5262") + AreaCode.create(:country => germany, :name => "Barntrup", :area_code => "5263") + AreaCode.create(:country => germany, :name => "Kalletal", :area_code => "5264") + AreaCode.create(:country => germany, :name => "Dörentrup", :area_code => "5265") + AreaCode.create(:country => germany, :name => "Lemgo-Kirchheide", :area_code => "5266") + AreaCode.create(:country => germany, :name => "Höxter", :area_code => "5271") + AreaCode.create(:country => germany, :name => "Brakel Westf", :area_code => "5272") + AreaCode.create(:country => germany, :name => "Beverungen", :area_code => "5273") + AreaCode.create(:country => germany, :name => "Nieheim", :area_code => "5274") + AreaCode.create(:country => germany, :name => "Höxter-Ottbergen", :area_code => "5275") + AreaCode.create(:country => germany, :name => "Marienmünster", :area_code => "5276") + AreaCode.create(:country => germany, :name => "Höxter-Fürstenau", :area_code => "5277") + AreaCode.create(:country => germany, :name => "Höxter-Ovenhausen", :area_code => "5278") + AreaCode.create(:country => germany, :name => "Bad Pyrmont", :area_code => "5281") + AreaCode.create(:country => germany, :name => "Schieder-Schwalenberg", :area_code => "5282") + AreaCode.create(:country => germany, :name => "Lügde-Rischenau", :area_code => "5283") + AreaCode.create(:country => germany, :name => "Schwalenberg", :area_code => "5284") + AreaCode.create(:country => germany, :name => "Bad Pyrmont-Kleinenberg", :area_code => "5285") + AreaCode.create(:country => germany, :name => "Ottenstein Niedersachs", :area_code => "5286") + AreaCode.create(:country => germany, :name => "Lichtenau-Atteln", :area_code => "5292") + AreaCode.create(:country => germany, :name => "Paderborn-Dahl", :area_code => "5293") + AreaCode.create(:country => germany, :name => "Hövelhof-Espeln", :area_code => "5294") + AreaCode.create(:country => germany, :name => "Lichtenau Westf", :area_code => "5295") + AreaCode.create(:country => germany, :name => "Salzgitter-Üfingen", :area_code => "5300") + AreaCode.create(:country => germany, :name => "Lehre-Essenrode", :area_code => "5301") + AreaCode.create(:country => germany, :name => "Vechelde", :area_code => "5302") + AreaCode.create(:country => germany, :name => "Wendeburg", :area_code => "5303") + AreaCode.create(:country => germany, :name => "Meine", :area_code => "5304") + AreaCode.create(:country => germany, :name => "Sickte", :area_code => "5305") + AreaCode.create(:country => germany, :name => "Cremlingen", :area_code => "5306") + AreaCode.create(:country => germany, :name => "Braunschweig-Wenden", :area_code => "5307") + AreaCode.create(:country => germany, :name => "Lehre", :area_code => "5308") + AreaCode.create(:country => germany, :name => "Lehre-Wendhausen", :area_code => "5309") + AreaCode.create(:country => germany, :name => "Braunschweig", :area_code => "531") + AreaCode.create(:country => germany, :name => "Torfhaus", :area_code => "5320") + AreaCode.create(:country => germany, :name => "Goslar", :area_code => "5321") + AreaCode.create(:country => germany, :name => "Bad Harzburg", :area_code => "5322") + AreaCode.create(:country => germany, :name => "Clausthal-Zellerfeld", :area_code => "5323") + AreaCode.create(:country => germany, :name => "Vienenburg", :area_code => "5324") + AreaCode.create(:country => germany, :name => "Goslar-Hahnenklee", :area_code => "5325") + AreaCode.create(:country => germany, :name => "Langelsheim", :area_code => "5326") + AreaCode.create(:country => germany, :name => "Bad Grund Harz", :area_code => "5327") + AreaCode.create(:country => germany, :name => "Altenau Harz", :area_code => "5328") + AreaCode.create(:country => germany, :name => "Schulenberg im Oberharz", :area_code => "5329") + AreaCode.create(:country => germany, :name => "Wolfenbüttel", :area_code => "5331") + AreaCode.create(:country => germany, :name => "Schöppenstedt", :area_code => "5332") + AreaCode.create(:country => germany, :name => "Dettum", :area_code => "5333") + AreaCode.create(:country => germany, :name => "Hornburg Kr Wolfenbüttel", :area_code => "5334") + AreaCode.create(:country => germany, :name => "Schladen", :area_code => "5335") + AreaCode.create(:country => germany, :name => "Semmenstedt", :area_code => "5336") + AreaCode.create(:country => germany, :name => "Kissenbrück", :area_code => "5337") + AreaCode.create(:country => germany, :name => "Gielde", :area_code => "5339") + AreaCode.create(:country => germany, :name => "Salzgitter", :area_code => "5341") + AreaCode.create(:country => germany, :name => "Lengede", :area_code => "5344") + AreaCode.create(:country => germany, :name => "Baddeckenstedt", :area_code => "5345") + AreaCode.create(:country => germany, :name => "Liebenburg", :area_code => "5346") + AreaCode.create(:country => germany, :name => "Burgdorf b Salzgitter", :area_code => "5347") + AreaCode.create(:country => germany, :name => "Helmstedt", :area_code => "5351") + AreaCode.create(:country => germany, :name => "Schöningen", :area_code => "5352") + AreaCode.create(:country => germany, :name => "Königslutter am Elm", :area_code => "5353") + AreaCode.create(:country => germany, :name => "Jerxheim", :area_code => "5354") + AreaCode.create(:country => germany, :name => "Frellstedt", :area_code => "5355") + AreaCode.create(:country => germany, :name => "Helmstedt-Barmke", :area_code => "5356") + AreaCode.create(:country => germany, :name => "Grasleben", :area_code => "5357") + AreaCode.create(:country => germany, :name => "Bahrdorf-Mackendorf", :area_code => "5358") + AreaCode.create(:country => germany, :name => "Wolfsburg", :area_code => "5361") + AreaCode.create(:country => germany, :name => "Wolfsburg-Fallersleben", :area_code => "5362") + AreaCode.create(:country => germany, :name => "Wolfsburg-Vorsfelde", :area_code => "5363") + AreaCode.create(:country => germany, :name => "Velpke", :area_code => "5364") + AreaCode.create(:country => germany, :name => "Wolfsburg-Neindorf", :area_code => "5365") + AreaCode.create(:country => germany, :name => "Jembke", :area_code => "5366") + AreaCode.create(:country => germany, :name => "Rühen", :area_code => "5367") + AreaCode.create(:country => germany, :name => "Parsau", :area_code => "5368") + AreaCode.create(:country => germany, :name => "Gifhorn", :area_code => "5371") + AreaCode.create(:country => germany, :name => "Meinersen", :area_code => "5372") + AreaCode.create(:country => germany, :name => "Hillerse Kr Gifhorn", :area_code => "5373") + AreaCode.create(:country => germany, :name => "Isenbüttel", :area_code => "5374") + AreaCode.create(:country => germany, :name => "Müden Aller", :area_code => "5375") + AreaCode.create(:country => germany, :name => "Wesendorf Kr Gifhorn", :area_code => "5376") + AreaCode.create(:country => germany, :name => "Ehra-Lessien", :area_code => "5377") + AreaCode.create(:country => germany, :name => "Sassenburg-Platendorf", :area_code => "5378") + AreaCode.create(:country => germany, :name => "Sassenburg-Grussendorf", :area_code => "5379") + AreaCode.create(:country => germany, :name => "Seesen", :area_code => "5381") + AreaCode.create(:country => germany, :name => "Bad Gandersheim", :area_code => "5382") + AreaCode.create(:country => germany, :name => "Lutter am Barenberge", :area_code => "5383") + AreaCode.create(:country => germany, :name => "Seesen-Groß Rhüden", :area_code => "5384") + AreaCode.create(:country => germany, :name => "Georgsmarienhütte", :area_code => "5401") + AreaCode.create(:country => germany, :name => "Bissendorf Kr Osnabrück", :area_code => "5402") + AreaCode.create(:country => germany, :name => "Bad Iburg", :area_code => "5403") + AreaCode.create(:country => germany, :name => "Westerkappeln", :area_code => "5404") + AreaCode.create(:country => germany, :name => "Hasbergen Kr Osnabrück", :area_code => "5405") + AreaCode.create(:country => germany, :name => "Belm", :area_code => "5406") + AreaCode.create(:country => germany, :name => "Wallenhorst", :area_code => "5407") + AreaCode.create(:country => germany, :name => "Hilter am Teutoburger Wald", :area_code => "5409") + AreaCode.create(:country => germany, :name => "Osnabrück", :area_code => "541") + AreaCode.create(:country => germany, :name => "Dissen am Teutoburger Wald", :area_code => "5421") + AreaCode.create(:country => germany, :name => "Melle", :area_code => "5422") + AreaCode.create(:country => germany, :name => "Versmold", :area_code => "5423") + AreaCode.create(:country => germany, :name => "Bad Rothenfelde", :area_code => "5424") + AreaCode.create(:country => germany, :name => "Borgholzhausen", :area_code => "5425") + AreaCode.create(:country => germany, :name => "Glandorf", :area_code => "5426") + AreaCode.create(:country => germany, :name => "Melle-Buer", :area_code => "5427") + AreaCode.create(:country => germany, :name => "Melle-Neuenkirchen", :area_code => "5428") + AreaCode.create(:country => germany, :name => "Melle-Wellingholzhausen", :area_code => "5429") + AreaCode.create(:country => germany, :name => "Quakenbrück", :area_code => "5431") + AreaCode.create(:country => germany, :name => "Löningen", :area_code => "5432") + AreaCode.create(:country => germany, :name => "Badbergen", :area_code => "5433") + AreaCode.create(:country => germany, :name => "Essen Oldenburg", :area_code => "5434") + AreaCode.create(:country => germany, :name => "Berge b Quakenbrück", :area_code => "5435") + AreaCode.create(:country => germany, :name => "Nortrup", :area_code => "5436") + AreaCode.create(:country => germany, :name => "Menslage", :area_code => "5437") + AreaCode.create(:country => germany, :name => "Bakum-Lüsche", :area_code => "5438") + AreaCode.create(:country => germany, :name => "Bersenbrück", :area_code => "5439") + AreaCode.create(:country => germany, :name => "Diepholz", :area_code => "5441") + AreaCode.create(:country => germany, :name => "Barnstorf Kr Diepholz", :area_code => "5442") + AreaCode.create(:country => germany, :name => "Lemförde", :area_code => "5443") + AreaCode.create(:country => germany, :name => "Wagenfeld", :area_code => "5444") + AreaCode.create(:country => germany, :name => "Drebber", :area_code => "5445") + AreaCode.create(:country => germany, :name => "Rehden", :area_code => "5446") + AreaCode.create(:country => germany, :name => "Lembruch", :area_code => "5447") + AreaCode.create(:country => germany, :name => "Barver", :area_code => "5448") + AreaCode.create(:country => germany, :name => "Ibbenbüren", :area_code => "5451") + AreaCode.create(:country => germany, :name => "Mettingen Westf", :area_code => "5452") + AreaCode.create(:country => germany, :name => "Recke", :area_code => "5453") + AreaCode.create(:country => germany, :name => "Hörstel-Riesenbeck", :area_code => "5454") + AreaCode.create(:country => germany, :name => "Tecklenburg-Brochterbeck", :area_code => "5455") + AreaCode.create(:country => germany, :name => "Westerkappeln-Velpe", :area_code => "5456") + AreaCode.create(:country => germany, :name => "Hopsten-Schale", :area_code => "5457") + AreaCode.create(:country => germany, :name => "Hopsten", :area_code => "5458") + AreaCode.create(:country => germany, :name => "Hörstel", :area_code => "5459") + AreaCode.create(:country => germany, :name => "Bramsche Hase", :area_code => "5461") + AreaCode.create(:country => germany, :name => "Ankum", :area_code => "5462") + AreaCode.create(:country => germany, :name => "Alfhausen", :area_code => "5464") + AreaCode.create(:country => germany, :name => "Neuenkirchen b Bramsche", :area_code => "5465") + AreaCode.create(:country => germany, :name => "Merzen", :area_code => "5466") + AreaCode.create(:country => germany, :name => "Voltlage", :area_code => "5467") + AreaCode.create(:country => germany, :name => "Bramsche-Engter", :area_code => "5468") + AreaCode.create(:country => germany, :name => "Bohmte", :area_code => "5471") + AreaCode.create(:country => germany, :name => "Bad Essen", :area_code => "5472") + AreaCode.create(:country => germany, :name => "Ostercappeln", :area_code => "5473") + AreaCode.create(:country => germany, :name => "Stemwede-Dielingen", :area_code => "5474") + AreaCode.create(:country => germany, :name => "Bohmte-Hunteburg", :area_code => "5475") + AreaCode.create(:country => germany, :name => "Ostercappeln-Venne", :area_code => "5476") + AreaCode.create(:country => germany, :name => "Lengerich Westf", :area_code => "5481") + AreaCode.create(:country => germany, :name => "Tecklenburg", :area_code => "5482") + AreaCode.create(:country => germany, :name => "Lienen", :area_code => "5483") + AreaCode.create(:country => germany, :name => "Lienen-Kattenvenne", :area_code => "5484") + AreaCode.create(:country => germany, :name => "Ladbergen", :area_code => "5485") + AreaCode.create(:country => germany, :name => "Damme Dümmer", :area_code => "5491") + AreaCode.create(:country => germany, :name => "Steinfeld Oldenburg", :area_code => "5492") + AreaCode.create(:country => germany, :name => "Neuenkirchen Kr Vechta", :area_code => "5493") + AreaCode.create(:country => germany, :name => "Holdorf Niedersachs", :area_code => "5494") + AreaCode.create(:country => germany, :name => "Vörden Kr Vechta", :area_code => "5495") + AreaCode.create(:country => germany, :name => "Dransfeld", :area_code => "5502") + AreaCode.create(:country => germany, :name => "Nörten-Hardenberg", :area_code => "5503") + AreaCode.create(:country => germany, :name => "Friedland Kr Göttingen", :area_code => "5504") + AreaCode.create(:country => germany, :name => "Hardegsen", :area_code => "5505") + AreaCode.create(:country => germany, :name => "Adelebsen", :area_code => "5506") + AreaCode.create(:country => germany, :name => "Ebergötzen", :area_code => "5507") + AreaCode.create(:country => germany, :name => "Gleichen-Rittmarshausen", :area_code => "5508") + AreaCode.create(:country => germany, :name => "Rosdorf Kr Göttingen", :area_code => "5509") + AreaCode.create(:country => germany, :name => "Göttingen", :area_code => "551") + AreaCode.create(:country => germany, :name => "Braunlage", :area_code => "5520") + AreaCode.create(:country => germany, :name => "Herzberg am Harz", :area_code => "5521") + AreaCode.create(:country => germany, :name => "Osterode am Harz", :area_code => "5522") + AreaCode.create(:country => germany, :name => "Bad Sachsa", :area_code => "5523") + AreaCode.create(:country => germany, :name => "Bad Lauterberg im Harz", :area_code => "5524") + AreaCode.create(:country => germany, :name => "Walkenried", :area_code => "5525") + AreaCode.create(:country => germany, :name => "Duderstadt", :area_code => "5527") + AreaCode.create(:country => germany, :name => "Gieboldehausen", :area_code => "5528") + AreaCode.create(:country => germany, :name => "Rhumspringe", :area_code => "5529") + AreaCode.create(:country => germany, :name => "Holzminden", :area_code => "5531") + AreaCode.create(:country => germany, :name => "Stadtoldendorf", :area_code => "5532") + AreaCode.create(:country => germany, :name => "Bodenwerder", :area_code => "5533") + AreaCode.create(:country => germany, :name => "Eschershausen a d Lenne", :area_code => "5534") + AreaCode.create(:country => germany, :name => "Polle", :area_code => "5535") + AreaCode.create(:country => germany, :name => "Holzminden-Neuhaus", :area_code => "5536") + AreaCode.create(:country => germany, :name => "Hann. Münden", :area_code => "5541") + AreaCode.create(:country => germany, :name => "Witzenhausen", :area_code => "5542") + AreaCode.create(:country => germany, :name => "Staufenberg Niedersachs", :area_code => "5543") + AreaCode.create(:country => germany, :name => "Reinhardshagen", :area_code => "5544") + AreaCode.create(:country => germany, :name => "Hedemünden", :area_code => "5545") + AreaCode.create(:country => germany, :name => "Scheden", :area_code => "5546") + AreaCode.create(:country => germany, :name => "Northeim", :area_code => "5551") + AreaCode.create(:country => germany, :name => "Katlenburg", :area_code => "5552") + AreaCode.create(:country => germany, :name => "Kalefeld", :area_code => "5553") + AreaCode.create(:country => germany, :name => "Moringen", :area_code => "5554") + AreaCode.create(:country => germany, :name => "Moringen-Fredelsloh", :area_code => "5555") + AreaCode.create(:country => germany, :name => "Lindau Harz", :area_code => "5556") + AreaCode.create(:country => germany, :name => "Einbeck", :area_code => "5561") + AreaCode.create(:country => germany, :name => "Dassel-Markoldendorf", :area_code => "5562") + AreaCode.create(:country => germany, :name => "Kreiensen", :area_code => "5563") + AreaCode.create(:country => germany, :name => "Dassel", :area_code => "5564") + AreaCode.create(:country => germany, :name => "Einbeck-Wenzen", :area_code => "5565") + AreaCode.create(:country => germany, :name => "Uslar", :area_code => "5571") + AreaCode.create(:country => germany, :name => "Bodenfelde", :area_code => "5572") + AreaCode.create(:country => germany, :name => "Uslar-Volpriehausen", :area_code => "5573") + AreaCode.create(:country => germany, :name => "Oberweser", :area_code => "5574") + AreaCode.create(:country => germany, :name => "Sankt Andreasberg", :area_code => "5582") + AreaCode.create(:country => germany, :name => "Braunlage-Hohegeiss", :area_code => "5583") + AreaCode.create(:country => germany, :name => "Hattorf am Harz", :area_code => "5584") + AreaCode.create(:country => germany, :name => "Herzberg-Sieber", :area_code => "5585") + AreaCode.create(:country => germany, :name => "Wieda", :area_code => "5586") + AreaCode.create(:country => germany, :name => "Gleichen-Bremke", :area_code => "5592") + AreaCode.create(:country => germany, :name => "Bovenden-Lenglern", :area_code => "5593") + AreaCode.create(:country => germany, :name => "Bovenden-Reyershausen", :area_code => "5594") + AreaCode.create(:country => germany, :name => "Schauenburg", :area_code => "5601") + AreaCode.create(:country => germany, :name => "Hessisch Lichtenau", :area_code => "5602") + AreaCode.create(:country => germany, :name => "Gudensberg", :area_code => "5603") + AreaCode.create(:country => germany, :name => "Grossalmerode", :area_code => "5604") + AreaCode.create(:country => germany, :name => "Kaufungen Hess", :area_code => "5605") + AreaCode.create(:country => germany, :name => "Zierenberg", :area_code => "5606") + AreaCode.create(:country => germany, :name => "Fuldatal", :area_code => "5607") + AreaCode.create(:country => germany, :name => "Söhrewald", :area_code => "5608") + AreaCode.create(:country => germany, :name => "Ahnatal", :area_code => "5609") + AreaCode.create(:country => germany, :name => "Kassel", :area_code => "561") + AreaCode.create(:country => germany, :name => "Bad Wildungen", :area_code => "5621") + AreaCode.create(:country => germany, :name => "Fritzlar", :area_code => "5622") + AreaCode.create(:country => germany, :name => "Edertal", :area_code => "5623") + AreaCode.create(:country => germany, :name => "Bad Emstal", :area_code => "5624") + AreaCode.create(:country => germany, :name => "Naumburg Hess", :area_code => "5625") + AreaCode.create(:country => germany, :name => "Bad Zwesten", :area_code => "5626") + AreaCode.create(:country => germany, :name => "Korbach", :area_code => "5631") + AreaCode.create(:country => germany, :name => "Willingen Upland", :area_code => "5632") + AreaCode.create(:country => germany, :name => "Diemelsee", :area_code => "5633") + AreaCode.create(:country => germany, :name => "Waldeck-Sachsenhausen", :area_code => "5634") + AreaCode.create(:country => germany, :name => "Vöhl", :area_code => "5635") + AreaCode.create(:country => germany, :name => "Lichtenfels-Goddelsheim", :area_code => "5636") + AreaCode.create(:country => germany, :name => "Warburg", :area_code => "5641") + AreaCode.create(:country => germany, :name => "Warburg-Scherfede", :area_code => "5642") + AreaCode.create(:country => germany, :name => "Borgentreich", :area_code => "5643") + AreaCode.create(:country => germany, :name => "Willebadessen-Peckelsheim", :area_code => "5644") + AreaCode.create(:country => germany, :name => "Borgentreich-Borgholz", :area_code => "5645") + AreaCode.create(:country => germany, :name => "Willebadessen", :area_code => "5646") + AreaCode.create(:country => germany, :name => "Lichtenau-Kleinenberg", :area_code => "5647") + AreaCode.create(:country => germany, :name => "Brakel-Gehrden", :area_code => "5648") + AreaCode.create(:country => germany, :name => "Cornberg", :area_code => "5650") + AreaCode.create(:country => germany, :name => "Eschwege", :area_code => "5651") + AreaCode.create(:country => germany, :name => "Bad Sooden-Allendorf", :area_code => "5652") + AreaCode.create(:country => germany, :name => "Sontra", :area_code => "5653") + AreaCode.create(:country => germany, :name => "Herleshausen", :area_code => "5654") + AreaCode.create(:country => germany, :name => "Wanfried", :area_code => "5655") + AreaCode.create(:country => germany, :name => "Waldkappel", :area_code => "5656") + AreaCode.create(:country => germany, :name => "Meissner", :area_code => "5657") + AreaCode.create(:country => germany, :name => "Wehretal", :area_code => "5658") + AreaCode.create(:country => germany, :name => "Ringgau", :area_code => "5659") + AreaCode.create(:country => germany, :name => "Melsungen", :area_code => "5661") + AreaCode.create(:country => germany, :name => "Felsberg Hess", :area_code => "5662") + AreaCode.create(:country => germany, :name => "Spangenberg", :area_code => "5663") + AreaCode.create(:country => germany, :name => "Morschen", :area_code => "5664") + AreaCode.create(:country => germany, :name => "Guxhagen", :area_code => "5665") + AreaCode.create(:country => germany, :name => "Hofgeismar", :area_code => "5671") + AreaCode.create(:country => germany, :name => "Bad Karlshafen", :area_code => "5672") + AreaCode.create(:country => germany, :name => "Immenhausen Hess", :area_code => "5673") + AreaCode.create(:country => germany, :name => "Grebenstein", :area_code => "5674") + AreaCode.create(:country => germany, :name => "Trendelburg", :area_code => "5675") + AreaCode.create(:country => germany, :name => "Liebenau Hess", :area_code => "5676") + AreaCode.create(:country => germany, :name => "Calden-Westuffeln", :area_code => "5677") + AreaCode.create(:country => germany, :name => "Homberg Efze", :area_code => "5681") + AreaCode.create(:country => germany, :name => "Borken Hessen", :area_code => "5682") + AreaCode.create(:country => germany, :name => "Wabern Hess", :area_code => "5683") + AreaCode.create(:country => germany, :name => "Frielendorf", :area_code => "5684") + AreaCode.create(:country => germany, :name => "Knüllwald", :area_code => "5685") + AreaCode.create(:country => germany, :name => "Schwarzenborn Knüll", :area_code => "5686") + AreaCode.create(:country => germany, :name => "Bad Arolsen", :area_code => "5691") + AreaCode.create(:country => germany, :name => "Wolfhagen", :area_code => "5692") + AreaCode.create(:country => germany, :name => "Volkmarsen", :area_code => "5693") + AreaCode.create(:country => germany, :name => "Diemelstadt", :area_code => "5694") + AreaCode.create(:country => germany, :name => "Twistetal", :area_code => "5695") + AreaCode.create(:country => germany, :name => "Bad Arolsen-Landau", :area_code => "5696") + AreaCode.create(:country => germany, :name => "Petershagen-Lahde", :area_code => "5702") + AreaCode.create(:country => germany, :name => "Hille", :area_code => "5703") + AreaCode.create(:country => germany, :name => "Petershagen-Friedewalde", :area_code => "5704") + AreaCode.create(:country => germany, :name => "Petershagen-Windheim", :area_code => "5705") + AreaCode.create(:country => germany, :name => "Porta Westfalica", :area_code => "5706") + AreaCode.create(:country => germany, :name => "Petershagen Weser", :area_code => "5707") + AreaCode.create(:country => germany, :name => "Minden Westf", :area_code => "571") + AreaCode.create(:country => germany, :name => "Stadthagen", :area_code => "5721") + AreaCode.create(:country => germany, :name => "Bückeburg", :area_code => "5722") + AreaCode.create(:country => germany, :name => "Bad Nenndorf", :area_code => "5723") + AreaCode.create(:country => germany, :name => "Obernkirchen", :area_code => "5724") + AreaCode.create(:country => germany, :name => "Lindhorst b Stadthagen", :area_code => "5725") + AreaCode.create(:country => germany, :name => "Wiedensahl", :area_code => "5726") + AreaCode.create(:country => germany, :name => "Bad Oeynhausen", :area_code => "5731") + AreaCode.create(:country => germany, :name => "Löhne", :area_code => "5732") + AreaCode.create(:country => germany, :name => "Vlotho", :area_code => "5733") + AreaCode.create(:country => germany, :name => "Bergkirchen Westf", :area_code => "5734") + AreaCode.create(:country => germany, :name => "Lübbecke", :area_code => "5741") + AreaCode.create(:country => germany, :name => "Preussisch Oldendorf", :area_code => "5742") + AreaCode.create(:country => germany, :name => "Espelkamp-Gestringen", :area_code => "5743") + AreaCode.create(:country => germany, :name => "Hüllhorst", :area_code => "5744") + AreaCode.create(:country => germany, :name => "Stemwede-Levern", :area_code => "5745") + AreaCode.create(:country => germany, :name => "Rödinghausen", :area_code => "5746") + AreaCode.create(:country => germany, :name => "Rinteln", :area_code => "5751") + AreaCode.create(:country => germany, :name => "Auetal-Hattendorf", :area_code => "5752") + AreaCode.create(:country => germany, :name => "Auetal-Bernsen", :area_code => "5753") + AreaCode.create(:country => germany, :name => "Extertal-Bremke", :area_code => "5754") + AreaCode.create(:country => germany, :name => "Kalletal-Varenholz", :area_code => "5755") + AreaCode.create(:country => germany, :name => "Stolzenau", :area_code => "5761") + AreaCode.create(:country => germany, :name => "Uchte", :area_code => "5763") + AreaCode.create(:country => germany, :name => "Steyerberg", :area_code => "5764") + AreaCode.create(:country => germany, :name => "Raddestorf", :area_code => "5765") + AreaCode.create(:country => germany, :name => "Rehburg-Loccum", :area_code => "5766") + AreaCode.create(:country => germany, :name => "Warmsen", :area_code => "5767") + AreaCode.create(:country => germany, :name => "Petershagen-Heimsen", :area_code => "5768") + AreaCode.create(:country => germany, :name => "Steyerberg-Voigtei", :area_code => "5769") + AreaCode.create(:country => germany, :name => "Rahden Westf", :area_code => "5771") + AreaCode.create(:country => germany, :name => "Espelkamp", :area_code => "5772") + AreaCode.create(:country => germany, :name => "Stemwede-Wehdem", :area_code => "5773") + AreaCode.create(:country => germany, :name => "Wagenfeld-Ströhen", :area_code => "5774") + AreaCode.create(:country => germany, :name => "Diepenau", :area_code => "5775") + AreaCode.create(:country => germany, :name => "Preussisch Ströhen", :area_code => "5776") + AreaCode.create(:country => germany, :name => "Diepenau-Essern", :area_code => "5777") + AreaCode.create(:country => germany, :name => "Wrestedt", :area_code => "5802") + AreaCode.create(:country => germany, :name => "Rosche", :area_code => "5803") + AreaCode.create(:country => germany, :name => "Rätzlingen Kr Uelzen", :area_code => "5804") + AreaCode.create(:country => germany, :name => "Oetzen", :area_code => "5805") + AreaCode.create(:country => germany, :name => "Barum b Bad Bevensen", :area_code => "5806") + AreaCode.create(:country => germany, :name => "Altenmedingen", :area_code => "5807") + AreaCode.create(:country => germany, :name => "Gerdau", :area_code => "5808") + AreaCode.create(:country => germany, :name => "Uelzen", :area_code => "581") + AreaCode.create(:country => germany, :name => "Suhlendorf", :area_code => "5820") + AreaCode.create(:country => germany, :name => "Bad Bevensen", :area_code => "5821") + AreaCode.create(:country => germany, :name => "Ebstorf", :area_code => "5822") + AreaCode.create(:country => germany, :name => "Bienenbüttel", :area_code => "5823") + AreaCode.create(:country => germany, :name => "Bad Bodenteich", :area_code => "5824") + AreaCode.create(:country => germany, :name => "Wieren", :area_code => "5825") + AreaCode.create(:country => germany, :name => "Suderburg", :area_code => "5826") + AreaCode.create(:country => germany, :name => "Unterlüß", :area_code => "5827") + AreaCode.create(:country => germany, :name => "Himbergen", :area_code => "5828") + AreaCode.create(:country => germany, :name => "Wriedel", :area_code => "5829") + AreaCode.create(:country => germany, :name => "Wittingen", :area_code => "5831") + AreaCode.create(:country => germany, :name => "Hankensbüttel", :area_code => "5832") + AreaCode.create(:country => germany, :name => "Brome", :area_code => "5833") + AreaCode.create(:country => germany, :name => "Wittingen-Knesebeck", :area_code => "5834") + AreaCode.create(:country => germany, :name => "Wahrenholz", :area_code => "5835") + AreaCode.create(:country => germany, :name => "Wittingen-Radenbeck", :area_code => "5836") + AreaCode.create(:country => germany, :name => "Sprakensehl", :area_code => "5837") + AreaCode.create(:country => germany, :name => "Gross Oesingen", :area_code => "5838") + AreaCode.create(:country => germany, :name => "Wittingen-Ohrdorf", :area_code => "5839") + AreaCode.create(:country => germany, :name => "Schnackenburg", :area_code => "5840") + AreaCode.create(:country => germany, :name => "Lüchow Wendland", :area_code => "5841") + AreaCode.create(:country => germany, :name => "Schnega", :area_code => "5842") + AreaCode.create(:country => germany, :name => "Wustrow", :area_code => "5843") + AreaCode.create(:country => germany, :name => "Clenze", :area_code => "5844") + AreaCode.create(:country => germany, :name => "Bergen Dumme", :area_code => "5845") + AreaCode.create(:country => germany, :name => "Gartow Niedersachs", :area_code => "5846") + AreaCode.create(:country => germany, :name => "Trebel", :area_code => "5848") + AreaCode.create(:country => germany, :name => "Waddeweitz", :area_code => "5849") + AreaCode.create(:country => germany, :name => "Neetze", :area_code => "5850") + AreaCode.create(:country => germany, :name => "Dahlenburg", :area_code => "5851") + AreaCode.create(:country => germany, :name => "Bleckede", :area_code => "5852") + AreaCode.create(:country => germany, :name => "Neu Darchau", :area_code => "5853") + AreaCode.create(:country => germany, :name => "Bleckede-Barskamp", :area_code => "5854") + AreaCode.create(:country => germany, :name => "Nahrendorf", :area_code => "5855") + AreaCode.create(:country => germany, :name => "Bleckede-Brackede", :area_code => "5857") + AreaCode.create(:country => germany, :name => "Hitzacker-Wietzetze", :area_code => "5858") + AreaCode.create(:country => germany, :name => "Thomasburg", :area_code => "5859") + AreaCode.create(:country => germany, :name => "Dannenberg Elbe", :area_code => "5861") + AreaCode.create(:country => germany, :name => "Hitzacker Elbe", :area_code => "5862") + AreaCode.create(:country => germany, :name => "Zernien", :area_code => "5863") + AreaCode.create(:country => germany, :name => "Jameln", :area_code => "5864") + AreaCode.create(:country => germany, :name => "Gusborn", :area_code => "5865") + AreaCode.create(:country => germany, :name => "Stoetze", :area_code => "5872") + AreaCode.create(:country => germany, :name => "Eimke", :area_code => "5873") + AreaCode.create(:country => germany, :name => "Soltendieck", :area_code => "5874") + AreaCode.create(:country => germany, :name => "Emmendorf", :area_code => "5875") + AreaCode.create(:country => germany, :name => "Gorleben", :area_code => "5882") + AreaCode.create(:country => germany, :name => "Lemgow", :area_code => "5883") + AreaCode.create(:country => germany, :name => "Fürstenau b Bramsche", :area_code => "5901") + AreaCode.create(:country => germany, :name => "Freren", :area_code => "5902") + AreaCode.create(:country => germany, :name => "Emsbüren", :area_code => "5903") + AreaCode.create(:country => germany, :name => "Lengerich Emsl", :area_code => "5904") + AreaCode.create(:country => germany, :name => "Beesten", :area_code => "5905") + AreaCode.create(:country => germany, :name => "Lünne", :area_code => "5906") + AreaCode.create(:country => germany, :name => "Geeste", :area_code => "5907") + AreaCode.create(:country => germany, :name => "Wietmarschen-Lohne", :area_code => "5908") + AreaCode.create(:country => germany, :name => "Wettrup", :area_code => "5909") + AreaCode.create(:country => germany, :name => "Lingen (Ems)", :area_code => "591") + AreaCode.create(:country => germany, :name => "Nordhorn", :area_code => "5921") + AreaCode.create(:country => germany, :name => "Bad Bentheim", :area_code => "5922") + AreaCode.create(:country => germany, :name => "Schüttorf", :area_code => "5923") + AreaCode.create(:country => germany, :name => "Bad Bentheim-Gildehaus", :area_code => "5924") + AreaCode.create(:country => germany, :name => "Wietmarschen", :area_code => "5925") + AreaCode.create(:country => germany, :name => "Engden", :area_code => "5926") + AreaCode.create(:country => germany, :name => "Meppen", :area_code => "5931") + AreaCode.create(:country => germany, :name => "Haren Ems", :area_code => "5932") + AreaCode.create(:country => germany, :name => "Lathen", :area_code => "5933") + AreaCode.create(:country => germany, :name => "Haren-Rütenbrock", :area_code => "5934") + AreaCode.create(:country => germany, :name => "Twist-Schöninghsdorf", :area_code => "5935") + AreaCode.create(:country => germany, :name => "Twist", :area_code => "5936") + AreaCode.create(:country => germany, :name => "Geeste-Gross Hesepe", :area_code => "5937") + AreaCode.create(:country => germany, :name => "Sustrum", :area_code => "5939") + AreaCode.create(:country => germany, :name => "Neuenhaus Dinkel", :area_code => "5941") + AreaCode.create(:country => germany, :name => "Uelsen", :area_code => "5942") + AreaCode.create(:country => germany, :name => "Emlichheim", :area_code => "5943") + AreaCode.create(:country => germany, :name => "Hoogstede", :area_code => "5944") + AreaCode.create(:country => germany, :name => "Wilsum", :area_code => "5945") + AreaCode.create(:country => germany, :name => "Georgsdorf", :area_code => "5946") + AreaCode.create(:country => germany, :name => "Laar Vechte", :area_code => "5947") + AreaCode.create(:country => germany, :name => "Itterbeck", :area_code => "5948") + AreaCode.create(:country => germany, :name => "Werlte", :area_code => "5951") + AreaCode.create(:country => germany, :name => "Sögel", :area_code => "5952") + AreaCode.create(:country => germany, :name => "Börger", :area_code => "5953") + AreaCode.create(:country => germany, :name => "Lorup", :area_code => "5954") + AreaCode.create(:country => germany, :name => "Esterwegen", :area_code => "5955") + AreaCode.create(:country => germany, :name => "Rastdorf", :area_code => "5956") + AreaCode.create(:country => germany, :name => "Lindern Oldenburg", :area_code => "5957") + AreaCode.create(:country => germany, :name => "Haselünne", :area_code => "5961") + AreaCode.create(:country => germany, :name => "Herzlake", :area_code => "5962") + AreaCode.create(:country => germany, :name => "Bawinkel", :area_code => "5963") + AreaCode.create(:country => germany, :name => "Lähden", :area_code => "5964") + AreaCode.create(:country => germany, :name => "Klein Berssen", :area_code => "5965") + AreaCode.create(:country => germany, :name => "Meppen-Apeldorn", :area_code => "5966") + AreaCode.create(:country => germany, :name => "Rheine", :area_code => "5971") + AreaCode.create(:country => germany, :name => "Neuenkirchen Kr Steinfurt", :area_code => "5973") + AreaCode.create(:country => germany, :name => "Rheine-Mesum", :area_code => "5975") + AreaCode.create(:country => germany, :name => "Salzbergen", :area_code => "5976") + AreaCode.create(:country => germany, :name => "Spelle", :area_code => "5977") + AreaCode.create(:country => germany, :name => "Hörstel-Dreierwalde", :area_code => "5978") + AreaCode.create(:country => germany, :name => "Ober-Mörlen", :area_code => "6002") + AreaCode.create(:country => germany, :name => "Rosbach v d Höhe", :area_code => "6003") + AreaCode.create(:country => germany, :name => "Lich-Eberstadt", :area_code => "6004") + AreaCode.create(:country => germany, :name => "Rosbach-Rodheim", :area_code => "6007") + AreaCode.create(:country => germany, :name => "Echzell", :area_code => "6008") + AreaCode.create(:country => germany, :name => "Heigenbrücken", :area_code => "6020") + AreaCode.create(:country => germany, :name => "Aschaffenburg", :area_code => "6021") + AreaCode.create(:country => germany, :name => "Obernburg a Main", :area_code => "6022") + AreaCode.create(:country => germany, :name => "Alzenau i Ufr", :area_code => "6023") + AreaCode.create(:country => germany, :name => "Schöllkrippen", :area_code => "6024") + AreaCode.create(:country => germany, :name => "Grossostheim", :area_code => "6026") + AreaCode.create(:country => germany, :name => "Stockstadt a Main", :area_code => "6027") + AreaCode.create(:country => germany, :name => "Sulzbach a Main", :area_code => "6028") + AreaCode.create(:country => germany, :name => "Mömbris", :area_code => "6029") + AreaCode.create(:country => germany, :name => "Friedberg Hess", :area_code => "6031") + AreaCode.create(:country => germany, :name => "Bad Nauheim", :area_code => "6032") + AreaCode.create(:country => germany, :name => "Butzbach", :area_code => "6033") + AreaCode.create(:country => germany, :name => "Wöllstadt", :area_code => "6034") + AreaCode.create(:country => germany, :name => "Reichelsheim Wetterau", :area_code => "6035") + AreaCode.create(:country => germany, :name => "Wölfersheim", :area_code => "6036") + AreaCode.create(:country => germany, :name => "Karben", :area_code => "6039") + AreaCode.create(:country => germany, :name => "Glauburg", :area_code => "6041") + AreaCode.create(:country => germany, :name => "Büdingen Hess", :area_code => "6042") + AreaCode.create(:country => germany, :name => "Nidda", :area_code => "6043") + AreaCode.create(:country => germany, :name => "Schotten Hess", :area_code => "6044") + AreaCode.create(:country => germany, :name => "Gedern", :area_code => "6045") + AreaCode.create(:country => germany, :name => "Ortenberg Hess", :area_code => "6046") + AreaCode.create(:country => germany, :name => "Altenstadt Hess", :area_code => "6047") + AreaCode.create(:country => germany, :name => "Büdingen-Eckartshausen", :area_code => "6048") + AreaCode.create(:country => germany, :name => "Kefenrod", :area_code => "6049") + AreaCode.create(:country => germany, :name => "Biebergemünd", :area_code => "6050") + AreaCode.create(:country => germany, :name => "Gelnhausen", :area_code => "6051") + AreaCode.create(:country => germany, :name => "Bad Orb", :area_code => "6052") + AreaCode.create(:country => germany, :name => "Wächtersbach", :area_code => "6053") + AreaCode.create(:country => germany, :name => "Birstein", :area_code => "6054") + AreaCode.create(:country => germany, :name => "Freigericht", :area_code => "6055") + AreaCode.create(:country => germany, :name => "Bad Soden-Salmünster", :area_code => "6056") + AreaCode.create(:country => germany, :name => "Flörsbachtal", :area_code => "6057") + AreaCode.create(:country => germany, :name => "Gründau", :area_code => "6058") + AreaCode.create(:country => germany, :name => "Jossgrund", :area_code => "6059") + AreaCode.create(:country => germany, :name => "Michelstadt", :area_code => "6061") + AreaCode.create(:country => germany, :name => "Erbach Odenw", :area_code => "6062") + AreaCode.create(:country => germany, :name => "Bad König", :area_code => "6063") + AreaCode.create(:country => germany, :name => "Michelstadt-Vielbrunn", :area_code => "6066") + AreaCode.create(:country => germany, :name => "Beerfelden", :area_code => "6068") + AreaCode.create(:country => germany, :name => "Dieburg", :area_code => "6071") + AreaCode.create(:country => germany, :name => "Babenhausen Hess", :area_code => "6073") + AreaCode.create(:country => germany, :name => "Rödermark", :area_code => "6074") + AreaCode.create(:country => germany, :name => "Gross-Umstadt", :area_code => "6078") + AreaCode.create(:country => germany, :name => "Usingen", :area_code => "6081") + AreaCode.create(:country => germany, :name => "Niederreifenberg", :area_code => "6082") + AreaCode.create(:country => germany, :name => "Weilrod", :area_code => "6083") + AreaCode.create(:country => germany, :name => "Schmitten Taunus", :area_code => "6084") + AreaCode.create(:country => germany, :name => "Waldsolms", :area_code => "6085") + AreaCode.create(:country => germany, :name => "Grävenwiesbach", :area_code => "6086") + AreaCode.create(:country => germany, :name => "Waldems", :area_code => "6087") + AreaCode.create(:country => germany, :name => "Heimbuchenthal", :area_code => "6092") + AreaCode.create(:country => germany, :name => "Laufach", :area_code => "6093") + AreaCode.create(:country => germany, :name => "Weibersbrunn", :area_code => "6094") + AreaCode.create(:country => germany, :name => "Bessenbach", :area_code => "6095") + AreaCode.create(:country => germany, :name => "Wiesen Unterfr", :area_code => "6096") + AreaCode.create(:country => germany, :name => "Bad Vilbel", :area_code => "6101") + AreaCode.create(:country => germany, :name => "Neu-Isenburg", :area_code => "6102") + AreaCode.create(:country => germany, :name => "Langen Hess", :area_code => "6103") + AreaCode.create(:country => germany, :name => "Heusenstamm", :area_code => "6104") + AreaCode.create(:country => germany, :name => "Mörfelden-Walldorf", :area_code => "6105") + AreaCode.create(:country => germany, :name => "Rodgau", :area_code => "6106") + AreaCode.create(:country => germany, :name => "Kelsterbach", :area_code => "6107") + AreaCode.create(:country => germany, :name => "Mühlheim am Main", :area_code => "6108") + AreaCode.create(:country => germany, :name => "Frankfurt-Bergen-Enkheim", :area_code => "6109") + AreaCode.create(:country => germany, :name => "Wiesbaden", :area_code => "611") + AreaCode.create(:country => germany, :name => "Aarbergen", :area_code => "6120") + AreaCode.create(:country => germany, :name => "Hofheim-Wallau", :area_code => "6122") + AreaCode.create(:country => germany, :name => "Eltville am Rhein", :area_code => "6123") + AreaCode.create(:country => germany, :name => "Bad Schwalbach", :area_code => "6124") + AreaCode.create(:country => germany, :name => "Idstein", :area_code => "6126") + AreaCode.create(:country => germany, :name => "Niedernhausen Taunus", :area_code => "6127") + AreaCode.create(:country => germany, :name => "Taunusstein", :area_code => "6128") + AreaCode.create(:country => germany, :name => "Schlangenbad", :area_code => "6129") + AreaCode.create(:country => germany, :name => "Schwabenheim an der Selz", :area_code => "6130") + AreaCode.create(:country => germany, :name => "Mainz", :area_code => "6131") + AreaCode.create(:country => germany, :name => "Ingelheim am Rhein", :area_code => "6132") + AreaCode.create(:country => germany, :name => "Oppenheim", :area_code => "6133") + AreaCode.create(:country => germany, :name => "Mainz-Kastel", :area_code => "6134") + AreaCode.create(:country => germany, :name => "Bodenheim Rhein", :area_code => "6135") + AreaCode.create(:country => germany, :name => "Nieder-Olm", :area_code => "6136") + AreaCode.create(:country => germany, :name => "Mommenheim", :area_code => "6138") + AreaCode.create(:country => germany, :name => "Budenheim", :area_code => "6139") + AreaCode.create(:country => germany, :name => "Rüsselsheim", :area_code => "6142") + AreaCode.create(:country => germany, :name => "Bischofsheim b Rüsselsheim", :area_code => "6144") + AreaCode.create(:country => germany, :name => "Flörsheim am Main", :area_code => "6145") + AreaCode.create(:country => germany, :name => "Hochheim am Main", :area_code => "6146") + AreaCode.create(:country => germany, :name => "Trebur", :area_code => "6147") + AreaCode.create(:country => germany, :name => "Weiterstadt", :area_code => "6150") + AreaCode.create(:country => germany, :name => "Darmstadt", :area_code => "6151") + AreaCode.create(:country => germany, :name => "Gross-Gerau", :area_code => "6152") + AreaCode.create(:country => germany, :name => "Ober-Ramstadt", :area_code => "6154") + AreaCode.create(:country => germany, :name => "Griesheim Hess", :area_code => "6155") + AreaCode.create(:country => germany, :name => "Pfungstadt", :area_code => "6157") + AreaCode.create(:country => germany, :name => "Riedstadt", :area_code => "6158") + AreaCode.create(:country => germany, :name => "Messel", :area_code => "6159") + AreaCode.create(:country => germany, :name => "Brensbach", :area_code => "6161") + AreaCode.create(:country => germany, :name => "Reinheim Odenw", :area_code => "6162") + AreaCode.create(:country => germany, :name => "Höchst i Odw", :area_code => "6163") + AreaCode.create(:country => germany, :name => "Reichelsheim Odenwald", :area_code => "6164") + AreaCode.create(:country => germany, :name => "Breuberg", :area_code => "6165") + AreaCode.create(:country => germany, :name => "Fischbachtal", :area_code => "6166") + AreaCode.create(:country => germany, :name => "Modautal", :area_code => "6167") + AreaCode.create(:country => germany, :name => "Oberursel Taunus", :area_code => "6171") + AreaCode.create(:country => germany, :name => "Bad Homburg v d Höhe", :area_code => "6172") + AreaCode.create(:country => germany, :name => "Kronberg im Taunus", :area_code => "6173") + AreaCode.create(:country => germany, :name => "Königstein im Taunus", :area_code => "6174") + AreaCode.create(:country => germany, :name => "Friedrichsdorf Taunus", :area_code => "6175") + AreaCode.create(:country => germany, :name => "Hanau", :area_code => "6181") + AreaCode.create(:country => germany, :name => "Seligenstadt", :area_code => "6182") + AreaCode.create(:country => germany, :name => "Erlensee", :area_code => "6183") + AreaCode.create(:country => germany, :name => "Langenselbold", :area_code => "6184") + AreaCode.create(:country => germany, :name => "Hammersbach Hess", :area_code => "6185") + AreaCode.create(:country => germany, :name => "Grosskrotzenburg", :area_code => "6186") + AreaCode.create(:country => germany, :name => "Schöneck", :area_code => "6187") + AreaCode.create(:country => germany, :name => "Kahl a Main", :area_code => "6188") + AreaCode.create(:country => germany, :name => "Hattersheim a Main", :area_code => "6190") + AreaCode.create(:country => germany, :name => "Hofheim am Taunus", :area_code => "6192") + AreaCode.create(:country => germany, :name => "Kelkheim Taunus", :area_code => "6195") + AreaCode.create(:country => germany, :name => "Bad Soden am Taunus", :area_code => "6196") + AreaCode.create(:country => germany, :name => "Eppstein", :area_code => "6198") + AreaCode.create(:country => germany, :name => "Weinheim Bergstr", :area_code => "6201") + AreaCode.create(:country => germany, :name => "Schwetzingen", :area_code => "6202") + AreaCode.create(:country => germany, :name => "Ladenburg", :area_code => "6203") + AreaCode.create(:country => germany, :name => "Viernheim", :area_code => "6204") + AreaCode.create(:country => germany, :name => "Hockenheim", :area_code => "6205") + AreaCode.create(:country => germany, :name => "Lampertheim", :area_code => "6206") + AreaCode.create(:country => germany, :name => "Wald-Michelbach", :area_code => "6207") + AreaCode.create(:country => germany, :name => "Mörlenbach", :area_code => "6209") + AreaCode.create(:country => germany, :name => "Mannheim", :area_code => "621") + AreaCode.create(:country => germany, :name => "Wilhelmsfeld", :area_code => "6220") + AreaCode.create(:country => germany, :name => "Heidelberg", :area_code => "6221") + AreaCode.create(:country => germany, :name => "Wiesloch", :area_code => "6222") + AreaCode.create(:country => germany, :name => "Neckargemünd", :area_code => "6223") + AreaCode.create(:country => germany, :name => "Sandhausen Baden", :area_code => "6224") + AreaCode.create(:country => germany, :name => "Meckesheim", :area_code => "6226") + AreaCode.create(:country => germany, :name => "Walldorf Baden", :area_code => "6227") + AreaCode.create(:country => germany, :name => "Schönau Odenw", :area_code => "6228") + AreaCode.create(:country => germany, :name => "Neckarsteinach", :area_code => "6229") + AreaCode.create(:country => germany, :name => "Hochdorf-Assenheim", :area_code => "6231") + AreaCode.create(:country => germany, :name => "Speyer", :area_code => "6232") + AreaCode.create(:country => germany, :name => "Frankenthal Pfalz", :area_code => "6233") + AreaCode.create(:country => germany, :name => "Mutterstadt", :area_code => "6234") + AreaCode.create(:country => germany, :name => "Schifferstadt", :area_code => "6235") + AreaCode.create(:country => germany, :name => "Neuhofen Pfalz", :area_code => "6236") + AreaCode.create(:country => germany, :name => "Maxdorf", :area_code => "6237") + AreaCode.create(:country => germany, :name => "Dirmstein", :area_code => "6238") + AreaCode.create(:country => germany, :name => "Bobenheim-Roxheim", :area_code => "6239") + AreaCode.create(:country => germany, :name => "Worms", :area_code => "6241") + AreaCode.create(:country => germany, :name => "Osthofen", :area_code => "6242") + AreaCode.create(:country => germany, :name => "Monsheim", :area_code => "6243") + AreaCode.create(:country => germany, :name => "Westhofen Rheinhess", :area_code => "6244") + AreaCode.create(:country => germany, :name => "Biblis", :area_code => "6245") + AreaCode.create(:country => germany, :name => "Eich Rheinhess", :area_code => "6246") + AreaCode.create(:country => germany, :name => "Worms-Pfeddersheim", :area_code => "6247") + AreaCode.create(:country => germany, :name => "Guntersblum", :area_code => "6249") + AreaCode.create(:country => germany, :name => "Bensheim", :area_code => "6251") + AreaCode.create(:country => germany, :name => "Heppenheim Bergstraße", :area_code => "6252") + AreaCode.create(:country => germany, :name => "Fürth Odenw", :area_code => "6253") + AreaCode.create(:country => germany, :name => "Lautertal Odenwald", :area_code => "6254") + AreaCode.create(:country => germany, :name => "Lindenfels", :area_code => "6255") + AreaCode.create(:country => germany, :name => "Lampertheim-Hüttenfeld", :area_code => "6256") + AreaCode.create(:country => germany, :name => "Seeheim-Jugenheim", :area_code => "6257") + AreaCode.create(:country => germany, :name => "Gernsheim", :area_code => "6258") + AreaCode.create(:country => germany, :name => "Mosbach Baden", :area_code => "6261") + AreaCode.create(:country => germany, :name => "Aglasterhausen", :area_code => "6262") + AreaCode.create(:country => germany, :name => "Neckargerach", :area_code => "6263") + AreaCode.create(:country => germany, :name => "Neudenau", :area_code => "6264") + AreaCode.create(:country => germany, :name => "Billigheim Baden", :area_code => "6265") + AreaCode.create(:country => germany, :name => "Hassmersheim", :area_code => "6266") + AreaCode.create(:country => germany, :name => "Fahrenbach Baden", :area_code => "6267") + AreaCode.create(:country => germany, :name => "Hüffenhardt", :area_code => "6268") + AreaCode.create(:country => germany, :name => "Gundelsheim Württ", :area_code => "6269") + AreaCode.create(:country => germany, :name => "Eberbach Baden", :area_code => "6271") + AreaCode.create(:country => germany, :name => "Hirschhorn Neckar", :area_code => "6272") + AreaCode.create(:country => germany, :name => "Waldbrunn Odenw", :area_code => "6274") + AreaCode.create(:country => germany, :name => "Rothenberg Odenw", :area_code => "6275") + AreaCode.create(:country => germany, :name => "Hesseneck", :area_code => "6276") + AreaCode.create(:country => germany, :name => "Buchen Odenwald", :area_code => "6281") + AreaCode.create(:country => germany, :name => "Walldürn", :area_code => "6282") + AreaCode.create(:country => germany, :name => "Hardheim Odenw", :area_code => "6283") + AreaCode.create(:country => germany, :name => "Mudau", :area_code => "6284") + AreaCode.create(:country => germany, :name => "Walldürn-Altheim", :area_code => "6285") + AreaCode.create(:country => germany, :name => "Walldürn-Rippberg", :area_code => "6286") + AreaCode.create(:country => germany, :name => "Limbach Baden", :area_code => "6287") + AreaCode.create(:country => germany, :name => "Adelsheim", :area_code => "6291") + AreaCode.create(:country => germany, :name => "Seckach", :area_code => "6292") + AreaCode.create(:country => germany, :name => "Schefflenz", :area_code => "6293") + AreaCode.create(:country => germany, :name => "Krautheim Jagst", :area_code => "6294") + AreaCode.create(:country => germany, :name => "RosenbergBaden", :area_code => "6295") + AreaCode.create(:country => germany, :name => "Ahorn Baden", :area_code => "6296") + AreaCode.create(:country => germany, :name => "Ravenstein Baden", :area_code => "6297") + AreaCode.create(:country => germany, :name => "Möckmühl", :area_code => "6298") + AreaCode.create(:country => germany, :name => "Otterbach Pfalz", :area_code => "6301") + AreaCode.create(:country => germany, :name => "Winnweiler", :area_code => "6302") + AreaCode.create(:country => germany, :name => "Enkenbach-Alsenborn", :area_code => "6303") + AreaCode.create(:country => germany, :name => "Wolfstein Pfalz", :area_code => "6304") + AreaCode.create(:country => germany, :name => "Hochspeyer", :area_code => "6305") + AreaCode.create(:country => germany, :name => "Trippstadt", :area_code => "6306") + AreaCode.create(:country => germany, :name => "Schopp", :area_code => "6307") + AreaCode.create(:country => germany, :name => "Olsbrücken", :area_code => "6308") + AreaCode.create(:country => germany, :name => "Kaiserslautern", :area_code => "631") + AreaCode.create(:country => germany, :name => "Neustadt an der Weinstraße", :area_code => "6321") + AreaCode.create(:country => germany, :name => "Bad Dürkheim", :area_code => "6322") + AreaCode.create(:country => germany, :name => "Edenkoben", :area_code => "6323") + AreaCode.create(:country => germany, :name => "Hassloch", :area_code => "6324") + AreaCode.create(:country => germany, :name => "Lambrecht Pfalz", :area_code => "6325") + AreaCode.create(:country => germany, :name => "Deidesheim", :area_code => "6326") + AreaCode.create(:country => germany, :name => "Neustadt-Lachen", :area_code => "6327") + AreaCode.create(:country => germany, :name => "Elmstein", :area_code => "6328") + AreaCode.create(:country => germany, :name => "Weidenthal Pfalz", :area_code => "6329") + AreaCode.create(:country => germany, :name => "Pirmasens", :area_code => "6331") + AreaCode.create(:country => germany, :name => "Zweibrücken", :area_code => "6332") + AreaCode.create(:country => germany, :name => "Waldfischbach-Burgalben", :area_code => "6333") + AreaCode.create(:country => germany, :name => "Thaleischweiler-Fröschen", :area_code => "6334") + AreaCode.create(:country => germany, :name => "Trulben", :area_code => "6335") + AreaCode.create(:country => germany, :name => "Dellfeld", :area_code => "6336") + AreaCode.create(:country => germany, :name => "Grossbundenbach", :area_code => "6337") + AreaCode.create(:country => germany, :name => "Hornbach Pfalz", :area_code => "6338") + AreaCode.create(:country => germany, :name => "Grosssteinhausen", :area_code => "6339") + AreaCode.create(:country => germany, :name => "Wörth-Schaidt", :area_code => "6340") + AreaCode.create(:country => germany, :name => "Landau in der Pfalz", :area_code => "6341") + AreaCode.create(:country => germany, :name => "Schweigen-Rechtenbach", :area_code => "6342") + AreaCode.create(:country => germany, :name => "Bad Bergzabern", :area_code => "6343") + AreaCode.create(:country => germany, :name => "Schwegenheim", :area_code => "6344") + AreaCode.create(:country => germany, :name => "Albersweiler", :area_code => "6345") + AreaCode.create(:country => germany, :name => "Annweiler am Trifels", :area_code => "6346") + AreaCode.create(:country => germany, :name => "Hochstadt Pfalz", :area_code => "6347") + AreaCode.create(:country => germany, :name => "Offenbach an der Queich", :area_code => "6348") + AreaCode.create(:country => germany, :name => "Billigheim-Ingenheim", :area_code => "6349") + AreaCode.create(:country => germany, :name => "Eisenberg Pfalz", :area_code => "6351") + AreaCode.create(:country => germany, :name => "Kirchheimbolanden", :area_code => "6352") + AreaCode.create(:country => germany, :name => "Freinsheim", :area_code => "6353") + AreaCode.create(:country => germany, :name => "Albisheim Pfrimm", :area_code => "6355") + AreaCode.create(:country => germany, :name => "Carlsberg Pfalz", :area_code => "6356") + AreaCode.create(:country => germany, :name => "Standenbühl", :area_code => "6357") + AreaCode.create(:country => germany, :name => "Kriegsfeld", :area_code => "6358") + AreaCode.create(:country => germany, :name => "Grünstadt", :area_code => "6359") + AreaCode.create(:country => germany, :name => "Rockenhausen", :area_code => "6361") + AreaCode.create(:country => germany, :name => "Alsenz", :area_code => "6362") + AreaCode.create(:country => germany, :name => "Niederkirchen", :area_code => "6363") + AreaCode.create(:country => germany, :name => "Nußbach Pfalz", :area_code => "6364") + AreaCode.create(:country => germany, :name => "Landstuhl", :area_code => "6371") + AreaCode.create(:country => germany, :name => "Bruchmühlbach-Miesau", :area_code => "6372") + AreaCode.create(:country => germany, :name => "Schönenberg-Kübelberg", :area_code => "6373") + AreaCode.create(:country => germany, :name => "Weilerbach", :area_code => "6374") + AreaCode.create(:country => germany, :name => "Wallhalben", :area_code => "6375") + AreaCode.create(:country => germany, :name => "Kusel", :area_code => "6381") + AreaCode.create(:country => germany, :name => "Lauterecken", :area_code => "6382") + AreaCode.create(:country => germany, :name => "Glan-Münchweiler", :area_code => "6383") + AreaCode.create(:country => germany, :name => "Konken", :area_code => "6384") + AreaCode.create(:country => germany, :name => "Reichenbach-Steegen", :area_code => "6385") + AreaCode.create(:country => germany, :name => "Altenkirchen Pfalz", :area_code => "6386") + AreaCode.create(:country => germany, :name => "Sankt Julian", :area_code => "6387") + AreaCode.create(:country => germany, :name => "Dahn", :area_code => "6391") + AreaCode.create(:country => germany, :name => "Hauenstein Pfalz", :area_code => "6392") + AreaCode.create(:country => germany, :name => "Fischbach bei Dahn", :area_code => "6393") + AreaCode.create(:country => germany, :name => "Bundenthal", :area_code => "6394") + AreaCode.create(:country => germany, :name => "Münchweiler an der Rodalb", :area_code => "6395") + AreaCode.create(:country => germany, :name => "Hinterweidenthal", :area_code => "6396") + AreaCode.create(:country => germany, :name => "Leimen Pfalz", :area_code => "6397") + AreaCode.create(:country => germany, :name => "Vorderweidenthal", :area_code => "6398") + AreaCode.create(:country => germany, :name => "Mücke", :area_code => "6400") + AreaCode.create(:country => germany, :name => "Grünberg Hess", :area_code => "6401") + AreaCode.create(:country => germany, :name => "Hungen", :area_code => "6402") + AreaCode.create(:country => germany, :name => "Linden Hess", :area_code => "6403") + AreaCode.create(:country => germany, :name => "Lich Hess", :area_code => "6404") + AreaCode.create(:country => germany, :name => "Laubach Hess", :area_code => "6405") + AreaCode.create(:country => germany, :name => "Lollar", :area_code => "6406") + AreaCode.create(:country => germany, :name => "Rabenau Hess", :area_code => "6407") + AreaCode.create(:country => germany, :name => "Buseck", :area_code => "6408") + AreaCode.create(:country => germany, :name => "Biebertal", :area_code => "6409") + AreaCode.create(:country => germany, :name => "Giessen", :area_code => "641") + AreaCode.create(:country => germany, :name => "Lahntal", :area_code => "6420") + AreaCode.create(:country => germany, :name => "Marburg", :area_code => "6421") + AreaCode.create(:country => germany, :name => "Kirchhain", :area_code => "6422") + AreaCode.create(:country => germany, :name => "Wetter Hessen", :area_code => "6423") + AreaCode.create(:country => germany, :name => "Ebsdorfergrund", :area_code => "6424") + AreaCode.create(:country => germany, :name => "Rauschenberg Hess", :area_code => "6425") + AreaCode.create(:country => germany, :name => "Fronhausen", :area_code => "6426") + AreaCode.create(:country => germany, :name => "Cölbe-Schönstadt", :area_code => "6427") + AreaCode.create(:country => germany, :name => "Stadtallendorf", :area_code => "6428") + AreaCode.create(:country => germany, :name => "Schweinsberg Hess", :area_code => "6429") + AreaCode.create(:country => germany, :name => "Hahnstätten", :area_code => "6430") + AreaCode.create(:country => germany, :name => "Limburg a d Lahn", :area_code => "6431") + AreaCode.create(:country => germany, :name => "Diez", :area_code => "6432") + AreaCode.create(:country => germany, :name => "Hadamar", :area_code => "6433") + AreaCode.create(:country => germany, :name => "Bad Camberg", :area_code => "6434") + AreaCode.create(:country => germany, :name => "Wallmerod", :area_code => "6435") + AreaCode.create(:country => germany, :name => "Dornburg Hess", :area_code => "6436") + AreaCode.create(:country => germany, :name => "Hünfelden", :area_code => "6438") + AreaCode.create(:country => germany, :name => "Holzappel", :area_code => "6439") + AreaCode.create(:country => germany, :name => "Kölschhausen", :area_code => "6440") + AreaCode.create(:country => germany, :name => "Wetzlar", :area_code => "6441") + AreaCode.create(:country => germany, :name => "Braunfels", :area_code => "6442") + AreaCode.create(:country => germany, :name => "Ehringshausen Dill", :area_code => "6443") + AreaCode.create(:country => germany, :name => "Bischoffen", :area_code => "6444") + AreaCode.create(:country => germany, :name => "Schöffengrund", :area_code => "6445") + AreaCode.create(:country => germany, :name => "Hohenahr", :area_code => "6446") + AreaCode.create(:country => germany, :name => "Langgöns-Niederkleen", :area_code => "6447") + AreaCode.create(:country => germany, :name => "Ehringshausen-Katzenfurt", :area_code => "6449") + AreaCode.create(:country => germany, :name => "Frankenberg Eder", :area_code => "6451") + AreaCode.create(:country => germany, :name => "Battenberg Eder", :area_code => "6452") + AreaCode.create(:country => germany, :name => "Gemünden Wohra", :area_code => "6453") + AreaCode.create(:country => germany, :name => "Lichtenfels-Sachsenberg", :area_code => "6454") + AreaCode.create(:country => germany, :name => "Frankenau Hess", :area_code => "6455") + AreaCode.create(:country => germany, :name => "Haina Kloster", :area_code => "6456") + AreaCode.create(:country => germany, :name => "Burgwald Eder", :area_code => "6457") + AreaCode.create(:country => germany, :name => "Rosenthal Hess", :area_code => "6458") + AreaCode.create(:country => germany, :name => "Biedenkopf", :area_code => "6461") + AreaCode.create(:country => germany, :name => "Gladenbach", :area_code => "6462") + AreaCode.create(:country => germany, :name => "Angelburg", :area_code => "6464") + AreaCode.create(:country => germany, :name => "Breidenbach b Biedenkopf", :area_code => "6465") + AreaCode.create(:country => germany, :name => "Dautphetal-Friedensdorf", :area_code => "6466") + AreaCode.create(:country => germany, :name => "Hatzfeld Eder", :area_code => "6467") + AreaCode.create(:country => germany, :name => "Dautphetal-Mornshausen", :area_code => "6468") + AreaCode.create(:country => germany, :name => "Weilburg", :area_code => "6471") + AreaCode.create(:country => germany, :name => "Weilmünster", :area_code => "6472") + AreaCode.create(:country => germany, :name => "Leun", :area_code => "6473") + AreaCode.create(:country => germany, :name => "Villmar-Aumenau", :area_code => "6474") + AreaCode.create(:country => germany, :name => "Weilmünster-Wolfenhausen", :area_code => "6475") + AreaCode.create(:country => germany, :name => "Mengerskirchen", :area_code => "6476") + AreaCode.create(:country => germany, :name => "Greifenstein-Nenderoth", :area_code => "6477") + AreaCode.create(:country => germany, :name => "Greifenstein-Ulm", :area_code => "6478") + AreaCode.create(:country => germany, :name => "Waldbrunn Westerwald", :area_code => "6479") + AreaCode.create(:country => germany, :name => "Runkel", :area_code => "6482") + AreaCode.create(:country => germany, :name => "Selters Taunus", :area_code => "6483") + AreaCode.create(:country => germany, :name => "Beselich", :area_code => "6484") + AreaCode.create(:country => germany, :name => "Nentershausen Westerw", :area_code => "6485") + AreaCode.create(:country => germany, :name => "Katzenelnbogen", :area_code => "6486") + AreaCode.create(:country => germany, :name => "Waldrach", :area_code => "6500") + AreaCode.create(:country => germany, :name => "Konz", :area_code => "6501") + AreaCode.create(:country => germany, :name => "Schweich", :area_code => "6502") + AreaCode.create(:country => germany, :name => "Hermeskeil", :area_code => "6503") + AreaCode.create(:country => germany, :name => "Thalfang", :area_code => "6504") + AreaCode.create(:country => germany, :name => "Kordel", :area_code => "6505") + AreaCode.create(:country => germany, :name => "Welschbillig", :area_code => "6506") + AreaCode.create(:country => germany, :name => "Neumagen-Dhron", :area_code => "6507") + AreaCode.create(:country => germany, :name => "Hetzerath Mosel", :area_code => "6508") + AreaCode.create(:country => germany, :name => "Büdlich", :area_code => "6509") + AreaCode.create(:country => germany, :name => "Trier", :area_code => "651") + AreaCode.create(:country => germany, :name => "Mettendorf", :area_code => "6522") + AreaCode.create(:country => germany, :name => "Holsthum", :area_code => "6523") + AreaCode.create(:country => germany, :name => "Rodershausen", :area_code => "6524") + AreaCode.create(:country => germany, :name => "Irrel", :area_code => "6525") + AreaCode.create(:country => germany, :name => "Bollendorf", :area_code => "6526") + AreaCode.create(:country => germany, :name => "Oberweis", :area_code => "6527") + AreaCode.create(:country => germany, :name => "Bernkastel-Kues", :area_code => "6531") + AreaCode.create(:country => germany, :name => "Zeltingen-Rachtig", :area_code => "6532") + AreaCode.create(:country => germany, :name => "Morbach Hunsrück", :area_code => "6533") + AreaCode.create(:country => germany, :name => "Mülheim Mosel", :area_code => "6534") + AreaCode.create(:country => germany, :name => "Osann-Monzel", :area_code => "6535") + AreaCode.create(:country => germany, :name => "Kleinich", :area_code => "6536") + AreaCode.create(:country => germany, :name => "Traben-Trarbach", :area_code => "6541") + AreaCode.create(:country => germany, :name => "Bullay", :area_code => "6542") + AreaCode.create(:country => germany, :name => "Büchenbeuren", :area_code => "6543") + AreaCode.create(:country => germany, :name => "Rhaunen", :area_code => "6544") + AreaCode.create(:country => germany, :name => "Blankenrath", :area_code => "6545") + AreaCode.create(:country => germany, :name => "Irrhausen", :area_code => "6550") + AreaCode.create(:country => germany, :name => "Prüm", :area_code => "6551") + AreaCode.create(:country => germany, :name => "Olzheim", :area_code => "6552") + AreaCode.create(:country => germany, :name => "Schönecken", :area_code => "6553") + AreaCode.create(:country => germany, :name => "Waxweiler", :area_code => "6554") + AreaCode.create(:country => germany, :name => "Bleialf", :area_code => "6555") + AreaCode.create(:country => germany, :name => "Pronsfeld", :area_code => "6556") + AreaCode.create(:country => germany, :name => "Hallschlag", :area_code => "6557") + AreaCode.create(:country => germany, :name => "Büdesheim Eifel", :area_code => "6558") + AreaCode.create(:country => germany, :name => "Leidenborn", :area_code => "6559") + AreaCode.create(:country => germany, :name => "Bitburg", :area_code => "6561") + AreaCode.create(:country => germany, :name => "Speicher", :area_code => "6562") + AreaCode.create(:country => germany, :name => "Kyllburg", :area_code => "6563") + AreaCode.create(:country => germany, :name => "Neuerburg Eifel", :area_code => "6564") + AreaCode.create(:country => germany, :name => "Dudeldorf", :area_code => "6565") + AreaCode.create(:country => germany, :name => "Körperich", :area_code => "6566") + AreaCode.create(:country => germany, :name => "Oberkail", :area_code => "6567") + AreaCode.create(:country => germany, :name => "Wolsfeld", :area_code => "6568") + AreaCode.create(:country => germany, :name => "Bickendorf", :area_code => "6569") + AreaCode.create(:country => germany, :name => "Wittlich", :area_code => "6571") + AreaCode.create(:country => germany, :name => "Manderscheid Eifel", :area_code => "6572") + AreaCode.create(:country => germany, :name => "Gillenfeld", :area_code => "6573") + AreaCode.create(:country => germany, :name => "Hasborn", :area_code => "6574") + AreaCode.create(:country => germany, :name => "Landscheid", :area_code => "6575") + AreaCode.create(:country => germany, :name => "Salmtal", :area_code => "6578") + AreaCode.create(:country => germany, :name => "Zemmer", :area_code => "6580") + AreaCode.create(:country => germany, :name => "Saarburg", :area_code => "6581") + AreaCode.create(:country => germany, :name => "Freudenburg", :area_code => "6582") + AreaCode.create(:country => germany, :name => "Palzem", :area_code => "6583") + AreaCode.create(:country => germany, :name => "Wellen Mosel", :area_code => "6584") + AreaCode.create(:country => germany, :name => "Ralingen", :area_code => "6585") + AreaCode.create(:country => germany, :name => "Beuren Hochwald", :area_code => "6586") + AreaCode.create(:country => germany, :name => "Zerf", :area_code => "6587") + AreaCode.create(:country => germany, :name => "Pluwig", :area_code => "6588") + AreaCode.create(:country => germany, :name => "Kell am See", :area_code => "6589") + AreaCode.create(:country => germany, :name => "Gerolstein", :area_code => "6591") + AreaCode.create(:country => germany, :name => "Daun", :area_code => "6592") + AreaCode.create(:country => germany, :name => "Hillesheim Eifel", :area_code => "6593") + AreaCode.create(:country => germany, :name => "Birresborn", :area_code => "6594") + AreaCode.create(:country => germany, :name => "Dockweiler", :area_code => "6595") + AreaCode.create(:country => germany, :name => "Üdersdorf", :area_code => "6596") + AreaCode.create(:country => germany, :name => "Jünkerath", :area_code => "6597") + AreaCode.create(:country => germany, :name => "Weidenbach b Gerolstein", :area_code => "6599") + AreaCode.create(:country => germany, :name => "Fulda", :area_code => "661") + AreaCode.create(:country => germany, :name => "Philippsthal Werra", :area_code => "6620") + AreaCode.create(:country => germany, :name => "Bad Hersfeld", :area_code => "6621") + AreaCode.create(:country => germany, :name => "Bebra", :area_code => "6622") + AreaCode.create(:country => germany, :name => "Rotenburg a d Fulda", :area_code => "6623") + AreaCode.create(:country => germany, :name => "Heringen Werra", :area_code => "6624") + AreaCode.create(:country => germany, :name => "Niederaula", :area_code => "6625") + AreaCode.create(:country => germany, :name => "Wildeck-Obersuhl", :area_code => "6626") + AreaCode.create(:country => germany, :name => "Nentershausen Hess", :area_code => "6627") + AreaCode.create(:country => germany, :name => "Oberaula", :area_code => "6628") + AreaCode.create(:country => germany, :name => "Schenklengsfeld", :area_code => "6629") + AreaCode.create(:country => germany, :name => "Schwalmtal-Storndorf", :area_code => "6630") + AreaCode.create(:country => germany, :name => "Alsfeld", :area_code => "6631") + AreaCode.create(:country => germany, :name => "Homberg Ohm", :area_code => "6633") + AreaCode.create(:country => germany, :name => "Gemünden Felda", :area_code => "6634") + AreaCode.create(:country => germany, :name => "Kirtorf", :area_code => "6635") + AreaCode.create(:country => germany, :name => "Romrod", :area_code => "6636") + AreaCode.create(:country => germany, :name => "Feldatal", :area_code => "6637") + AreaCode.create(:country => germany, :name => "Schwalmtal-Renzendorf", :area_code => "6638") + AreaCode.create(:country => germany, :name => "Ottrau", :area_code => "6639") + AreaCode.create(:country => germany, :name => "Lauterbach Hessen", :area_code => "6641") + AreaCode.create(:country => germany, :name => "Schlitz", :area_code => "6642") + AreaCode.create(:country => germany, :name => "Herbstein", :area_code => "6643") + AreaCode.create(:country => germany, :name => "Grebenhain", :area_code => "6644") + AreaCode.create(:country => germany, :name => "Ulrichstein", :area_code => "6645") + AreaCode.create(:country => germany, :name => "Grebenau", :area_code => "6646") + AreaCode.create(:country => germany, :name => "Herbstein-Stockhausen", :area_code => "6647") + AreaCode.create(:country => germany, :name => "Bad Salzschlirf", :area_code => "6648") + AreaCode.create(:country => germany, :name => "Hosenfeld", :area_code => "6650") + AreaCode.create(:country => germany, :name => "Rasdorf", :area_code => "6651") + AreaCode.create(:country => germany, :name => "Hünfeld", :area_code => "6652") + AreaCode.create(:country => germany, :name => "Burghaun", :area_code => "6653") + AreaCode.create(:country => germany, :name => "Gersfeld Rhön", :area_code => "6654") + AreaCode.create(:country => germany, :name => "Neuhof Kr Fulda", :area_code => "6655") + AreaCode.create(:country => germany, :name => "Ebersburg", :area_code => "6656") + AreaCode.create(:country => germany, :name => "Hofbieber", :area_code => "6657") + AreaCode.create(:country => germany, :name => "Poppenhausen Wasserkuppe", :area_code => "6658") + AreaCode.create(:country => germany, :name => "Eichenzell", :area_code => "6659") + AreaCode.create(:country => germany, :name => "Steinau-Marjoss", :area_code => "6660") + AreaCode.create(:country => germany, :name => "Schlüchtern", :area_code => "6661") + AreaCode.create(:country => germany, :name => "Steinau an der Straße", :area_code => "6663") + AreaCode.create(:country => germany, :name => "Sinntal-Sterbfritz", :area_code => "6664") + AreaCode.create(:country => germany, :name => "Sinntal-Altengronau", :area_code => "6665") + AreaCode.create(:country => germany, :name => "Freiensteinau", :area_code => "6666") + AreaCode.create(:country => germany, :name => "Steinau-Ulmbach", :area_code => "6667") + AreaCode.create(:country => germany, :name => "Birstein-Lichenroth", :area_code => "6668") + AreaCode.create(:country => germany, :name => "Neuhof-Hauswurz", :area_code => "6669") + AreaCode.create(:country => germany, :name => "Ludwigsau Hess", :area_code => "6670") + AreaCode.create(:country => germany, :name => "Eiterfeld", :area_code => "6672") + AreaCode.create(:country => germany, :name => "Haunetal", :area_code => "6673") + AreaCode.create(:country => germany, :name => "Friedewald Hess", :area_code => "6674") + AreaCode.create(:country => germany, :name => "Breitenbach a Herzberg", :area_code => "6675") + AreaCode.create(:country => germany, :name => "Hohenroda Hess", :area_code => "6676") + AreaCode.create(:country => germany, :name => "Neuenstein Hess", :area_code => "6677") + AreaCode.create(:country => germany, :name => "Wildeck-Hönebach", :area_code => "6678") + AreaCode.create(:country => germany, :name => "Hilders", :area_code => "6681") + AreaCode.create(:country => germany, :name => "Tann Rhön", :area_code => "6682") + AreaCode.create(:country => germany, :name => "Ehrenberg Rhön", :area_code => "6683") + AreaCode.create(:country => germany, :name => "Hofbieber-Schwarzbach", :area_code => "6684") + AreaCode.create(:country => germany, :name => "Schwalmstadt", :area_code => "6691") + AreaCode.create(:country => germany, :name => "Neustadt Hessen", :area_code => "6692") + AreaCode.create(:country => germany, :name => "Neuental", :area_code => "6693") + AreaCode.create(:country => germany, :name => "Neukirchen Knüll", :area_code => "6694") + AreaCode.create(:country => germany, :name => "Jesberg", :area_code => "6695") + AreaCode.create(:country => germany, :name => "Gilserberg", :area_code => "6696") + AreaCode.create(:country => germany, :name => "Willingshausen", :area_code => "6697") + AreaCode.create(:country => germany, :name => "Schrecksbach", :area_code => "6698") + AreaCode.create(:country => germany, :name => "Sprendlingen Rheinhess", :area_code => "6701") + AreaCode.create(:country => germany, :name => "Wöllstein Rheinhess", :area_code => "6703") + AreaCode.create(:country => germany, :name => "Langenlonsheim", :area_code => "6704") + AreaCode.create(:country => germany, :name => "Wallhausen Nahe", :area_code => "6706") + AreaCode.create(:country => germany, :name => "Windesheim", :area_code => "6707") + AreaCode.create(:country => germany, :name => "Bad Münster am Stein-Ebernburg", :area_code => "6708") + AreaCode.create(:country => germany, :name => "Fürfeld Kr Bad Kreuznach", :area_code => "6709") + AreaCode.create(:country => germany, :name => "Bad Kreuznach", :area_code => "671") + AreaCode.create(:country => germany, :name => "Bingen am Rhein", :area_code => "6721") + AreaCode.create(:country => germany, :name => "Rüdesheim am Rhein", :area_code => "6722") + AreaCode.create(:country => germany, :name => "Oestrich-Winkel", :area_code => "6723") + AreaCode.create(:country => germany, :name => "Stromberg Hunsrück", :area_code => "6724") + AreaCode.create(:country => germany, :name => "Gau-Algesheim", :area_code => "6725") + AreaCode.create(:country => germany, :name => "Lorch Rheingau", :area_code => "6726") + AreaCode.create(:country => germany, :name => "Gensingen", :area_code => "6727") + AreaCode.create(:country => germany, :name => "Ober-Hilbersheim", :area_code => "6728") + AreaCode.create(:country => germany, :name => "Alzey", :area_code => "6731") + AreaCode.create(:country => germany, :name => "Wörrstadt", :area_code => "6732") + AreaCode.create(:country => germany, :name => "Gau-Odernheim", :area_code => "6733") + AreaCode.create(:country => germany, :name => "Flonheim", :area_code => "6734") + AreaCode.create(:country => germany, :name => "Eppelsheim", :area_code => "6735") + AreaCode.create(:country => germany, :name => "Bechenheim", :area_code => "6736") + AreaCode.create(:country => germany, :name => "Köngernheim", :area_code => "6737") + AreaCode.create(:country => germany, :name => "St Goar", :area_code => "6741") + AreaCode.create(:country => germany, :name => "Boppard", :area_code => "6742") + AreaCode.create(:country => germany, :name => "Bacharach", :area_code => "6743") + AreaCode.create(:country => germany, :name => "Oberwesel", :area_code => "6744") + AreaCode.create(:country => germany, :name => "Gondershausen", :area_code => "6745") + AreaCode.create(:country => germany, :name => "Pfalzfeld", :area_code => "6746") + AreaCode.create(:country => germany, :name => "Emmelshausen", :area_code => "6747") + AreaCode.create(:country => germany, :name => "Bad Sobernheim", :area_code => "6751") + AreaCode.create(:country => germany, :name => "Kirn Nahe", :area_code => "6752") + AreaCode.create(:country => germany, :name => "Meisenheim", :area_code => "6753") + AreaCode.create(:country => germany, :name => "Martinstein", :area_code => "6754") + AreaCode.create(:country => germany, :name => "Odernheim am Glan", :area_code => "6755") + AreaCode.create(:country => germany, :name => "Winterbach Soonwald", :area_code => "6756") + AreaCode.create(:country => germany, :name => "Becherbach bei Kirn", :area_code => "6757") + AreaCode.create(:country => germany, :name => "Waldböckelheim", :area_code => "6758") + AreaCode.create(:country => germany, :name => "Simmern Hunsrück", :area_code => "6761") + AreaCode.create(:country => germany, :name => "Kastellaun", :area_code => "6762") + AreaCode.create(:country => germany, :name => "Kirchberg Hunsrück", :area_code => "6763") + AreaCode.create(:country => germany, :name => "Rheinböllen", :area_code => "6764") + AreaCode.create(:country => germany, :name => "Gemünden Hunsrück", :area_code => "6765") + AreaCode.create(:country => germany, :name => "Kisselbach", :area_code => "6766") + AreaCode.create(:country => germany, :name => "St Goarshausen", :area_code => "6771") + AreaCode.create(:country => germany, :name => "Nastätten", :area_code => "6772") + AreaCode.create(:country => germany, :name => "Kamp-Bornhofen", :area_code => "6773") + AreaCode.create(:country => germany, :name => "Kaub", :area_code => "6774") + AreaCode.create(:country => germany, :name => "Strüth Taunus", :area_code => "6775") + AreaCode.create(:country => germany, :name => "Dachsenhausen", :area_code => "6776") + AreaCode.create(:country => germany, :name => "Idar-Oberstein", :area_code => "6781") + AreaCode.create(:country => germany, :name => "Birkenfeld Nahe", :area_code => "6782") + AreaCode.create(:country => germany, :name => "Baumholder", :area_code => "6783") + AreaCode.create(:country => germany, :name => "Weierbach", :area_code => "6784") + AreaCode.create(:country => germany, :name => "Herrstein", :area_code => "6785") + AreaCode.create(:country => germany, :name => "Kempfeld", :area_code => "6786") + AreaCode.create(:country => germany, :name => "Niederbrombach", :area_code => "6787") + AreaCode.create(:country => germany, :name => "Sien", :area_code => "6788") + AreaCode.create(:country => germany, :name => "Heimbach Nahe", :area_code => "6789") + AreaCode.create(:country => germany, :name => "Völklingen-Lauterbach", :area_code => "6802") + AreaCode.create(:country => germany, :name => "Mandelbachtal-Ommersheim", :area_code => "6803") + AreaCode.create(:country => germany, :name => "Mandelbachtal", :area_code => "6804") + AreaCode.create(:country => germany, :name => "Kleinblittersdorf", :area_code => "6805") + AreaCode.create(:country => germany, :name => "Heusweiler", :area_code => "6806") + AreaCode.create(:country => germany, :name => "Grossrosseln", :area_code => "6809") + AreaCode.create(:country => germany, :name => "Saarbrücken", :area_code => "681") + AreaCode.create(:country => germany, :name => "Neunkirchen Saar", :area_code => "6821") + AreaCode.create(:country => germany, :name => "Ottweiler", :area_code => "6824") + AreaCode.create(:country => germany, :name => "Illingen Saar", :area_code => "6825") + AreaCode.create(:country => germany, :name => "Bexbach", :area_code => "6826") + AreaCode.create(:country => germany, :name => "Eppelborn", :area_code => "6827") + AreaCode.create(:country => germany, :name => "Saarlouis", :area_code => "6831") + AreaCode.create(:country => germany, :name => "Beckingen-Reimsbach", :area_code => "6832") + AreaCode.create(:country => germany, :name => "Rehlingen-Siersburg", :area_code => "6833") + AreaCode.create(:country => germany, :name => "Bous", :area_code => "6834") + AreaCode.create(:country => germany, :name => "Beckingen", :area_code => "6835") + AreaCode.create(:country => germany, :name => "Überherrn", :area_code => "6836") + AreaCode.create(:country => germany, :name => "Wallerfangen", :area_code => "6837") + AreaCode.create(:country => germany, :name => "Saarwellingen", :area_code => "6838") + AreaCode.create(:country => germany, :name => "Homburg Saar", :area_code => "6841") + AreaCode.create(:country => germany, :name => "Blieskastel", :area_code => "6842") + AreaCode.create(:country => germany, :name => "Gersheim", :area_code => "6843") + AreaCode.create(:country => germany, :name => "Blieskastel-Altheim", :area_code => "6844") + AreaCode.create(:country => germany, :name => "Homburg-Einöd", :area_code => "6848") + AreaCode.create(:country => germany, :name => "Kirkel", :area_code => "6849") + AreaCode.create(:country => germany, :name => "St Wendel", :area_code => "6851") + AreaCode.create(:country => germany, :name => "Nohfelden", :area_code => "6852") + AreaCode.create(:country => germany, :name => "Marpingen", :area_code => "6853") + AreaCode.create(:country => germany, :name => "Oberthal Saar", :area_code => "6854") + AreaCode.create(:country => germany, :name => "Freisen", :area_code => "6855") + AreaCode.create(:country => germany, :name => "St Wendel-Niederkirchen", :area_code => "6856") + AreaCode.create(:country => germany, :name => "Namborn", :area_code => "6857") + AreaCode.create(:country => germany, :name => "Ottweiler-Fürth", :area_code => "6858") + AreaCode.create(:country => germany, :name => "Merzig", :area_code => "6861") + AreaCode.create(:country => germany, :name => "Mettlach", :area_code => "6864") + AreaCode.create(:country => germany, :name => "Mettlach-Orscholz", :area_code => "6865") + AreaCode.create(:country => germany, :name => "Perl-Nennig", :area_code => "6866") + AreaCode.create(:country => germany, :name => "Perl", :area_code => "6867") + AreaCode.create(:country => germany, :name => "Mettlach-Tünsdorf", :area_code => "6868") + AreaCode.create(:country => germany, :name => "Merzig-Silwingen", :area_code => "6869") + AreaCode.create(:country => germany, :name => "Wadern", :area_code => "6871") + AreaCode.create(:country => germany, :name => "Losheim am See", :area_code => "6872") + AreaCode.create(:country => germany, :name => "Nonnweiler", :area_code => "6873") + AreaCode.create(:country => germany, :name => "Wadern-Nunkirchen", :area_code => "6874") + AreaCode.create(:country => germany, :name => "Nonnweiler-Primstal", :area_code => "6875") + AreaCode.create(:country => germany, :name => "Weiskirchen Saar", :area_code => "6876") + AreaCode.create(:country => germany, :name => "Lebach", :area_code => "6881") + AreaCode.create(:country => germany, :name => "Schmelz Saar", :area_code => "6887") + AreaCode.create(:country => germany, :name => "Lebach-Steinbach", :area_code => "6888") + AreaCode.create(:country => germany, :name => "Saarbrücken-Ensheim", :area_code => "6893") + AreaCode.create(:country => germany, :name => "St Ingbert", :area_code => "6894") + AreaCode.create(:country => germany, :name => "Sulzbach Saar", :area_code => "6897") + AreaCode.create(:country => germany, :name => "Völklingen", :area_code => "6898") + AreaCode.create(:country => germany, :name => "Frankfurt am Main", :area_code => "69") + AreaCode.create(:country => germany, :name => "Kirchheim unter Teck", :area_code => "7021") + AreaCode.create(:country => germany, :name => "Nürtingen", :area_code => "7022") + AreaCode.create(:country => germany, :name => "Weilheim an der Teck", :area_code => "7023") + AreaCode.create(:country => germany, :name => "Wendlingen am Neckar", :area_code => "7024") + AreaCode.create(:country => germany, :name => "Neuffen", :area_code => "7025") + AreaCode.create(:country => germany, :name => "Lenningen", :area_code => "7026") + AreaCode.create(:country => germany, :name => "Böblingen", :area_code => "7031") + AreaCode.create(:country => germany, :name => "Herrenberg", :area_code => "7032") + AreaCode.create(:country => germany, :name => "Weil Der Stadt", :area_code => "7033") + AreaCode.create(:country => germany, :name => "Ehningen", :area_code => "7034") + AreaCode.create(:country => germany, :name => "Mühlacker", :area_code => "7041") + AreaCode.create(:country => germany, :name => "Vaihingen an der Enz", :area_code => "7042") + AreaCode.create(:country => germany, :name => "Maulbronn", :area_code => "7043") + AreaCode.create(:country => germany, :name => "Mönsheim", :area_code => "7044") + AreaCode.create(:country => germany, :name => "Oberderdingen", :area_code => "7045") + AreaCode.create(:country => germany, :name => "Zaberfeld", :area_code => "7046") + AreaCode.create(:country => germany, :name => "Calw", :area_code => "7051") + AreaCode.create(:country => germany, :name => "Bad Liebenzell", :area_code => "7052") + AreaCode.create(:country => germany, :name => "Bad Teinach-Zavelstein", :area_code => "7053") + AreaCode.create(:country => germany, :name => "Wildberg Württ", :area_code => "7054") + AreaCode.create(:country => germany, :name => "Neuweiler Kr Calw", :area_code => "7055") + AreaCode.create(:country => germany, :name => "Gechingen", :area_code => "7056") + AreaCode.create(:country => germany, :name => "Beilstein Württ", :area_code => "7062") + AreaCode.create(:country => germany, :name => "Bad Wimpfen", :area_code => "7063") + AreaCode.create(:country => germany, :name => "Bad Rappenau-Bonfeld", :area_code => "7066") + AreaCode.create(:country => germany, :name => "Tübingen", :area_code => "7071") + AreaCode.create(:country => germany, :name => "Gomaringen", :area_code => "7072") + AreaCode.create(:country => germany, :name => "Ammerbuch", :area_code => "7073") + AreaCode.create(:country => germany, :name => "Bad Wildbad", :area_code => "7081") + AreaCode.create(:country => germany, :name => "Neuenbürg Württ", :area_code => "7082") + AreaCode.create(:country => germany, :name => "Bad Herrenalb", :area_code => "7083") + AreaCode.create(:country => germany, :name => "Schömberg b Neuenbürg", :area_code => "7084") + AreaCode.create(:country => germany, :name => "Enzklösterle", :area_code => "7085") + AreaCode.create(:country => germany, :name => "Stuttgart", :area_code => "711") + AreaCode.create(:country => germany, :name => "Reutlingen", :area_code => "7121") + AreaCode.create(:country => germany, :name => "St Johann Württ", :area_code => "7122") + AreaCode.create(:country => germany, :name => "Metzingen Württ", :area_code => "7123") + AreaCode.create(:country => germany, :name => "Trochtelfingen Hohenz", :area_code => "7124") + AreaCode.create(:country => germany, :name => "Bad Urach", :area_code => "7125") + AreaCode.create(:country => germany, :name => "Burladingen-Melchingen", :area_code => "7126") + AreaCode.create(:country => germany, :name => "Neckartenzlingen", :area_code => "7127") + AreaCode.create(:country => germany, :name => "Sonnenbühl", :area_code => "7128") + AreaCode.create(:country => germany, :name => "Lichtenstein Württ", :area_code => "7129") + AreaCode.create(:country => germany, :name => "Löwenstein Württ", :area_code => "7130") + AreaCode.create(:country => germany, :name => "Heilbronn Neckar", :area_code => "7131") + AreaCode.create(:country => germany, :name => "Neckarsulm", :area_code => "7132") + AreaCode.create(:country => germany, :name => "Lauffen am Neckar", :area_code => "7133") + AreaCode.create(:country => germany, :name => "Weinsberg", :area_code => "7134") + AreaCode.create(:country => germany, :name => "Brackenheim", :area_code => "7135") + AreaCode.create(:country => germany, :name => "Bad Friedrichshall", :area_code => "7136") + AreaCode.create(:country => germany, :name => "Schwaigern", :area_code => "7138") + AreaCode.create(:country => germany, :name => "Neuenstadt am Kocher", :area_code => "7139") + AreaCode.create(:country => germany, :name => "Ludwigsburg Württ", :area_code => "7141") + AreaCode.create(:country => germany, :name => "Bietigheim-Bissingen", :area_code => "7142") + AreaCode.create(:country => germany, :name => "Besigheim", :area_code => "7143") + AreaCode.create(:country => germany, :name => "Marbach am Neckar", :area_code => "7144") + AreaCode.create(:country => germany, :name => "Markgröningen", :area_code => "7145") + AreaCode.create(:country => germany, :name => "Remseck am Neckar", :area_code => "7146") + AreaCode.create(:country => germany, :name => "Sachsenheim Württ", :area_code => "7147") + AreaCode.create(:country => germany, :name => "Grossbottwar", :area_code => "7148") + AreaCode.create(:country => germany, :name => "Korntal-Münchingen", :area_code => "7150") + AreaCode.create(:country => germany, :name => "Waiblingen", :area_code => "7151") + AreaCode.create(:country => germany, :name => "Leonberg Württ", :area_code => "7152") + AreaCode.create(:country => germany, :name => "Plochingen", :area_code => "7153") + AreaCode.create(:country => germany, :name => "Kornwestheim", :area_code => "7154") + AreaCode.create(:country => germany, :name => "Ditzingen", :area_code => "7156") + AreaCode.create(:country => germany, :name => "Waldenbuch", :area_code => "7157") + AreaCode.create(:country => germany, :name => "Neuhausen auf den Fildern", :area_code => "7158") + AreaCode.create(:country => germany, :name => "Renningen", :area_code => "7159") + AreaCode.create(:country => germany, :name => "Göppingen", :area_code => "7161") + AreaCode.create(:country => germany, :name => "Süßen", :area_code => "7162") + AreaCode.create(:country => germany, :name => "Ebersbach an der Fils", :area_code => "7163") + AreaCode.create(:country => germany, :name => "Boll Kr Göppingen", :area_code => "7164") + AreaCode.create(:country => germany, :name => "Göppingen-Hohenstaufen", :area_code => "7165") + AreaCode.create(:country => germany, :name => "Adelberg", :area_code => "7166") + AreaCode.create(:country => germany, :name => "Schwäbisch Gmünd", :area_code => "7171") + AreaCode.create(:country => germany, :name => "Lorch Württ", :area_code => "7172") + AreaCode.create(:country => germany, :name => "Heubach", :area_code => "7173") + AreaCode.create(:country => germany, :name => "Mögglingen", :area_code => "7174") + AreaCode.create(:country => germany, :name => "Leinzell", :area_code => "7175") + AreaCode.create(:country => germany, :name => "Spraitbach", :area_code => "7176") + AreaCode.create(:country => germany, :name => "Schorndorf Württ", :area_code => "7181") + AreaCode.create(:country => germany, :name => "Welzheim", :area_code => "7182") + AreaCode.create(:country => germany, :name => "Rudersberg Württ", :area_code => "7183") + AreaCode.create(:country => germany, :name => "Kaisersbach", :area_code => "7184") + AreaCode.create(:country => germany, :name => "Backnang", :area_code => "7191") + AreaCode.create(:country => germany, :name => "Murrhardt", :area_code => "7192") + AreaCode.create(:country => germany, :name => "Sulzbach an der Murr", :area_code => "7193") + AreaCode.create(:country => germany, :name => "Spiegelberg", :area_code => "7194") + AreaCode.create(:country => germany, :name => "Winnenden", :area_code => "7195") + AreaCode.create(:country => germany, :name => "Karlsbad", :area_code => "7202") + AreaCode.create(:country => germany, :name => "Walzbachtal", :area_code => "7203") + AreaCode.create(:country => germany, :name => "Malsch-Völkersbach", :area_code => "7204") + AreaCode.create(:country => germany, :name => "Karlsruhe", :area_code => "721") + AreaCode.create(:country => germany, :name => "Forbach-Hundsbach", :area_code => "7220") + AreaCode.create(:country => germany, :name => "Baden-Baden", :area_code => "7221") + AreaCode.create(:country => germany, :name => "Rastatt", :area_code => "7222") + AreaCode.create(:country => germany, :name => "Bühl Baden", :area_code => "7223") + AreaCode.create(:country => germany, :name => "Gernsbach", :area_code => "7224") + AreaCode.create(:country => germany, :name => "Gaggenau", :area_code => "7225") + AreaCode.create(:country => germany, :name => "Bühl-Sand", :area_code => "7226") + AreaCode.create(:country => germany, :name => "Lichtenau Baden", :area_code => "7227") + AreaCode.create(:country => germany, :name => "Forbach", :area_code => "7228") + AreaCode.create(:country => germany, :name => "Iffezheim", :area_code => "7229") + AreaCode.create(:country => germany, :name => "Pforzheim", :area_code => "7231") + AreaCode.create(:country => germany, :name => "Königsbach-Stein", :area_code => "7232") + AreaCode.create(:country => germany, :name => "Niefern-Öschelbronn", :area_code => "7233") + AreaCode.create(:country => germany, :name => "Tiefenbronn", :area_code => "7234") + AreaCode.create(:country => germany, :name => "Unterreichenbach Kr Calw", :area_code => "7235") + AreaCode.create(:country => germany, :name => "Keltern", :area_code => "7236") + AreaCode.create(:country => germany, :name => "Neulingen Enzkreis", :area_code => "7237") + AreaCode.create(:country => germany, :name => "Pfinztal", :area_code => "7240") + AreaCode.create(:country => germany, :name => "Rheinstetten", :area_code => "7242") + AreaCode.create(:country => germany, :name => "Ettlingen", :area_code => "7243") + AreaCode.create(:country => germany, :name => "Weingarten Baden", :area_code => "7244") + AreaCode.create(:country => germany, :name => "Durmersheim", :area_code => "7245") + AreaCode.create(:country => germany, :name => "Malsch Kr Karlsruhe", :area_code => "7246") + AreaCode.create(:country => germany, :name => "Linkenheim-Hochstetten", :area_code => "7247") + AreaCode.create(:country => germany, :name => "Marxzell", :area_code => "7248") + AreaCode.create(:country => germany, :name => "Stutensee", :area_code => "7249") + AreaCode.create(:country => germany, :name => "Kraichtal", :area_code => "7250") + AreaCode.create(:country => germany, :name => "Bruchsal", :area_code => "7251") + AreaCode.create(:country => germany, :name => "Bretten", :area_code => "7252") + AreaCode.create(:country => germany, :name => "Bad Schönborn", :area_code => "7253") + AreaCode.create(:country => germany, :name => "Waghäusel", :area_code => "7254") + AreaCode.create(:country => germany, :name => "Graben-Neudorf", :area_code => "7255") + AreaCode.create(:country => germany, :name => "Philippsburg", :area_code => "7256") + AreaCode.create(:country => germany, :name => "Bruchsal-Untergrombach", :area_code => "7257") + AreaCode.create(:country => germany, :name => "Oberderdingen-Flehingen", :area_code => "7258") + AreaCode.create(:country => germany, :name => "Östringen-Odenheim", :area_code => "7259") + AreaCode.create(:country => germany, :name => "Sinsheim-Hilsbach", :area_code => "7260") + AreaCode.create(:country => germany, :name => "Sinsheim", :area_code => "7261") + AreaCode.create(:country => germany, :name => "Eppingen", :area_code => "7262") + AreaCode.create(:country => germany, :name => "Waibstadt", :area_code => "7263") + AreaCode.create(:country => germany, :name => "Bad Rappenau", :area_code => "7264") + AreaCode.create(:country => germany, :name => "Angelbachtal", :area_code => "7265") + AreaCode.create(:country => germany, :name => "Kirchardt", :area_code => "7266") + AreaCode.create(:country => germany, :name => "Gemmingen", :area_code => "7267") + AreaCode.create(:country => germany, :name => "Bad Rappenau-Obergimpern", :area_code => "7268") + AreaCode.create(:country => germany, :name => "Sulzfeld Baden", :area_code => "7269") + AreaCode.create(:country => germany, :name => "Wörth am Rhein", :area_code => "7271") + AreaCode.create(:country => germany, :name => "Rülzheim", :area_code => "7272") + AreaCode.create(:country => germany, :name => "Hagenbach Pfalz", :area_code => "7273") + AreaCode.create(:country => germany, :name => "Germersheim", :area_code => "7274") + AreaCode.create(:country => germany, :name => "Kandel", :area_code => "7275") + AreaCode.create(:country => germany, :name => "Herxheim bei Landau Pfalz", :area_code => "7276") + AreaCode.create(:country => germany, :name => "Wörth-Büchelberg", :area_code => "7277") + AreaCode.create(:country => germany, :name => "Roggenburg", :area_code => "7300") + AreaCode.create(:country => germany, :name => "Pfaffenhofen a d Roth", :area_code => "7302") + AreaCode.create(:country => germany, :name => "Illertissen", :area_code => "7303") + AreaCode.create(:country => germany, :name => "Blaustein Württ", :area_code => "7304") + AreaCode.create(:country => germany, :name => "Erbach Donau", :area_code => "7305") + AreaCode.create(:country => germany, :name => "Vöhringen Iller", :area_code => "7306") + AreaCode.create(:country => germany, :name => "Senden Iller", :area_code => "7307") + AreaCode.create(:country => germany, :name => "Nersingen", :area_code => "7308") + AreaCode.create(:country => germany, :name => "Weissenhorn", :area_code => "7309") + AreaCode.create(:country => germany, :name => "Ulm Donau", :area_code => "731") + AreaCode.create(:country => germany, :name => "Heidenheim a d Brenz", :area_code => "7321") + AreaCode.create(:country => germany, :name => "Giengen a d Brenz", :area_code => "7322") + AreaCode.create(:country => germany, :name => "Gerstetten", :area_code => "7323") + AreaCode.create(:country => germany, :name => "Herbrechtingen", :area_code => "7324") + AreaCode.create(:country => germany, :name => "Sontheim a d Brenz", :area_code => "7325") + AreaCode.create(:country => germany, :name => "Neresheim", :area_code => "7326") + AreaCode.create(:country => germany, :name => "Dischingen", :area_code => "7327") + AreaCode.create(:country => germany, :name => "Königsbronn", :area_code => "7328") + AreaCode.create(:country => germany, :name => "Steinheim am Albuch", :area_code => "7329") + AreaCode.create(:country => germany, :name => "Geislingen an der Steige", :area_code => "7331") + AreaCode.create(:country => germany, :name => "Lauterstein", :area_code => "7332") + AreaCode.create(:country => germany, :name => "Laichingen", :area_code => "7333") + AreaCode.create(:country => germany, :name => "Deggingen", :area_code => "7334") + AreaCode.create(:country => germany, :name => "Wiesensteig", :area_code => "7335") + AreaCode.create(:country => germany, :name => "Lonsee", :area_code => "7336") + AreaCode.create(:country => germany, :name => "Nellingen Alb", :area_code => "7337") + AreaCode.create(:country => germany, :name => "Neenstetten", :area_code => "7340") + AreaCode.create(:country => germany, :name => "Buch b Illertissen", :area_code => "7343") + AreaCode.create(:country => germany, :name => "Blaubeuren", :area_code => "7344") + AreaCode.create(:country => germany, :name => "Langenau Württ", :area_code => "7345") + AreaCode.create(:country => germany, :name => "Illerkirchberg", :area_code => "7346") + AreaCode.create(:country => germany, :name => "Dietenheim", :area_code => "7347") + AreaCode.create(:country => germany, :name => "Beimerstetten", :area_code => "7348") + AreaCode.create(:country => germany, :name => "Biberach an der Riß", :area_code => "7351") + AreaCode.create(:country => germany, :name => "Ochsenhausen", :area_code => "7352") + AreaCode.create(:country => germany, :name => "Schwendi", :area_code => "7353") + AreaCode.create(:country => germany, :name => "Erolzheim", :area_code => "7354") + AreaCode.create(:country => germany, :name => "Hochdorf Riß", :area_code => "7355") + AreaCode.create(:country => germany, :name => "Schemmerhofen", :area_code => "7356") + AreaCode.create(:country => germany, :name => "Attenweiler", :area_code => "7357") + AreaCode.create(:country => germany, :name => "Eberhardzell-Füramoos", :area_code => "7358") + AreaCode.create(:country => germany, :name => "Aalen", :area_code => "7361") + AreaCode.create(:country => germany, :name => "Bopfingen", :area_code => "7362") + AreaCode.create(:country => germany, :name => "Lauchheim", :area_code => "7363") + AreaCode.create(:country => germany, :name => "Oberkochen", :area_code => "7364") + AreaCode.create(:country => germany, :name => "Essingen Württ", :area_code => "7365") + AreaCode.create(:country => germany, :name => "Abtsgmünd", :area_code => "7366") + AreaCode.create(:country => germany, :name => "Aalen-Ebnat", :area_code => "7367") + AreaCode.create(:country => germany, :name => "Riedlingen Württ", :area_code => "7371") + AreaCode.create(:country => germany, :name => "Zwiefalten", :area_code => "7373") + AreaCode.create(:country => germany, :name => "Uttenweiler", :area_code => "7374") + AreaCode.create(:country => germany, :name => "Obermarchtal", :area_code => "7375") + AreaCode.create(:country => germany, :name => "Langenenslingen", :area_code => "7376") + AreaCode.create(:country => germany, :name => "Münsingen", :area_code => "7381") + AreaCode.create(:country => germany, :name => "Römerstein", :area_code => "7382") + AreaCode.create(:country => germany, :name => "Münsingen-Buttenhausen", :area_code => "7383") + AreaCode.create(:country => germany, :name => "Schelklingen-Hütten", :area_code => "7384") + AreaCode.create(:country => germany, :name => "Gomadingen", :area_code => "7385") + AreaCode.create(:country => germany, :name => "Hayingen", :area_code => "7386") + AreaCode.create(:country => germany, :name => "Hohenstein Württ", :area_code => "7387") + AreaCode.create(:country => germany, :name => "Pfronstetten", :area_code => "7388") + AreaCode.create(:country => germany, :name => "Heroldstatt", :area_code => "7389") + AreaCode.create(:country => germany, :name => "Ehingen Donau", :area_code => "7391") + AreaCode.create(:country => germany, :name => "Laupheim", :area_code => "7392") + AreaCode.create(:country => germany, :name => "Munderkingen", :area_code => "7393") + AreaCode.create(:country => germany, :name => "Schelklingen", :area_code => "7394") + AreaCode.create(:country => germany, :name => "Ehingen-Dächingen", :area_code => "7395") + AreaCode.create(:country => germany, :name => "Fluorn-Winzeln", :area_code => "7402") + AreaCode.create(:country => germany, :name => "Dunningen", :area_code => "7403") + AreaCode.create(:country => germany, :name => "Epfendorf", :area_code => "7404") + AreaCode.create(:country => germany, :name => "Rottweil", :area_code => "741") + AreaCode.create(:country => germany, :name => "Deisslingen", :area_code => "7420") + AreaCode.create(:country => germany, :name => "Schramberg", :area_code => "7422") + AreaCode.create(:country => germany, :name => "Oberndorf am Neckar", :area_code => "7423") + AreaCode.create(:country => germany, :name => "Spaichingen", :area_code => "7424") + AreaCode.create(:country => germany, :name => "Trossingen", :area_code => "7425") + AreaCode.create(:country => germany, :name => "Gosheim", :area_code => "7426") + AreaCode.create(:country => germany, :name => "Schömberg b Balingen", :area_code => "7427") + AreaCode.create(:country => germany, :name => "Rosenfeld", :area_code => "7428") + AreaCode.create(:country => germany, :name => "Egesheim", :area_code => "7429") + AreaCode.create(:country => germany, :name => "Albstadt-Ebingen", :area_code => "7431") + AreaCode.create(:country => germany, :name => "Albstadt-Tailfingen", :area_code => "7432") + AreaCode.create(:country => germany, :name => "Balingen", :area_code => "7433") + AreaCode.create(:country => germany, :name => "Winterlingen", :area_code => "7434") + AreaCode.create(:country => germany, :name => "Albstadt-Laufen", :area_code => "7435") + AreaCode.create(:country => germany, :name => "Messstetten-Oberdigisheim", :area_code => "7436") + AreaCode.create(:country => germany, :name => "Bad Rippoldsau", :area_code => "7440") + AreaCode.create(:country => germany, :name => "Freudenstadt", :area_code => "7441") + AreaCode.create(:country => germany, :name => "Baiersbronn", :area_code => "7442") + AreaCode.create(:country => germany, :name => "Dornstetten", :area_code => "7443") + AreaCode.create(:country => germany, :name => "Alpirsbach", :area_code => "7444") + AreaCode.create(:country => germany, :name => "Pfalzgrafenweiler", :area_code => "7445") + AreaCode.create(:country => germany, :name => "Lossburg", :area_code => "7446") + AreaCode.create(:country => germany, :name => "Baiersbronn-Schwarzenberg", :area_code => "7447") + AreaCode.create(:country => germany, :name => "Seewald", :area_code => "7448") + AreaCode.create(:country => germany, :name => "Baiersbronn-Obertal", :area_code => "7449") + AreaCode.create(:country => germany, :name => "Horb am Neckar", :area_code => "7451") + AreaCode.create(:country => germany, :name => "Nagold", :area_code => "7452") + AreaCode.create(:country => germany, :name => "Altensteig Württ", :area_code => "7453") + AreaCode.create(:country => germany, :name => "Sulz am Neckar", :area_code => "7454") + AreaCode.create(:country => germany, :name => "Dornhan", :area_code => "7455") + AreaCode.create(:country => germany, :name => "Haiterbach", :area_code => "7456") + AreaCode.create(:country => germany, :name => "Rottenburg-Ergenzingen", :area_code => "7457") + AreaCode.create(:country => germany, :name => "Ebhausen", :area_code => "7458") + AreaCode.create(:country => germany, :name => "Nagold-Hochdorf", :area_code => "7459") + AreaCode.create(:country => germany, :name => "Tuttlingen", :area_code => "7461") + AreaCode.create(:country => germany, :name => "Immendingen", :area_code => "7462") + AreaCode.create(:country => germany, :name => "Mühlheim an der Donau", :area_code => "7463") + AreaCode.create(:country => germany, :name => "Talheim Kr Tuttlingen", :area_code => "7464") + AreaCode.create(:country => germany, :name => "Emmingen-Liptingen", :area_code => "7465") + AreaCode.create(:country => germany, :name => "Beuron", :area_code => "7466") + AreaCode.create(:country => germany, :name => "Neuhausen ob Eck", :area_code => "7467") + AreaCode.create(:country => germany, :name => "Hechingen", :area_code => "7471") + AreaCode.create(:country => germany, :name => "Rottenburg am Neckar", :area_code => "7472") + AreaCode.create(:country => germany, :name => "Mössingen", :area_code => "7473") + AreaCode.create(:country => germany, :name => "Haigerloch", :area_code => "7474") + AreaCode.create(:country => germany, :name => "Burladingen", :area_code => "7475") + AreaCode.create(:country => germany, :name => "Bisingen", :area_code => "7476") + AreaCode.create(:country => germany, :name => "Jungingen b Hechingen", :area_code => "7477") + AreaCode.create(:country => germany, :name => "Hirrlingen", :area_code => "7478") + AreaCode.create(:country => germany, :name => "Horb-Dettingen", :area_code => "7482") + AreaCode.create(:country => germany, :name => "Horb-Mühringen", :area_code => "7483") + AreaCode.create(:country => germany, :name => "Simmersfeld", :area_code => "7484") + AreaCode.create(:country => germany, :name => "Empfingen", :area_code => "7485") + AreaCode.create(:country => germany, :name => "Horb-Altheim", :area_code => "7486") + AreaCode.create(:country => germany, :name => "Wolpertswende", :area_code => "7502") + AreaCode.create(:country => germany, :name => "Wilhelmsdorf Württ", :area_code => "7503") + AreaCode.create(:country => germany, :name => "Horgenzell", :area_code => "7504") + AreaCode.create(:country => germany, :name => "Fronreute", :area_code => "7505") + AreaCode.create(:country => germany, :name => "Wangen-Leupolz", :area_code => "7506") + AreaCode.create(:country => germany, :name => "Ravensburg", :area_code => "751") + AreaCode.create(:country => germany, :name => "Bodnegg", :area_code => "7520") + AreaCode.create(:country => germany, :name => "Wangen im Allgäu", :area_code => "7522") + AreaCode.create(:country => germany, :name => "Bad Waldsee", :area_code => "7524") + AreaCode.create(:country => germany, :name => "Aulendorf", :area_code => "7525") + AreaCode.create(:country => germany, :name => "Wolfegg", :area_code => "7527") + AreaCode.create(:country => germany, :name => "Neukirch b Tettnang", :area_code => "7528") + AreaCode.create(:country => germany, :name => "Waldburg Württ", :area_code => "7529") + AreaCode.create(:country => germany, :name => "Konstanz", :area_code => "7531") + AreaCode.create(:country => germany, :name => "Meersburg", :area_code => "7532") + AreaCode.create(:country => germany, :name => "Allensbach", :area_code => "7533") + AreaCode.create(:country => germany, :name => "Reichenau Baden", :area_code => "7534") + AreaCode.create(:country => germany, :name => "Friedrichshafen", :area_code => "7541") + AreaCode.create(:country => germany, :name => "Tettnang", :area_code => "7542") + AreaCode.create(:country => germany, :name => "Kressbronn am Bodensee", :area_code => "7543") + AreaCode.create(:country => germany, :name => "Markdorf", :area_code => "7544") + AreaCode.create(:country => germany, :name => "Immenstaad am Bodensee", :area_code => "7545") + AreaCode.create(:country => germany, :name => "Oberteuringen", :area_code => "7546") + AreaCode.create(:country => germany, :name => "Überlingen Bodensee", :area_code => "7551") + AreaCode.create(:country => germany, :name => "Pfullendorf", :area_code => "7552") + AreaCode.create(:country => germany, :name => "Salem Baden", :area_code => "7553") + AreaCode.create(:country => germany, :name => "Heiligenberg Baden", :area_code => "7554") + AreaCode.create(:country => germany, :name => "Deggenhausertal", :area_code => "7555") + AreaCode.create(:country => germany, :name => "Uhldingen-Mühlhofen", :area_code => "7556") + AreaCode.create(:country => germany, :name => "Herdwangen-Schönach", :area_code => "7557") + AreaCode.create(:country => germany, :name => "Illmensee", :area_code => "7558") + AreaCode.create(:country => germany, :name => "Leutkirch im Allgäu", :area_code => "7561") + AreaCode.create(:country => germany, :name => "Isny im Allgäu", :area_code => "7562") + AreaCode.create(:country => germany, :name => "Kisslegg", :area_code => "7563") + AreaCode.create(:country => germany, :name => "Bad Wurzach", :area_code => "7564") + AreaCode.create(:country => germany, :name => "Aichstetten Kr Ravensburg", :area_code => "7565") + AreaCode.create(:country => germany, :name => "Argenbühl", :area_code => "7566") + AreaCode.create(:country => germany, :name => "Leutkirch-Friesenhofen", :area_code => "7567") + AreaCode.create(:country => germany, :name => "Bad Wurzach-Hauerz", :area_code => "7568") + AreaCode.create(:country => germany, :name => "Isny-Eisenbach", :area_code => "7569") + AreaCode.create(:country => germany, :name => "Sigmaringen-Gutenstein", :area_code => "7570") + AreaCode.create(:country => germany, :name => "Sigmaringen", :area_code => "7571") + AreaCode.create(:country => germany, :name => "Mengen Württ", :area_code => "7572") + AreaCode.create(:country => germany, :name => "Stetten am kalten Markt", :area_code => "7573") + AreaCode.create(:country => germany, :name => "Gammertingen", :area_code => "7574") + AreaCode.create(:country => germany, :name => "Messkirch", :area_code => "7575") + AreaCode.create(:country => germany, :name => "Krauchenwies", :area_code => "7576") + AreaCode.create(:country => germany, :name => "Veringenstadt", :area_code => "7577") + AreaCode.create(:country => germany, :name => "Wald Hohenz", :area_code => "7578") + AreaCode.create(:country => germany, :name => "Schwenningen Baden", :area_code => "7579") + AreaCode.create(:country => germany, :name => "Saulgau", :area_code => "7581") + AreaCode.create(:country => germany, :name => "Bad Buchau", :area_code => "7582") + AreaCode.create(:country => germany, :name => "Bad Schussenried", :area_code => "7583") + AreaCode.create(:country => germany, :name => "Altshausen", :area_code => "7584") + AreaCode.create(:country => germany, :name => "Ostrach", :area_code => "7585") + AreaCode.create(:country => germany, :name => "Herbertingen", :area_code => "7586") + AreaCode.create(:country => germany, :name => "Hosskirch", :area_code => "7587") + AreaCode.create(:country => germany, :name => "Oberried Breisgau", :area_code => "7602") + AreaCode.create(:country => germany, :name => "Freiburg im Breisgau", :area_code => "761") + AreaCode.create(:country => germany, :name => "Schopfheim-Gersbach", :area_code => "7620") + AreaCode.create(:country => germany, :name => "Lörrach", :area_code => "7621") + AreaCode.create(:country => germany, :name => "Schopfheim", :area_code => "7622") + AreaCode.create(:country => germany, :name => "Rheinfelden Baden", :area_code => "7623") + AreaCode.create(:country => germany, :name => "Grenzach-Wyhlen", :area_code => "7624") + AreaCode.create(:country => germany, :name => "Zell im Wiesental", :area_code => "7625") + AreaCode.create(:country => germany, :name => "Kandern", :area_code => "7626") + AreaCode.create(:country => germany, :name => "Steinen Kr Lörrach", :area_code => "7627") + AreaCode.create(:country => germany, :name => "Efringen-Kirchen", :area_code => "7628") + AreaCode.create(:country => germany, :name => "Tegernau Baden", :area_code => "7629") + AreaCode.create(:country => germany, :name => "Müllheim Baden", :area_code => "7631") + AreaCode.create(:country => germany, :name => "Badenweiler", :area_code => "7632") + AreaCode.create(:country => germany, :name => "Staufen im Breisgau", :area_code => "7633") + AreaCode.create(:country => germany, :name => "Sulzburg", :area_code => "7634") + AreaCode.create(:country => germany, :name => "Schliengen", :area_code => "7635") + AreaCode.create(:country => germany, :name => "Münstertal Schwarzwald", :area_code => "7636") + AreaCode.create(:country => germany, :name => "Emmendingen", :area_code => "7641") + AreaCode.create(:country => germany, :name => "Endingen Kaiserstuh", :area_code => "7642") + AreaCode.create(:country => germany, :name => "Herbolzheim Breisgau", :area_code => "7643") + AreaCode.create(:country => germany, :name => "Kenzingen", :area_code => "7644") + AreaCode.create(:country => germany, :name => "Freiamt", :area_code => "7645") + AreaCode.create(:country => germany, :name => "Weisweil Breisgau", :area_code => "7646") + AreaCode.create(:country => germany, :name => "Titisee-Neustadt", :area_code => "7651") + AreaCode.create(:country => germany, :name => "Hinterzarten", :area_code => "7652") + AreaCode.create(:country => germany, :name => "Lenzkirch", :area_code => "7653") + AreaCode.create(:country => germany, :name => "Löffingen", :area_code => "7654") + AreaCode.create(:country => germany, :name => "Feldberg-Altglashütten", :area_code => "7655") + AreaCode.create(:country => germany, :name => "Schluchsee", :area_code => "7656") + AreaCode.create(:country => germany, :name => "Eisenbach Hochschwarzwald", :area_code => "7657") + AreaCode.create(:country => germany, :name => "St Peter Schwarzw", :area_code => "7660") + AreaCode.create(:country => germany, :name => "Kirchzarten", :area_code => "7661") + AreaCode.create(:country => germany, :name => "Vogtsburg im Kaiserstuh", :area_code => "7662") + AreaCode.create(:country => germany, :name => "Eichstetten", :area_code => "7663") + AreaCode.create(:country => germany, :name => "Freiburg-Tiengen", :area_code => "7664") + AreaCode.create(:country => germany, :name => "March Breisgau", :area_code => "7665") + AreaCode.create(:country => germany, :name => "Denzlingen", :area_code => "7666") + AreaCode.create(:country => germany, :name => "Breisach am Rhein", :area_code => "7667") + AreaCode.create(:country => germany, :name => "Ihringen", :area_code => "7668") + AreaCode.create(:country => germany, :name => "St Märgen", :area_code => "7669") + AreaCode.create(:country => germany, :name => "Todtnau", :area_code => "7671") + AreaCode.create(:country => germany, :name => "St Blasien", :area_code => "7672") + AreaCode.create(:country => germany, :name => "Schönau im Schwarzwald", :area_code => "7673") + AreaCode.create(:country => germany, :name => "Todtmoos", :area_code => "7674") + AreaCode.create(:country => germany, :name => "Bernau Baden", :area_code => "7675") + AreaCode.create(:country => germany, :name => "Feldberg Schwarzwald", :area_code => "7676") + AreaCode.create(:country => germany, :name => "Waldkirch Breisgau", :area_code => "7681") + AreaCode.create(:country => germany, :name => "Elzach", :area_code => "7682") + AreaCode.create(:country => germany, :name => "Simonswald", :area_code => "7683") + AreaCode.create(:country => germany, :name => "Glottertal", :area_code => "7684") + AreaCode.create(:country => germany, :name => "Gutach-Bleibach", :area_code => "7685") + AreaCode.create(:country => germany, :name => "Blumberg Baden", :area_code => "7702") + AreaCode.create(:country => germany, :name => "Bonndorf im Schwarzwald", :area_code => "7703") + AreaCode.create(:country => germany, :name => "Geisingen Baden", :area_code => "7704") + AreaCode.create(:country => germany, :name => "Wolterdingen Schwarzw", :area_code => "7705") + AreaCode.create(:country => germany, :name => "Oberbaldingen", :area_code => "7706") + AreaCode.create(:country => germany, :name => "Bräunlingen", :area_code => "7707") + AreaCode.create(:country => germany, :name => "Geisingen-Leipferdingen", :area_code => "7708") + AreaCode.create(:country => germany, :name => "Wutach", :area_code => "7709") + AreaCode.create(:country => germany, :name => "Donaueschingen", :area_code => "771") + AreaCode.create(:country => germany, :name => "Schwenningen a Neckar", :area_code => "7720") + AreaCode.create(:country => germany, :name => "Villingen i Schwarzw", :area_code => "7721") + AreaCode.create(:country => germany, :name => "Triberg im Schwarzwald", :area_code => "7722") + AreaCode.create(:country => germany, :name => "Furtwangen im Schwarzwald", :area_code => "7723") + AreaCode.create(:country => germany, :name => "St Georgen im Schwarzwald", :area_code => "7724") + AreaCode.create(:country => germany, :name => "Königsfeld im Schwarzwald", :area_code => "7725") + AreaCode.create(:country => germany, :name => "Bad Dürrheim", :area_code => "7726") + AreaCode.create(:country => germany, :name => "Vöhrenbach", :area_code => "7727") + AreaCode.create(:country => germany, :name => "Niedereschach", :area_code => "7728") + AreaCode.create(:country => germany, :name => "Tennenbronn", :area_code => "7729") + AreaCode.create(:country => germany, :name => "Singen Hohentwiel", :area_code => "7731") + AreaCode.create(:country => germany, :name => "Radolfzell am Bodensee", :area_code => "7732") + AreaCode.create(:country => germany, :name => "Engen Hegau", :area_code => "7733") + AreaCode.create(:country => germany, :name => "Gailingen", :area_code => "7734") + AreaCode.create(:country => germany, :name => "Öhningen", :area_code => "7735") + AreaCode.create(:country => germany, :name => "Tengen", :area_code => "7736") + AreaCode.create(:country => germany, :name => "Steisslingen", :area_code => "7738") + AreaCode.create(:country => germany, :name => "Hilzingen", :area_code => "7739") + AreaCode.create(:country => germany, :name => "Tiengen Hochrhein", :area_code => "7741") + AreaCode.create(:country => germany, :name => "Klettgau", :area_code => "7742") + AreaCode.create(:country => germany, :name => "Ühlingen-Birkendorf", :area_code => "7743") + AreaCode.create(:country => germany, :name => "Stühlingen", :area_code => "7744") + AreaCode.create(:country => germany, :name => "Jestetten", :area_code => "7745") + AreaCode.create(:country => germany, :name => "Wutöschingen", :area_code => "7746") + AreaCode.create(:country => germany, :name => "Berau", :area_code => "7747") + AreaCode.create(:country => germany, :name => "Grafenhausen Hochschwarzw", :area_code => "7748") + AreaCode.create(:country => germany, :name => "Waldshut", :area_code => "7751") + AreaCode.create(:country => germany, :name => "Albbruck", :area_code => "7753") + AreaCode.create(:country => germany, :name => "Görwihl", :area_code => "7754") + AreaCode.create(:country => germany, :name => "Weilheim Kr Waldshut", :area_code => "7755") + AreaCode.create(:country => germany, :name => "Bad Säckingen", :area_code => "7761") + AreaCode.create(:country => germany, :name => "Wehr Baden", :area_code => "7762") + AreaCode.create(:country => germany, :name => "Murg", :area_code => "7763") + AreaCode.create(:country => germany, :name => "Herrischried", :area_code => "7764") + AreaCode.create(:country => germany, :name => "Rickenbach Hotzenw", :area_code => "7765") + AreaCode.create(:country => germany, :name => "Stockach", :area_code => "7771") + AreaCode.create(:country => germany, :name => "Bodman-Ludwigshafen", :area_code => "7773") + AreaCode.create(:country => germany, :name => "Eigeltingen", :area_code => "7774") + AreaCode.create(:country => germany, :name => "Mühlingen", :area_code => "7775") + AreaCode.create(:country => germany, :name => "Sauldorf", :area_code => "7777") + AreaCode.create(:country => germany, :name => "Oberkirch Baden", :area_code => "7802") + AreaCode.create(:country => germany, :name => "Gengenbach", :area_code => "7803") + AreaCode.create(:country => germany, :name => "Oppenau", :area_code => "7804") + AreaCode.create(:country => germany, :name => "Appenweier", :area_code => "7805") + AreaCode.create(:country => germany, :name => "Bad Peterstal-Griesbach", :area_code => "7806") + AreaCode.create(:country => germany, :name => "Neuried Ortenaukreis", :area_code => "7807") + AreaCode.create(:country => germany, :name => "Hohberg b Offenburg", :area_code => "7808") + AreaCode.create(:country => germany, :name => "Offenburg", :area_code => "781") + AreaCode.create(:country => germany, :name => "Lahr Schwarzwald", :area_code => "7821") + AreaCode.create(:country => germany, :name => "Ettenheim", :area_code => "7822") + AreaCode.create(:country => germany, :name => "Seelbach Schutter", :area_code => "7823") + AreaCode.create(:country => germany, :name => "Schwanau", :area_code => "7824") + AreaCode.create(:country => germany, :name => "Kippenheim", :area_code => "7825") + AreaCode.create(:country => germany, :name => "Schuttertal", :area_code => "7826") + AreaCode.create(:country => germany, :name => "Hausach", :area_code => "7831") + AreaCode.create(:country => germany, :name => "Haslach im Kinzigtal", :area_code => "7832") + AreaCode.create(:country => germany, :name => "Hornberg Schwarzwaldbahn", :area_code => "7833") + AreaCode.create(:country => germany, :name => "Wolfach", :area_code => "7834") + AreaCode.create(:country => germany, :name => "Zell am Harmersbach", :area_code => "7835") + AreaCode.create(:country => germany, :name => "Schiltach", :area_code => "7836") + AreaCode.create(:country => germany, :name => "Oberharmersbach", :area_code => "7837") + AreaCode.create(:country => germany, :name => "Nordrach", :area_code => "7838") + AreaCode.create(:country => germany, :name => "Schapbach", :area_code => "7839") + AreaCode.create(:country => germany, :name => "Achern", :area_code => "7841") + AreaCode.create(:country => germany, :name => "Kappelrodeck", :area_code => "7842") + AreaCode.create(:country => germany, :name => "Renchen", :area_code => "7843") + AreaCode.create(:country => germany, :name => "Rheinau", :area_code => "7844") + AreaCode.create(:country => germany, :name => "Kehl", :area_code => "7851") + AreaCode.create(:country => germany, :name => "Willstätt", :area_code => "7852") + AreaCode.create(:country => germany, :name => "Kehl-Bodersweier", :area_code => "7853") + AreaCode.create(:country => germany, :name => "Kehl-Goldscheuer", :area_code => "7854") + AreaCode.create(:country => germany, :name => "Mainhardt", :area_code => "7903") + AreaCode.create(:country => germany, :name => "Ilshofen", :area_code => "7904") + AreaCode.create(:country => germany, :name => "Langenburg", :area_code => "7905") + AreaCode.create(:country => germany, :name => "Braunsbach", :area_code => "7906") + AreaCode.create(:country => germany, :name => "Schwäbisch Hall-Sulzdorf", :area_code => "7907") + AreaCode.create(:country => germany, :name => "Schwäbisch Hall", :area_code => "791") + AreaCode.create(:country => germany, :name => "Boxberg Baden", :area_code => "7930") + AreaCode.create(:country => germany, :name => "Bad Mergentheim", :area_code => "7931") + AreaCode.create(:country => germany, :name => "Niederstetten Württ", :area_code => "7932") + AreaCode.create(:country => germany, :name => "Creglingen", :area_code => "7933") + AreaCode.create(:country => germany, :name => "Weikersheim", :area_code => "7934") + AreaCode.create(:country => germany, :name => "Schrozberg", :area_code => "7935") + AreaCode.create(:country => germany, :name => "Schrozberg-Bartenstein", :area_code => "7936") + AreaCode.create(:country => germany, :name => "Dörzbach", :area_code => "7937") + AreaCode.create(:country => germany, :name => "Mulfingen Jagst", :area_code => "7938") + AreaCode.create(:country => germany, :name => "Schrozberg-Spielbach", :area_code => "7939") + AreaCode.create(:country => germany, :name => "Künzelsau", :area_code => "7940") + AreaCode.create(:country => germany, :name => "Öhringen", :area_code => "7941") + AreaCode.create(:country => germany, :name => "Neuenstein Württ", :area_code => "7942") + AreaCode.create(:country => germany, :name => "Schöntal Jagst", :area_code => "7943") + AreaCode.create(:country => germany, :name => "Kupferzell", :area_code => "7944") + AreaCode.create(:country => germany, :name => "Wüstenrot", :area_code => "7945") + AreaCode.create(:country => germany, :name => "Bretzfeld", :area_code => "7946") + AreaCode.create(:country => germany, :name => "Forchtenberg", :area_code => "7947") + AreaCode.create(:country => germany, :name => "Öhringen-Ohrnberg", :area_code => "7948") + AreaCode.create(:country => germany, :name => "Pfedelbach-Untersteinbach", :area_code => "7949") + AreaCode.create(:country => germany, :name => "Schnelldorf", :area_code => "7950") + AreaCode.create(:country => germany, :name => "Crailsheim", :area_code => "7951") + AreaCode.create(:country => germany, :name => "Gerabronn", :area_code => "7952") + AreaCode.create(:country => germany, :name => "Blaufelden", :area_code => "7953") + AreaCode.create(:country => germany, :name => "Kirchberg an der Jagst", :area_code => "7954") + AreaCode.create(:country => germany, :name => "Wallhausen Württ", :area_code => "7955") + AreaCode.create(:country => germany, :name => "Kressberg", :area_code => "7957") + AreaCode.create(:country => germany, :name => "Rot Am See-Brettheim", :area_code => "7958") + AreaCode.create(:country => germany, :name => "Frankenhardt", :area_code => "7959") + AreaCode.create(:country => germany, :name => "Ellwangen Jagst", :area_code => "7961") + AreaCode.create(:country => germany, :name => "Fichtenau", :area_code => "7962") + AreaCode.create(:country => germany, :name => "Adelmannsfelden", :area_code => "7963") + AreaCode.create(:country => germany, :name => "Stödtlen", :area_code => "7964") + AreaCode.create(:country => germany, :name => "Ellwangen-Röhlingen", :area_code => "7965") + AreaCode.create(:country => germany, :name => "Unterschneidheim", :area_code => "7966") + AreaCode.create(:country => germany, :name => "Jagstzell", :area_code => "7967") + AreaCode.create(:country => germany, :name => "Gaildorf", :area_code => "7971") + AreaCode.create(:country => germany, :name => "Gschwend b Gaildorf", :area_code => "7972") + AreaCode.create(:country => germany, :name => "Obersontheim", :area_code => "7973") + AreaCode.create(:country => germany, :name => "Bühlerzell", :area_code => "7974") + AreaCode.create(:country => germany, :name => "Untergröningen", :area_code => "7975") + AreaCode.create(:country => germany, :name => "Sulzbach-Laufen", :area_code => "7976") + AreaCode.create(:country => germany, :name => "Oberrot b Gaildorf", :area_code => "7977") + AreaCode.create(:country => germany, :name => "Weyarn", :area_code => "8020") + AreaCode.create(:country => germany, :name => "Waakirchen", :area_code => "8021") + AreaCode.create(:country => germany, :name => "Tegernsee", :area_code => "8022") + AreaCode.create(:country => germany, :name => "Bayrischzell", :area_code => "8023") + AreaCode.create(:country => germany, :name => "Holzkirchen", :area_code => "8024") + AreaCode.create(:country => germany, :name => "Miesbach", :area_code => "8025") + AreaCode.create(:country => germany, :name => "Hausham", :area_code => "8026") + AreaCode.create(:country => germany, :name => "Dietramszell", :area_code => "8027") + AreaCode.create(:country => germany, :name => "Fischbachau", :area_code => "8028") + AreaCode.create(:country => germany, :name => "Kreuth b Tegernsee", :area_code => "8029") + AreaCode.create(:country => germany, :name => "Rosenheim Oberbay", :area_code => "8031") + AreaCode.create(:country => germany, :name => "Rohrdorf Kr Rosenheim", :area_code => "8032") + AreaCode.create(:country => germany, :name => "Oberaudorf", :area_code => "8033") + AreaCode.create(:country => germany, :name => "Brannenburg", :area_code => "8034") + AreaCode.create(:country => germany, :name => "Raubling", :area_code => "8035") + AreaCode.create(:country => germany, :name => "Stephanskirchen Simssee", :area_code => "8036") + AreaCode.create(:country => germany, :name => "Vogtareuth", :area_code => "8038") + AreaCode.create(:country => germany, :name => "Rott a Inn", :area_code => "8039") + AreaCode.create(:country => germany, :name => "Bad Tölz", :area_code => "8041") + AreaCode.create(:country => germany, :name => "Lenggries", :area_code => "8042") + AreaCode.create(:country => germany, :name => "Jachenau", :area_code => "8043") + AreaCode.create(:country => germany, :name => "Lenggries-Fall", :area_code => "8045") + AreaCode.create(:country => germany, :name => "Bad Heilbrunn", :area_code => "8046") + AreaCode.create(:country => germany, :name => "Prien a Chiemsee", :area_code => "8051") + AreaCode.create(:country => germany, :name => "Aschau i Chiemgau", :area_code => "8052") + AreaCode.create(:country => germany, :name => "Bad Endorf", :area_code => "8053") + AreaCode.create(:country => germany, :name => "Breitbrunn a Chiemsee", :area_code => "8054") + AreaCode.create(:country => germany, :name => "Halfing", :area_code => "8055") + AreaCode.create(:country => germany, :name => "Eggstätt", :area_code => "8056") + AreaCode.create(:country => germany, :name => "Aschau-Sachrang", :area_code => "8057") + AreaCode.create(:country => germany, :name => "Bad Aibling", :area_code => "8061") + AreaCode.create(:country => germany, :name => "Bruckmühl Mangfall", :area_code => "8062") + AreaCode.create(:country => germany, :name => "Feldkirchen-Westerham", :area_code => "8063") + AreaCode.create(:country => germany, :name => "Au b Bad Aibling", :area_code => "8064") + AreaCode.create(:country => germany, :name => "Tuntenhausen-Schönau", :area_code => "8065") + AreaCode.create(:country => germany, :name => "Bad Feilnbach", :area_code => "8066") + AreaCode.create(:country => germany, :name => "Tuntenhausen", :area_code => "8067") + AreaCode.create(:country => germany, :name => "Wasserburg a Inn", :area_code => "8071") + AreaCode.create(:country => germany, :name => "Haag i OB", :area_code => "8072") + AreaCode.create(:country => germany, :name => "Gars a Inn", :area_code => "8073") + AreaCode.create(:country => germany, :name => "Schnaitsee", :area_code => "8074") + AreaCode.create(:country => germany, :name => "Amerang", :area_code => "8075") + AreaCode.create(:country => germany, :name => "Pfaffing", :area_code => "8076") + AreaCode.create(:country => germany, :name => "Dorfen Stadt", :area_code => "8081") + AreaCode.create(:country => germany, :name => "Schwindegg", :area_code => "8082") + AreaCode.create(:country => germany, :name => "Isen", :area_code => "8083") + AreaCode.create(:country => germany, :name => "Taufkirchen Vils", :area_code => "8084") + AreaCode.create(:country => germany, :name => "Sankt Wolfgang", :area_code => "8085") + AreaCode.create(:country => germany, :name => "Buchbach Oberbay", :area_code => "8086") + AreaCode.create(:country => germany, :name => "Kirchseeon", :area_code => "8091") + AreaCode.create(:country => germany, :name => "Grafing b München", :area_code => "8092") + AreaCode.create(:country => germany, :name => "Glonn Kr Ebersberg", :area_code => "8093") + AreaCode.create(:country => germany, :name => "Steinhöring", :area_code => "8094") + AreaCode.create(:country => germany, :name => "Aying", :area_code => "8095") + AreaCode.create(:country => germany, :name => "Höhenkirchen-Siegertsbrunn", :area_code => "8102") + AreaCode.create(:country => germany, :name => "Sauerlach", :area_code => "8104") + AreaCode.create(:country => germany, :name => "Gilching", :area_code => "8105") + AreaCode.create(:country => germany, :name => "Vaterstetten", :area_code => "8106") + AreaCode.create(:country => germany, :name => "Hallbergmoos", :area_code => "811") + AreaCode.create(:country => germany, :name => "Markt Schwaben", :area_code => "8121") + AreaCode.create(:country => germany, :name => "Erding", :area_code => "8122") + AreaCode.create(:country => germany, :name => "Moosinning", :area_code => "8123") + AreaCode.create(:country => germany, :name => "Forstern Oberbay", :area_code => "8124") + AreaCode.create(:country => germany, :name => "Dachau", :area_code => "8131") + AreaCode.create(:country => germany, :name => "Haimhausen Oberbay", :area_code => "8133") + AreaCode.create(:country => germany, :name => "Odelzhausen", :area_code => "8134") + AreaCode.create(:country => germany, :name => "Sulzemoos", :area_code => "8135") + AreaCode.create(:country => germany, :name => "Markt Indersdorf", :area_code => "8136") + AreaCode.create(:country => germany, :name => "Petershausen", :area_code => "8137") + AreaCode.create(:country => germany, :name => "Schwabhausen b Dachau", :area_code => "8138") + AreaCode.create(:country => germany, :name => "Röhrmoos", :area_code => "8139") + AreaCode.create(:country => germany, :name => "Fürstenfeldbruck", :area_code => "8141") + AreaCode.create(:country => germany, :name => "Olching", :area_code => "8142") + AreaCode.create(:country => germany, :name => "Inning a Ammersee", :area_code => "8143") + AreaCode.create(:country => germany, :name => "Grafrath", :area_code => "8144") + AreaCode.create(:country => germany, :name => "Mammendorf", :area_code => "8145") + AreaCode.create(:country => germany, :name => "Moorenweis", :area_code => "8146") + AreaCode.create(:country => germany, :name => "Starnberg", :area_code => "8151") + AreaCode.create(:country => germany, :name => "Herrsching a Ammersee", :area_code => "8152") + AreaCode.create(:country => germany, :name => "Wessling", :area_code => "8153") + AreaCode.create(:country => germany, :name => "Feldafing", :area_code => "8157") + AreaCode.create(:country => germany, :name => "Tutzing", :area_code => "8158") + AreaCode.create(:country => germany, :name => "Freising", :area_code => "8161") + AreaCode.create(:country => germany, :name => "Neufahrn b Freising", :area_code => "8165") + AreaCode.create(:country => germany, :name => "Allershausen Oberbay", :area_code => "8166") + AreaCode.create(:country => germany, :name => "Zolling", :area_code => "8167") + AreaCode.create(:country => germany, :name => "Attenkirchen", :area_code => "8168") + AreaCode.create(:country => germany, :name => "Straßlach-Dingharting", :area_code => "8170") + AreaCode.create(:country => germany, :name => "Wolfratshausen", :area_code => "8171") + AreaCode.create(:country => germany, :name => "Egling b Wolfratshausen", :area_code => "8176") + AreaCode.create(:country => germany, :name => "Münsing Starnberger See", :area_code => "8177") + AreaCode.create(:country => germany, :name => "Icking", :area_code => "8178") + AreaCode.create(:country => germany, :name => "Eurasburg a d Loisach", :area_code => "8179") + AreaCode.create(:country => germany, :name => "Landsberg a Lech", :area_code => "8191") + AreaCode.create(:country => germany, :name => "Schondorf a Ammersee", :area_code => "8192") + AreaCode.create(:country => germany, :name => "Geltendorf", :area_code => "8193") + AreaCode.create(:country => germany, :name => "Vilgertshofen", :area_code => "8194") + AreaCode.create(:country => germany, :name => "Weil Kr Landsberg a Lech", :area_code => "8195") + AreaCode.create(:country => germany, :name => "Pürgen", :area_code => "8196") + AreaCode.create(:country => germany, :name => "Althegnenberg", :area_code => "8202") + AreaCode.create(:country => germany, :name => "Grossaitingen", :area_code => "8203") + AreaCode.create(:country => germany, :name => "Mickhausen", :area_code => "8204") + AreaCode.create(:country => germany, :name => "Dasing", :area_code => "8205") + AreaCode.create(:country => germany, :name => "Egling a d Paar", :area_code => "8206") + AreaCode.create(:country => germany, :name => "Affing", :area_code => "8207") + AreaCode.create(:country => germany, :name => "Eurasburg b Augsburg", :area_code => "8208") + AreaCode.create(:country => germany, :name => "Augsburg", :area_code => "821") + AreaCode.create(:country => germany, :name => "Günzburg", :area_code => "8221") + AreaCode.create(:country => germany, :name => "Burgau Schwab", :area_code => "8222") + AreaCode.create(:country => germany, :name => "Ichenhausen", :area_code => "8223") + AreaCode.create(:country => germany, :name => "Offingen Donau", :area_code => "8224") + AreaCode.create(:country => germany, :name => "Jettingen-Scheppach", :area_code => "8225") + AreaCode.create(:country => germany, :name => "Bibertal", :area_code => "8226") + AreaCode.create(:country => germany, :name => "Gablingen", :area_code => "8230") + AreaCode.create(:country => germany, :name => "Königsbrunn b Augsburg", :area_code => "8231") + AreaCode.create(:country => germany, :name => "Schwabmünchen", :area_code => "8232") + AreaCode.create(:country => germany, :name => "Kissing", :area_code => "8233") + AreaCode.create(:country => germany, :name => "Bobingen", :area_code => "8234") + AreaCode.create(:country => germany, :name => "Fischach", :area_code => "8236") + AreaCode.create(:country => germany, :name => "Aindling", :area_code => "8237") + AreaCode.create(:country => germany, :name => "Gessertshausen", :area_code => "8238") + AreaCode.create(:country => germany, :name => "Langenneufnach", :area_code => "8239") + AreaCode.create(:country => germany, :name => "Buchloe", :area_code => "8241") + AreaCode.create(:country => germany, :name => "Fuchstal", :area_code => "8243") + AreaCode.create(:country => germany, :name => "Türkheim Wertach", :area_code => "8245") + AreaCode.create(:country => germany, :name => "Waal", :area_code => "8246") + AreaCode.create(:country => germany, :name => "Bad Wörishofen", :area_code => "8247") + AreaCode.create(:country => germany, :name => "Lamerdingen", :area_code => "8248") + AreaCode.create(:country => germany, :name => "Ettringen Wertach", :area_code => "8249") + AreaCode.create(:country => germany, :name => "Hilgertshausen-Tandern", :area_code => "8250") + AreaCode.create(:country => germany, :name => "Aichach", :area_code => "8251") + AreaCode.create(:country => germany, :name => "Schrobenhausen", :area_code => "8252") + AreaCode.create(:country => germany, :name => "Pöttmes", :area_code => "8253") + AreaCode.create(:country => germany, :name => "Altomünster", :area_code => "8254") + AreaCode.create(:country => germany, :name => "Inchenhofen", :area_code => "8257") + AreaCode.create(:country => germany, :name => "Sielenbach", :area_code => "8258") + AreaCode.create(:country => germany, :name => "Schiltberg", :area_code => "8259") + AreaCode.create(:country => germany, :name => "Mindelheim", :area_code => "8261") + AreaCode.create(:country => germany, :name => "Mittelneufnach", :area_code => "8262") + AreaCode.create(:country => germany, :name => "Breitenbrunn Schwab", :area_code => "8263") + AreaCode.create(:country => germany, :name => "Pfaffenhausen Schwab", :area_code => "8265") + AreaCode.create(:country => germany, :name => "Kirchheim i Schw", :area_code => "8266") + AreaCode.create(:country => germany, :name => "Dirlewang", :area_code => "8267") + AreaCode.create(:country => germany, :name => "Tussenhausen", :area_code => "8268") + AreaCode.create(:country => germany, :name => "Unteregg b Mindelheim", :area_code => "8269") + AreaCode.create(:country => germany, :name => "Meitingen", :area_code => "8271") + AreaCode.create(:country => germany, :name => "Wertingen", :area_code => "8272") + AreaCode.create(:country => germany, :name => "Nordendorf", :area_code => "8273") + AreaCode.create(:country => germany, :name => "Buttenwiesen", :area_code => "8274") + AreaCode.create(:country => germany, :name => "Baar Schwaben", :area_code => "8276") + AreaCode.create(:country => germany, :name => "Thannhausen Schwab", :area_code => "8281") + AreaCode.create(:country => germany, :name => "Krumbach Schwaben", :area_code => "8282") + AreaCode.create(:country => germany, :name => "Neuburg a d Kammel", :area_code => "8283") + AreaCode.create(:country => germany, :name => "Ziemetshausen", :area_code => "8284") + AreaCode.create(:country => germany, :name => "Burtenbach", :area_code => "8285") + AreaCode.create(:country => germany, :name => "Zusmarshausen", :area_code => "8291") + AreaCode.create(:country => germany, :name => "Dinkelscherben", :area_code => "8292") + AreaCode.create(:country => germany, :name => "Welden b Augsburg", :area_code => "8293") + AreaCode.create(:country => germany, :name => "Horgau", :area_code => "8294") + AreaCode.create(:country => germany, :name => "Altenmünster Schwab", :area_code => "8295") + AreaCode.create(:country => germany, :name => "Villenbach", :area_code => "8296") + AreaCode.create(:country => germany, :name => "Görisried", :area_code => "8302") + AreaCode.create(:country => germany, :name => "Waltenhofen", :area_code => "8303") + AreaCode.create(:country => germany, :name => "Wildpoldsried", :area_code => "8304") + AreaCode.create(:country => germany, :name => "Ronsberg", :area_code => "8306") + AreaCode.create(:country => germany, :name => "Kempten Allgäu", :area_code => "831") + AreaCode.create(:country => germany, :name => "Missen-Wilhams", :area_code => "8320") + AreaCode.create(:country => germany, :name => "Sonthofen", :area_code => "8321") + AreaCode.create(:country => germany, :name => "Oberstdorf", :area_code => "8322") + AreaCode.create(:country => germany, :name => "Immenstadt i Allgäu", :area_code => "8323") + AreaCode.create(:country => germany, :name => "Hindelang", :area_code => "8324") + AreaCode.create(:country => germany, :name => "Oberstaufen-Thalkirchdorf", :area_code => "8325") + AreaCode.create(:country => germany, :name => "Fischen i Allgäu", :area_code => "8326") + AreaCode.create(:country => germany, :name => "Rettenberg", :area_code => "8327") + AreaCode.create(:country => germany, :name => "Balderschwang", :area_code => "8328") + AreaCode.create(:country => germany, :name => "Riezlern (Österreich)", :area_code => "8329") + AreaCode.create(:country => germany, :name => "Legau", :area_code => "8330") + AreaCode.create(:country => germany, :name => "Memmingen", :area_code => "8331") + AreaCode.create(:country => germany, :name => "Ottobeuren", :area_code => "8332") + AreaCode.create(:country => germany, :name => "Babenhausen Schwab", :area_code => "8333") + AreaCode.create(:country => germany, :name => "Bad Grönenbach", :area_code => "8334") + AreaCode.create(:country => germany, :name => "Fellheim", :area_code => "8335") + AreaCode.create(:country => germany, :name => "Erkheim", :area_code => "8336") + AreaCode.create(:country => germany, :name => "Altenstadt Iller", :area_code => "8337") + AreaCode.create(:country => germany, :name => "Böhen", :area_code => "8338") + AreaCode.create(:country => germany, :name => "Baisweil", :area_code => "8340") + AreaCode.create(:country => germany, :name => "Kaufbeuren", :area_code => "8341") + AreaCode.create(:country => germany, :name => "Marktoberdorf", :area_code => "8342") + AreaCode.create(:country => germany, :name => "Aitrang", :area_code => "8343") + AreaCode.create(:country => germany, :name => "Westendorf b Kaufbeuren", :area_code => "8344") + AreaCode.create(:country => germany, :name => "Stöttwang", :area_code => "8345") + AreaCode.create(:country => germany, :name => "Pforzen", :area_code => "8346") + AreaCode.create(:country => germany, :name => "Friesenried", :area_code => "8347") + AreaCode.create(:country => germany, :name => "Bidingen", :area_code => "8348") + AreaCode.create(:country => germany, :name => "Stötten a Auerberg", :area_code => "8349") + AreaCode.create(:country => germany, :name => "Nesselwang", :area_code => "8361") + AreaCode.create(:country => germany, :name => "Füssen", :area_code => "8362") + AreaCode.create(:country => germany, :name => "Pfronten", :area_code => "8363") + AreaCode.create(:country => germany, :name => "Seeg", :area_code => "8364") + AreaCode.create(:country => germany, :name => "Wertach", :area_code => "8365") + AreaCode.create(:country => germany, :name => "Oy-Mittelberg", :area_code => "8366") + AreaCode.create(:country => germany, :name => "Roßhaupten Forggensee", :area_code => "8367") + AreaCode.create(:country => germany, :name => "Halblech", :area_code => "8368") + AreaCode.create(:country => germany, :name => "Rückholz", :area_code => "8369") + AreaCode.create(:country => germany, :name => "Wiggensbach", :area_code => "8370") + AreaCode.create(:country => germany, :name => "Obergünzburg", :area_code => "8372") + AreaCode.create(:country => germany, :name => "Altusried", :area_code => "8373") + AreaCode.create(:country => germany, :name => "Dietmannsried", :area_code => "8374") + AreaCode.create(:country => germany, :name => "Weitnau", :area_code => "8375") + AreaCode.create(:country => germany, :name => "Sulzberg Allgäu", :area_code => "8376") + AreaCode.create(:country => germany, :name => "Unterthingau", :area_code => "8377") + AreaCode.create(:country => germany, :name => "Buchenberg b Kempten", :area_code => "8378") + AreaCode.create(:country => germany, :name => "Waltenhofen-Oberdorf", :area_code => "8379") + AreaCode.create(:country => germany, :name => "Achberg", :area_code => "8380") + AreaCode.create(:country => germany, :name => "Lindenberg i Allgäu", :area_code => "8381") + AreaCode.create(:country => germany, :name => "Lindau Bodensee", :area_code => "8382") + AreaCode.create(:country => germany, :name => "Grünenbach Allgäu", :area_code => "8383") + AreaCode.create(:country => germany, :name => "Röthenbach Allgäu", :area_code => "8384") + AreaCode.create(:country => germany, :name => "Hergatz", :area_code => "8385") + AreaCode.create(:country => germany, :name => "Oberstaufen", :area_code => "8386") + AreaCode.create(:country => germany, :name => "Weiler-Simmerberg", :area_code => "8387") + AreaCode.create(:country => germany, :name => "Hergensweiler", :area_code => "8388") + AreaCode.create(:country => germany, :name => "Weissensberg", :area_code => "8389") + AreaCode.create(:country => germany, :name => "Markt Rettenbach", :area_code => "8392") + AreaCode.create(:country => germany, :name => "Holzgünz", :area_code => "8393") + AreaCode.create(:country => germany, :name => "Lautrach", :area_code => "8394") + AreaCode.create(:country => germany, :name => "Tannheim Württ", :area_code => "8395") + AreaCode.create(:country => germany, :name => "Münchsmünster", :area_code => "8402") + AreaCode.create(:country => germany, :name => "Pförring", :area_code => "8403") + AreaCode.create(:country => germany, :name => "Oberdolling", :area_code => "8404") + AreaCode.create(:country => germany, :name => "Stammham b Ingolstadt", :area_code => "8405") + AreaCode.create(:country => germany, :name => "Böhmfeld", :area_code => "8406") + AreaCode.create(:country => germany, :name => "Grossmehring", :area_code => "8407") + AreaCode.create(:country => germany, :name => "Ingolstadt Donau", :area_code => "841") + AreaCode.create(:country => germany, :name => "Eichstätt Bay", :area_code => "8421") + AreaCode.create(:country => germany, :name => "Dollnstein", :area_code => "8422") + AreaCode.create(:country => germany, :name => "Titting", :area_code => "8423") + AreaCode.create(:country => germany, :name => "Nassenfels", :area_code => "8424") + AreaCode.create(:country => germany, :name => "Walting Kr Eichstätt", :area_code => "8426") + AreaCode.create(:country => germany, :name => "Wellheim", :area_code => "8427") + AreaCode.create(:country => germany, :name => "Neuburg a d Donau", :area_code => "8431") + AreaCode.create(:country => germany, :name => "Burgheim", :area_code => "8432") + AreaCode.create(:country => germany, :name => "Königsmoos", :area_code => "8433") + AreaCode.create(:country => germany, :name => "Rennertshofen", :area_code => "8434") + AreaCode.create(:country => germany, :name => "Ehekirchen", :area_code => "8435") + AreaCode.create(:country => germany, :name => "Pfaffenhofen a d Ilm", :area_code => "8441") + AreaCode.create(:country => germany, :name => "Wolnzach", :area_code => "8442") + AreaCode.create(:country => germany, :name => "Hohenwart Paar", :area_code => "8443") + AreaCode.create(:country => germany, :name => "Schweitenkirchen", :area_code => "8444") + AreaCode.create(:country => germany, :name => "Gerolsbach", :area_code => "8445") + AreaCode.create(:country => germany, :name => "Pörnbach", :area_code => "8446") + AreaCode.create(:country => germany, :name => "Ingolstadt-Zuchering", :area_code => "8450") + AreaCode.create(:country => germany, :name => "Geisenfeld", :area_code => "8452") + AreaCode.create(:country => germany, :name => "Reichertshofen Oberbay", :area_code => "8453") + AreaCode.create(:country => germany, :name => "Karlshuld", :area_code => "8454") + AreaCode.create(:country => germany, :name => "Lenting", :area_code => "8456") + AreaCode.create(:country => germany, :name => "Vohburg a d Donau", :area_code => "8457") + AreaCode.create(:country => germany, :name => "Gaimersheim", :area_code => "8458") + AreaCode.create(:country => germany, :name => "Manching", :area_code => "8459") + AreaCode.create(:country => germany, :name => "Berching-Holnstein", :area_code => "8460") + AreaCode.create(:country => germany, :name => "Beilngries", :area_code => "8461") + AreaCode.create(:country => germany, :name => "Berching", :area_code => "8462") + AreaCode.create(:country => germany, :name => "Greding", :area_code => "8463") + AreaCode.create(:country => germany, :name => "Dietfurt a d Altmühl", :area_code => "8464") + AreaCode.create(:country => germany, :name => "Kipfenberg", :area_code => "8465") + AreaCode.create(:country => germany, :name => "Denkendorf Oberbay", :area_code => "8466") + AreaCode.create(:country => germany, :name => "Kinding", :area_code => "8467") + AreaCode.create(:country => germany, :name => "Altmannstein-Pondorf", :area_code => "8468") + AreaCode.create(:country => germany, :name => "Freystadt-Burggriesbach", :area_code => "8469") + AreaCode.create(:country => germany, :name => "Thyrnau", :area_code => "8501") + AreaCode.create(:country => germany, :name => "Fürstenzell", :area_code => "8502") + AreaCode.create(:country => germany, :name => "Neuhaus a Inn", :area_code => "8503") + AreaCode.create(:country => germany, :name => "Tittling", :area_code => "8504") + AreaCode.create(:country => germany, :name => "Hutthurm", :area_code => "8505") + AreaCode.create(:country => germany, :name => "Bad Höhenstadt", :area_code => "8506") + AreaCode.create(:country => germany, :name => "Neuburg a Inn", :area_code => "8507") + AreaCode.create(:country => germany, :name => "Ruderting", :area_code => "8509") + AreaCode.create(:country => germany, :name => "Passau", :area_code => "851") + AreaCode.create(:country => germany, :name => "Pocking", :area_code => "8531") + AreaCode.create(:country => germany, :name => "Griesbach i Rottal", :area_code => "8532") + AreaCode.create(:country => germany, :name => "Rotthalmünster", :area_code => "8533") + AreaCode.create(:country => germany, :name => "Tettenweis", :area_code => "8534") + AreaCode.create(:country => germany, :name => "Haarbach", :area_code => "8535") + AreaCode.create(:country => germany, :name => "Kößlarn", :area_code => "8536") + AreaCode.create(:country => germany, :name => "Bad Füssing-Aigen", :area_code => "8537") + AreaCode.create(:country => germany, :name => "Pocking-Hartkirchen", :area_code => "8538") + AreaCode.create(:country => germany, :name => "Vilshofen Niederbay", :area_code => "8541") + AreaCode.create(:country => germany, :name => "Ortenburg", :area_code => "8542") + AreaCode.create(:country => germany, :name => "Aidenbach", :area_code => "8543") + AreaCode.create(:country => germany, :name => "Eging a See", :area_code => "8544") + AreaCode.create(:country => germany, :name => "Hofkirchen Bay", :area_code => "8545") + AreaCode.create(:country => germany, :name => "Windorf-Otterskirchen", :area_code => "8546") + AreaCode.create(:country => germany, :name => "Osterhofen-Gergweis", :area_code => "8547") + AreaCode.create(:country => germany, :name => "Vilshofen-Sandbach", :area_code => "8548") + AreaCode.create(:country => germany, :name => "Vilshofen-Pleinting", :area_code => "8549") + AreaCode.create(:country => germany, :name => "Philippsreut", :area_code => "8550") + AreaCode.create(:country => germany, :name => "Freyung", :area_code => "8551") + AreaCode.create(:country => germany, :name => "Grafenau Niederbay", :area_code => "8552") + AreaCode.create(:country => germany, :name => "Spiegelau", :area_code => "8553") + AreaCode.create(:country => germany, :name => "Schönberg Niederbay", :area_code => "8554") + AreaCode.create(:country => germany, :name => "Perlesreut", :area_code => "8555") + AreaCode.create(:country => germany, :name => "Haidmühle", :area_code => "8556") + AreaCode.create(:country => germany, :name => "Mauth", :area_code => "8557") + AreaCode.create(:country => germany, :name => "Hohenau Niederbay", :area_code => "8558") + AreaCode.create(:country => germany, :name => "Pfarrkirchen Niederbay", :area_code => "8561") + AreaCode.create(:country => germany, :name => "Triftern", :area_code => "8562") + AreaCode.create(:country => germany, :name => "Bad Birnbach Rottal", :area_code => "8563") + AreaCode.create(:country => germany, :name => "Johanniskirchen", :area_code => "8564") + AreaCode.create(:country => germany, :name => "Dietersburg-Baumgarten", :area_code => "8565") + AreaCode.create(:country => germany, :name => "Simbach a Inn", :area_code => "8571") + AreaCode.create(:country => germany, :name => "Tann Niederbay", :area_code => "8572") + AreaCode.create(:country => germany, :name => "Ering", :area_code => "8573") + AreaCode.create(:country => germany, :name => "Wittibreut", :area_code => "8574") + AreaCode.create(:country => germany, :name => "Waldkirchen Niederbay", :area_code => "8581") + AreaCode.create(:country => germany, :name => "Röhrnbach", :area_code => "8582") + AreaCode.create(:country => germany, :name => "Neureichenau", :area_code => "8583") + AreaCode.create(:country => germany, :name => "Breitenberg Niederbay", :area_code => "8584") + AreaCode.create(:country => germany, :name => "Grainet", :area_code => "8585") + AreaCode.create(:country => germany, :name => "Hauzenberg", :area_code => "8586") + AreaCode.create(:country => germany, :name => "Obernzell", :area_code => "8591") + AreaCode.create(:country => germany, :name => "Wegscheid Niederbay", :area_code => "8592") + AreaCode.create(:country => germany, :name => "Untergriesbach", :area_code => "8593") + AreaCode.create(:country => germany, :name => "Traunstein", :area_code => "861") + AreaCode.create(:country => germany, :name => "Trostberg", :area_code => "8621") + AreaCode.create(:country => germany, :name => "Tacherting- Peterskirchen", :area_code => "8622") + AreaCode.create(:country => germany, :name => "Kirchweidach", :area_code => "8623") + AreaCode.create(:country => germany, :name => "Obing", :area_code => "8624") + AreaCode.create(:country => germany, :name => "Kienberg Oberbay", :area_code => "8628") + AreaCode.create(:country => germany, :name => "Palling", :area_code => "8629") + AreaCode.create(:country => germany, :name => "Oberneukirchen", :area_code => "8630") + AreaCode.create(:country => germany, :name => "Mühldorf a Inn", :area_code => "8631") + AreaCode.create(:country => germany, :name => "Tüßling", :area_code => "8633") + AreaCode.create(:country => germany, :name => "Garching a d Alz", :area_code => "8634") + AreaCode.create(:country => germany, :name => "Pleiskirchen", :area_code => "8635") + AreaCode.create(:country => germany, :name => "Ampfing", :area_code => "8636") + AreaCode.create(:country => germany, :name => "Lohkirchen", :area_code => "8637") + AreaCode.create(:country => germany, :name => "Waldkraiburg", :area_code => "8638") + AreaCode.create(:country => germany, :name => "Neumarkt-Sankt Veit", :area_code => "8639") + AreaCode.create(:country => germany, :name => "Reit Im Winkl", :area_code => "8640") + AreaCode.create(:country => germany, :name => "Grassau Kr Traunstein", :area_code => "8641") + AreaCode.create(:country => germany, :name => "Übersee", :area_code => "8642") + AreaCode.create(:country => germany, :name => "Schleching", :area_code => "8649") + AreaCode.create(:country => germany, :name => "Marktschellenberg", :area_code => "8650") + AreaCode.create(:country => germany, :name => "Bad Reichenhall", :area_code => "8651") + AreaCode.create(:country => germany, :name => "Berchtesgaden", :area_code => "8652") + AreaCode.create(:country => germany, :name => "Freilassing", :area_code => "8654") + AreaCode.create(:country => germany, :name => "Anger", :area_code => "8656") + AreaCode.create(:country => germany, :name => "Ramsau b Berchtesgaden", :area_code => "8657") + AreaCode.create(:country => germany, :name => "Grabenstätt Chiemsee", :area_code => "8661") + AreaCode.create(:country => germany, :name => "Siegsdorf Kr Traunstein", :area_code => "8662") + AreaCode.create(:country => germany, :name => "Ruhpolding", :area_code => "8663") + AreaCode.create(:country => germany, :name => "Chieming", :area_code => "8664") + AreaCode.create(:country => germany, :name => "Inzell", :area_code => "8665") + AreaCode.create(:country => germany, :name => "Teisendorf", :area_code => "8666") + AreaCode.create(:country => germany, :name => "Seeon-Seebruck", :area_code => "8667") + AreaCode.create(:country => germany, :name => "Traunreut", :area_code => "8669") + AreaCode.create(:country => germany, :name => "Reischach Kr Altötting", :area_code => "8670") + AreaCode.create(:country => germany, :name => "Altötting", :area_code => "8671") + AreaCode.create(:country => germany, :name => "Burghausen Salzach", :area_code => "8677") + AreaCode.create(:country => germany, :name => "Marktl", :area_code => "8678") + AreaCode.create(:country => germany, :name => "Burgkirchen a d Alz", :area_code => "8679") + AreaCode.create(:country => germany, :name => "Waging a See", :area_code => "8681") + AreaCode.create(:country => germany, :name => "Laufen Salzach", :area_code => "8682") + AreaCode.create(:country => germany, :name => "Tittmoning", :area_code => "8683") + AreaCode.create(:country => germany, :name => "Fridolfing", :area_code => "8684") + AreaCode.create(:country => germany, :name => "Kirchanschöring", :area_code => "8685") + AreaCode.create(:country => germany, :name => "Petting", :area_code => "8686") + AreaCode.create(:country => germany, :name => "Taching-Tengling", :area_code => "8687") + AreaCode.create(:country => germany, :name => "Wörth a d Isar", :area_code => "8702") + AreaCode.create(:country => germany, :name => "Essenbach", :area_code => "8703") + AreaCode.create(:country => germany, :name => "Altdorf-Pfettrach", :area_code => "8704") + AreaCode.create(:country => germany, :name => "Altfraunhofen", :area_code => "8705") + AreaCode.create(:country => germany, :name => "Vilsheim", :area_code => "8706") + AreaCode.create(:country => germany, :name => "Adlkofen", :area_code => "8707") + AreaCode.create(:country => germany, :name => "Weihmichl-Unterneuhausen", :area_code => "8708") + AreaCode.create(:country => germany, :name => "Eching Niederbay", :area_code => "8709") + AreaCode.create(:country => germany, :name => "Landshut", :area_code => "871") + AreaCode.create(:country => germany, :name => "Eggenfelden", :area_code => "8721") + AreaCode.create(:country => germany, :name => "Gangkofen", :area_code => "8722") + AreaCode.create(:country => germany, :name => "Arnstorf", :area_code => "8723") + AreaCode.create(:country => germany, :name => "Massing", :area_code => "8724") + AreaCode.create(:country => germany, :name => "Wurmannsquick", :area_code => "8725") + AreaCode.create(:country => germany, :name => "Schönau Niederbay", :area_code => "8726") + AreaCode.create(:country => germany, :name => "Falkenberg Niederbay", :area_code => "8727") + AreaCode.create(:country => germany, :name => "Geratskirchen", :area_code => "8728") + AreaCode.create(:country => germany, :name => "Dingolfing", :area_code => "8731") + AreaCode.create(:country => germany, :name => "Frontenhausen", :area_code => "8732") + AreaCode.create(:country => germany, :name => "Mengkofen", :area_code => "8733") + AreaCode.create(:country => germany, :name => "Reisbach Niederbay", :area_code => "8734") + AreaCode.create(:country => germany, :name => "Gangkofen-Kollbach", :area_code => "8735") + AreaCode.create(:country => germany, :name => "Vilsbiburg", :area_code => "8741") + AreaCode.create(:country => germany, :name => "Velden Vils", :area_code => "8742") + AreaCode.create(:country => germany, :name => "Geisenhausen", :area_code => "8743") + AreaCode.create(:country => germany, :name => "Gerzen", :area_code => "8744") + AreaCode.create(:country => germany, :name => "Bodenkirchen", :area_code => "8745") + AreaCode.create(:country => germany, :name => "Mainburg", :area_code => "8751") + AreaCode.create(:country => germany, :name => "Au i d Hallertau", :area_code => "8752") + AreaCode.create(:country => germany, :name => "Elsendorf Niederbay", :area_code => "8753") + AreaCode.create(:country => germany, :name => "Volkenschwand", :area_code => "8754") + AreaCode.create(:country => germany, :name => "Nandlstadt", :area_code => "8756") + AreaCode.create(:country => germany, :name => "Moosburg a d Isar", :area_code => "8761") + AreaCode.create(:country => germany, :name => "Wartenberg Oberbay", :area_code => "8762") + AreaCode.create(:country => germany, :name => "Mauern Kr Freising", :area_code => "8764") + AreaCode.create(:country => germany, :name => "Bruckberg Niederbay", :area_code => "8765") + AreaCode.create(:country => germany, :name => "Gammelsdorf", :area_code => "8766") + AreaCode.create(:country => germany, :name => "Ergoldsbach", :area_code => "8771") + AreaCode.create(:country => germany, :name => "Mallersdorf-Pfaffenberg", :area_code => "8772") + AreaCode.create(:country => germany, :name => "Neufahrn i NB", :area_code => "8773") + AreaCode.create(:country => germany, :name => "Bayerbach b Ergoldsbach", :area_code => "8774") + AreaCode.create(:country => germany, :name => "Rottenburg a d Laaber", :area_code => "8781") + AreaCode.create(:country => germany, :name => "Pfeffenhausen", :area_code => "8782") + AreaCode.create(:country => germany, :name => "Rohr i NB", :area_code => "8783") + AreaCode.create(:country => germany, :name => "Hohenthann", :area_code => "8784") + AreaCode.create(:country => germany, :name => "Rottenburg-Oberroning", :area_code => "8785") + AreaCode.create(:country => germany, :name => "Seeshaupt", :area_code => "8801") + AreaCode.create(:country => germany, :name => "Huglfing", :area_code => "8802") + AreaCode.create(:country => germany, :name => "Peissenberg", :area_code => "8803") + AreaCode.create(:country => germany, :name => "Hohenpeissenberg", :area_code => "8805") + AreaCode.create(:country => germany, :name => "Utting a Ammersee", :area_code => "8806") + AreaCode.create(:country => germany, :name => "Dießen a Ammersee", :area_code => "8807") + AreaCode.create(:country => germany, :name => "Pähl", :area_code => "8808") + AreaCode.create(:country => germany, :name => "Wessobrunn", :area_code => "8809") + AreaCode.create(:country => germany, :name => "Weilheim i OB", :area_code => "881") + AreaCode.create(:country => germany, :name => "Garmisch-Partenkirchen", :area_code => "8821") + AreaCode.create(:country => germany, :name => "Oberammergau", :area_code => "8822") + AreaCode.create(:country => germany, :name => "Mittenwald", :area_code => "8823") + AreaCode.create(:country => germany, :name => "Oberau Loisach", :area_code => "8824") + AreaCode.create(:country => germany, :name => "Krün", :area_code => "8825") + AreaCode.create(:country => germany, :name => "Murnau a Staffelsee", :area_code => "8841") + AreaCode.create(:country => germany, :name => "Bad Kohlgrub", :area_code => "8845") + AreaCode.create(:country => germany, :name => "Uffing a Staffelsee", :area_code => "8846") + AreaCode.create(:country => germany, :name => "Obersöchering", :area_code => "8847") + AreaCode.create(:country => germany, :name => "Kochel a See", :area_code => "8851") + AreaCode.create(:country => germany, :name => "Penzberg", :area_code => "8856") + AreaCode.create(:country => germany, :name => "Benediktbeuern", :area_code => "8857") + AreaCode.create(:country => germany, :name => "Kochel-Walchensee", :area_code => "8858") + AreaCode.create(:country => germany, :name => "Bernbeuren", :area_code => "8860") + AreaCode.create(:country => germany, :name => "Schongau", :area_code => "8861") + AreaCode.create(:country => germany, :name => "Steingaden Oberbay", :area_code => "8862") + AreaCode.create(:country => germany, :name => "Rottenbuch Oberbay", :area_code => "8867") + AreaCode.create(:country => germany, :name => "Schwabsoien", :area_code => "8868") + AreaCode.create(:country => germany, :name => "Kinsau", :area_code => "8869") + AreaCode.create(:country => germany, :name => "München", :area_code => "89") + AreaCode.create(:country => germany, :name => "Donauwörth", :area_code => "906") + AreaCode.create(:country => germany, :name => "Tapfheim", :area_code => "9070") + AreaCode.create(:country => germany, :name => "Dillingen a d Donau", :area_code => "9071") + AreaCode.create(:country => germany, :name => "Lauingen Donau", :area_code => "9072") + AreaCode.create(:country => germany, :name => "Gundelfingen a d Donau", :area_code => "9073") + AreaCode.create(:country => germany, :name => "Höchstädt a d Donau", :area_code => "9074") + AreaCode.create(:country => germany, :name => "Glött", :area_code => "9075") + AreaCode.create(:country => germany, :name => "Wittislingen", :area_code => "9076") + AreaCode.create(:country => germany, :name => "Bachhagel", :area_code => "9077") + AreaCode.create(:country => germany, :name => "Mertingen", :area_code => "9078") + AreaCode.create(:country => germany, :name => "Harburg Schwaben", :area_code => "9080") + AreaCode.create(:country => germany, :name => "Nördlingen", :area_code => "9081") + AreaCode.create(:country => germany, :name => "Oettingen i Bay", :area_code => "9082") + AreaCode.create(:country => germany, :name => "Möttingen", :area_code => "9083") + AreaCode.create(:country => germany, :name => "Bissingen Schwab", :area_code => "9084") + AreaCode.create(:country => germany, :name => "Alerheim", :area_code => "9085") + AreaCode.create(:country => germany, :name => "Fremdingen", :area_code => "9086") + AreaCode.create(:country => germany, :name => "Marktoffingen", :area_code => "9087") + AreaCode.create(:country => germany, :name => "Mönchsdeggingen", :area_code => "9088") + AreaCode.create(:country => germany, :name => "Bissingen-Unterringingen", :area_code => "9089") + AreaCode.create(:country => germany, :name => "Rain Lech", :area_code => "9090") + AreaCode.create(:country => germany, :name => "Monheim Schwab", :area_code => "9091") + AreaCode.create(:country => germany, :name => "Wemding", :area_code => "9092") + AreaCode.create(:country => germany, :name => "Polsingen", :area_code => "9093") + AreaCode.create(:country => germany, :name => "Tagmersheim", :area_code => "9094") + AreaCode.create(:country => germany, :name => "Marxheim", :area_code => "9097") + AreaCode.create(:country => germany, :name => "Kaisheim", :area_code => "9099") + AreaCode.create(:country => germany, :name => "Langenzenn", :area_code => "9101") + AreaCode.create(:country => germany, :name => "Wilhermsdorf", :area_code => "9102") + AreaCode.create(:country => germany, :name => "Cadolzburg", :area_code => "9103") + AreaCode.create(:country => germany, :name => "Emskirchen", :area_code => "9104") + AreaCode.create(:country => germany, :name => "Grosshabersdorf", :area_code => "9105") + AreaCode.create(:country => germany, :name => "Markt Erlbach", :area_code => "9106") + AreaCode.create(:country => germany, :name => "Trautskirchen", :area_code => "9107") + AreaCode.create(:country => germany, :name => "Nürnberg", :area_code => "911") + AreaCode.create(:country => germany, :name => "Leinburg", :area_code => "9120") + AreaCode.create(:country => germany, :name => "Schwabach", :area_code => "9122") + AreaCode.create(:country => germany, :name => "Lauf a d Pegnitz", :area_code => "9123") + AreaCode.create(:country => germany, :name => "Eckental", :area_code => "9126") + AreaCode.create(:country => germany, :name => "Rosstal Mittelfr", :area_code => "9127") + AreaCode.create(:country => germany, :name => "Feucht", :area_code => "9128") + AreaCode.create(:country => germany, :name => "Wendelstein", :area_code => "9129") + AreaCode.create(:country => germany, :name => "Erlangen", :area_code => "9131") + AreaCode.create(:country => germany, :name => "Herzogenaurach", :area_code => "9132") + AreaCode.create(:country => germany, :name => "Baiersdorf Mittelfr", :area_code => "9133") + AreaCode.create(:country => germany, :name => "Neunkirchen a Brand", :area_code => "9134") + AreaCode.create(:country => germany, :name => "Heßdorf Mittelfr", :area_code => "9135") + AreaCode.create(:country => germany, :name => "Weißenburg i Bay", :area_code => "9141") + AreaCode.create(:country => germany, :name => "Treuchtlingen", :area_code => "9142") + AreaCode.create(:country => germany, :name => "Pappenheim Mittelfr", :area_code => "9143") + AreaCode.create(:country => germany, :name => "Pleinfeld", :area_code => "9144") + AreaCode.create(:country => germany, :name => "Solnhofen", :area_code => "9145") + AreaCode.create(:country => germany, :name => "Markt Berolzheim", :area_code => "9146") + AreaCode.create(:country => germany, :name => "Nennslingen", :area_code => "9147") + AreaCode.create(:country => germany, :name => "Ettenstatt", :area_code => "9148") + AreaCode.create(:country => germany, :name => "Weissenburg-Suffersheim", :area_code => "9149") + AreaCode.create(:country => germany, :name => "Hersbruck", :area_code => "9151") + AreaCode.create(:country => germany, :name => "Hartenstein Mittelfr", :area_code => "9152") + AreaCode.create(:country => germany, :name => "Schnaittach", :area_code => "9153") + AreaCode.create(:country => germany, :name => "Pommelsbrunn", :area_code => "9154") + AreaCode.create(:country => germany, :name => "Simmelsdorf", :area_code => "9155") + AreaCode.create(:country => germany, :name => "Neuhaus a d Pegnitz", :area_code => "9156") + AreaCode.create(:country => germany, :name => "Alfeld Mittelfr", :area_code => "9157") + AreaCode.create(:country => germany, :name => "Offenhausen Mittelfr", :area_code => "9158") + AreaCode.create(:country => germany, :name => "Neustadt a d Aisch", :area_code => "9161") + AreaCode.create(:country => germany, :name => "Scheinfeld", :area_code => "9162") + AreaCode.create(:country => germany, :name => "Dachsbach", :area_code => "9163") + AreaCode.create(:country => germany, :name => "Langenfeld Mittelfr", :area_code => "9164") + AreaCode.create(:country => germany, :name => "Sugenheim", :area_code => "9165") + AreaCode.create(:country => germany, :name => "Münchsteinach", :area_code => "9166") + AreaCode.create(:country => germany, :name => "Oberscheinfeld", :area_code => "9167") + AreaCode.create(:country => germany, :name => "Schwanstetten", :area_code => "9170") + AreaCode.create(:country => germany, :name => "Roth Mittelfr", :area_code => "9171") + AreaCode.create(:country => germany, :name => "Georgensgmünd", :area_code => "9172") + AreaCode.create(:country => germany, :name => "Thalmässing", :area_code => "9173") + AreaCode.create(:country => germany, :name => "Hilpoltstein", :area_code => "9174") + AreaCode.create(:country => germany, :name => "Spalt", :area_code => "9175") + AreaCode.create(:country => germany, :name => "Allersberg", :area_code => "9176") + AreaCode.create(:country => germany, :name => "Heideck", :area_code => "9177") + AreaCode.create(:country => germany, :name => "Abenberg Mittelfr", :area_code => "9178") + AreaCode.create(:country => germany, :name => "Freystadt", :area_code => "9179") + AreaCode.create(:country => germany, :name => "Pyrbaum", :area_code => "9180") + AreaCode.create(:country => germany, :name => "Neumarkt i d Opf", :area_code => "9181") + AreaCode.create(:country => germany, :name => "Velburg", :area_code => "9182") + AreaCode.create(:country => germany, :name => "Burgthann", :area_code => "9183") + AreaCode.create(:country => germany, :name => "Deining Oberpf", :area_code => "9184") + AreaCode.create(:country => germany, :name => "Mühlhausen Oberpf", :area_code => "9185") + AreaCode.create(:country => germany, :name => "Lauterhofen Oberpf", :area_code => "9186") + AreaCode.create(:country => germany, :name => "Altdorf b Nürnberg", :area_code => "9187") + AreaCode.create(:country => germany, :name => "Postbauer-Heng", :area_code => "9188") + AreaCode.create(:country => germany, :name => "Berg b Neumarkt i d Opf", :area_code => "9189") + AreaCode.create(:country => germany, :name => "Heroldsbach", :area_code => "9190") + AreaCode.create(:country => germany, :name => "Forchheim Oberfr", :area_code => "9191") + AreaCode.create(:country => germany, :name => "Gräfenberg", :area_code => "9192") + AreaCode.create(:country => germany, :name => "Höchstadt a d Aisch", :area_code => "9193") + AreaCode.create(:country => germany, :name => "Ebermannstadt", :area_code => "9194") + AreaCode.create(:country => germany, :name => "Adelsdorf Mittelfr", :area_code => "9195") + AreaCode.create(:country => germany, :name => "Wiesenttal", :area_code => "9196") + AreaCode.create(:country => germany, :name => "Egloffstein", :area_code => "9197") + AreaCode.create(:country => germany, :name => "Heiligenstadt i Ofr", :area_code => "9198") + AreaCode.create(:country => germany, :name => "Kunreuth", :area_code => "9199") + AreaCode.create(:country => germany, :name => "Gesees", :area_code => "9201") + AreaCode.create(:country => germany, :name => "Waischenfeld", :area_code => "9202") + AreaCode.create(:country => germany, :name => "Neudrossenfeld", :area_code => "9203") + AreaCode.create(:country => germany, :name => "Plankenfels", :area_code => "9204") + AreaCode.create(:country => germany, :name => "Vorbach", :area_code => "9205") + AreaCode.create(:country => germany, :name => "Mistelgau-Obernsees", :area_code => "9206") + AreaCode.create(:country => germany, :name => "Königsfeld Oberfr", :area_code => "9207") + AreaCode.create(:country => germany, :name => "Bindlach", :area_code => "9208") + AreaCode.create(:country => germany, :name => "Emtmannsberg", :area_code => "9209") + AreaCode.create(:country => germany, :name => "Bayreuth", :area_code => "921") + AreaCode.create(:country => germany, :name => "Kasendorf-Azendorf", :area_code => "9220") + AreaCode.create(:country => germany, :name => "Kulmbach", :area_code => "9221") + AreaCode.create(:country => germany, :name => "Presseck", :area_code => "9222") + AreaCode.create(:country => germany, :name => "Rugendorf", :area_code => "9223") + AreaCode.create(:country => germany, :name => "Stadtsteinach", :area_code => "9225") + AreaCode.create(:country => germany, :name => "Neuenmarkt", :area_code => "9227") + AreaCode.create(:country => germany, :name => "Thurnau", :area_code => "9228") + AreaCode.create(:country => germany, :name => "Mainleus", :area_code => "9229") + AreaCode.create(:country => germany, :name => "Marktredwitz", :area_code => "9231") + AreaCode.create(:country => germany, :name => "Wunsiedel", :area_code => "9232") + AreaCode.create(:country => germany, :name => "Arzberg Oberfr", :area_code => "9233") + AreaCode.create(:country => germany, :name => "Neusorg", :area_code => "9234") + AreaCode.create(:country => germany, :name => "Thierstein", :area_code => "9235") + AreaCode.create(:country => germany, :name => "Nagel", :area_code => "9236") + AreaCode.create(:country => germany, :name => "Röslau", :area_code => "9238") + AreaCode.create(:country => germany, :name => "Pegnitz", :area_code => "9241") + AreaCode.create(:country => germany, :name => "Gößweinstein", :area_code => "9242") + AreaCode.create(:country => germany, :name => "Pottenstein", :area_code => "9243") + AreaCode.create(:country => germany, :name => "Betzenstein", :area_code => "9244") + AreaCode.create(:country => germany, :name => "Obertrubach", :area_code => "9245") + AreaCode.create(:country => germany, :name => "Pegnitz-Trockau", :area_code => "9246") + AreaCode.create(:country => germany, :name => "Münchberg", :area_code => "9251") + AreaCode.create(:country => germany, :name => "Helmbrechts", :area_code => "9252") + AreaCode.create(:country => germany, :name => "Weissenstadt", :area_code => "9253") + AreaCode.create(:country => germany, :name => "Gefrees", :area_code => "9254") + AreaCode.create(:country => germany, :name => "Marktleugast", :area_code => "9255") + AreaCode.create(:country => germany, :name => "Stammbach", :area_code => "9256") + AreaCode.create(:country => germany, :name => "Zell Oberfr", :area_code => "9257") + AreaCode.create(:country => germany, :name => "Wilhelmsthal Oberfr", :area_code => "9260") + AreaCode.create(:country => germany, :name => "Kronach", :area_code => "9261") + AreaCode.create(:country => germany, :name => "Wallenfels", :area_code => "9262") + AreaCode.create(:country => germany, :name => "Ludwigsstadt", :area_code => "9263") + AreaCode.create(:country => germany, :name => "Küps", :area_code => "9264") + AreaCode.create(:country => germany, :name => "Pressig", :area_code => "9265") + AreaCode.create(:country => germany, :name => "Mitwitz", :area_code => "9266") + AreaCode.create(:country => germany, :name => "Nordhalben", :area_code => "9267") + AreaCode.create(:country => germany, :name => "Teuschnitz", :area_code => "9268") + AreaCode.create(:country => germany, :name => "Tettau Kr Kronach", :area_code => "9269") + AreaCode.create(:country => germany, :name => "Creussen", :area_code => "9270") + AreaCode.create(:country => germany, :name => "Thurnau-Alladorf", :area_code => "9271") + AreaCode.create(:country => germany, :name => "Fichtelberg", :area_code => "9272") + AreaCode.create(:country => germany, :name => "Bad Berneck i Fichtelgebirge", :area_code => "9273") + AreaCode.create(:country => germany, :name => "Hollfeld", :area_code => "9274") + AreaCode.create(:country => germany, :name => "Speichersdorf", :area_code => "9275") + AreaCode.create(:country => germany, :name => "Bischofsgrün", :area_code => "9276") + AreaCode.create(:country => germany, :name => "Warmensteinach", :area_code => "9277") + AreaCode.create(:country => germany, :name => "Weidenberg", :area_code => "9278") + AreaCode.create(:country => germany, :name => "Mistelgau", :area_code => "9279") + AreaCode.create(:country => germany, :name => "Selbitz Oberfr", :area_code => "9280") + AreaCode.create(:country => germany, :name => "Hof Saale", :area_code => "9281") + AreaCode.create(:country => germany, :name => "Naila", :area_code => "9282") + AreaCode.create(:country => germany, :name => "Rehau", :area_code => "9283") + AreaCode.create(:country => germany, :name => "Schwarzenbach a d Saale", :area_code => "9284") + AreaCode.create(:country => germany, :name => "Kirchenlamitz", :area_code => "9285") + AreaCode.create(:country => germany, :name => "Oberkotzau", :area_code => "9286") + AreaCode.create(:country => germany, :name => "Selb", :area_code => "9287") + AreaCode.create(:country => germany, :name => "Bad Steben", :area_code => "9288") + AreaCode.create(:country => germany, :name => "Schwarzenbach a Wald", :area_code => "9289") + AreaCode.create(:country => germany, :name => "Konradsreuth", :area_code => "9292") + AreaCode.create(:country => germany, :name => "Berg Oberfr", :area_code => "9293") + AreaCode.create(:country => germany, :name => "Regnitzlosau", :area_code => "9294") + AreaCode.create(:country => germany, :name => "Töpen", :area_code => "9295") + AreaCode.create(:country => germany, :name => "Rottendorf Unterfr", :area_code => "9302") + AreaCode.create(:country => germany, :name => "Eibelstadt", :area_code => "9303") + AreaCode.create(:country => germany, :name => "Estenfeld", :area_code => "9305") + AreaCode.create(:country => germany, :name => "Kist", :area_code => "9306") + AreaCode.create(:country => germany, :name => "Altertheim", :area_code => "9307") + AreaCode.create(:country => germany, :name => "Würzburg", :area_code => "931") + AreaCode.create(:country => germany, :name => "Kitzingen", :area_code => "9321") + AreaCode.create(:country => germany, :name => "Iphofen", :area_code => "9323") + AreaCode.create(:country => germany, :name => "Dettelbach", :area_code => "9324") + AreaCode.create(:country => germany, :name => "Kleinlangheim", :area_code => "9325") + AreaCode.create(:country => germany, :name => "Markt Einersheim", :area_code => "9326") + AreaCode.create(:country => germany, :name => "Ochsenfurt", :area_code => "9331") + AreaCode.create(:country => germany, :name => "Marktbreit", :area_code => "9332") + AreaCode.create(:country => germany, :name => "Sommerhausen", :area_code => "9333") + AreaCode.create(:country => germany, :name => "Giebelstadt", :area_code => "9334") + AreaCode.create(:country => germany, :name => "Aub Kr Würzburg", :area_code => "9335") + AreaCode.create(:country => germany, :name => "Bütthard", :area_code => "9336") + AreaCode.create(:country => germany, :name => "Gaukönigshofen", :area_code => "9337") + AreaCode.create(:country => germany, :name => "Röttingen Unterfr", :area_code => "9338") + AreaCode.create(:country => germany, :name => "Ippesheim", :area_code => "9339") + AreaCode.create(:country => germany, :name => "Königheim-Brehmen", :area_code => "9340") + AreaCode.create(:country => germany, :name => "Tauberbischofsheim", :area_code => "9341") + AreaCode.create(:country => germany, :name => "Wertheim", :area_code => "9342") + AreaCode.create(:country => germany, :name => "Lauda-Königshofen", :area_code => "9343") + AreaCode.create(:country => germany, :name => "Gerchsheim", :area_code => "9344") + AreaCode.create(:country => germany, :name => "Külsheim Baden", :area_code => "9345") + AreaCode.create(:country => germany, :name => "Grünsfeld", :area_code => "9346") + AreaCode.create(:country => germany, :name => "Wittighausen", :area_code => "9347") + AreaCode.create(:country => germany, :name => "Werbach-Gamburg", :area_code => "9348") + AreaCode.create(:country => germany, :name => "Werbach-Wenkheim", :area_code => "9349") + AreaCode.create(:country => germany, :name => "Eussenheim-Hundsbach", :area_code => "9350") + AreaCode.create(:country => germany, :name => "Gemünden a Main", :area_code => "9351") + AreaCode.create(:country => germany, :name => "Lohr a Main", :area_code => "9352") + AreaCode.create(:country => germany, :name => "Karlstadt", :area_code => "9353") + AreaCode.create(:country => germany, :name => "Rieneck", :area_code => "9354") + AreaCode.create(:country => germany, :name => "Frammersbach", :area_code => "9355") + AreaCode.create(:country => germany, :name => "Burgsinn", :area_code => "9356") + AreaCode.create(:country => germany, :name => "Gräfendorf Bay", :area_code => "9357") + AreaCode.create(:country => germany, :name => "Gössenheim", :area_code => "9358") + AreaCode.create(:country => germany, :name => "Karlstadt-Wiesenfeld", :area_code => "9359") + AreaCode.create(:country => germany, :name => "Thüngen", :area_code => "9360") + AreaCode.create(:country => germany, :name => "Arnstein Unterfr", :area_code => "9363") + AreaCode.create(:country => germany, :name => "Zellingen", :area_code => "9364") + AreaCode.create(:country => germany, :name => "Rimpar", :area_code => "9365") + AreaCode.create(:country => germany, :name => "Geroldshausen Unterfr", :area_code => "9366") + AreaCode.create(:country => germany, :name => "Unterpleichfeld", :area_code => "9367") + AreaCode.create(:country => germany, :name => "Uettingen", :area_code => "9369") + AreaCode.create(:country => germany, :name => "Miltenberg", :area_code => "9371") + AreaCode.create(:country => germany, :name => "Klingenberg a Main", :area_code => "9372") + AreaCode.create(:country => germany, :name => "Amorbach", :area_code => "9373") + AreaCode.create(:country => germany, :name => "Eschau", :area_code => "9374") + AreaCode.create(:country => germany, :name => "Freudenberg Baden", :area_code => "9375") + AreaCode.create(:country => germany, :name => "Collenberg", :area_code => "9376") + AreaCode.create(:country => germany, :name => "Freudenberg-Boxtal", :area_code => "9377") + AreaCode.create(:country => germany, :name => "Eichenbühl-Riedern", :area_code => "9378") + AreaCode.create(:country => germany, :name => "Volkach", :area_code => "9381") + AreaCode.create(:country => germany, :name => "Gerolzhofen", :area_code => "9382") + AreaCode.create(:country => germany, :name => "Wiesentheid", :area_code => "9383") + AreaCode.create(:country => germany, :name => "Schwanfeld", :area_code => "9384") + AreaCode.create(:country => germany, :name => "Kolitzheim", :area_code => "9385") + AreaCode.create(:country => germany, :name => "Prosselsheim", :area_code => "9386") + AreaCode.create(:country => germany, :name => "Marktheidenfeld", :area_code => "9391") + AreaCode.create(:country => germany, :name => "Faulbach Unterfr", :area_code => "9392") + AreaCode.create(:country => germany, :name => "Rothenfels Unterfr", :area_code => "9393") + AreaCode.create(:country => germany, :name => "Esselbach", :area_code => "9394") + AreaCode.create(:country => germany, :name => "Triefenstein", :area_code => "9395") + AreaCode.create(:country => germany, :name => "Urspringen b Lohr", :area_code => "9396") + AreaCode.create(:country => germany, :name => "Wertheim-Dertingen", :area_code => "9397") + AreaCode.create(:country => germany, :name => "Birkenfeld b Würzburg", :area_code => "9398") + AreaCode.create(:country => germany, :name => "Neutraubling", :area_code => "9401") + AreaCode.create(:country => germany, :name => "Regenstauf", :area_code => "9402") + AreaCode.create(:country => germany, :name => "Donaustauf", :area_code => "9403") + AreaCode.create(:country => germany, :name => "Nittendorf", :area_code => "9404") + AreaCode.create(:country => germany, :name => "Bad Abbach", :area_code => "9405") + AreaCode.create(:country => germany, :name => "Mintraching", :area_code => "9406") + AreaCode.create(:country => germany, :name => "Wenzenbach", :area_code => "9407") + AreaCode.create(:country => germany, :name => "Altenthann", :area_code => "9408") + AreaCode.create(:country => germany, :name => "Pielenhofen", :area_code => "9409") + AreaCode.create(:country => germany, :name => "Regensburg", :area_code => "941") + AreaCode.create(:country => germany, :name => "Feldkirchen Niederbay", :area_code => "9420") + AreaCode.create(:country => germany, :name => "Straubing", :area_code => "9421") + AreaCode.create(:country => germany, :name => "Bogen Niederbay", :area_code => "9422") + AreaCode.create(:country => germany, :name => "Geiselhöring", :area_code => "9423") + AreaCode.create(:country => germany, :name => "Strasskirchen", :area_code => "9424") + AreaCode.create(:country => germany, :name => "Oberschneiding", :area_code => "9426") + AreaCode.create(:country => germany, :name => "Leiblfing", :area_code => "9427") + AreaCode.create(:country => germany, :name => "Kirchroth", :area_code => "9428") + AreaCode.create(:country => germany, :name => "Rain Niederbay", :area_code => "9429") + AreaCode.create(:country => germany, :name => "Schwandorf", :area_code => "9431") + AreaCode.create(:country => germany, :name => "Nabburg", :area_code => "9433") + AreaCode.create(:country => germany, :name => "Bodenwöhr", :area_code => "9434") + AreaCode.create(:country => germany, :name => "Schwarzenfeld", :area_code => "9435") + AreaCode.create(:country => germany, :name => "Nittenau", :area_code => "9436") + AreaCode.create(:country => germany, :name => "Fensterbach", :area_code => "9438") + AreaCode.create(:country => germany, :name => "Neunburg-Kemnath", :area_code => "9439") + AreaCode.create(:country => germany, :name => "Kelheim", :area_code => "9441") + AreaCode.create(:country => germany, :name => "Riedenburg", :area_code => "9442") + AreaCode.create(:country => germany, :name => "Abensberg", :area_code => "9443") + AreaCode.create(:country => germany, :name => "Siegenburg", :area_code => "9444") + AreaCode.create(:country => germany, :name => "Neustadt a d Donau", :area_code => "9445") + AreaCode.create(:country => germany, :name => "Altmannstein", :area_code => "9446") + AreaCode.create(:country => germany, :name => "Essing", :area_code => "9447") + AreaCode.create(:country => germany, :name => "Hausen Niederbay", :area_code => "9448") + AreaCode.create(:country => germany, :name => "Schierling", :area_code => "9451") + AreaCode.create(:country => germany, :name => "Langquaid", :area_code => "9452") + AreaCode.create(:country => germany, :name => "Thalmassing", :area_code => "9453") + AreaCode.create(:country => germany, :name => "Aufhausen Oberpf", :area_code => "9454") + AreaCode.create(:country => germany, :name => "Roding", :area_code => "9461") + AreaCode.create(:country => germany, :name => "Falkenstein Oberpf", :area_code => "9462") + AreaCode.create(:country => germany, :name => "Wald Oberpf", :area_code => "9463") + AreaCode.create(:country => germany, :name => "Walderbach", :area_code => "9464") + AreaCode.create(:country => germany, :name => "Neukirchen-Balbini", :area_code => "9465") + AreaCode.create(:country => germany, :name => "Stamsried", :area_code => "9466") + AreaCode.create(:country => germany, :name => "Michelsneukirchen", :area_code => "9467") + AreaCode.create(:country => germany, :name => "Zell Oberpf", :area_code => "9468") + AreaCode.create(:country => germany, :name => "Roding-Neubäu", :area_code => "9469") + AreaCode.create(:country => germany, :name => "Burglengenfeld", :area_code => "9471") + AreaCode.create(:country => germany, :name => "Hohenfels Oberpf", :area_code => "9472") + AreaCode.create(:country => germany, :name => "Kallmünz", :area_code => "9473") + AreaCode.create(:country => germany, :name => "Schmidmühlen", :area_code => "9474") + AreaCode.create(:country => germany, :name => "Sünching", :area_code => "9480") + AreaCode.create(:country => germany, :name => "Pfatter", :area_code => "9481") + AreaCode.create(:country => germany, :name => "Wörth a d Donau", :area_code => "9482") + AreaCode.create(:country => germany, :name => "Brennberg", :area_code => "9484") + AreaCode.create(:country => germany, :name => "Hemau", :area_code => "9491") + AreaCode.create(:country => germany, :name => "Parsberg", :area_code => "9492") + AreaCode.create(:country => germany, :name => "Beratzhausen", :area_code => "9493") + AreaCode.create(:country => germany, :name => "Breitenbrunn Oberpf", :area_code => "9495") + AreaCode.create(:country => germany, :name => "Seubersdorf i d Opf", :area_code => "9497") + AreaCode.create(:country => germany, :name => "Laaber", :area_code => "9498") + AreaCode.create(:country => germany, :name => "Painten", :area_code => "9499") + AreaCode.create(:country => germany, :name => "Frensdorf", :area_code => "9502") + AreaCode.create(:country => germany, :name => "Oberhaid Oberfr", :area_code => "9503") + AreaCode.create(:country => germany, :name => "Stadelhofen", :area_code => "9504") + AreaCode.create(:country => germany, :name => "Litzendorf", :area_code => "9505") + AreaCode.create(:country => germany, :name => "Bamberg", :area_code => "951") + AreaCode.create(:country => germany, :name => "Hassfurt", :area_code => "9521") + AreaCode.create(:country => germany, :name => "Eltmann", :area_code => "9522") + AreaCode.create(:country => germany, :name => "Hofheim i Ufr", :area_code => "9523") + AreaCode.create(:country => germany, :name => "Zeil a Main", :area_code => "9524") + AreaCode.create(:country => germany, :name => "Königsberg i Bay", :area_code => "9525") + AreaCode.create(:country => germany, :name => "Riedbach", :area_code => "9526") + AreaCode.create(:country => germany, :name => "Knetzgau", :area_code => "9527") + AreaCode.create(:country => germany, :name => "Donnersdorf", :area_code => "9528") + AreaCode.create(:country => germany, :name => "Oberaurach", :area_code => "9529") + AreaCode.create(:country => germany, :name => "Ebern", :area_code => "9531") + AreaCode.create(:country => germany, :name => "Maroldsweisach", :area_code => "9532") + AreaCode.create(:country => germany, :name => "Untermerzbach", :area_code => "9533") + AreaCode.create(:country => germany, :name => "Burgpreppach", :area_code => "9534") + AreaCode.create(:country => germany, :name => "Pfarrweisach", :area_code => "9535") + AreaCode.create(:country => germany, :name => "Kirchlauter", :area_code => "9536") + AreaCode.create(:country => germany, :name => "Schesslitz", :area_code => "9542") + AreaCode.create(:country => germany, :name => "Hirschaid", :area_code => "9543") + AreaCode.create(:country => germany, :name => "Baunach", :area_code => "9544") + AreaCode.create(:country => germany, :name => "Buttenheim", :area_code => "9545") + AreaCode.create(:country => germany, :name => "Burgebrach", :area_code => "9546") + AreaCode.create(:country => germany, :name => "Zapfendorf", :area_code => "9547") + AreaCode.create(:country => germany, :name => "Mühlhausen Mittelfr", :area_code => "9548") + AreaCode.create(:country => germany, :name => "Lisberg", :area_code => "9549") + AreaCode.create(:country => germany, :name => "Burgwindheim", :area_code => "9551") + AreaCode.create(:country => germany, :name => "Burghaslach", :area_code => "9552") + AreaCode.create(:country => germany, :name => "Ebrach Oberfr", :area_code => "9553") + AreaCode.create(:country => germany, :name => "Untersteinbach Unterfr", :area_code => "9554") + AreaCode.create(:country => germany, :name => "Schlüsselfeld-Aschbach", :area_code => "9555") + AreaCode.create(:country => germany, :name => "Geiselwind", :area_code => "9556") + AreaCode.create(:country => germany, :name => "Grub a Forst", :area_code => "9560") + AreaCode.create(:country => germany, :name => "Coburg", :area_code => "9561") + AreaCode.create(:country => germany, :name => "Sonnefeld", :area_code => "9562") + AreaCode.create(:country => germany, :name => "Rödental", :area_code => "9563") + AreaCode.create(:country => germany, :name => "Bad Rodach", :area_code => "9564") + AreaCode.create(:country => germany, :name => "Untersiemau", :area_code => "9565") + AreaCode.create(:country => germany, :name => "Meeder", :area_code => "9566") + AreaCode.create(:country => germany, :name => "Seßlach-Gemünda", :area_code => "9567") + AreaCode.create(:country => germany, :name => "Neustadt b Coburg", :area_code => "9568") + AreaCode.create(:country => germany, :name => "Sesslach", :area_code => "9569") + AreaCode.create(:country => germany, :name => "Lichtenfels Bay", :area_code => "9571") + AreaCode.create(:country => germany, :name => "Burgkunstadt", :area_code => "9572") + AreaCode.create(:country => germany, :name => "Staffelstein Oberfr", :area_code => "9573") + AreaCode.create(:country => germany, :name => "Marktzeuln", :area_code => "9574") + AreaCode.create(:country => germany, :name => "Weismain", :area_code => "9575") + AreaCode.create(:country => germany, :name => "Lichtenfels-Isling", :area_code => "9576") + AreaCode.create(:country => germany, :name => "Neustadt a d Waldnaab", :area_code => "9602") + AreaCode.create(:country => germany, :name => "Floss", :area_code => "9603") + AreaCode.create(:country => germany, :name => "Wernberg-Köblitz", :area_code => "9604") + AreaCode.create(:country => germany, :name => "Weiherhammer", :area_code => "9605") + AreaCode.create(:country => germany, :name => "Pfreimd", :area_code => "9606") + AreaCode.create(:country => germany, :name => "Luhe-Wildenau", :area_code => "9607") + AreaCode.create(:country => germany, :name => "Kohlberg Oberpf", :area_code => "9608") + AreaCode.create(:country => germany, :name => "Weiden i d Opf", :area_code => "961") + AreaCode.create(:country => germany, :name => "Amberg Oberpf", :area_code => "9621") + AreaCode.create(:country => germany, :name => "Hirschau Oberpf", :area_code => "9622") + AreaCode.create(:country => germany, :name => "Ensdorf Oberpf", :area_code => "9624") + AreaCode.create(:country => germany, :name => "Kastl b Amberg", :area_code => "9625") + AreaCode.create(:country => germany, :name => "Hohenburg", :area_code => "9626") + AreaCode.create(:country => germany, :name => "Freudenberg Oberpf", :area_code => "9627") + AreaCode.create(:country => germany, :name => "Ursensollen", :area_code => "9628") + AreaCode.create(:country => germany, :name => "Tirschenreuth", :area_code => "9631") + AreaCode.create(:country => germany, :name => "Waldsassen", :area_code => "9632") + AreaCode.create(:country => germany, :name => "Mitterteich", :area_code => "9633") + AreaCode.create(:country => germany, :name => "Wiesau", :area_code => "9634") + AreaCode.create(:country => germany, :name => "Bärnau", :area_code => "9635") + AreaCode.create(:country => germany, :name => "Plößberg", :area_code => "9636") + AreaCode.create(:country => germany, :name => "Falkenberg Oberpf", :area_code => "9637") + AreaCode.create(:country => germany, :name => "Neualbenreuth", :area_code => "9638") + AreaCode.create(:country => germany, :name => "Mähring", :area_code => "9639") + AreaCode.create(:country => germany, :name => "Grafenwöhr", :area_code => "9641") + AreaCode.create(:country => germany, :name => "Kemnath Stadt", :area_code => "9642") + AreaCode.create(:country => germany, :name => "Auerbach i d Opf", :area_code => "9643") + AreaCode.create(:country => germany, :name => "Pressath", :area_code => "9644") + AreaCode.create(:country => germany, :name => "Eschenbach i d Opf", :area_code => "9645") + AreaCode.create(:country => germany, :name => "Freihung", :area_code => "9646") + AreaCode.create(:country => germany, :name => "Kirchenthumbach", :area_code => "9647") + AreaCode.create(:country => germany, :name => "Neustadt a Kulm", :area_code => "9648") + AreaCode.create(:country => germany, :name => "Vohenstrauss", :area_code => "9651") + AreaCode.create(:country => germany, :name => "Waidhaus", :area_code => "9652") + AreaCode.create(:country => germany, :name => "Eslarn", :area_code => "9653") + AreaCode.create(:country => germany, :name => "Pleystein", :area_code => "9654") + AreaCode.create(:country => germany, :name => "Tännesberg", :area_code => "9655") + AreaCode.create(:country => germany, :name => "Moosbach b Vohenstrauß", :area_code => "9656") + AreaCode.create(:country => germany, :name => "Waldthurn", :area_code => "9657") + AreaCode.create(:country => germany, :name => "Georgenberg", :area_code => "9658") + AreaCode.create(:country => germany, :name => "Leuchtenberg", :area_code => "9659") + AreaCode.create(:country => germany, :name => "Sulzbach-Rosenberg", :area_code => "9661") + AreaCode.create(:country => germany, :name => "Vilseck", :area_code => "9662") + AreaCode.create(:country => germany, :name => "Neukirchen b Sulzbach-Rosenberg", :area_code => "9663") + AreaCode.create(:country => germany, :name => "Hahnbach", :area_code => "9664") + AreaCode.create(:country => germany, :name => "Königstein Oberpf", :area_code => "9665") + AreaCode.create(:country => germany, :name => "Illschwang", :area_code => "9666") + AreaCode.create(:country => germany, :name => "Oberviechtach", :area_code => "9671") + AreaCode.create(:country => germany, :name => "Neunburg vorm Wald", :area_code => "9672") + AreaCode.create(:country => germany, :name => "Tiefenbach Oberpf", :area_code => "9673") + AreaCode.create(:country => germany, :name => "Schönsee", :area_code => "9674") + AreaCode.create(:country => germany, :name => "Altendorf a Nabburg", :area_code => "9675") + AreaCode.create(:country => germany, :name => "Winklarn", :area_code => "9676") + AreaCode.create(:country => germany, :name => "Oberviechtach-Pullenried", :area_code => "9677") + AreaCode.create(:country => germany, :name => "Windischeschenbach", :area_code => "9681") + AreaCode.create(:country => germany, :name => "Erbendorf", :area_code => "9682") + AreaCode.create(:country => germany, :name => "Friedenfels", :area_code => "9683") + AreaCode.create(:country => germany, :name => "Sandberg Unterfr", :area_code => "9701") + AreaCode.create(:country => germany, :name => "Euerdorf", :area_code => "9704") + AreaCode.create(:country => germany, :name => "Bad Bocklet", :area_code => "9708") + AreaCode.create(:country => germany, :name => "Bad Kissingen", :area_code => "971") + AreaCode.create(:country => germany, :name => "Üchtelhausen", :area_code => "9720") + AreaCode.create(:country => germany, :name => "Schweinfurt", :area_code => "9721") + AreaCode.create(:country => germany, :name => "Werneck", :area_code => "9722") + AreaCode.create(:country => germany, :name => "Röthlein", :area_code => "9723") + AreaCode.create(:country => germany, :name => "Stadtlauringen", :area_code => "9724") + AreaCode.create(:country => germany, :name => "Poppenhausen Unterfr", :area_code => "9725") + AreaCode.create(:country => germany, :name => "Euerbach", :area_code => "9726") + AreaCode.create(:country => germany, :name => "Schonungen-Marktsteinach", :area_code => "9727") + AreaCode.create(:country => germany, :name => "Wülfershausen Unterfr", :area_code => "9728") + AreaCode.create(:country => germany, :name => "Grettstadt", :area_code => "9729") + AreaCode.create(:country => germany, :name => "Hammelburg", :area_code => "9732") + AreaCode.create(:country => germany, :name => "Münnerstadt", :area_code => "9733") + AreaCode.create(:country => germany, :name => "Burkardroth", :area_code => "9734") + AreaCode.create(:country => germany, :name => "Massbach", :area_code => "9735") + AreaCode.create(:country => germany, :name => "Oberthulba", :area_code => "9736") + AreaCode.create(:country => germany, :name => "Wartmannsroth", :area_code => "9737") + AreaCode.create(:country => germany, :name => "Rottershausen", :area_code => "9738") + AreaCode.create(:country => germany, :name => "Bad Brückenau", :area_code => "9741") + AreaCode.create(:country => germany, :name => "Kalbach Rhön", :area_code => "9742") + AreaCode.create(:country => germany, :name => "Zeitlofs-Detter", :area_code => "9744") + AreaCode.create(:country => germany, :name => "Wildflecken", :area_code => "9745") + AreaCode.create(:country => germany, :name => "Zeitlofs", :area_code => "9746") + AreaCode.create(:country => germany, :name => "Geroda Bay", :area_code => "9747") + AreaCode.create(:country => germany, :name => "Motten", :area_code => "9748") + AreaCode.create(:country => germany, :name => "Oberbach Unterfr", :area_code => "9749") + AreaCode.create(:country => germany, :name => "Bad Königshofen i Grabfeld", :area_code => "9761") + AreaCode.create(:country => germany, :name => "Saal a d Saale", :area_code => "9762") + AreaCode.create(:country => germany, :name => "Sulzdorf a d Lederhecke", :area_code => "9763") + AreaCode.create(:country => germany, :name => "Höchheim", :area_code => "9764") + AreaCode.create(:country => germany, :name => "Trappstadt", :area_code => "9765") + AreaCode.create(:country => germany, :name => "Grosswenkheim", :area_code => "9766") + AreaCode.create(:country => germany, :name => "Bad Neustadt a d Saale", :area_code => "9771") + AreaCode.create(:country => germany, :name => "Bischofsheim a d Rhön", :area_code => "9772") + AreaCode.create(:country => germany, :name => "Unsleben", :area_code => "9773") + AreaCode.create(:country => germany, :name => "Oberelsbach", :area_code => "9774") + AreaCode.create(:country => germany, :name => "Schönau a d Brend", :area_code => "9775") + AreaCode.create(:country => germany, :name => "Mellrichstadt", :area_code => "9776") + AreaCode.create(:country => germany, :name => "Ostheim v d Rhön", :area_code => "9777") + AreaCode.create(:country => germany, :name => "Fladungen", :area_code => "9778") + AreaCode.create(:country => germany, :name => "Nordheim v d Rhön", :area_code => "9779") + AreaCode.create(:country => germany, :name => "Ansbach-Katterbach", :area_code => "9802") + AreaCode.create(:country => germany, :name => "Colmberg", :area_code => "9803") + AreaCode.create(:country => germany, :name => "Aurach", :area_code => "9804") + AreaCode.create(:country => germany, :name => "Burgoberbach", :area_code => "9805") + AreaCode.create(:country => germany, :name => "Ansbach", :area_code => "981") + AreaCode.create(:country => germany, :name => "Lehrberg", :area_code => "9820") + AreaCode.create(:country => germany, :name => "Bechhofen a d Heide", :area_code => "9822") + AreaCode.create(:country => germany, :name => "Leutershausen", :area_code => "9823") + AreaCode.create(:country => germany, :name => "Dietenhofen", :area_code => "9824") + AreaCode.create(:country => germany, :name => "Herrieden", :area_code => "9825") + AreaCode.create(:country => germany, :name => "Weidenbach Mittelfr", :area_code => "9826") + AreaCode.create(:country => germany, :name => "Lichtenau Mittelfr", :area_code => "9827") + AreaCode.create(:country => germany, :name => "Rügland", :area_code => "9828") + AreaCode.create(:country => germany, :name => "Flachslanden", :area_code => "9829") + AreaCode.create(:country => germany, :name => "Gunzenhausen", :area_code => "9831") + AreaCode.create(:country => germany, :name => "Wassertrüdingen", :area_code => "9832") + AreaCode.create(:country => germany, :name => "Heidenheim Mittelfr", :area_code => "9833") + AreaCode.create(:country => germany, :name => "Theilenhofen", :area_code => "9834") + AreaCode.create(:country => germany, :name => "Ehingen Mittelfr", :area_code => "9835") + AreaCode.create(:country => germany, :name => "Gunzenhausen-Cronheim", :area_code => "9836") + AreaCode.create(:country => germany, :name => "Haundorf", :area_code => "9837") + AreaCode.create(:country => germany, :name => "Bad Windsheim", :area_code => "9841") + AreaCode.create(:country => germany, :name => "Uffenheim", :area_code => "9842") + AreaCode.create(:country => germany, :name => "Burgbernheim", :area_code => "9843") + AreaCode.create(:country => germany, :name => "Obernzenn", :area_code => "9844") + AreaCode.create(:country => germany, :name => "Oberdachstetten", :area_code => "9845") + AreaCode.create(:country => germany, :name => "Ipsheim", :area_code => "9846") + AreaCode.create(:country => germany, :name => "Ergersheim", :area_code => "9847") + AreaCode.create(:country => germany, :name => "Simmershofen", :area_code => "9848") + AreaCode.create(:country => germany, :name => "Dinkelsbühl", :area_code => "9851") + AreaCode.create(:country => germany, :name => "Feuchtwangen", :area_code => "9852") + AreaCode.create(:country => germany, :name => "Wilburgstetten", :area_code => "9853") + AreaCode.create(:country => germany, :name => "Wittelshofen", :area_code => "9854") + AreaCode.create(:country => germany, :name => "Dentlein a Forst", :area_code => "9855") + AreaCode.create(:country => germany, :name => "Dürrwangen", :area_code => "9856") + AreaCode.create(:country => germany, :name => "Schopfloch Mittelfr", :area_code => "9857") + AreaCode.create(:country => germany, :name => "Rothenburg ob der Tauber", :area_code => "9861") + AreaCode.create(:country => germany, :name => "Adelshofen Mittelfr", :area_code => "9865") + AreaCode.create(:country => germany, :name => "Geslau", :area_code => "9867") + AreaCode.create(:country => germany, :name => "Schillingsfürst", :area_code => "9868") + AreaCode.create(:country => germany, :name => "Wettringen Mittelfr", :area_code => "9869") + AreaCode.create(:country => germany, :name => "Windsbach", :area_code => "9871") + AreaCode.create(:country => germany, :name => "Heilsbronn", :area_code => "9872") + AreaCode.create(:country => germany, :name => "Abenberg-Wassermungenau", :area_code => "9873") + AreaCode.create(:country => germany, :name => "Neuendettelsau", :area_code => "9874") + AreaCode.create(:country => germany, :name => "Wolframs-Eschenbach", :area_code => "9875") + AreaCode.create(:country => germany, :name => "Rohr Mittelfr", :area_code => "9876") + AreaCode.create(:country => germany, :name => "Hengersberg Bay", :area_code => "9901") + AreaCode.create(:country => germany, :name => "Schöllnach", :area_code => "9903") + AreaCode.create(:country => germany, :name => "Lalling", :area_code => "9904") + AreaCode.create(:country => germany, :name => "Bernried Niederbay", :area_code => "9905") + AreaCode.create(:country => germany, :name => "Mariaposching", :area_code => "9906") + AreaCode.create(:country => germany, :name => "Zenting", :area_code => "9907") + AreaCode.create(:country => germany, :name => "Schöfweg", :area_code => "9908") + AreaCode.create(:country => germany, :name => "Deggendorf", :area_code => "991") + AreaCode.create(:country => germany, :name => "Bischofsmais", :area_code => "9920") + AreaCode.create(:country => germany, :name => "Regen", :area_code => "9921") + AreaCode.create(:country => germany, :name => "Zwiesel", :area_code => "9922") + AreaCode.create(:country => germany, :name => "Teisnach", :area_code => "9923") + AreaCode.create(:country => germany, :name => "Bodenmais", :area_code => "9924") + AreaCode.create(:country => germany, :name => "Bayerisch Eisenstein", :area_code => "9925") + AreaCode.create(:country => germany, :name => "Frauenau", :area_code => "9926") + AreaCode.create(:country => germany, :name => "Kirchberg Wald", :area_code => "9927") + AreaCode.create(:country => germany, :name => "Kirchdorf i Wald", :area_code => "9928") + AreaCode.create(:country => germany, :name => "Ruhmannsfelden", :area_code => "9929") + AreaCode.create(:country => germany, :name => "Plattling", :area_code => "9931") + AreaCode.create(:country => germany, :name => "Osterhofen", :area_code => "9932") + AreaCode.create(:country => germany, :name => "Wallersdorf", :area_code => "9933") + AreaCode.create(:country => germany, :name => "Stephansposching", :area_code => "9935") + AreaCode.create(:country => germany, :name => "Wallerfing", :area_code => "9936") + AreaCode.create(:country => germany, :name => "Oberpöring", :area_code => "9937") + AreaCode.create(:country => germany, :name => "Moos Niederbay", :area_code => "9938") + AreaCode.create(:country => germany, :name => "Kötzting", :area_code => "9941") + AreaCode.create(:country => germany, :name => "Viechtach", :area_code => "9942") + AreaCode.create(:country => germany, :name => "Lam Oberpf", :area_code => "9943") + AreaCode.create(:country => germany, :name => "Miltach", :area_code => "9944") + AreaCode.create(:country => germany, :name => "Arnbruck", :area_code => "9945") + AreaCode.create(:country => germany, :name => "Hohenwarth b Kötzing", :area_code => "9946") + AreaCode.create(:country => germany, :name => "Neukirchen b Hl Blut", :area_code => "9947") + AreaCode.create(:country => germany, :name => "Eschlkam", :area_code => "9948") + AreaCode.create(:country => germany, :name => "Landau a d Isar", :area_code => "9951") + AreaCode.create(:country => germany, :name => "Eichendorf", :area_code => "9952") + AreaCode.create(:country => germany, :name => "Pilsting", :area_code => "9953") + AreaCode.create(:country => germany, :name => "SimbachNiederbay", :area_code => "9954") + AreaCode.create(:country => germany, :name => "Mamming", :area_code => "9955") + AreaCode.create(:country => germany, :name => "Eichendorf-Aufhausen", :area_code => "9956") + AreaCode.create(:country => germany, :name => "Mitterfels", :area_code => "9961") + AreaCode.create(:country => germany, :name => "Schwarzach Niederbay", :area_code => "9962") + AreaCode.create(:country => germany, :name => "Konzell", :area_code => "9963") + AreaCode.create(:country => germany, :name => "Stallwang", :area_code => "9964") + AreaCode.create(:country => germany, :name => "Sankt Englmar", :area_code => "9965") + AreaCode.create(:country => germany, :name => "Wiesenfelden", :area_code => "9966") + AreaCode.create(:country => germany, :name => "Cham", :area_code => "9971") + AreaCode.create(:country => germany, :name => "Waldmünchen", :area_code => "9972") + AreaCode.create(:country => germany, :name => "Furth i Wald", :area_code => "9973") + AreaCode.create(:country => germany, :name => "Traitsching", :area_code => "9974") + AreaCode.create(:country => germany, :name => "Waldmünchen-Geigant", :area_code => "9975") + AreaCode.create(:country => germany, :name => "Rötz", :area_code => "9976") + AreaCode.create(:country => germany, :name => "Arnschwang", :area_code => "9977") + AreaCode.create(:country => germany, :name => "Schönthal Oberpf", :area_code => "9978") + + # Mobilfunknetze + # + '01511, 01512, 01514, 01515, 0160, 0170, 0171, 0175'.gsub(/[^0-9\,]/,'').split(/,/).each do |area_code| + AreaCode.create(:country => germany, :name => "D1 Mobilfunknetz (Telekom)", :area_code => area_code.gsub(/^0/,'')) + end + + '01520, 01522, 01525, 0162, 0172, 0173, 0174'.gsub(/[^0-9\,]/,'').split(/,/).each do |area_code| + AreaCode.create(:country => germany, :name => "Vodafone Mobilfunknetz", :area_code => area_code.gsub(/^0/,'')) + end + + '01577, 01578, 0163, 0177, 0178, 01570, 01575'.gsub(/[^0-9\,]/,'').split(/,/).each do |area_code| + AreaCode.create(:country => germany, :name => "E-Plus Mobilfunknetz", :area_code => area_code.gsub(/^0/,'')) + end + + '0176, 0179, 0159, 01505'.gsub(/[^0-9\,]/,'').split(/,/).each do |area_code| + AreaCode.create(:country => germany, :name => "O2 Mobilfunknetz", :area_code => area_code.gsub(/^0/,'')) + end + + # Sondervorwahlen + # + AreaCode.create(:country => germany, :name => "Service-Dienste", :area_code => "180") + AreaCode.create(:country => germany, :name => "Persönliche Rufnummern", :area_code => "700") + AreaCode.create(:country => germany, :name => "Entgeltfreie Telefondienste", :area_code => "800") + AreaCode.create(:country => germany, :name => "Premium Dienste", :area_code => "900") + end + end + + def down + germany = Country.find_by_name('Germany') + germany.area_codes.destroy_all + end +end diff --git a/db/migrate/20120119155619_area_codes_poland.rb b/db/migrate/20120119155619_area_codes_poland.rb new file mode 100644 index 0000000..fdf520b --- /dev/null +++ b/db/migrate/20120119155619_area_codes_poland.rb @@ -0,0 +1,69 @@ +# ruby encoding: utf-8 + +class AreaCodesPoland < ActiveRecord::Migration + def up + poland = Country.find_by_name('Poland') + + ################################################################ + # AreaCodes Poland + ################################################################ + + ActiveRecord::Base.transaction do + AreaCode.create(:country => poland, :name => "Biała Podlaska", :area_code => "83") + AreaCode.create(:country => poland, :name => "Białystok", :area_code => "85") + AreaCode.create(:country => poland, :name => "Bielsko-Biała", :area_code => "33") + AreaCode.create(:country => poland, :name => "Bydgoszcz", :area_code => "52") + AreaCode.create(:country => poland, :name => "Chełm", :area_code => "82") + AreaCode.create(:country => poland, :name => "Ciechanów", :area_code => "23") + AreaCode.create(:country => poland, :name => "Częstochowa", :area_code => "34") + AreaCode.create(:country => poland, :name => "Elbląg", :area_code => "55") + AreaCode.create(:country => poland, :name => "Gdańsk", :area_code => "58") + AreaCode.create(:country => poland, :name => "Gorzów Wielkopolski", :area_code => "95") + AreaCode.create(:country => poland, :name => "Jelenia Góra", :area_code => "75") + AreaCode.create(:country => poland, :name => "Kalisz", :area_code => "62") + AreaCode.create(:country => poland, :name => "Katowice", :area_code => "32") + AreaCode.create(:country => poland, :name => "Kielce", :area_code => "41") + AreaCode.create(:country => poland, :name => "Konin", :area_code => "63") + AreaCode.create(:country => poland, :name => "Koszalin", :area_code => "94") + AreaCode.create(:country => poland, :name => "Kraków", :area_code => "12") + AreaCode.create(:country => poland, :name => "Krosno", :area_code => "13") + AreaCode.create(:country => poland, :name => "Legnica", :area_code => "76") + AreaCode.create(:country => poland, :name => "Leszno", :area_code => "65") + AreaCode.create(:country => poland, :name => "Łódź", :area_code => "42") + AreaCode.create(:country => poland, :name => "Łomża", :area_code => "86") + AreaCode.create(:country => poland, :name => "Lublin", :area_code => "81") + AreaCode.create(:country => poland, :name => "Nowy Sącz", :area_code => "18") + AreaCode.create(:country => poland, :name => "Olsztyn", :area_code => "89") + AreaCode.create(:country => poland, :name => "Opole", :area_code => "77") + AreaCode.create(:country => poland, :name => "Ostrołęka", :area_code => "29") + AreaCode.create(:country => poland, :name => "Piła", :area_code => "67") + AreaCode.create(:country => poland, :name => "Piotrków Trybunalski", :area_code => "44") + AreaCode.create(:country => poland, :name => "Płock", :area_code => "24") + AreaCode.create(:country => poland, :name => "Poznań", :area_code => "61") + AreaCode.create(:country => poland, :name => "Przemyśl", :area_code => "16") + AreaCode.create(:country => poland, :name => "Radom", :area_code => "48") + AreaCode.create(:country => poland, :name => "Rzeszów", :area_code => "17") + AreaCode.create(:country => poland, :name => "Siedlce", :area_code => "25") + AreaCode.create(:country => poland, :name => "Sieradz", :area_code => "43") + AreaCode.create(:country => poland, :name => "Skierniewice", :area_code => "46") + AreaCode.create(:country => poland, :name => "Słupsk", :area_code => "59") + AreaCode.create(:country => poland, :name => "Suwałki", :area_code => "87") + AreaCode.create(:country => poland, :name => "Szczecin", :area_code => "91") + AreaCode.create(:country => poland, :name => "Tarnobrzeg", :area_code => "15") + AreaCode.create(:country => poland, :name => "Tarnów", :area_code => "14") + AreaCode.create(:country => poland, :name => "Toruń", :area_code => "56") + AreaCode.create(:country => poland, :name => "Wałbrzych", :area_code => "74") + AreaCode.create(:country => poland, :name => "Warszawa", :area_code => "22") + AreaCode.create(:country => poland, :name => "Włocławek", :area_code => "54") + AreaCode.create(:country => poland, :name => "Wrocław", :area_code => "71") + AreaCode.create(:country => poland, :name => "Zamość", :area_code => "84") + AreaCode.create(:country => poland, :name => "Zielona Góra", :area_code => "68") + end + + end + + def down + poland = Country.find_by_name('Poland') + poland.area_codes.destroy_all + end +end diff --git a/db/migrate/20120119161017_call_forward_cases.rb b/db/migrate/20120119161017_call_forward_cases.rb new file mode 100644 index 0000000..2f6466b --- /dev/null +++ b/db/migrate/20120119161017_call_forward_cases.rb @@ -0,0 +1,23 @@ +# ruby encoding: utf-8 + +class CallForwardCases < ActiveRecord::Migration + def up + ################################################################ + # Call forward cases + ################################################################ + + [ + 'always', + 'busy', + 'noanswer', + 'offline', + 'assistant', + ].each { |case_name| + CallForwardCase.create( :value => case_name ) + } + end + + def down + CallForwardCase.destroy_all + end +end diff --git a/db/migrate/20120119161152_snom_phones.rb b/db/migrate/20120119161152_snom_phones.rb new file mode 100644 index 0000000..88c35f9 --- /dev/null +++ b/db/migrate/20120119161152_snom_phones.rb @@ -0,0 +1,63 @@ +# ruby encoding: utf-8 + +class SnomPhones < ActiveRecord::Migration + def up + ################################################################ + # Manufacturers + ################################################################ + add_column :phone_models, :uuid, :string rescue puts "column already added" + snom = Manufacturer.find_or_create_by_ieee_name('SNOM Technology AG', + { + :name => "SNOM Technology AG", + :homepage_url => 'http://www.snom.com' + } + ) + + + ################################################################ + # OUIs + ################################################################ + + snom.ouis.find_or_create_by_value('000413') + + + ################################################################ + # Phone models + ################################################################ + + snom300 = snom.phone_models.create(:name => 'Snom 300', + :product_homepage_url => 'http://www.snom.com/en/products/ip-phones/snom-300/', + :product_manual_homepage_url => 'http://wiki.snom.com/Snom300/Documentation') + + snom320 = snom.phone_models.create(:name => 'Snom 320', + :product_homepage_url => 'http://www.snom.com/en/products/ip-phones/snom-320/', + :product_manual_homepage_url => 'http://wiki.snom.com/Snom320/Documentation') + + snom360 = snom.phone_models.create(:name => 'Snom 360', + :product_homepage_url => 'http://www.snom.com/en/products/ip-phones/snom-360/', + :product_manual_homepage_url => 'http://wiki.snom.com/Snom360/Documentation') + + snom370 = snom.phone_models.create(:name => 'Snom 370', + :product_homepage_url => 'http://www.snom.com/en/products/ip-phones/snom-370/', + :product_manual_homepage_url => 'http://wiki.snom.com/Snom370/Documentation') + + snom820 = snom.phone_models.create(:name => 'Snom 820', + :product_homepage_url => 'http://www.snom.com/en/products/ip-phones/snom-820/', + :product_manual_homepage_url => 'http://wiki.snom.com/Snom820/Documentation') + + snom821 = snom.phone_models.create(:name => 'Snom 821', + :product_homepage_url => 'http://www.snom.com/en/products/ip-phones/snom-821/', + :product_manual_homepage_url => 'http://wiki.snom.com/Snom821/Documentation') + + snom870 = snom.phone_models.create(:name => 'Snom 870', + :product_homepage_url => 'http://www.snom.com/en/products/ip-phones/snom-870/', + :product_manual_homepage_url => 'http://wiki.snom.com/Snom870/Documentation') + + end + + def down + Manufacturer.destroy_all + Oui.destroy_all + PhoneModel.destroy_all + end +end diff --git a/db/migrate/20120124131057_add_clip_number_to_sip_account.rb b/db/migrate/20120124131057_add_clip_number_to_sip_account.rb new file mode 100644 index 0000000..e981fb4 --- /dev/null +++ b/db/migrate/20120124131057_add_clip_number_to_sip_account.rb @@ -0,0 +1,6 @@ +class AddClipNumberToSipAccount < ActiveRecord::Migration + def change + add_column :sip_accounts, :clip, :string + remove_column :sip_accounts, :clip_phone_number_id + end +end diff --git a/db/migrate/20120124133950_add_clip_no_screening_to_sip_account.rb b/db/migrate/20120124133950_add_clip_no_screening_to_sip_account.rb new file mode 100644 index 0000000..ce2a7e9 --- /dev/null +++ b/db/migrate/20120124133950_add_clip_no_screening_to_sip_account.rb @@ -0,0 +1,6 @@ +class AddClipNoScreeningToSipAccount < ActiveRecord::Migration + def change + add_column :sip_accounts, :clip_no_screening, :string + remove_column :sip_accounts, :clip + end +end diff --git a/db/migrate/20120124135953_add_clip_to_sip_account.rb b/db/migrate/20120124135953_add_clip_to_sip_account.rb new file mode 100644 index 0000000..51d3243 --- /dev/null +++ b/db/migrate/20120124135953_add_clip_to_sip_account.rb @@ -0,0 +1,5 @@ +class AddClipToSipAccount < ActiveRecord::Migration + def change + add_column :sip_accounts, :clip, :boolean + end +end diff --git a/db/migrate/20120124142001_remove_name_from_softkey.rb b/db/migrate/20120124142001_remove_name_from_softkey.rb new file mode 100644 index 0000000..7fc17d5 --- /dev/null +++ b/db/migrate/20120124142001_remove_name_from_softkey.rb @@ -0,0 +1,9 @@ +class RemoveNameFromSoftkey < ActiveRecord::Migration + def up + remove_column :softkeys, :name + end + + def down + add_column :softkeys, :name, :string + end +end diff --git a/db/migrate/20120126090831_create_callthroughs.rb b/db/migrate/20120126090831_create_callthroughs.rb new file mode 100644 index 0000000..e7d146e --- /dev/null +++ b/db/migrate/20120126090831_create_callthroughs.rb @@ -0,0 +1,15 @@ +class CreateCallthroughs < ActiveRecord::Migration + def self.up + create_table :callthroughs do |t| + t.integer :tenant_id + t.string :name + t.integer :sip_account_id + t.string :clip_no_screening + t.timestamps + end + end + + def self.down + drop_table :callthroughs + end +end diff --git a/db/migrate/20120127101726_create_system_messages.rb b/db/migrate/20120127101726_create_system_messages.rb new file mode 100644 index 0000000..830f54a --- /dev/null +++ b/db/migrate/20120127101726_create_system_messages.rb @@ -0,0 +1,13 @@ +class CreateSystemMessages < ActiveRecord::Migration + def self.up + create_table :system_messages do |t| + t.integer :user_id + t.string :content + t.timestamps + end + end + + def self.down + drop_table :system_messages + end +end diff --git a/db/migrate/20120128143538_openstage_phones.rb b/db/migrate/20120128143538_openstage_phones.rb new file mode 100644 index 0000000..64d5cb0 --- /dev/null +++ b/db/migrate/20120128143538_openstage_phones.rb @@ -0,0 +1,25 @@ +class OpenstagePhones < ActiveRecord::Migration + def up + siemens = Manufacturer.find_or_create_by_ieee_name('Siemens Enterprise CommunicationsGmbH & Co. KG', + :name => 'Siemens Enterprise Communications', + :homepage_url => 'http://www.siemens-enterprise.com') + + siemens.phone_models.find_or_create_by_name('Openstage 40', + :product_homepage_url => 'http://www.siemens-enterprise.com/de/products/devices-and-clients/open-stage-desktop-phones.aspx', + :product_manual_homepage_url => 'http://www.siemens-enterprise.com/de/support/downloads-phones-devices/~/media/BOL%20Documents/BOL%20Internet/1%20DownloadFile_P31003S2000U120010019@NETINFO.ashx') + + siemens.phone_models.find_or_create_by_name('Openstage 60', + :product_homepage_url => 'http://www.siemens-enterprise.com/de/products/devices-and-clients/open-stage-desktop-phones.aspx', + :product_manual_homepage_url => 'http://www.siemens-enterprise.com/de/support/downloads-phones-devices/~/media/BOL%20Documents/BOL%20Internet/1%20DownloadFile_P31003S2000U119010019@NETINFO.ashx') + + + siemens.phone_models.find_or_create_by_name('Openstage 80', + :product_homepage_url => 'http://www.siemens-enterprise.com/de/products/devices-and-clients/open-stage-desktop-phones.aspx', + :product_manual_homepage_url => 'http://www.siemens-enterprise.com/de/support/downloads-phones-devices/~/media/BOL%20Documents/BOL%20Internet/1%20DownloadFile_P31003S2000U119010019@NETINFO.ashx') + end + + def down + siemens = Manufacturer.find_by_ieee_name('Siemens Enterprise CommunicationsGmbH & Co. KG') + siemens.destroy + end +end diff --git a/db/migrate/20120128191113_create_access_authorizations.rb b/db/migrate/20120128191113_create_access_authorizations.rb new file mode 100644 index 0000000..b05a288 --- /dev/null +++ b/db/migrate/20120128191113_create_access_authorizations.rb @@ -0,0 +1,17 @@ +class CreateAccessAuthorizations < ActiveRecord::Migration + def self.up + create_table :access_authorizations do |t| + t.string :access_authorizationable_type + t.integer :access_authorizationable_id + t.string :name + t.string :login + t.string :pin + t.integer :position + t.timestamps + end + end + + def self.down + drop_table :access_authorizations + end +end diff --git a/db/migrate/20120129094444_create_whitelists.rb b/db/migrate/20120129094444_create_whitelists.rb new file mode 100644 index 0000000..9e31632 --- /dev/null +++ b/db/migrate/20120129094444_create_whitelists.rb @@ -0,0 +1,15 @@ +class CreateWhitelists < ActiveRecord::Migration + def self.up + create_table :whitelists do |t| + t.string :name + t.string :whitelistable_type + t.integer :whitelistable_id + t.integer :position + t.timestamps + end + end + + def self.down + drop_table :whitelists + end +end diff --git a/db/migrate/20120130155801_add_sip_account_id_to_access_authorization.rb b/db/migrate/20120130155801_add_sip_account_id_to_access_authorization.rb new file mode 100644 index 0000000..6907791 --- /dev/null +++ b/db/migrate/20120130155801_add_sip_account_id_to_access_authorization.rb @@ -0,0 +1,6 @@ +class AddSipAccountIdToAccessAuthorization < ActiveRecord::Migration + def change + add_column :access_authorizations, :sip_account_id, :integer + remove_column :callthroughs, :sip_account_id + end +end diff --git a/db/migrate/20120203093048_create_hunt_groups.rb b/db/migrate/20120203093048_create_hunt_groups.rb new file mode 100644 index 0000000..1f2cfb6 --- /dev/null +++ b/db/migrate/20120203093048_create_hunt_groups.rb @@ -0,0 +1,15 @@ +class CreateHuntGroups < ActiveRecord::Migration + def self.up + create_table :hunt_groups do |t| + t.integer :tenant_id + t.string :name + t.string :strategy + t.integer :seconds_between_jumps + t.timestamps + end + end + + def self.down + drop_table :hunt_groups + end +end diff --git a/db/migrate/20120203120739_create_hunt_group_members.rb b/db/migrate/20120203120739_create_hunt_group_members.rb new file mode 100644 index 0000000..31eb620 --- /dev/null +++ b/db/migrate/20120203120739_create_hunt_group_members.rb @@ -0,0 +1,16 @@ +class CreateHuntGroupMembers < ActiveRecord::Migration + def self.up + create_table :hunt_group_members do |t| + t.integer :hunt_group_id + t.string :name + t.integer :position + t.boolean :active + t.boolean :can_switch_status_itself + t.timestamps + end + end + + def self.down + drop_table :hunt_group_members + end +end diff --git a/db/migrate/20120210121455_add_language_id_to_users.rb b/db/migrate/20120210121455_add_language_id_to_users.rb new file mode 100644 index 0000000..9d6765e --- /dev/null +++ b/db/migrate/20120210121455_add_language_id_to_users.rb @@ -0,0 +1,5 @@ +class AddLanguageIdToUsers < ActiveRecord::Migration + def change + add_column :users, :language_id, :integer + end +end diff --git a/db/migrate/20120215160448_create_softkey_functions.rb b/db/migrate/20120215160448_create_softkey_functions.rb new file mode 100644 index 0000000..569c50c --- /dev/null +++ b/db/migrate/20120215160448_create_softkey_functions.rb @@ -0,0 +1,14 @@ +class CreateSoftkeyFunctions < ActiveRecord::Migration + def up + create_table :softkey_functions do |t| + t.string :name + + t.timestamps + end + + end + + def down + drop_table :softkey_functions + end +end diff --git a/db/migrate/20120215160449_add_position_to_softkey_function.rb b/db/migrate/20120215160449_add_position_to_softkey_function.rb new file mode 100644 index 0000000..16f6b06 --- /dev/null +++ b/db/migrate/20120215160449_add_position_to_softkey_function.rb @@ -0,0 +1,7 @@ +class AddPositionToSoftkeyFunction < ActiveRecord::Migration + def change + add_column :softkey_functions, :position, :integer + add_index :softkey_functions, :position + add_index :softkey_functions, :name + end +end diff --git a/db/migrate/20120215161054_add_softkey_function_id_to_softkey.rb b/db/migrate/20120215161054_add_softkey_function_id_to_softkey.rb new file mode 100644 index 0000000..0f6600f --- /dev/null +++ b/db/migrate/20120215161054_add_softkey_function_id_to_softkey.rb @@ -0,0 +1,5 @@ +class AddSoftkeyFunctionIdToSoftkey < ActiveRecord::Migration + def change + add_column :softkeys, :softkey_function_id, :integer + end +end diff --git a/db/migrate/20120218182205_create_api_rows.rb b/db/migrate/20120218182205_create_api_rows.rb new file mode 100644 index 0000000..d949b3f --- /dev/null +++ b/db/migrate/20120218182205_create_api_rows.rb @@ -0,0 +1,21 @@ +class CreateApiRows < ActiveRecord::Migration + def change + create_table :api_rows do |t| + t.string :user_id + t.string :user_name + t.string :last_name + t.string :middle_name + t.string :first_name + t.string :office_phone_number + t.string :internal_extension + t.string :mobile_phone_number + t.string :fax_phone_number + t.string :email + t.string :pin + t.datetime :pin_updated_at + t.string :photo_file_name + + t.timestamps + end + end +end diff --git a/db/migrate/20120219210950_remove_state_from_user.rb b/db/migrate/20120219210950_remove_state_from_user.rb new file mode 100644 index 0000000..3bb8f59 --- /dev/null +++ b/db/migrate/20120219210950_remove_state_from_user.rb @@ -0,0 +1,9 @@ +class RemoveStateFromUser < ActiveRecord::Migration + def up + remove_column :users, :state + end + + def down + add_column :users, :state, :string + end +end diff --git a/db/migrate/20120228154913_create_cdrs.rb b/db/migrate/20120228154913_create_cdrs.rb new file mode 100644 index 0000000..f9f92b7 --- /dev/null +++ b/db/migrate/20120228154913_create_cdrs.rb @@ -0,0 +1,33 @@ +class CreateCdrs < ActiveRecord::Migration + def self.up + create_table :cdrs, :id => false do |t| + t.string :uuid, :limit => '256', :primary => true + t.integer :account_id + t.string :account_type, :limit => '256' + t.string :bleg_uuid, :limit => '256' + t.integer :bleg_account_id + t.string :bleg_account_type, :limit => '256' + t.string :dialed_number, :limit => '256' + t.string :destination_number, :limit => '256' + t.string :caller_id_number, :limit => '256' + t.string :caller_id_name, :limit => '256' + t.string :callee_id_number, :limit => '256' + t.string :callee_id_name, :limit => '256' + t.datetime :start_stamp + t.datetime :answer_stamp + t.datetime :end_stamp + t.integer :duration + t.integer :billsec + t.string :hangup_cause, :limit => '256' + t.string :dialstatus, :limit => '256' + t.string :forwarding_number, :limit => '256' + t.integer :forwarding_account_id + t.string :forwarding_account_type, :limit => '256' + t.string :forwarding_service, :limit => '256' + end + end + + def self.down + drop_table :cdrs + end +end diff --git a/db/migrate/20120301094049_add_voicemail_boolean_to_call_forward.rb b/db/migrate/20120301094049_add_voicemail_boolean_to_call_forward.rb new file mode 100644 index 0000000..a5ac2f0 --- /dev/null +++ b/db/migrate/20120301094049_add_voicemail_boolean_to_call_forward.rb @@ -0,0 +1,6 @@ +class AddVoicemailBooleanToCallForward < ActiveRecord::Migration + def change + add_column :call_forwards, :to_voicemail, :boolean + + end +end diff --git a/db/migrate/20120312175918_add_hunt_group_id_to_call_forward.rb b/db/migrate/20120312175918_add_hunt_group_id_to_call_forward.rb new file mode 100644 index 0000000..f5d3f79 --- /dev/null +++ b/db/migrate/20120312175918_add_hunt_group_id_to_call_forward.rb @@ -0,0 +1,6 @@ +class AddHuntGroupIdToCallForward < ActiveRecord::Migration + def change + add_column :call_forwards, :hunt_group_id, :integer + + end +end diff --git a/db/migrate/20120313121920_add_polymorphic_to_call_forward.rb b/db/migrate/20120313121920_add_polymorphic_to_call_forward.rb new file mode 100644 index 0000000..3f7bae7 --- /dev/null +++ b/db/migrate/20120313121920_add_polymorphic_to_call_forward.rb @@ -0,0 +1,9 @@ +class AddPolymorphicToCallForward < ActiveRecord::Migration + def change + add_column :call_forwards, :call_forwardable_type, :string + add_column :call_forwards, :call_forwardable_id, :integer + + remove_column :call_forwards, :hunt_group_id + remove_column :call_forwards, :to_voicemail + end +end diff --git a/db/migrate/20120314094720_add_send_voicemail_as_email_attachment_to_user.rb b/db/migrate/20120314094720_add_send_voicemail_as_email_attachment_to_user.rb new file mode 100644 index 0000000..7b74c4f --- /dev/null +++ b/db/migrate/20120314094720_add_send_voicemail_as_email_attachment_to_user.rb @@ -0,0 +1,6 @@ +class AddSendVoicemailAsEmailAttachmentToUser < ActiveRecord::Migration + def change + add_column :users, :send_voicemail_as_email_attachment, :boolean + + end +end diff --git a/db/migrate/20120329133930_add_bleg_read_time_to_cdrs.rb b/db/migrate/20120329133930_add_bleg_read_time_to_cdrs.rb new file mode 100644 index 0000000..c09eb6a --- /dev/null +++ b/db/migrate/20120329133930_add_bleg_read_time_to_cdrs.rb @@ -0,0 +1,6 @@ +class AddBlegReadTimeToCdrs < ActiveRecord::Migration + def change + add_column :cdrs, :bleg_read_time, :datetime + + end +end diff --git a/db/migrate/20120409092724_add_importer_checksum_to_user.rb b/db/migrate/20120409092724_add_importer_checksum_to_user.rb new file mode 100644 index 0000000..a457bcf --- /dev/null +++ b/db/migrate/20120409092724_add_importer_checksum_to_user.rb @@ -0,0 +1,6 @@ +class AddImporterChecksumToUser < ActiveRecord::Migration + def change + add_column :users, :importer_checksum, :string + + end +end diff --git a/db/migrate/20120409160614_add_description_to_sip_account.rb b/db/migrate/20120409160614_add_description_to_sip_account.rb new file mode 100644 index 0000000..7e848f0 --- /dev/null +++ b/db/migrate/20120409160614_add_description_to_sip_account.rb @@ -0,0 +1,6 @@ +class AddDescriptionToSipAccount < ActiveRecord::Migration + def change + add_column :sip_accounts, :description, :string + + end +end diff --git a/db/migrate/20120416074152_callforward_rules_act_per_sip_account_to_sip_account.rb b/db/migrate/20120416074152_callforward_rules_act_per_sip_account_to_sip_account.rb new file mode 100644 index 0000000..72cac09 --- /dev/null +++ b/db/migrate/20120416074152_callforward_rules_act_per_sip_account_to_sip_account.rb @@ -0,0 +1,5 @@ +class CallforwardRulesActPerSipAccountToSipAccount < ActiveRecord::Migration + def change + add_column :sip_accounts, :callforward_rules_act_per_sip_account, :boolean + end +end diff --git a/db/migrate/20120418194029_add_call_forward_id_to_softkeys.rb b/db/migrate/20120418194029_add_call_forward_id_to_softkeys.rb new file mode 100644 index 0000000..c9ad754 --- /dev/null +++ b/db/migrate/20120418194029_add_call_forward_id_to_softkeys.rb @@ -0,0 +1,6 @@ +class AddCallForwardIdToSoftkeys < ActiveRecord::Migration + def change + add_column :softkeys, :call_forward_id, :integer + + end +end diff --git a/db/migrate/20120421073538_add_position_to_call_forward.rb b/db/migrate/20120421073538_add_position_to_call_forward.rb new file mode 100644 index 0000000..e8bf4aa --- /dev/null +++ b/db/migrate/20120421073538_add_position_to_call_forward.rb @@ -0,0 +1,6 @@ +class AddPositionToCallForward < ActiveRecord::Migration + def change + add_column :call_forwards, :position, :integer + + end +end diff --git a/db/migrate/20120421075735_populate_softkey_functions.rb b/db/migrate/20120421075735_populate_softkey_functions.rb new file mode 100644 index 0000000..36eb02f --- /dev/null +++ b/db/migrate/20120421075735_populate_softkey_functions.rb @@ -0,0 +1,9 @@ +class PopulateSoftkeyFunctions < ActiveRecord::Migration + def up + SoftkeyFunction.create(:name => 'call_forwarding') + end + + def down + SoftkeyFunction.where(:name => 'call_forwarding').destroy_all + end +end diff --git a/db/migrate/20120422072551_populate_softkey_function.rb b/db/migrate/20120422072551_populate_softkey_function.rb new file mode 100644 index 0000000..91f5fd2 --- /dev/null +++ b/db/migrate/20120422072551_populate_softkey_function.rb @@ -0,0 +1,18 @@ +class PopulateSoftkeyFunction < ActiveRecord::Migration + def up + ['speed_dial', 'blf', 'dtmf', 'log_out', 'log_in', 'conference'].each do |function_name| + SoftkeyFunction.create(:name => function_name) + end + + SoftkeyFunction.where(:position => nil).order(:id).each do |softkey_function| + softkey_function.update_attributes(:position => softkey_function.id) if softkey_function.position.nil? + end + deactivated_softkey_function = SoftkeyFunction.create(:name => 'deactivated') + deactivated_softkey_function.move_to_top + end + + def down + SoftkeyFunction.where(:name => ['speed_dial', 'blf', 'dtmf', 'log_out', 'log_in', 'conference', 'deactivated'] ).destroy_all + end + +end diff --git a/db/migrate/20120424062951_add_hotdeskable_to_sip_account.rb b/db/migrate/20120424062951_add_hotdeskable_to_sip_account.rb new file mode 100644 index 0000000..3d917d6 --- /dev/null +++ b/db/migrate/20120424062951_add_hotdeskable_to_sip_account.rb @@ -0,0 +1,6 @@ +class AddHotdeskableToSipAccount < ActiveRecord::Migration + def change + add_column :sip_accounts, :hotdeskable, :boolean + + end +end diff --git a/db/migrate/20120425112414_add_nightly_reboot_to_phone.rb b/db/migrate/20120425112414_add_nightly_reboot_to_phone.rb new file mode 100644 index 0000000..46ddf37 --- /dev/null +++ b/db/migrate/20120425112414_add_nightly_reboot_to_phone.rb @@ -0,0 +1,6 @@ +class AddNightlyRebootToPhone < ActiveRecord::Migration + def change + add_column :phones, :nightly_reboot, :boolean + + end +end diff --git a/db/migrate/20120509071426_add_from_field_to_tenant.rb b/db/migrate/20120509071426_add_from_field_to_tenant.rb new file mode 100644 index 0000000..b99dc3e --- /dev/null +++ b/db/migrate/20120509071426_add_from_field_to_tenant.rb @@ -0,0 +1,8 @@ +class AddFromFieldToTenant < ActiveRecord::Migration + def change + add_column :tenants, :from_field_voicemail_email, :string + + add_column :tenants, :from_field_pin_change_email, :string + + end +end diff --git a/db/migrate/20120510110311_add_forwarding_read_time_to_cdrs.rb b/db/migrate/20120510110311_add_forwarding_read_time_to_cdrs.rb new file mode 100644 index 0000000..b152457 --- /dev/null +++ b/db/migrate/20120510110311_add_forwarding_read_time_to_cdrs.rb @@ -0,0 +1,6 @@ +class AddForwardingReadTimeToCdrs < ActiveRecord::Migration + def change + add_column :cdrs, :forwarding_read_time, :datetime + + end +end diff --git a/db/migrate/20120513154359_create_gui_functions.rb b/db/migrate/20120513154359_create_gui_functions.rb new file mode 100644 index 0000000..e1d814c --- /dev/null +++ b/db/migrate/20120513154359_create_gui_functions.rb @@ -0,0 +1,14 @@ +class CreateGuiFunctions < ActiveRecord::Migration + def self.up + create_table :gui_functions do |t| + t.string :category + t.string :name + t.string :description + t.timestamps + end + end + + def self.down + drop_table :gui_functions + end +end diff --git a/db/migrate/20120513155342_create_gui_function_memberships.rb b/db/migrate/20120513155342_create_gui_function_memberships.rb new file mode 100644 index 0000000..d271853 --- /dev/null +++ b/db/migrate/20120513155342_create_gui_function_memberships.rb @@ -0,0 +1,12 @@ +class CreateGuiFunctionMemberships < ActiveRecord::Migration + def change + create_table :gui_function_memberships do |t| + t.integer :gui_function_id + t.integer :user_group_id + t.boolean :activated + t.string :output + + t.timestamps + end + end +end diff --git a/db/migrate/20120513185233_add_gui_functions.rb b/db/migrate/20120513185233_add_gui_functions.rb new file mode 100644 index 0000000..c70d61d --- /dev/null +++ b/db/migrate/20120513185233_add_gui_functions.rb @@ -0,0 +1,47 @@ +class AddGuiFunctions < ActiveRecord::Migration + def up + GuiFunction.create(:category => 'Top navigation bar', :name => 'user_avatar_in_top_navigation_bar', + :description => 'Show the user avatar in the top navigaction bar.') + GuiFunction.create(:category => 'Top navigation bar', :name => 'search_field_in_top_navigation_bar', + :description => 'Show the search field for phone book entries in the top navigation bar.') + GuiFunction.create(:category => 'Top navigation bar', :name => 'navigation_items_in_top_navigation_bar', + :description => 'Show the navigation items in the top navigation bar.') + GuiFunction.create(:category => 'User show view', :name => 'show_phone_books_in_user_show_view', + :description => 'Show the available phone books in the user show view.') + GuiFunction.create(:category => 'Footer', :name => 'amooma_commercial_support_link_in_footer', + :description => 'Show a link to the AMOOMA commerical support page in the footer.') + GuiFunction.create(:category => 'Footer', :name => 'gemeinschaft_mailinglist_link_in_footer', + :description => 'Show a link to the Gemeinschaft Mailinglist in the footer.') + GuiFunction.create(:category => 'User edit view', :name => 'name_data_fields_in_user_edit_form', + :description => 'Show gender, first name, middle name, last name in the User edit form.') + GuiFunction.create(:category => 'User edit view', :name => 'user_name_field_in_user_edit_form', + :description => 'Show user name (login) field in the User edit form.') + GuiFunction.create(:category => 'User edit view', :name => 'email_field_in_user_edit_form', + :description => 'Show e-mail field in the User edit form.') + GuiFunction.create(:category => 'User edit view', :name => 'password_fields_in_user_edit_form', + :description => 'Show password fields in the User edit form.') + GuiFunction.create(:category => 'User edit view', :name => 'pin_fields_in_user_edit_form', + :description => 'Show PIN fields in the User edit form.') + GuiFunction.create(:category => 'Call Forward edit view', :name => 'depth_field_in_call_forward_form', + :description => 'Show depth field in the call forward form.') + GuiFunction.create(:category => 'Call Forward index view', :name => 'depth_field_value_in_index_table', + :description => 'Show depth field in the call forwards table.') + + CallForwardCase.all.each do |call_forward_case| + GuiFunction.create(:category => 'Call Forward edit view', :name => "call_forward_case_#{call_forward_case.value.downcase}_field_in_call_forward_form", + :description => "Show the call forward case '#{call_forward_case.value}' in the forward form.") + end + + GuiFunction.create(:category => 'Call Forward edit view', :name => 'huntgroup_in_destination_field_in_call_forward_form', + :description => 'Show huntgroups in the destination field of the call forward form.') + + SoftkeyFunction.all.each do |softkey_function| + GuiFunction.create(:category => 'Softkey edit view', :name => "softkey_function_#{softkey_function.name.downcase}_field_in_softkey_form", + :description => "Show the softkey function '#{softkey_function.name}' in the softkey form.") + end + end + + def down + GuiFunction.destroy_all + end +end diff --git a/db/migrate/20120515104749_add_hunt_group_function_key.rb b/db/migrate/20120515104749_add_hunt_group_function_key.rb new file mode 100644 index 0000000..02689d5 --- /dev/null +++ b/db/migrate/20120515104749_add_hunt_group_function_key.rb @@ -0,0 +1,14 @@ +class AddHuntGroupFunctionKey < ActiveRecord::Migration + def up + softkey_function = SoftkeyFunction.find_or_create_by_name('hunt_group_membership') + gui_function = GuiFunction.find_or_create_by_name("softkey_function_#{softkey_function.name.downcase}_field_in_softkey_form", + :category => 'Softkey edit view', + :description => "Show the softkey function '#{softkey_function.name}' in the softkey form.") + end + + def down + softkey_function = SoftkeyFunction.find_by_name('hunt_group_membership') + softkey_function.destroy + GuiFunction.find_by_name("softkey_function_#{softkey_function.name.downcase}_field_in_softkey_form").destroy + end +end diff --git a/db/migrate/20120515124103_add_hold_softkey_function.rb b/db/migrate/20120515124103_add_hold_softkey_function.rb new file mode 100644 index 0000000..4e16587 --- /dev/null +++ b/db/migrate/20120515124103_add_hold_softkey_function.rb @@ -0,0 +1,14 @@ +class AddHoldSoftkeyFunction < ActiveRecord::Migration + def up + softkey_function = SoftkeyFunction.find_or_create_by_name('hold') + gui_function = GuiFunction.find_or_create_by_name("softkey_function_#{softkey_function.name.downcase}_field_in_softkey_form", + :category => 'Softkey edit view', + :description => "Show the softkey function '#{softkey_function.name}' in the softkey form.") + end + + def down + softkey_function = SoftkeyFunction.find_by_name('hold') + softkey_function.destroy + GuiFunction.find_by_name("softkey_function_#{softkey_function.name.downcase}_field_in_softkey_form").destroy + end +end diff --git a/db/migrate/20120611093946_create_gs_nodes.rb b/db/migrate/20120611093946_create_gs_nodes.rb new file mode 100644 index 0000000..4aa167d --- /dev/null +++ b/db/migrate/20120611093946_create_gs_nodes.rb @@ -0,0 +1,14 @@ +class CreateGsNodes < ActiveRecord::Migration + def self.up + create_table :gs_nodes do |t| + t.string :name + t.string :ip_address + t.boolean :push_updates + t.timestamps + end + end + + def self.down + drop_table :gs_nodes + end +end diff --git a/db/migrate/20120611150245_add_gs_node_id_to_phone_number.rb b/db/migrate/20120611150245_add_gs_node_id_to_phone_number.rb new file mode 100644 index 0000000..d896e5d --- /dev/null +++ b/db/migrate/20120611150245_add_gs_node_id_to_phone_number.rb @@ -0,0 +1,8 @@ +class AddGsNodeIdToPhoneNumber < ActiveRecord::Migration + def change + add_column :phone_numbers, :gs_node_id, :integer + + add_column :phone_numbers, :gs_node_original_id, :integer + + end +end diff --git a/db/migrate/20120614113123_add_gs_node_information_to_user.rb b/db/migrate/20120614113123_add_gs_node_information_to_user.rb new file mode 100644 index 0000000..171958f --- /dev/null +++ b/db/migrate/20120614113123_add_gs_node_information_to_user.rb @@ -0,0 +1,8 @@ +class AddGsNodeInformationToUser < ActiveRecord::Migration + def change + add_column :users, :gs_node_id, :integer + + add_column :users, :gs_node_original_id, :integer + + end +end diff --git a/db/migrate/20120614113931_add_gs_node_information_to_sip_account.rb b/db/migrate/20120614113931_add_gs_node_information_to_sip_account.rb new file mode 100644 index 0000000..057f543 --- /dev/null +++ b/db/migrate/20120614113931_add_gs_node_information_to_sip_account.rb @@ -0,0 +1,8 @@ +class AddGsNodeInformationToSipAccount < ActiveRecord::Migration + def change + add_column :sip_accounts, :gs_node_id, :integer + + add_column :sip_accounts, :gs_node_original_id, :integer + + end +end diff --git a/db/migrate/20120614115938_add_gs_node_information_to_hunt_group.rb b/db/migrate/20120614115938_add_gs_node_information_to_hunt_group.rb new file mode 100644 index 0000000..0e5bdd3 --- /dev/null +++ b/db/migrate/20120614115938_add_gs_node_information_to_hunt_group.rb @@ -0,0 +1,8 @@ +class AddGsNodeInformationToHuntGroup < ActiveRecord::Migration + def change + add_column :hunt_groups, :gs_node_id, :integer + + add_column :hunt_groups, :gs_node_original_id, :integer + + end +end diff --git a/db/migrate/20120617065247_create_gs_cluster_sync_log_entries.rb b/db/migrate/20120617065247_create_gs_cluster_sync_log_entries.rb new file mode 100644 index 0000000..83d6a5b --- /dev/null +++ b/db/migrate/20120617065247_create_gs_cluster_sync_log_entries.rb @@ -0,0 +1,17 @@ +class CreateGsClusterSyncLogEntries < ActiveRecord::Migration + def self.up + create_table :gs_cluster_sync_log_entries do |t| + t.integer :gs_node_id + t.string :class_name + t.string :action + t.text :content + t.string :status + t.string :history + t.timestamps + end + end + + def self.down + drop_table :gs_cluster_sync_log_entries + end +end diff --git a/db/migrate/20120617193636_add_site_to_gs_node.rb b/db/migrate/20120617193636_add_site_to_gs_node.rb new file mode 100644 index 0000000..f2f6545 --- /dev/null +++ b/db/migrate/20120617193636_add_site_to_gs_node.rb @@ -0,0 +1,12 @@ +class AddSiteToGsNode < ActiveRecord::Migration + def change + add_column :gs_nodes, :site, :string + + add_column :gs_nodes, :element_name, :string + + rename_column :gs_nodes, :push_updates, :push_updates_to + + add_column :gs_nodes, :accepts_updates_from, :boolean + + end +end diff --git a/db/migrate/20120626094238_add_uuid_to_phone_number.rb b/db/migrate/20120626094238_add_uuid_to_phone_number.rb new file mode 100644 index 0000000..095981e --- /dev/null +++ b/db/migrate/20120626094238_add_uuid_to_phone_number.rb @@ -0,0 +1,11 @@ +class AddUuidToPhoneNumber < ActiveRecord::Migration + def change + add_column :phone_numbers, :uuid, :string rescue puts "column already added" + add_column :sip_accounts, :uuid, :string + add_column :hunt_groups, :uuid, :string + + add_index :phone_numbers, :uuid rescue puts "index already added" + add_index :sip_accounts, :uuid + add_index :hunt_groups, :uuid + end +end diff --git a/db/migrate/20120626095730_add_uuid_to_users.rb b/db/migrate/20120626095730_add_uuid_to_users.rb new file mode 100644 index 0000000..dbd5008 --- /dev/null +++ b/db/migrate/20120626095730_add_uuid_to_users.rb @@ -0,0 +1,6 @@ +class AddUuidToUsers < ActiveRecord::Migration + def change + add_column :users, :uuid, :string + add_index :users, :uuid + end +end diff --git a/db/migrate/20120715175419_add_homebase_ip_address_to_gs_cluster_sync_log_entry.rb b/db/migrate/20120715175419_add_homebase_ip_address_to_gs_cluster_sync_log_entry.rb new file mode 100644 index 0000000..318ff25 --- /dev/null +++ b/db/migrate/20120715175419_add_homebase_ip_address_to_gs_cluster_sync_log_entry.rb @@ -0,0 +1,6 @@ +class AddHomebaseIpAddressToGsClusterSyncLogEntry < ActiveRecord::Migration + def change + add_column :gs_cluster_sync_log_entries, :homebase_ip_address, :string + + end +end diff --git a/db/migrate/20120720070000_add_waiting_to_be_syned_to_gs_cluster_sync_log_entry.rb b/db/migrate/20120720070000_add_waiting_to_be_syned_to_gs_cluster_sync_log_entry.rb new file mode 100644 index 0000000..d0d67be --- /dev/null +++ b/db/migrate/20120720070000_add_waiting_to_be_syned_to_gs_cluster_sync_log_entry.rb @@ -0,0 +1,6 @@ +class AddWaitingToBeSynedToGsClusterSyncLogEntry < ActiveRecord::Migration + def change + add_column :gs_cluster_sync_log_entries, :waiting_to_be_synced, :boolean + + end +end diff --git a/db/migrate/20120722072517_add_association_to_gs_cluster_sync_log_entry.rb b/db/migrate/20120722072517_add_association_to_gs_cluster_sync_log_entry.rb new file mode 100644 index 0000000..6c9c050 --- /dev/null +++ b/db/migrate/20120722072517_add_association_to_gs_cluster_sync_log_entry.rb @@ -0,0 +1,8 @@ +class AddAssociationToGsClusterSyncLogEntry < ActiveRecord::Migration + def change + add_column :gs_cluster_sync_log_entries, :association_method, :string + + add_column :gs_cluster_sync_log_entries, :association_uuid, :string + + end +end diff --git a/db/migrate/20120723065038_add_uuid_to_tenants.rb b/db/migrate/20120723065038_add_uuid_to_tenants.rb new file mode 100644 index 0000000..14d6912 --- /dev/null +++ b/db/migrate/20120723065038_add_uuid_to_tenants.rb @@ -0,0 +1,6 @@ +class AddUuidToTenants < ActiveRecord::Migration + def change + add_column :tenants, :uuid, :string + + end +end diff --git a/db/migrate/20120724131815_add_uuid_to_conferences.rb b/db/migrate/20120724131815_add_uuid_to_conferences.rb new file mode 100644 index 0000000..17c26a5 --- /dev/null +++ b/db/migrate/20120724131815_add_uuid_to_conferences.rb @@ -0,0 +1,6 @@ +class AddUuidToConferences < ActiveRecord::Migration + def change + add_column :conferences, :uuid, :string + + end +end diff --git a/db/migrate/20120724131905_add_uuid_to_callthroughs.rb b/db/migrate/20120724131905_add_uuid_to_callthroughs.rb new file mode 100644 index 0000000..9ec067b --- /dev/null +++ b/db/migrate/20120724131905_add_uuid_to_callthroughs.rb @@ -0,0 +1,6 @@ +class AddUuidToCallthroughs < ActiveRecord::Migration + def change + add_column :callthroughs, :uuid, :string + + end +end diff --git a/db/migrate/20120724131956_add_uuid_to_fax_accounts.rb b/db/migrate/20120724131956_add_uuid_to_fax_accounts.rb new file mode 100644 index 0000000..177260f --- /dev/null +++ b/db/migrate/20120724131956_add_uuid_to_fax_accounts.rb @@ -0,0 +1,6 @@ +class AddUuidToFaxAccounts < ActiveRecord::Migration + def change + add_column :fax_accounts, :uuid, :string + + end +end diff --git a/db/migrate/20120727102518_add_uuid_to_phone_book_entry.rb b/db/migrate/20120727102518_add_uuid_to_phone_book_entry.rb new file mode 100644 index 0000000..920fe15 --- /dev/null +++ b/db/migrate/20120727102518_add_uuid_to_phone_book_entry.rb @@ -0,0 +1,7 @@ +class AddUuidToPhoneBookEntry < ActiveRecord::Migration + def change + add_column :phone_book_entries, :uuid, :string + add_index :phone_book_entries, :uuid + + end +end diff --git a/db/migrate/20120727105750_add_uuid_to_access_authorization.rb b/db/migrate/20120727105750_add_uuid_to_access_authorization.rb new file mode 100644 index 0000000..78f3197 --- /dev/null +++ b/db/migrate/20120727105750_add_uuid_to_access_authorization.rb @@ -0,0 +1,7 @@ +class AddUuidToAccessAuthorization < ActiveRecord::Migration + def change + add_column :access_authorizations, :uuid, :string + add_index :access_authorizations, :uuid + + end +end diff --git a/db/migrate/20120727110501_add_access_authorization_user_id_to_phone_number.rb b/db/migrate/20120727110501_add_access_authorization_user_id_to_phone_number.rb new file mode 100644 index 0000000..325952d --- /dev/null +++ b/db/migrate/20120727110501_add_access_authorization_user_id_to_phone_number.rb @@ -0,0 +1,6 @@ +class AddAccessAuthorizationUserIdToPhoneNumber < ActiveRecord::Migration + def change + add_column :phone_numbers, :access_authorization_user_id, :integer + + end +end diff --git a/db/migrate/20120728073802_add_is_native_to_user.rb b/db/migrate/20120728073802_add_is_native_to_user.rb new file mode 100644 index 0000000..6cfc160 --- /dev/null +++ b/db/migrate/20120728073802_add_is_native_to_user.rb @@ -0,0 +1,6 @@ +class AddIsNativeToUser < ActiveRecord::Migration + def change + add_column :users, :is_native, :boolean + + end +end diff --git a/db/migrate/20120728073904_add_is_native_to_phone_number.rb b/db/migrate/20120728073904_add_is_native_to_phone_number.rb new file mode 100644 index 0000000..6d4782d --- /dev/null +++ b/db/migrate/20120728073904_add_is_native_to_phone_number.rb @@ -0,0 +1,6 @@ +class AddIsNativeToPhoneNumber < ActiveRecord::Migration + def change + add_column :phone_numbers, :is_native, :boolean + + end +end diff --git a/db/migrate/20120728091842_add_is_native_to_sip_account.rb b/db/migrate/20120728091842_add_is_native_to_sip_account.rb new file mode 100644 index 0000000..de230d1 --- /dev/null +++ b/db/migrate/20120728091842_add_is_native_to_sip_account.rb @@ -0,0 +1,6 @@ +class AddIsNativeToSipAccount < ActiveRecord::Migration + def change + add_column :sip_accounts, :is_native, :boolean + + end +end diff --git a/db/migrate/20120728113705_add_uuid_to_hunt_group_member.rb b/db/migrate/20120728113705_add_uuid_to_hunt_group_member.rb new file mode 100644 index 0000000..fc2fb5c --- /dev/null +++ b/db/migrate/20120728113705_add_uuid_to_hunt_group_member.rb @@ -0,0 +1,7 @@ +class AddUuidToHuntGroupMember < ActiveRecord::Migration + def change + add_column :hunt_group_members, :uuid, :string + add_index :hunt_group_members, :uuid + + end +end diff --git a/db/migrate/20120728133144_add_uuid_to_phone_number_range.rb b/db/migrate/20120728133144_add_uuid_to_phone_number_range.rb new file mode 100644 index 0000000..1674ea0 --- /dev/null +++ b/db/migrate/20120728133144_add_uuid_to_phone_number_range.rb @@ -0,0 +1,7 @@ +class AddUuidToPhoneNumberRange < ActiveRecord::Migration + def change + add_column :phone_number_ranges, :uuid, :string + add_index :phone_number_ranges, :uuid + + end +end diff --git a/db/migrate/20120728135133_add_uuid_to_fax_documents.rb b/db/migrate/20120728135133_add_uuid_to_fax_documents.rb new file mode 100644 index 0000000..c1d96d1 --- /dev/null +++ b/db/migrate/20120728135133_add_uuid_to_fax_documents.rb @@ -0,0 +1,6 @@ +class AddUuidToFaxDocuments < ActiveRecord::Migration + def change + add_column :fax_documents, :uuid, :string + + end +end diff --git a/db/migrate/20120802132854_add_notification_to_voicemail_msgs.rb b/db/migrate/20120802132854_add_notification_to_voicemail_msgs.rb new file mode 100644 index 0000000..4ebc33e --- /dev/null +++ b/db/migrate/20120802132854_add_notification_to_voicemail_msgs.rb @@ -0,0 +1,6 @@ +class AddNotificationToVoicemailMsgs < ActiveRecord::Migration + def change + add_column :voicemail_msgs, :notification, :boolean + + end +end diff --git a/db/migrate/20120813085708_add_uuid_to_conference_invitee.rb b/db/migrate/20120813085708_add_uuid_to_conference_invitee.rb new file mode 100644 index 0000000..9017767 --- /dev/null +++ b/db/migrate/20120813085708_add_uuid_to_conference_invitee.rb @@ -0,0 +1,6 @@ +class AddUuidToConferenceInvitee < ActiveRecord::Migration + def change + add_column :conference_invitees, :uuid, :string + + end +end diff --git a/db/migrate/20120821105942_create_automatic_call_distributors.rb b/db/migrate/20120821105942_create_automatic_call_distributors.rb new file mode 100644 index 0000000..6bfa921 --- /dev/null +++ b/db/migrate/20120821105942_create_automatic_call_distributors.rb @@ -0,0 +1,22 @@ +class CreateAutomaticCallDistributors < ActiveRecord::Migration + def self.up + create_table :automatic_call_distributors do |t| + t.string :uuid + t.string :name + t.string :strategy + t.string :automatic_call_distributorable_type + t.integer :automatic_call_distributorable_id + t.integer :max_callers + t.integer :agent_timeout + t.integer :retry_timeout + t.string :join + t.string :leave + t.integer :gs_node_id + t.timestamps + end + end + + def self.down + drop_table :automatic_call_distributors + end +end diff --git a/db/migrate/20120822094609_create_acd_callers.rb b/db/migrate/20120822094609_create_acd_callers.rb new file mode 100644 index 0000000..fd1d30c --- /dev/null +++ b/db/migrate/20120822094609_create_acd_callers.rb @@ -0,0 +1,18 @@ +class CreateAcdCallers < ActiveRecord::Migration + def self.up + create_table :acd_callers do |t| + t.string :channel_uuid + t.integer :automatic_call_distributor_id + t.string :status + t.datetime :enter_time + t.datetime :agent_answer_time + t.string :callback_number + t.integer :callback_attempts + t.timestamps + end + end + + def self.down + drop_table :acd_callers + end +end diff --git a/db/migrate/20120822124716_create_acd_agents.rb b/db/migrate/20120822124716_create_acd_agents.rb new file mode 100644 index 0000000..bfd1ab6 --- /dev/null +++ b/db/migrate/20120822124716_create_acd_agents.rb @@ -0,0 +1,19 @@ +class CreateAcdAgents < ActiveRecord::Migration + def self.up + create_table :acd_agents do |t| + t.string :uuid + t.string :name + t.string :status + t.integer :automatic_call_distributor_id + t.datetime :last_call + t.integer :calls_answered + t.string :destination_type + t.integer :destination_id + t.timestamps + end + end + + def self.down + drop_table :acd_agents + end +end diff --git a/db/migrate/20120927101336_add_announce_position_to_automatic_call_distributors.rb b/db/migrate/20120927101336_add_announce_position_to_automatic_call_distributors.rb new file mode 100644 index 0000000..7b92cdb --- /dev/null +++ b/db/migrate/20120927101336_add_announce_position_to_automatic_call_distributors.rb @@ -0,0 +1,6 @@ +class AddAnnouncePositionToAutomaticCallDistributors < ActiveRecord::Migration + def change + add_column :automatic_call_distributors, :announce_position, :integer + + end +end diff --git a/db/migrate/20120927101507_add_announce_call_agents_to_automatic_call_distributors.rb b/db/migrate/20120927101507_add_announce_call_agents_to_automatic_call_distributors.rb new file mode 100644 index 0000000..7010ce7 --- /dev/null +++ b/db/migrate/20120927101507_add_announce_call_agents_to_automatic_call_distributors.rb @@ -0,0 +1,6 @@ +class AddAnnounceCallAgentsToAutomaticCallDistributors < ActiveRecord::Migration + def change + add_column :automatic_call_distributors, :announce_call_agents, :boolean + + end +end diff --git a/db/migrate/20120927101636_add_greeting_to_automatic_call_distributors.rb b/db/migrate/20120927101636_add_greeting_to_automatic_call_distributors.rb new file mode 100644 index 0000000..0eead96 --- /dev/null +++ b/db/migrate/20120927101636_add_greeting_to_automatic_call_distributors.rb @@ -0,0 +1,6 @@ +class AddGreetingToAutomaticCallDistributors < ActiveRecord::Migration + def change + add_column :automatic_call_distributors, :greeting, :string + + end +end diff --git a/db/migrate/20120927101800_add_goodbye_to_automatic_call_distributors.rb b/db/migrate/20120927101800_add_goodbye_to_automatic_call_distributors.rb new file mode 100644 index 0000000..5313bba --- /dev/null +++ b/db/migrate/20120927101800_add_goodbye_to_automatic_call_distributors.rb @@ -0,0 +1,6 @@ +class AddGoodbyeToAutomaticCallDistributors < ActiveRecord::Migration + def change + add_column :automatic_call_distributors, :goodbye, :string + + end +end diff --git a/db/migrate/20120927101908_add_music_to_automatic_call_distributors.rb b/db/migrate/20120927101908_add_music_to_automatic_call_distributors.rb new file mode 100644 index 0000000..3f39b38 --- /dev/null +++ b/db/migrate/20120927101908_add_music_to_automatic_call_distributors.rb @@ -0,0 +1,6 @@ +class AddMusicToAutomaticCallDistributors < ActiveRecord::Migration + def change + add_column :automatic_call_distributors, :music, :string + + end +end diff --git a/db/migrate/20120927110240_change_data_type_for_announce_call_agents.rb b/db/migrate/20120927110240_change_data_type_for_announce_call_agents.rb new file mode 100644 index 0000000..9ae0277 --- /dev/null +++ b/db/migrate/20120927110240_change_data_type_for_announce_call_agents.rb @@ -0,0 +1,13 @@ +class ChangeDataTypeForAnnounceCallAgents < ActiveRecord::Migration + def up + change_table :automatic_call_distributors do |t| + t.change :announce_call_agents, :string + end + end + + def down + change_table :automatic_call_distributors do |t| + t.change :announce_call_agents, :boolean + end + end +end diff --git a/db/migrate/20121009135700_add_automatic_call_distributor_function_key.rb b/db/migrate/20121009135700_add_automatic_call_distributor_function_key.rb new file mode 100644 index 0000000..b6fa561 --- /dev/null +++ b/db/migrate/20121009135700_add_automatic_call_distributor_function_key.rb @@ -0,0 +1,14 @@ +class AddAutomaticCallDistributorFunctionKey < ActiveRecord::Migration + def up + softkey_function = SoftkeyFunction.find_or_create_by_name('acd_membership') + gui_function = GuiFunction.find_or_create_by_name("softkey_function_#{softkey_function.name.downcase}_field_in_softkey_form", + :category => 'Softkey edit view', + :description => "Show the softkey function '#{softkey_function.name}' in the softkey form.") + end + + def down + softkey_function = SoftkeyFunction.find_by_name('acd_membership') + softkey_function.destroy + GuiFunction.find_by_name("softkey_function_#{softkey_function.name.downcase}_field_in_softkey_form").destroy + end +end diff --git a/db/migrate/20121012071908_create_call_histories.rb b/db/migrate/20121012071908_create_call_histories.rb new file mode 100644 index 0000000..d40e566 --- /dev/null +++ b/db/migrate/20121012071908_create_call_histories.rb @@ -0,0 +1,29 @@ +class CreateCallHistories < ActiveRecord::Migration + def change + create_table :call_histories do |t| + t.string :call_historyable_type + t.integer :call_historyable_id + t.string :entry_type + t.string :caller_account_type + t.integer :caller_account_id + t.string :caller_id_number + t.string :caller_id_name + t.string :caller_channel_uuid + t.string :callee_account_type + t.integer :callee_account_id + t.string :callee_id_number + t.string :callee_id_name + t.string :auth_account_type + t.integer :auth_account_id + t.string :forwarding_service + t.string :destination_number + t.datetime :start_stamp + t.integer :duration + t.string :result + t.boolean :read_flag + t.boolean :returned_flag + + t.timestamps + end + end +end diff --git a/db/migrate/20121105123841_add_uuid_to_phone_books.rb b/db/migrate/20121105123841_add_uuid_to_phone_books.rb new file mode 100644 index 0000000..cc51b3f --- /dev/null +++ b/db/migrate/20121105123841_add_uuid_to_phone_books.rb @@ -0,0 +1,6 @@ +class AddUuidToPhoneBooks < ActiveRecord::Migration + def change + add_column :phone_books, :uuid, :string + + end +end diff --git a/db/migrate/20121105123943_add_uuid_to_addresses.rb b/db/migrate/20121105123943_add_uuid_to_addresses.rb new file mode 100644 index 0000000..1d67c73 --- /dev/null +++ b/db/migrate/20121105123943_add_uuid_to_addresses.rb @@ -0,0 +1,6 @@ +class AddUuidToAddresses < ActiveRecord::Migration + def change + add_column :addresses, :uuid, :string + + end +end diff --git a/db/migrate/20121105124208_add_uuid_to_call_forwards.rb b/db/migrate/20121105124208_add_uuid_to_call_forwards.rb new file mode 100644 index 0000000..6689bec --- /dev/null +++ b/db/migrate/20121105124208_add_uuid_to_call_forwards.rb @@ -0,0 +1,6 @@ +class AddUuidToCallForwards < ActiveRecord::Migration + def change + add_column :call_forwards, :uuid, :string + + end +end diff --git a/db/migrate/20121105124343_add_uuid_to_phone_models.rb b/db/migrate/20121105124343_add_uuid_to_phone_models.rb new file mode 100644 index 0000000..49acc8b --- /dev/null +++ b/db/migrate/20121105124343_add_uuid_to_phone_models.rb @@ -0,0 +1,6 @@ +class AddUuidToPhoneModels < ActiveRecord::Migration + def change + add_column :phone_models, :uuid, :string rescue puts "column already added" + + end +end diff --git a/db/migrate/20121105124904_add_uuid_to_softkeys.rb b/db/migrate/20121105124904_add_uuid_to_softkeys.rb new file mode 100644 index 0000000..0ba13e8 --- /dev/null +++ b/db/migrate/20121105124904_add_uuid_to_softkeys.rb @@ -0,0 +1,6 @@ +class AddUuidToSoftkeys < ActiveRecord::Migration + def change + add_column :softkeys, :uuid, :string + + end +end diff --git a/db/migrate/20121105125043_add_uuid_to_whitelists.rb b/db/migrate/20121105125043_add_uuid_to_whitelists.rb new file mode 100644 index 0000000..11f1766 --- /dev/null +++ b/db/migrate/20121105125043_add_uuid_to_whitelists.rb @@ -0,0 +1,6 @@ +class AddUuidToWhitelists < ActiveRecord::Migration + def change + add_column :whitelists, :uuid, :string + + end +end diff --git a/db/migrate/20121105125232_populate_uuid_field.rb b/db/migrate/20121105125232_populate_uuid_field.rb new file mode 100644 index 0000000..4a83bc8 --- /dev/null +++ b/db/migrate/20121105125232_populate_uuid_field.rb @@ -0,0 +1,138 @@ +class PopulateUuidField < ActiveRecord::Migration + def up + AccessAuthorization.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + AcdAgent.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + AutomaticCallDistributor.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + Callthrough.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + ConferenceInvitee.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + Conference.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + FaxAccount.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + FaxDocument.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + HuntGroupMember.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + HuntGroup.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + PhoneBookEntry.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + PhoneNumberRange.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + PhoneNumber.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + SipAccount.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + Tenant.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + User.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + PhoneBook.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + Address.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + CallForward.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + PhoneModel.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + Softkey.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + + Whitelist.where('uuid IS NULL OR uuid = ""').each do |record| + uuid = UUID.new + record.uuid = uuid.generate + record.save + end + end + + def down + end +end diff --git a/db/migrate/20121105144944_add_last_sync_to_gs_nodes.rb b/db/migrate/20121105144944_add_last_sync_to_gs_nodes.rb new file mode 100644 index 0000000..aea4e64 --- /dev/null +++ b/db/migrate/20121105144944_add_last_sync_to_gs_nodes.rb @@ -0,0 +1,6 @@ +class AddLastSyncToGsNodes < ActiveRecord::Migration + def change + add_column :gs_nodes, :last_sync, :datetime + + end +end diff --git a/db/migrate/20121107072526_add_bridge_stamp_to_cdrs.rb b/db/migrate/20121107072526_add_bridge_stamp_to_cdrs.rb new file mode 100644 index 0000000..42cb949 --- /dev/null +++ b/db/migrate/20121107072526_add_bridge_stamp_to_cdrs.rb @@ -0,0 +1,6 @@ +class AddBridgeStampToCdrs < ActiveRecord::Migration + def change + add_column :cdrs, :bridge_stamp, :datetime + + end +end diff --git a/db/migrate/20121121080400_add_notify_to_voicemail_prefs.rb b/db/migrate/20121121080400_add_notify_to_voicemail_prefs.rb new file mode 100644 index 0000000..f09c3f6 --- /dev/null +++ b/db/migrate/20121121080400_add_notify_to_voicemail_prefs.rb @@ -0,0 +1,6 @@ +class AddNotifyToVoicemailPrefs < ActiveRecord::Migration + def change + add_column :voicemail_prefs, :notify, :boolean + + end +end diff --git a/db/migrate/20121121080548_add_attachment_to_voicemail_prefs.rb b/db/migrate/20121121080548_add_attachment_to_voicemail_prefs.rb new file mode 100644 index 0000000..27d9386 --- /dev/null +++ b/db/migrate/20121121080548_add_attachment_to_voicemail_prefs.rb @@ -0,0 +1,6 @@ +class AddAttachmentToVoicemailPrefs < ActiveRecord::Migration + def change + add_column :voicemail_prefs, :attachment, :boolean + + end +end diff --git a/db/migrate/20121121080649_add_mark_read_to_voicemail_prefs.rb b/db/migrate/20121121080649_add_mark_read_to_voicemail_prefs.rb new file mode 100644 index 0000000..52e6afe --- /dev/null +++ b/db/migrate/20121121080649_add_mark_read_to_voicemail_prefs.rb @@ -0,0 +1,6 @@ +class AddMarkReadToVoicemailPrefs < ActiveRecord::Migration + def change + add_column :voicemail_prefs, :mark_read, :boolean + + end +end diff --git a/db/migrate/20121121081552_add_purge_to_voicemail_prefs.rb b/db/migrate/20121121081552_add_purge_to_voicemail_prefs.rb new file mode 100644 index 0000000..6188849 --- /dev/null +++ b/db/migrate/20121121081552_add_purge_to_voicemail_prefs.rb @@ -0,0 +1,6 @@ +class AddPurgeToVoicemailPrefs < ActiveRecord::Migration + def change + add_column :voicemail_prefs, :purge, :boolean + + end +end diff --git a/db/migrate/20121125084332_add_provisioning_key_to_phones.rb b/db/migrate/20121125084332_add_provisioning_key_to_phones.rb new file mode 100644 index 0000000..4b11461 --- /dev/null +++ b/db/migrate/20121125084332_add_provisioning_key_to_phones.rb @@ -0,0 +1,6 @@ +class AddProvisioningKeyToPhones < ActiveRecord::Migration + def change + add_column :phones, :provisioning_key, :string + + end +end diff --git a/db/migrate/20121125084447_add_provisioning_key_active_to_phones.rb b/db/migrate/20121125084447_add_provisioning_key_active_to_phones.rb new file mode 100644 index 0000000..c304b79 --- /dev/null +++ b/db/migrate/20121125084447_add_provisioning_key_active_to_phones.rb @@ -0,0 +1,6 @@ +class AddProvisioningKeyActiveToPhones < ActiveRecord::Migration + def change + add_column :phones, :provisioning_key_active, :boolean + + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000..1395e1b --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,967 @@ +# encoding: UTF-8 +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended to check this file into your version control system. + +ActiveRecord::Schema.define(:version => 20121125084447) do + + create_table "access_authorizations", :force => true do |t| + t.string "access_authorizationable_type" + t.integer "access_authorizationable_id" + t.string "name" + t.string "login" + t.string "pin" + t.integer "position" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "sip_account_id" + t.string "uuid" + end + + add_index "access_authorizations", ["uuid"], :name => "index_access_authorizations_on_uuid" + + create_table "acd_agents", :force => true do |t| + t.string "uuid" + t.string "name" + t.string "status" + t.integer "automatic_call_distributor_id" + t.datetime "last_call" + t.integer "calls_answered" + t.string "destination_type" + t.integer "destination_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "acd_callers", :force => true do |t| + t.string "channel_uuid" + t.integer "automatic_call_distributor_id" + t.string "status" + t.datetime "enter_time" + t.datetime "agent_answer_time" + t.string "callback_number" + t.integer "callback_attempts" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "addresses", :force => true do |t| + t.integer "phone_book_entry_id" + t.string "line1" + t.string "line2" + t.string "street" + t.string "zip_code" + t.string "city" + t.integer "country_id" + t.integer "position" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "uuid" + end + + create_table "aliases", :id => false, :force => true do |t| + t.integer "sticky" + t.string "alias", :limit => 128 + t.string "command", :limit => 4096 + t.string "hostname", :limit => 256 + end + + add_index "aliases", ["alias"], :name => "alias1" + + create_table "api_rows", :force => true do |t| + t.string "user_id" + t.string "user_name" + t.string "last_name" + t.string "middle_name" + t.string "first_name" + t.string "office_phone_number" + t.string "internal_extension" + t.string "mobile_phone_number" + t.string "fax_phone_number" + t.string "email" + t.string "pin" + t.datetime "pin_updated_at" + t.string "photo_file_name" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "area_codes", :force => true do |t| + t.integer "country_id" + t.string "name" + t.string "area_code" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "central_office_code" + end + + create_table "automatic_call_distributors", :force => true do |t| + t.string "uuid" + t.string "name" + t.string "strategy" + t.string "automatic_call_distributorable_type" + t.integer "automatic_call_distributorable_id" + t.integer "max_callers" + t.integer "agent_timeout" + t.integer "retry_timeout" + t.string "join" + t.string "leave" + t.integer "gs_node_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "announce_position" + t.string "announce_call_agents" + t.string "greeting" + t.string "goodbye" + t.string "music" + end + + create_table "call_forward_cases", :force => true do |t| + t.string "value" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "call_forward_cases", ["value"], :name => "call_forward_cases_value_index", :unique => true + + create_table "call_forwards", :force => true do |t| + t.integer "call_forward_case_id" + t.integer "timeout" + t.string "destination" + t.string "source" + t.boolean "active" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "phone_number_id" + t.integer "depth" + t.string "call_forwardable_type" + t.integer "call_forwardable_id" + t.integer "position" + t.string "uuid" + end + + add_index "call_forwards", ["phone_number_id"], :name => "index_call_forwards_on_phone_number_id" + + create_table "call_histories", :force => true do |t| + t.string "call_historyable_type" + t.integer "call_historyable_id" + t.string "entry_type" + t.string "caller_account_type" + t.integer "caller_account_id" + t.string "caller_id_number" + t.string "caller_id_name" + t.string "caller_channel_uuid" + t.string "callee_account_type" + t.integer "callee_account_id" + t.string "callee_id_number" + t.string "callee_id_name" + t.string "auth_account_type" + t.integer "auth_account_id" + t.string "forwarding_service" + t.string "destination_number" + t.datetime "start_stamp" + t.integer "duration" + t.string "result" + t.boolean "read_flag" + t.boolean "returned_flag" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "calls", :id => false, :force => true do |t| + t.string "call_uuid" + t.string "call_created", :limit => 128 + t.integer "call_created_epoch" + t.string "function", :limit => 1024 + t.string "caller_cid_name", :limit => 1024 + t.string "caller_cid_num", :limit => 256 + t.string "caller_dest_num", :limit => 256 + t.string "caller_chan_name", :limit => 1024 + t.string "caller_uuid", :limit => 256 + t.string "callee_cid_name", :limit => 1024 + t.string "callee_cid_numcallee_dest_num", :limit => 256 + t.string "callee_chan_name", :limit => 1024 + t.string "callee_uuid", :limit => 256 + t.string "hostname", :limit => 256 + end + + add_index "calls", ["call_uuid", "hostname"], :name => "eeuuindex2" + add_index "calls", ["callee_uuid", "hostname"], :name => "eeuuindex" + add_index "calls", ["caller_uuid", "hostname"], :name => "eruuindex" + add_index "calls", ["hostname"], :name => "calls1" + + create_table "callthroughs", :force => true do |t| + t.integer "tenant_id" + t.string "name" + t.string "clip_no_screening" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "uuid" + end + + create_table "cdrs", :id => false, :force => true do |t| + t.string "uuid", :limit => 256 + t.integer "account_id" + t.string "account_type", :limit => 256 + t.string "bleg_uuid", :limit => 256 + t.integer "bleg_account_id" + t.string "bleg_account_type", :limit => 256 + t.string "dialed_number", :limit => 256 + t.string "destination_number", :limit => 256 + t.string "caller_id_number", :limit => 256 + t.string "caller_id_name", :limit => 256 + t.string "callee_id_number", :limit => 256 + t.string "callee_id_name", :limit => 256 + t.datetime "start_stamp" + t.datetime "answer_stamp" + t.datetime "end_stamp" + t.integer "duration" + t.integer "billsec" + t.string "hangup_cause", :limit => 256 + t.string "dialstatus", :limit => 256 + t.string "forwarding_number", :limit => 256 + t.integer "forwarding_account_id" + t.string "forwarding_account_type", :limit => 256 + t.string "forwarding_service", :limit => 256 + t.datetime "bleg_read_time" + t.datetime "forwarding_read_time" + t.datetime "bridge_stamp" + end + + create_table "channels", :id => false, :force => true do |t| + t.string "uuid", :limit => 256 + t.string "direction", :limit => 32 + t.string "created", :limit => 128 + t.integer "created_epoch" + t.string "name", :limit => 1024 + t.string "state", :limit => 64 + t.string "cid_name", :limit => 1024 + t.string "cid_num", :limit => 256 + t.string "ip_addr", :limit => 256 + t.string "dest", :limit => 1024 + t.string "application", :limit => 128 + t.string "application_data", :limit => 4096 + t.string "dialplan", :limit => 128 + t.string "context", :limit => 128 + t.string "read_codec", :limit => 128 + t.string "read_rate", :limit => 32 + t.string "read_bit_rate", :limit => 32 + t.string "write_codec", :limit => 128 + t.string "write_rate", :limit => 32 + t.string "write_bit_rate", :limit => 32 + t.string "secure", :limit => 32 + t.string "hostname", :limit => 256 + t.string "presence_id", :limit => 4096 + t.string "presence_data", :limit => 4096 + t.string "callstate", :limit => 64 + t.string "callee_name", :limit => 1024 + t.string "callee_num", :limit => 256 + t.string "callee_direction", :limit => 5 + t.string "call_uuid", :limit => 256 + end + + add_index "channels", ["call_uuid", "hostname"], :name => "uuindex2" + add_index "channels", ["hostname"], :name => "channels1" + add_index "channels", ["uuid", "hostname"], :name => "uuindex", :unique => true + + create_table "complete", :id => false, :force => true do |t| + t.integer "sticky" + t.string "a1", :limit => 128 + t.string "a2", :limit => 128 + t.string "a3", :limit => 128 + t.string "a4", :limit => 128 + t.string "a5", :limit => 128 + t.string "a6", :limit => 128 + t.string "a7", :limit => 128 + t.string "a8", :limit => 128 + t.string "a9", :limit => 128 + t.string "a10", :limit => 128 + t.string "hostname", :limit => 256 + end + + add_index "complete", ["a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a10", "hostname"], :name => "complete11" + add_index "complete", ["a1", "hostname"], :name => "complete1" + add_index "complete", ["a10", "hostname"], :name => "complete10" + add_index "complete", ["a2", "hostname"], :name => "complete2" + add_index "complete", ["a3", "hostname"], :name => "complete3" + add_index "complete", ["a4", "hostname"], :name => "complete4" + add_index "complete", ["a5", "hostname"], :name => "complete5" + add_index "complete", ["a6", "hostname"], :name => "complete6" + add_index "complete", ["a7", "hostname"], :name => "complete7" + add_index "complete", ["a8", "hostname"], :name => "complete8" + add_index "complete", ["a9", "hostname"], :name => "complete9" + + create_table "conference_invitees", :force => true do |t| + t.integer "conference_id" + t.integer "phone_book_entry_id" + t.string "pin" + t.boolean "speaker" + t.boolean "moderator" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "uuid" + end + + create_table "conferences", :force => true do |t| + t.string "name" + t.datetime "start" + t.datetime "end" + t.text "description" + t.string "pin" + t.text "state" + t.boolean "open_for_anybody" + t.string "conferenceable_type" + t.integer "conferenceable_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "max_members" + t.boolean "announce_new_member_by_name" + t.boolean "announce_left_member_by_name" + t.string "uuid" + end + + create_table "countries", :force => true do |t| + t.string "name" + t.string "country_code" + t.string "international_call_prefix" + t.string "trunk_prefix" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "delayed_jobs", :force => true do |t| + t.integer "priority", :default => 0 + t.integer "attempts", :default => 0 + t.text "handler" + t.text "last_error" + t.datetime "run_at" + t.datetime "locked_at" + t.datetime "failed_at" + t.string "locked_by" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "delayed_jobs", ["priority", "run_at"], :name => "delayed_jobs_priority" + + create_table "fax_accounts", :force => true do |t| + t.string "fax_accountable_type" + t.integer "fax_accountable_id" + t.string "name" + t.string "email" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "tenant_id" + t.string "station_id" + t.integer "days_till_auto_delete" + t.integer "retries" + t.string "uuid" + end + + create_table "fax_documents", :force => true do |t| + t.boolean "inbound" + t.string "state" + t.integer "transmission_time" + t.datetime "sent_at" + t.integer "document_total_pages" + t.integer "document_transferred_pages" + t.boolean "ecm_requested" + t.boolean "ecm_used" + t.string "image_resolution" + t.string "image_size" + t.string "local_station_id" + t.integer "result_code" + t.string "remote_station_id" + t.boolean "success" + t.integer "transfer_rate" + t.string "document" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "fax_account_id" + t.string "caller_id_number" + t.string "caller_id_name" + t.integer "retry_counter" + t.string "tiff" + t.integer "fax_resolution_id" + t.string "uuid" + end + + create_table "fax_pages", :force => true do |t| + t.integer "position" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "fax_page" + end + + create_table "fax_resolutions", :force => true do |t| + t.string "name" + t.string "resolution_value" + t.integer "position" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "fax_thumbnails", :force => true do |t| + t.integer "fax_document_id" + t.integer "position" + t.string "thumbnail" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "faxes", :force => true do |t| + t.boolean "inbound" + t.integer "faxable_id" + t.string "faxable_type" + t.string "state" + t.integer "transmission_time" + t.datetime "sent_at" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "document_total_pages" + t.integer "document_transferred_pages" + t.boolean "ecm_requested" + t.boolean "ecm_used" + t.string "image_resolution" + t.string "image_size" + t.string "local_station_id" + t.integer "result_code" + t.string "result_text" + t.string "remote_station_id" + t.boolean "success" + t.integer "transfer_rate" + t.string "t38_gateway_format" + t.string "t38_peer" + t.string "fax" + end + + create_table "fifo_bridge", :id => false, :force => true do |t| + t.string "fifo_name", :limit => 1024, :null => false + t.string "caller_uuid", :null => false + t.string "caller_caller_id_name", :null => false + t.string "caller_caller_id_number", :null => false + t.string "consumer_uuid", :null => false + t.string "consumer_outgoing_uuid" + t.integer "bridge_start" + end + + create_table "fifo_callers", :id => false, :force => true do |t| + t.string "fifo_name", :null => false + t.string "uuid", :null => false + t.string "caller_caller_id_name" + t.string "caller_caller_id_number" + t.integer "timestamp" + end + + create_table "fifo_outbound", :id => false, :force => true do |t| + t.string "uuid" + t.string "fifo_name" + t.string "originate_string" + t.integer "simo_count" + t.integer "use_count" + t.integer "timeout" + t.integer "lag" + t.integer "next_avail", :default => 0, :null => false + t.integer "expires", :default => 0, :null => false + t.integer "static", :default => 0, :null => false + t.integer "outbound_call_count", :default => 0, :null => false + t.integer "outbound_fail_count", :default => 0, :null => false + t.string "hostname" + t.integer "taking_calls", :default => 1, :null => false + t.string "status" + t.integer "outbound_call_total_count", :default => 0, :null => false + t.integer "outbound_fail_total_count", :default => 0, :null => false + t.integer "active_time", :default => 0, :null => false + t.integer "inactive_time", :default => 0, :null => false + t.integer "manual_calls_out_count", :default => 0, :null => false + t.integer "manual_calls_in_count", :default => 0, :null => false + t.integer "manual_calls_out_total_count", :default => 0, :null => false + t.integer "manual_calls_in_total_count", :default => 0, :null => false + t.integer "ring_count", :default => 0, :null => false + t.integer "start_time", :default => 0, :null => false + t.integer "stop_time", :default => 0, :null => false + end + + create_table "gemeinschaft_setups", :force => true do |t| + t.integer "user_id" + t.integer "sip_domain_id" + t.integer "country_id" + t.integer "language_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "gs_cluster_sync_log_entries", :force => true do |t| + t.integer "gs_node_id" + t.string "class_name" + t.string "action" + t.text "content" + t.string "status" + t.string "history" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "homebase_ip_address" + t.boolean "waiting_to_be_synced" + t.string "association_method" + t.string "association_uuid" + end + + create_table "gs_nodes", :force => true do |t| + t.string "name" + t.string "ip_address" + t.boolean "push_updates_to" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "site" + t.string "element_name" + t.boolean "accepts_updates_from" + t.datetime "last_sync" + end + + create_table "gui_function_memberships", :force => true do |t| + t.integer "gui_function_id" + t.integer "user_group_id" + t.boolean "activated" + t.string "output" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "gui_functions", :force => true do |t| + t.string "category" + t.string "name" + t.string "description" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "hunt_group_members", :force => true do |t| + t.integer "hunt_group_id" + t.string "name" + t.integer "position" + t.boolean "active" + t.boolean "can_switch_status_itself" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "uuid" + end + + add_index "hunt_group_members", ["uuid"], :name => "index_hunt_group_members_on_uuid" + + create_table "hunt_groups", :force => true do |t| + t.integer "tenant_id" + t.string "name" + t.string "strategy" + t.integer "seconds_between_jumps" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "gs_node_id" + t.integer "gs_node_original_id" + t.string "uuid" + end + + add_index "hunt_groups", ["uuid"], :name => "index_hunt_groups_on_uuid" + + create_table "interfaces", :id => false, :force => true do |t| + t.string "type", :limit => 128 + t.string "name", :limit => 1024 + t.string "description", :limit => 4096 + t.string "ikey", :limit => 1024 + t.string "filename", :limit => 4096 + t.string "syntax", :limit => 4096 + t.string "hostname", :limit => 256 + end + + create_table "languages", :force => true do |t| + t.string "name" + t.string "code" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "manufacturers", :force => true do |t| + t.string "name" + t.string "ieee_name" + t.string "homepage_url" + t.string "state" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "nat", :id => false, :force => true do |t| + t.integer "sticky" + t.integer "port" + t.integer "proto" + t.string "hostname", :limit => 256 + end + + add_index "nat", ["port", "proto", "hostname"], :name => "nat_map_port_proto" + + create_table "ouis", :force => true do |t| + t.integer "manufacturer_id" + t.string "value" + t.string "state" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "phone_book_entries", :force => true do |t| + t.integer "phone_book_id" + t.string "first_name" + t.string "middle_name" + t.string "last_name" + t.string "title" + t.string "nickname" + t.string "organization" + t.boolean "is_organization" + t.string "department" + t.string "job_title" + t.boolean "is_male" + t.date "birthday" + t.string "birth_name" + t.string "state" + t.text "description" + t.integer "position" + t.string "homepage_personal" + t.string "homepage_organization" + t.string "twitter_account" + t.string "facebook_account" + t.string "google_plus_account" + t.string "xing_account" + t.string "linkedin_account" + t.string "mobileme_account" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "image" + t.string "first_name_phonetic" + t.string "last_name_phonetic" + t.string "organization_phonetic" + t.string "value_of_to_s" + t.string "uuid" + end + + add_index "phone_book_entries", ["first_name"], :name => "index_phone_book_entries_on_first_name" + add_index "phone_book_entries", ["first_name_phonetic"], :name => "index_phone_book_entries_on_first_name_phonetic" + add_index "phone_book_entries", ["last_name"], :name => "index_phone_book_entries_on_last_name" + add_index "phone_book_entries", ["last_name_phonetic"], :name => "index_phone_book_entries_on_last_name_phonetic" + add_index "phone_book_entries", ["organization"], :name => "index_phone_book_entries_on_organization" + add_index "phone_book_entries", ["organization_phonetic"], :name => "index_phone_book_entries_on_organization_phonetic" + add_index "phone_book_entries", ["uuid"], :name => "index_phone_book_entries_on_uuid" + + create_table "phone_books", :force => true do |t| + t.string "name" + t.string "description" + t.integer "phone_bookable_id" + t.string "phone_bookable_type" + t.string "state" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "uuid" + end + + create_table "phone_models", :force => true do |t| + t.string "name" + t.string "manufacturer_id" + t.string "product_manual_homepage_url" + t.string "product_homepage_url" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "state" + t.string "uuid" + end + + create_table "phone_number_ranges", :force => true do |t| + t.string "name" + t.text "description" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "phone_number_rangeable_type" + t.integer "phone_number_rangeable_id" + t.string "uuid" + end + + add_index "phone_number_ranges", ["uuid"], :name => "index_phone_number_ranges_on_uuid" + + create_table "phone_numbers", :force => true do |t| + t.string "name" + t.string "number" + t.string "country_code" + t.string "area_code" + t.string "subscriber_number" + t.string "extension" + t.integer "position" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "central_office_code" + t.string "phone_numberable_type" + t.integer "phone_numberable_id" + t.string "state" + t.string "value_of_to_s" + t.integer "gs_node_id" + t.integer "gs_node_original_id" + t.string "uuid" + t.integer "access_authorization_user_id" + t.boolean "is_native" + end + + add_index "phone_numbers", ["uuid"], :name => "index_phone_numbers_on_uuid" + + create_table "phone_sip_accounts", :force => true do |t| + t.integer "phone_id" + t.integer "sip_account_id" + t.integer "position" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "phones", :force => true do |t| + t.string "mac_address" + t.integer "phone_model_id" + t.string "ip_address" + t.string "last_ip_address" + t.string "http_user" + t.string "http_password" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "state" + t.string "phoneable_type" + t.integer "phoneable_id" + t.boolean "hot_deskable" + t.boolean "nightly_reboot" + t.string "provisioning_key" + t.boolean "provisioning_key_active" + end + + create_table "registrations", :id => false, :force => true do |t| + t.string "reg_user" + t.string "realm", :limit => 256 + t.string "token", :limit => 256 + t.text "url" + t.integer "expires" + t.string "network_ip", :limit => 256 + t.string "network_port", :limit => 256 + t.string "network_proto", :limit => 256 + t.string "hostname", :limit => 256 + end + + add_index "registrations", ["reg_user", "realm", "hostname"], :name => "regindex1" + + create_table "ringtones", :force => true do |t| + t.string "ringtoneable_type" + t.integer "ringtoneable_id" + t.string "audio" + t.integer "bellcore_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "sessions", :force => true do |t| + t.string "session_id", :null => false + t.text "data" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id" + add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at" + + create_table "sip_accounts", :force => true do |t| + t.string "sip_accountable_type" + t.integer "sip_accountable_id" + t.string "auth_name" + t.string "caller_name" + t.string "password" + t.string "voicemail_pin" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "value_of_to_s" + t.integer "tenant_id" + t.integer "sip_domain_id" + t.boolean "call_waiting" + t.boolean "clir" + t.string "clip_no_screening" + t.boolean "clip" + t.string "description" + t.boolean "callforward_rules_act_per_sip_account" + t.boolean "hotdeskable" + t.integer "gs_node_id" + t.integer "gs_node_original_id" + t.string "uuid" + t.boolean "is_native" + end + + add_index "sip_accounts", ["uuid"], :name => "index_sip_accounts_on_uuid" + + create_table "sip_domains", :force => true do |t| + t.string "host" + t.string "realm" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "softkey_functions", :force => true do |t| + t.string "name" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "position" + end + + add_index "softkey_functions", ["name"], :name => "index_softkey_functions_on_name" + add_index "softkey_functions", ["position"], :name => "index_softkey_functions_on_position" + + create_table "softkeys", :force => true do |t| + t.string "function" + t.string "number" + t.string "label" + t.integer "position" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "sip_account_id" + t.integer "softkey_function_id" + t.integer "call_forward_id" + t.string "uuid" + end + + create_table "system_messages", :force => true do |t| + t.integer "user_id" + t.string "content" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "tasks", :id => false, :force => true do |t| + t.integer "task_id" + t.string "task_desc", :limit => 4096 + t.string "task_group", :limit => 1024 + t.integer "task_sql_manager" + t.string "hostname", :limit => 256 + end + + add_index "tasks", ["hostname", "task_id"], :name => "tasks1", :unique => true + + create_table "tenant_memberships", :force => true do |t| + t.integer "tenant_id" + t.integer "user_id" + t.string "state" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "tenants", :force => true do |t| + t.string "name" + t.text "description" + t.string "state" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "country_id" + t.integer "sip_domain_id" + t.integer "language_id" + t.string "internal_extension_ranges" + t.string "did_list" + t.string "from_field_voicemail_email" + t.string "from_field_pin_change_email" + t.string "uuid" + end + + create_table "user_group_memberships", :force => true do |t| + t.integer "user_group_id" + t.integer "user_id" + t.string "state" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "user_groups", :force => true do |t| + t.string "name" + t.text "description" + t.integer "tenant_id" + t.integer "position" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "users", :force => true do |t| + t.string "user_name" + t.string "email" + t.string "password_digest" + t.string "first_name" + t.string "middle_name" + t.string "last_name" + t.boolean "male" + t.string "gemeinschaft_unique_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "image" + t.integer "current_tenant_id" + t.string "pin_salt" + t.string "pin_hash" + t.integer "language_id" + t.boolean "send_voicemail_as_email_attachment" + t.string "importer_checksum" + t.integer "gs_node_id" + t.integer "gs_node_original_id" + t.string "uuid" + t.boolean "is_native" + end + + add_index "users", ["uuid"], :name => "index_users_on_uuid" + + create_table "voicemail_msgs", :id => false, :force => true do |t| + t.integer "created_epoch" + t.integer "read_epoch" + t.string "username" + t.string "domain" + t.string "uuid" + t.string "cid_name" + t.string "cid_number" + t.string "in_folder" + t.string "file_path" + t.integer "message_len" + t.string "flags" + t.string "read_flags" + t.string "forwarded_by" + t.boolean "notification" + end + + add_index "voicemail_msgs", ["created_epoch"], :name => "voicemail_msgs_idx1" + add_index "voicemail_msgs", ["domain"], :name => "voicemail_msgs_idx3" + add_index "voicemail_msgs", ["forwarded_by"], :name => "voicemail_msgs_idx7" + add_index "voicemail_msgs", ["in_folder"], :name => "voicemail_msgs_idx5" + add_index "voicemail_msgs", ["read_flags"], :name => "voicemail_msgs_idx6" + add_index "voicemail_msgs", ["username"], :name => "voicemail_msgs_idx2" + add_index "voicemail_msgs", ["uuid"], :name => "voicemail_msgs_idx4" + + create_table "voicemail_prefs", :id => false, :force => true do |t| + t.string "username" + t.string "domain" + t.string "name_path" + t.string "greeting_path" + t.string "password" + t.boolean "notify" + t.boolean "attachment" + t.boolean "mark_read" + t.boolean "purge" + end + + add_index "voicemail_prefs", ["domain"], :name => "voicemail_prefs_idx2" + add_index "voicemail_prefs", ["username"], :name => "voicemail_prefs_idx1" + + create_table "whitelists", :force => true do |t| + t.string "name" + t.string "whitelistable_type" + t.integer "whitelistable_id" + t.integer "position" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "uuid" + end + +end diff --git a/db/seeds.rb b/db/seeds.rb new file mode 100644 index 0000000..d640036 --- /dev/null +++ b/db/seeds.rb @@ -0,0 +1,5 @@ +# ruby encoding: utf-8 +# +# This file should contain all the record creation needed to seed the database with its default values. +# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). + diff --git a/db/to-dos/20120119160732_emergency_numbers_germany.rb b/db/to-dos/20120119160732_emergency_numbers_germany.rb new file mode 100644 index 0000000..9dd9131 --- /dev/null +++ b/db/to-dos/20120119160732_emergency_numbers_germany.rb @@ -0,0 +1,30 @@ +# ruby encoding: utf-8 + +class EmergencyNumbersGermany < ActiveRecord::Migration + + def up + # add_column :phone_numbers, :uuid, :string + # add_index :phone_numbers, :uuid + + germany = Country.find_by_name('Germany') + + ################################################################ + # Emergency numbers which shouldn't be used as extensions + ################################################################ + notruf_nummern = germany.phone_number_ranges.find_or_create_by_name(SERVICE_NUMBERS) + notruf_nummern.phone_numbers.find_or_create_by_name_and_number('Polizei', '110') + notruf_nummern.phone_numbers.find_or_create_by_name_and_number('Feuerwehr', '112') + notruf_nummern.phone_numbers.find_or_create_by_name_and_number('Zentrale Behördenrufnummer', '115') + notruf_nummern.phone_numbers.find_or_create_by_name_and_number('Krankenwagen', '19222') + notruf_nummern.phone_numbers.find_or_create_by_name_and_number('Weisser Ring e. V.', '116006') + notruf_nummern.phone_numbers.find_or_create_by_name_and_number('Nummer gegen Kummer e. V.', '116111') + notruf_nummern.phone_numbers.find_or_create_by_name_and_number('Zentrale Anlaufstelle zur Sperrung elektronischer Berechtigungen', '116116') + notruf_nummern.phone_numbers.find_or_create_by_name_and_number('Kassenärztliche Vereinigung: ärztliche Bereitschaftsdienste', '116117') + notruf_nummern.phone_numbers.find_or_create_by_name_and_number('Katholische Bundesarbeitsgemeinschaft für Ehe-, Familien- und Lebensberatung, Telefonseelsorge', '116123') + end + + def down + germany = Country.find_by_name('Germany') + germany.phone_number_ranges.where(:name => SERVICE_NUMBERS).destroy_all + end +end diff --git a/db/to-dos/20120223142004_add_more_german_area_codes.rb b/db/to-dos/20120223142004_add_more_german_area_codes.rb new file mode 100644 index 0000000..2eb2554 --- /dev/null +++ b/db/to-dos/20120223142004_add_more_german_area_codes.rb @@ -0,0 +1,58 @@ +# encoding: UTF-8 + +class AddMoreGermanAreaCodes < ActiveRecord::Migration + def up + # http://www.bundesnetzagentur.de/cln_1912/DE/Sachgebiete/Telekommunikation/RegulierungTelekommunikation/Nummernverwaltung/Nummernverwaltung_node.html + + germany = Country.find_by_name('Germany') + german_service_number_range = germany.phone_number_ranges.find_by_name('service_numbers') + + # Harmonisierte Dienste von sozialem Wert + # + (0..9).each do |x| + (0..9).each do |y| + (0..9).each do |z| + german_service_number_range.phone_numbers.create( + :name => "Harmonisierte Dienste von sozialem Wert", + :number => "116#{x}#{y}#{z}" + ) + end + end + end + + # Auskunftsdienste + # + (0..9).each do |x| + (0..9).each do |y| + german_service_number_range.phone_numbers.create( + :name => "Auskunftsdienste", + :number => "118#{x}#{y}" + ) + end + end + + # Online-Dienste + # + (0..9).each do |x| + (0..9).each do |y| + (0..9).each do |z| + AreaCode.create( + :country_id => germany.id, + :name => 'Online-Dienste', + :area_code => "19#{x}#{y}#{z}" + ) + end + end + end + + AreaCode.create(:country_id => germany.id, :name => 'Neuartige Dienste', :area_code => '12') + AreaCode.create(:country_id => germany.id, :name => 'Massenverkehrs-Dienste', :area_code => '137') + AreaCode.create(:country_id => germany.id, :name => 'Nutzergruppen', :area_code => '18') + AreaCode.create(:country_id => germany.id, :name => 'Internationale Virtuelle Private Netze', :area_code => '181') + AreaCode.create(:country_id => germany.id, :name => 'Nationale Teilnehmernummern', :area_code => '32') + AreaCode.create(:country_id => germany.id, :name => 'Anwählprogramme (Dialer)', :area_code => '9009') + end + + def down + end +end diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..b086f6f --- /dev/null +++ b/install.sh @@ -0,0 +1,214 @@ +#!/bin/bash + +echo -e "Gemeinschaft Version 5.0 Installation for Debian Linux\n" + +GS_DIR="/opt/GS5" + +aptitude update + +# Ask for the Github account data +# +echo -e "github username:\n" +read USERNAME + +echo -e "github password:\n" +read PASSWORD + +# Upgrade everything to be on the safe side. +# +echo "Upgrade the server ..." +aptitude upgrade + +# Install git +# +echo -e "Installing git ...\n" +aptitude install -y git + +# Install the mysql server without asking for a password +# +echo "Installing MySQL server ..." +apt-get install -y debconf-utils + +mysql_password= +export DEBIAN_FRONTEND=noninteractive +debconf-set-selections <<< 'mysql-server-5.1 mysql-server/root_password password '$mysql_password'' +debconf-set-selections <<< 'mysql-server-5.1 mysql-server/root_password_again password '$mysql_password'' +apt-get -y install mysql-server + +# Clone the git repository +# +echo -e "Downloading GS5 ...\n" + +cd /opt + +git clone "https://$USERNAME:$PASSWORD@github.com/amooma/GS5.git" "${GS_DIR}" + +if [ -f "${GS_DIR}/config/application.rb" ] +then + echo " OK" +else + echo " ERROR" + exit 1 +fi + +echo -e "Installing GS5 dependencies ...\n" + +aptitude install -y \ + sqlite3 \ + libsqlite3-dev \ + libjpeg62 \ + ghostscript \ + imagemagick \ + libtiff-tools \ + +cd /usr/local/src/ + +wget "http://65.23.153.46/GS5/freeswitch_1.1.beta2.1-1_i386.deb" +wget "http://65.23.153.46/GS5/freeswitch-lang-en_1.1.beta2.1-1_i386.deb" +wget "http://65.23.153.46/GS5/freeswitch-lang-de_1.1.beta2.1-1_i386.deb" +wget "http://65.23.153.46/GS5/freeswitch-lua_1.1.beta2.1-1_i386.deb" +wget "http://65.23.153.46/GS5/unixodbc_2.3.1-1_i386.deb" +wget "http://65.23.153.46/GS5/mysql-connector-odbc_5.1.11-1_i386.deb" +wget "http://65.23.153.46/GS5/luasql_2.1.1-1_i386.deb" +wget "http://files.freeswitch.org/freeswitch-sounds-en-us-callie-8000-1.0.16.tar.gz" +wget "http://files.freeswitch.org/freeswitch-sounds-en-us-callie-16000-1.0.16.tar.gz" +wget "http://files.freeswitch.org/freeswitch-sounds-music-8000-1.0.8.tar.gz" +wget "http://files.freeswitch.org/freeswitch-sounds-music-16000-1.0.8.tar.gz" + +echo -e "Installing FreeSWITCH dependencies ...\n" + +aptitude install -y \ + libasound2 \ + libcurl3 \ + libogg0 \ + libvorbis0a + +echo -e "Installing FreeSWITCH ...\n" + +DEBIAN_FRONTEND=noninteractive dpkg -i /usr/local/src/freeswitch_1.1.beta2.1-1_i386.deb +DEBIAN_FRONTEND=noninteractive dpkg -i /usr/local/src/freeswitch-lang-en_1.1.beta2.1-1_i386.deb +DEBIAN_FRONTEND=noninteractive dpkg -i /usr/local/src/freeswitch-freeswitch-lang-de_1.1.beta2.1-1_i386.deb +DEBIAN_FRONTEND=noninteractive dpkg -i /usr/local/src/freeswitch-lua_1.1.beta2.1-1_i386.deb +DEBIAN_FRONTEND=noninteractive dpkg -i /usr/local/src/unixodbc_2.3.1-1_i386.deb +DEBIAN_FRONTEND=noninteractive dpkg -i /usr/local/src/mysql-connector-odbc_5.1.11-1_i386.deb +DEBIAN_FRONTEND=noninteractive dpkg -i /usr/local/src/luasql_2.1.1-1_i386.deb + +aptitude -f install + +sed -i 's/FREESWITCH_ENABLED="false"/FREESWITCH_ENABLED="true"/' /etc/default/freeswitch +sed -i 's/^FREESWITCH_PARAMS.*/FREESWITCH_PARAMS="-nc"/' /etc/default/freeswitch + +echo -e "Installing Dependencies ...\n" + +aptitude -y install \ + curl \ + build-essential \ + libncurses5-dev \ + zlib1g-dev \ + libssl-dev \ + libreadline-dev \ + libcurl4-openssl-dev + +# Install RVM +# +bash < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer ) + +source /etc/profile.d/rvm.sh + +rvm install 1.9.2 +rvm use 1.9.2 --default + +# Install stuff which is needed to build specific gems +# +apt-get -y install libmysqlclient15-dev +apt-get -y install libxslt-dev libxml2-dev + +echo -e "Installing GS5 gems ...\n" +cd "${GS_DIR}" +bundle install + +echo -e "Setting up database ...\n" + +mysqladmin create gemeinschaft + +echo "[ODBC Drivers] +MyODBC = Installed +/usr/local/lib/libmyodbc5.so = Installed + +[gemeinschaft] +Description = MySQL database for Gemeinschaft +Driver = /usr/lib/libmyodbc5.so +" >/usr/local/etc/odbcinst.ini + +echo "[gemeinschaft] +Description = MySQL database for Gemeinschaft +Driver = /usr/local/lib/libmyodbc5.so +SERVER = localhost +PORT = 3306 +DATABASE = gemeinschaft +OPTION = 67108864 +USER = gemeinschaft +PASSWORD = gemeinschaft +" >/usr/local/etc/odbc.ini + +mysql -e "GRANT ALL PRIVILEGES ON gemeinschaft.* TO gemeinschaft @'%' IDENTIFIED BY 'gemeinschaft';" +mysql -e "FLUSH PRIVILEGES" + +bundle exec rake db:migrate RAILS_ENV="production" + +echo -e "Extracting FreeSWITCH sounds ...\n" + +mkdir -p /opt/freeswitch/sounds +cd /opt/freeswitch/sounds + +tar -xzf "/usr/local/src/freeswitch-sounds-en-us-callie-8000-1.0.16.tar.gz" +tar -xzf "/usr/local/src/freeswitch-sounds-en-us-callie-16000-1.0.16.tar.gz" +tar -xzf "/usr/local/src/freeswitch-sounds-music-8000-1.0.8.tar.gz" +tar -xzf "/usr/local/src/freeswitch-sounds-music-16000-1.0.8.tar.gz" + +echo -e "Creating FreeSWITCH configuration ...\n" + +rm -fr /opt/freeswitch/conf +rm -fr /opt/freeswitch/scripts + +ln -s "${GS_DIR}/misc/freeswitch/conf/" /opt/freeswitch/conf +ln -s "${GS_DIR}/misc/freeswitch/scripts/" /opt/freeswitch/scripts + +echo -e "Setting up permissions ...\n" + +addgroup gemeinschaft || true +adduser freeswitch gemeinschaft --quiet + +chgrp -R gemeinschaft "${GS_DIR}" +chmod -R g+w "${GS_DIR}" + +# Create FreeSWITCH log directory +mkdir /var/log/freeswitch/ +chown freeswitch:gemeinschaft /var/log/freeswitch/ + +# Installation of nginx and passenger +# +apt-get -y install libpcre3-dev +gem install passenger +passenger-install-nginx-module --auto --auto-download --prefix=/opt/nginx + +rm -f /opt/nginx/conf/nginx.conf +ln -s "${GS_DIR}/misc/nginx/nginx.conf" /opt/nginx/conf/nginx.conf + +adduser www-data gemeinschaft --quiet + +# Generate CSS +# +cd "${GS_DIR}" +RAILS_ENV=production bundle exec rake assets:precompile + +echo "Done!" + +echo "You can start the webserver with /opt/nginx/sbin/nginx" + + +# Ensure FreeSWITCH has permission to write to Fax directory +#chmod -R g+w /opt/gemeinschaft/public/uploads/fax_document/ +#chgrp -R gemeinschaft /opt/gemeinschaft/public/uploads/fax_document/ +#chmod -R g+w /tmp/GS-5.0/ +#chgrp -R gemeinschaft /tmp/GS-5.0/ diff --git a/lib/activerecord_extensions.rb b/lib/activerecord_extensions.rb new file mode 100644 index 0000000..50c44be --- /dev/null +++ b/lib/activerecord_extensions.rb @@ -0,0 +1,72 @@ +class ActiveRecord::Base + + before_validation :populate_uuid, :on => :create + before_validation :populate_gs_node_id, :on => :create + + # Set a UUID. + # + def populate_uuid + if self.attribute_names.include?('uuid') && self.uuid.blank? + uuid = UUID.new + self.uuid = uuid.generate + end + end + + # Set the gs_node_id if not already set. + # + def populate_gs_node_id + if self.attribute_names.include?('gs_node_id') && self.gs_node_id.blank? + self.gs_node_id = GsNode.where(:ip_address => HOMEBASE_IP_ADDRESS).first.try(:id) + end + end + + # Create a new GsClusterSyncLogEntry. + # This will be populated automatically to GsNode.all.where(...) + # + def create_on_other_gs_nodes(association_method = nil, association_uuid = nil) + action_on_other_gs_nodes('create', self.to_json, nil, association_method, association_uuid) + end + + def destroy_on_other_gs_nodes + action_on_other_gs_nodes('destroy', self.to_json) + end + + def update_on_other_gs_nodes(association_method = nil, association_uuid = nil) + action_on_other_gs_nodes('update', self.changes.to_json, 'Changed: ' + self.changed.to_json, association_method, association_uuid) + end + + def action_on_other_gs_nodes(action, content, history = nil, association_method = nil, association_uuid = nil) + # One doesn't make sense without the other. + # + if association_method.blank? || association_uuid.blank? + association_method = nil + association_uuid = nil + end + history = nil if history.blank? + if !self.attribute_names.include?('is_native') + logger.error "Couldn't #{action} #{self.class} with the ID #{self.id} on other GsNodes because #{self.class} doesn't have a is_native attribute." + else + if self.is_native != false + if defined? WRITE_GS_CLUSTER_SYNC_LOG && WRITE_GS_CLUSTER_SYNC_LOG == true + if !(defined? $gs_cluster_loop_protection) || $gs_cluster_loop_protection != true + begin + GsClusterSyncLogEntry.create( + :class_name => self.class.name, + :action => action, + :content => content, + :history => history, + :homebase_ip_address => HOMEBASE_IP_ADDRESS, + :waiting_to_be_synced => true, + :association_method => association_method, + :association_uuid => association_uuid + ) + rescue + logger.error "Couldn't add action: #{action} for #{self.class} with the ID #{self.id} to gs_cluster_log_entries." + end + end + end + end + end + end + +end
\ No newline at end of file diff --git a/lib/agi_server.rb b/lib/agi_server.rb new file mode 100644 index 0000000..29c06c4 --- /dev/null +++ b/lib/agi_server.rb @@ -0,0 +1,123 @@ +module AGIServer + DEFAULT_AGI_SERVER_HOST = nil + DEFAULT_AGI_SERVER_PORT = 4573 + + def self.log_debug(message) + puts "DEBUG-AGI-Server: #{message.to_s}" + #Rails.logger.debug "AGI-Server: #{message}" + end + + def self.log_error(message) + puts "ERROR-AGI-Server: #{message.to_s}" + #Rails.logger.error "AGI-Server: #{message}" + end + + class Client + AGI_AVAILABLE_METHODS = ['directory_lookup'] + @client = nil + @options = nil + + def set_variable(variable_name, variable_value) + if !@client + return false + end + @client.puts "SET VARIABLE #{variable_name.to_s} \"#{variable_value.to_s}\"" + end + + def directory_lookup + number = @options['agi_arg_1'].to_s.gsub(/[^0-9A-Za-z\*_-]/, '') + number_type = @options['agi_arg_3'].to_s + client_id = @options['agi_arg_2'].to_i + + if number.blank? + number = @options['agi_dnid'].to_s.gsub(/[^0-9A-Za-z\*_-]/, '') + number_type = "unknown" + client_id = 1 + end + + if client_id > 0 + if number != "" + phone_number = PhoneNumber.where(:number => number, :phone_numberable_type => "SipAccount").first + if phone_number.blank? + set_variable(:directory_status, 'unknown') + set_variable(:directory_message, 'Number not found in directory') + return nil + end + + set_variable(:directory_status, 'exact_match') + set_variable(:directory_message, 'Exact match') + set_variable(:directory_number_type, phone_number.phone_numberable_type) + set_variable(:directory_number_destination, phone_number.phone_numberable.to_s) + set_variable(:directory_number_host, "1") + set_variable(:directory_number_name, phone_number.name) + set_variable(:directory_number_number, phone_number.number) + set_variable(:directory_number_country_code, phone_number.country_code) + set_variable(:directory_number_area_code, phone_number.area_code) + set_variable(:directory_number_central_office_code, phone_number.central_office_code) + set_variable(:directory_number_subscriber_number, phone_number.subscriber_number) + set_variable(:directory_number_extension, phone_number.extension) + + if phone_number.phone_numberable_type == "SipAccount" + set_variable(:directory_caller_name, phone_number.phone_numberable.caller_name) + end + else + set_variable(:directory_status, 'error') + set_variable(:directory_message, 'No number specified') + end + else + set_variable(:directory_status, 'error') + set_variable(:directory_message, 'No ID') + end + end + + def handler(client) + @client = client + @options = Hash.new + while @client + buffer = @client.gets().strip + if buffer == "" + if @options['agi_network_script'] && AGI_AVAILABLE_METHODS.include?(@options['agi_network_script'].to_s.downcase) + self.send(@options['agi_network_script'].to_s.downcase) + end + break + elsif buffer =~ /^.*:\s/ + key, value = buffer.split(': ') + @options[key]= value + end + end + @client.close + end + end + + def self.server( host = DEFAULT_AGI_SERVER_HOST, port = DEFAULT_AGI_SERVER_PORT ) + + log_debug("Starting server process.") + require 'socket' + server = TCPServer.open(4573) + if server + run_server = true + end + + client_handler_id = 0 + while run_server + log_debug("Server listening on: #{server.local_address.ip_address}:#{server.local_address.ip_port}") + + Thread.start(server.accept) do |client| + remote_ip = client.remote_address.ip_address + remote_port = client.remote_address.ip_port + + begin + client_handler = Client.new + client_handler_id = client_handler.object_id + log_debug("[#{client_handler_id}] Connection opened: #{remote_ip}:#{remote_port}") + client_handler.handler(client) + log_debug("[#{client_handler_id}] Connection closed: #{remote_ip}:#{remote_port}") + rescue => e + log_error("[#{client_handler_id}] #{e.class.to_s}: #{e.to_s}, closing connection: #{remote_ip}:#{remote_port}") + ensure + client.close() + end + end + end + end +end diff --git a/lib/assets/.gitkeep b/lib/assets/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/assets/.gitkeep diff --git a/lib/freeswitch_event.rb b/lib/freeswitch_event.rb new file mode 100644 index 0000000..b6e5cbc --- /dev/null +++ b/lib/freeswitch_event.rb @@ -0,0 +1,133 @@ +class FreeswitchEventSocket + DEFAULT_HOST = "127.0.0.1" + DEFAULT_PORT = 8021 + DEFAULT_PASSWORD = "ClueCon" + DEFAULT_SOCKET_TIMEOUT = 20 + @socket = nil + + def auth(password) + self.command("auth #{password}") + result = self.result() + + if result && result["Reply-Text"] == "+OK accepted" + return true + end + return false + end + + def connect(password = DEFAULT_PASSWORD, event_host = DEFAULT_HOST, event_port = DEFAULT_PORT) + begin + @socket = TCPSocket.open(event_host, event_port) + rescue + return false + end + + if not @socket + return false + end + + result = self.result() + if result && result["Content-Type"] == "auth/request" then + if not self.auth(password) + return false + end + end + + return true + end + + def command(command) + @socket.puts("#{command}\n\n") + end + + def close() + @socket.close + end + + def read() + return @socket.recv(1024) + end + + def result() + reply = self.read() + if reply.class == String + message = Hash.new() + header, body = reply.split("\n\n", 2) + if ! body.blank? + message['_BODY'] = body + end + header.split("\n").each do |line| + key, value = line.split(/\: */, 2) + message[key] = value + end + return message + end + return nil + end +end + +class FreeswitchEvent + def initialize(event_type) + @event_type = event_type + @event_header = Array.new() + @event_body = nil + @event_header.push("sendevent #{@event_type}") + end + + def add_header(name, value) + @event_header.push("#{name}: #{value}") + end + + def add_body(body) + @event_body = body + end + + def fire() + if @event_body && @event_body.length > 0 + self.add_header("content-length", @event_body.length); + end + + event = FreeswitchEventSocket.new() + if event && event.connect() + event.command(@event_header.join("\n")) + event.command( @event_body) + result = event.result() + event.close() + + if result && result["Reply-Text"] == "+OK" + return true + end + end + return false + end +end + +class FreeswitchAPI + def self.execute(command, arguments, bgapi = false) + + event = FreeswitchEventSocket.new() + if event && event.connect() + api = bgapi ? 'bgapi' : 'api' + event.command( "#{api} #{command} #{arguments}") + result = event.result() + if result && result["Content-Type"] == 'api/response' && result["_BODY"].blank? + result = event.result() + end + event.close() + + if result + if result.has_key?('Reply-Text') && result['Reply-Text'] =~ /^\+OK/ + if result.has_key?('Job-UUID') && ! result['Job-UUID'].blank? + return result['Job-UUID']; + else + return true; + end + elsif result.has_key?('_BODY') && result['_BODY'] =~ /^\+OK/ + return true; + end + end + end + + return false + end +end diff --git a/lib/generators/nifty.rb b/lib/generators/nifty.rb new file mode 100644 index 0000000..1c3d6d7 --- /dev/null +++ b/lib/generators/nifty.rb @@ -0,0 +1,28 @@ +require 'rails/generators/base' + +module Nifty + module Generators + class Base < Rails::Generators::Base #:nodoc: + def self.source_root + @_nifty_source_root ||= File.expand_path(File.join(File.dirname(__FILE__), 'nifty', generator_name, 'templates')) + end + + def self.banner + "rails generate nifty:#{generator_name} #{self.arguments.map{ |a| a.usage }.join(' ')} [options]" + end + + private + + def add_gem(name, options = {}) + gemfile_content = File.read(destination_path("Gemfile")) + File.open(destination_path("Gemfile"), 'a') { |f| f.write("\n") } unless gemfile_content =~ /\n\Z/ + gem name, options unless gemfile_content.include? name + end + + def print_usage + self.class.help(Thor::Base.shell.new) + exit + end + end + end +end diff --git a/lib/generators/nifty/authentication/USAGE b/lib/generators/nifty/authentication/USAGE new file mode 100644 index 0000000..89f0a64 --- /dev/null +++ b/lib/generators/nifty/authentication/USAGE @@ -0,0 +1,50 @@ +Description: + Generates a user model, users controller, and sessions controller. The + users controller handles the registration and the sessions controller + handles authentication. This is similar to restful_authentication, but + simpler. + + IMPORTANT: This generator uses the "title" helper method which is generated + by the nifty_layout generator. You may want to run that generator first. + +Usage: + If you do not pass any arguments, the model name will default to "user", and + the authentication controller will default to "session". You can override + each of these respectively by passing one or two arguments. Either name can + be CamelCased or under_scored. + + Make sure to setup the authlogic gem if you are using that option. + + gem "authlogic" # in Gemfile + +Examples: + rails generate nifty:authentication + + Creates user model, users_controller, and sessions_controller. + + rails generate nifty:authentication account + + Creates account model, accounts_controller, and sessions_controller. + + rails generate nifty:authentication Account UserSession + + Creates account model, accounts_controller, and user_sessions_controller. + +Methods: + There are several methods generated which you can use in your application. + Here's a common example of what you might add to your layout. + + <% if logged_in? %> + Welcome <%= current_user.username %>! Not you? + <%= link_to "Log out", logout_path %> + <% else %> + <%= link_to "Sign up", signup_path %> or + <%= link_to "log in", login_path %>. + <% end %> + + You can also restrict unregistered users from accessing a controller using + a before filter. For example. + + before_filter :login_required, :except => [:index, :show] + + See the generated file lib/authentication.rb for details.
\ No newline at end of file diff --git a/lib/generators/nifty/authentication/authentication_generator.rb b/lib/generators/nifty/authentication/authentication_generator.rb new file mode 100644 index 0000000..d4dcbde --- /dev/null +++ b/lib/generators/nifty/authentication/authentication_generator.rb @@ -0,0 +1,154 @@ +require 'generators/nifty' +require 'rails/generators/migration' + +module Nifty + module Generators + class AuthenticationGenerator < Base + include Rails::Generators::Migration + + argument :user_name, :type => :string, :default => 'user', :banner => 'user_name' + argument :session_name, :type => :string, :default => '[[DEFAULT]]', :banner => 'sessions_controller_name' + + class_option :testunit, :desc => 'Use test/unit for test files.', :group => 'Test framework', :type => :boolean + class_option :rspec, :desc => 'Use RSpec for test files.', :group => 'Test framework', :type => :boolean + class_option :shoulda, :desc => 'Use shoulda for test files.', :group => 'Test framework', :type => :boolean + + class_option :haml, :desc => 'Generate HAML views instead of ERB.', :type => :boolean + class_option :authlogic, :desc => 'Use Authlogic for authentication.', :type => :boolean + + def add_gems + add_gem "bcrypt-ruby", :require => "bcrypt" + add_gem "mocha", :group => :test + end + + def create_model_files + template 'user.rb', "app/models/#{user_singular_name}.rb" + template 'authlogic_session.rb', "app/models/#{user_singular_name}_session.rb" if options.authlogic? + end + + def create_controller_files + template 'users_controller.rb', "app/controllers/#{user_plural_name}_controller.rb" + template 'sessions_controller.rb', "app/controllers/#{session_plural_name}_controller.rb" + end + + def create_helper_files + template 'users_helper.rb', "app/helpers/#{user_plural_name}_helper.rb" + template 'sessions_helper.rb', "app/helpers/#{session_plural_name}_helper.rb" + end + + def create_view_files + template "views/#{view_language}/signup.html.#{view_language}", "app/views/#{user_plural_name}/new.html.#{view_language}" + template "views/#{view_language}/edit.html.#{view_language}", "app/views/#{user_plural_name}/edit.html.#{view_language}" + template "views/#{view_language}/_form.html.#{view_language}", "app/views/#{user_plural_name}/_form.html.#{view_language}" + template "views/#{view_language}/login.html.#{view_language}", "app/views/#{session_plural_name}/new.html.#{view_language}" + end + + def create_lib_files + template 'controller_authentication.rb', 'lib/controller_authentication.rb' + end + + def create_routes + route "resources #{user_plural_name.to_sym.inspect}" + route "resources #{session_plural_name.to_sym.inspect}" + route "match 'login' => '#{session_plural_name}#new', :as => :login" + route "match 'logout' => '#{session_plural_name}#destroy', :as => :logout" + route "match 'signup' => '#{user_plural_name}#new', :as => :signup" + route "match '#{user_singular_name}/edit' => '#{user_plural_name}#edit', :as => :edit_current_#{user_singular_name}" + end + + def create_migration + migration_template 'migration.rb', "db/migrate/create_#{user_plural_name}.rb" + end + + def load_and_include_authentication + inject_into_class "config/application.rb", "Application", " config.autoload_paths << \"\#{config.root}/lib\"" + inject_into_class "app/controllers/application_controller.rb", "ApplicationController", " include ControllerAuthentication\n" + end + + def create_test_files + if test_framework == :rspec + template 'fixtures.yml', "spec/fixtures/#{user_plural_name}.yml" + template 'tests/rspec/user.rb', "spec/models/#{user_singular_name}_spec.rb" + template 'tests/rspec/users_controller.rb', "spec/controllers/#{user_plural_name}_controller_spec.rb" + template 'tests/rspec/sessions_controller.rb', "spec/controllers/#{session_plural_name}_controller_spec.rb" + else + template 'fixtures.yml', "test/fixtures/#{user_plural_name}.yml" + template "tests/#{test_framework}/user.rb", "test/unit/#{user_singular_name}_test.rb" + template "tests/#{test_framework}/users_controller.rb", "test/functional/#{user_plural_name}_controller_test.rb" + template "tests/#{test_framework}/sessions_controller.rb", "test/functional/#{session_plural_name}_controller_test.rb" + end + end + + private + + def session_name + @_session_name ||= @session_name == '[[DEFAULT]]' ? + (options.authlogic? ? user_name + '_session' : 'session') : + @session_name + end + + def user_singular_name + user_name.underscore + end + + def user_plural_name + user_singular_name.pluralize + end + + def user_class_name + user_name.camelize + end + + def user_plural_class_name + user_plural_name.camelize + end + + def session_singular_name + session_name.underscore + end + + def session_plural_name + session_singular_name.pluralize + end + + def session_class_name + session_name.camelize + end + + def session_plural_class_name + session_plural_name.camelize + end + + def view_language + options.haml? ? 'haml' : 'erb' + end + + def test_framework + return @test_framework if defined?(@test_framework) + if options.testunit? + return @test_framework = :testunit + elsif options.rspec? + return @test_framework = :rspec + elsif options.shoulda? + return @test_framework = :shoulda + else + return @test_framework = File.exist?(destination_path('spec')) ? :rspec : :testunit + end + end + + def destination_path(path) + File.join(destination_root, path) + end + + # FIXME: Should be proxied to ActiveRecord::Generators::Base + # Implement the required interface for Rails::Generators::Migration. + def self.next_migration_number(dirname) #:nodoc: + if ActiveRecord::Base.timestamped_migrations + Time.now.utc.strftime("%Y%m%d%H%M%S") + else + "%.3d" % (current_migration_number(dirname) + 1) + end + end + end + end +end diff --git a/lib/generators/nifty/authentication/templates/authlogic_session.rb b/lib/generators/nifty/authentication/templates/authlogic_session.rb new file mode 100644 index 0000000..676cfd0 --- /dev/null +++ b/lib/generators/nifty/authentication/templates/authlogic_session.rb @@ -0,0 +1,2 @@ +class <%= session_class_name %> < Authlogic::Session::Base +end diff --git a/lib/generators/nifty/authentication/templates/controller_authentication.rb b/lib/generators/nifty/authentication/templates/controller_authentication.rb new file mode 100644 index 0000000..6d34ab0 --- /dev/null +++ b/lib/generators/nifty/authentication/templates/controller_authentication.rb @@ -0,0 +1,60 @@ +# This module is included in your application controller which makes +# several methods available to all controllers and views. Here's a +# common example you might add to your application layout file. +# +# <%% if logged_in? %> +# Welcome <%%= current_<%= user_singular_name %>.username %>. +# <%%= link_to "Edit profile", edit_current_<%= user_singular_name %>_path %> or +# <%%= link_to "Log out", logout_path %> +# <%% else %> +# <%%= link_to "Sign up", signup_path %> or +# <%%= link_to "log in", login_path %>. +# <%% end %> +# +# You can also restrict unregistered users from accessing a controller using +# a before filter. For example. +# +# before_filter :login_required, :except => [:index, :show] +module ControllerAuthentication + def self.included(controller) + controller.send :helper_method, :current_<%= user_singular_name %>, :logged_in?, :redirect_to_target_or_default + end + +<%- if options[:authlogic] -%> + def current_<%= session_singular_name %> + return @current_<%= session_singular_name %> if defined?(@current_<%= session_singular_name %>) + @current_<%= session_singular_name %> = <%= session_class_name %>.find + end + + def current_<%= user_singular_name %> + return @current_<%= user_singular_name %> if defined?(@current_<%= user_singular_name %>) + @current_<%= user_singular_name %> = current_<%= session_singular_name %> && current_<%= session_singular_name %>.record + end +<%- else -%> + def current_<%= user_singular_name %> + @current_<%= user_singular_name %> ||= <%= user_class_name %>.find(session[:<%= user_singular_name %>_id]) if session[:<%= user_singular_name %>_id] + end +<%- end -%> + + def logged_in? + current_<%= user_singular_name %> + end + + def login_required + unless logged_in? + store_target_location + redirect_to login_url, :alert => "You must first log in or sign up before accessing this page." + end + end + + def redirect_to_target_or_default(default, *args) + redirect_to(session[:return_to] || default, *args) + session[:return_to] = nil + end + + private + + def store_target_location + session[:return_to] = request.url + end +end diff --git a/lib/generators/nifty/authentication/templates/fixtures.yml b/lib/generators/nifty/authentication/templates/fixtures.yml new file mode 100644 index 0000000..c52532b --- /dev/null +++ b/lib/generators/nifty/authentication/templates/fixtures.yml @@ -0,0 +1,24 @@ +# password: "secret" +foo: + username: foo + email: foo@example.com +<%- if options[:authlogic] -%> + persistence_token: d5ddba13ed4408ea2b0a12ab18ed2d2eda086279736bdc121ca726a11f1e4b99217d9c534c2cc4ebb22729349c8c5fdbe1529e1f2c3c5859c62ef4dd9feea25c + crypted_password: 3d16c326648cccafe3d4b4cb024475c381dda92f430dfedf6f933e1f61203bacb6bae2437849bdb43b06be335e23790e4aa03902b3c28c3bbbbe27d501e521f3 + password_salt: n6z_wtpWoIsHgQb5IcFd +<%- else -%> + password_hash: 3488f5f7efecab14b91eb96169e5e1ee518a569f + password_salt: bef65e058905c379436d80d1a32e7374b139e7b0 +<%- end -%> + +bar: + username: bar + email: bar@example.com +<%- if options[:authlogic] -%> + persistence_token: 19e074bd7cb506ab3e7e53e41f24f0ab3221c8cb68111f4c1aa43965114ad734233979a50a9463537487cdca18c279ac91c4bc83693d589625d446493322394c + crypted_password: 3bc9f4113ca645a186765df3d31a9352d0067bf2304ba0cdd6b08a7f3d58c6668ab1762fa3e76aef466ea2ff188399d8e6c40244fa59312bb4112292dac9f7f0 + password_salt: UiAh9ejabnKRxqsiK0xO +<%- else -%> + password_hash: 3488f5f7efecab14b91eb96169e5e1ee518a569f + password_salt: bef65e058905c379436d80d1a32e7374b139e7b0 +<%- end -%> diff --git a/lib/generators/nifty/authentication/templates/migration.rb b/lib/generators/nifty/authentication/templates/migration.rb new file mode 100644 index 0000000..c945df3 --- /dev/null +++ b/lib/generators/nifty/authentication/templates/migration.rb @@ -0,0 +1,20 @@ +class Create<%= user_plural_class_name %> < ActiveRecord::Migration + def self.up + create_table :<%= user_plural_name %> do |t| + t.string :username + t.string :email + <%- if options[:authlogic] -%> + t.string :persistence_token + t.string :crypted_password + <%- else -%> + t.string :password_hash + <%- end -%> + t.string :password_salt + t.timestamps + end + end + + def self.down + drop_table :<%= user_plural_name %> + end +end diff --git a/lib/generators/nifty/authentication/templates/sessions_controller.rb b/lib/generators/nifty/authentication/templates/sessions_controller.rb new file mode 100644 index 0000000..65e77c1 --- /dev/null +++ b/lib/generators/nifty/authentication/templates/sessions_controller.rb @@ -0,0 +1,41 @@ +class <%= session_plural_class_name %>Controller < ApplicationController +<%- if options[:authlogic] -%> + def new + @<%= session_singular_name %> = <%= session_class_name %>.new + end + + def create + @<%= session_singular_name %> = <%= session_class_name %>.new(params[:<%= session_singular_name %>]) + if @<%= session_singular_name %>.save + redirect_to_target_or_default root_url, :notice => "Logged in successfully." + else + render :new + end + end + + def destroy + @<%= session_singular_name %> = <%= session_class_name %>.find + @<%= session_singular_name %>.destroy + redirect_to root_url, :notice => "You have been logged out." + end +<%- else -%> + def new + end + + def create + <%= user_singular_name %> = <%= user_class_name %>.authenticate(params[:login], params[:password]) + if <%= user_singular_name %> + session[:<%= user_singular_name %>_id] = <%= user_singular_name %>.id + redirect_to_target_or_default root_url, :notice => "Logged in successfully." + else + flash.now[:alert] = "Invalid login or password." + render :new + end + end + + def destroy + session[:<%= user_singular_name %>_id] = nil + redirect_to root_url, :notice => "You have been logged out." + end +<%- end -%> +end diff --git a/lib/generators/nifty/authentication/templates/sessions_helper.rb b/lib/generators/nifty/authentication/templates/sessions_helper.rb new file mode 100644 index 0000000..0958537 --- /dev/null +++ b/lib/generators/nifty/authentication/templates/sessions_helper.rb @@ -0,0 +1,2 @@ +module <%= session_plural_class_name %>Helper +end diff --git a/lib/generators/nifty/authentication/templates/tests/rspec/sessions_controller.rb b/lib/generators/nifty/authentication/templates/tests/rspec/sessions_controller.rb new file mode 100644 index 0000000..e0953cc --- /dev/null +++ b/lib/generators/nifty/authentication/templates/tests/rspec/sessions_controller.rb @@ -0,0 +1,39 @@ +require File.dirname(__FILE__) + '/../spec_helper' + +describe <%= session_plural_class_name %>Controller do + fixtures :all + render_views + + it "new action should render new template" do + get :new + response.should render_template(:new) + end + +<%- if options[:authlogic] -%> + it "create action should render new template when authentication is invalid" do + post :create, :<%= session_singular_name %> => { :username => "foo", :password => "badpassword" } + response.should render_template(:new) + <%= session_class_name %>.find.should be_nil + end + + it "create action should redirect when authentication is valid" do + post :create, :<%= session_singular_name %> => { :username => "foo", :password => "secret" } + response.should redirect_to(root_url) + <%= session_class_name %>.find.<%= user_singular_name %>.should == <%= user_plural_name %>(:foo) + end +<%- else -%> + it "create action should render new template when authentication is invalid" do + <%= user_class_name %>.stubs(:authenticate).returns(nil) + post :create + response.should render_template(:new) + session['<%= user_singular_name %>_id'].should be_nil + end + + it "create action should redirect when authentication is valid" do + <%= user_class_name %>.stubs(:authenticate).returns(<%= user_class_name %>.first) + post :create + response.should redirect_to(root_url) + session['<%= user_singular_name %>_id'].should == <%= user_class_name %>.first.id + end +<%- end -%> +end diff --git a/lib/generators/nifty/authentication/templates/tests/rspec/user.rb b/lib/generators/nifty/authentication/templates/tests/rspec/user.rb new file mode 100644 index 0000000..a3f7e92 --- /dev/null +++ b/lib/generators/nifty/authentication/templates/tests/rspec/user.rb @@ -0,0 +1,83 @@ +require File.dirname(__FILE__) + '/../spec_helper' + +describe <%= user_class_name %> do +<%- unless options[:authlogic] -%> + def new_<%= user_singular_name %>(attributes = {}) + attributes[:username] ||= 'foo' + attributes[:email] ||= 'foo@example.com' + attributes[:password] ||= 'abc123' + attributes[:password_confirmation] ||= attributes[:password] + <%= user_class_name %>.new(attributes) + end + + before(:each) do + <%= user_class_name %>.delete_all + end + + it "should be valid" do + new_<%= user_singular_name %>.should be_valid + end + + it "should require username" do + new_<%= user_singular_name %>(:username => '').should have(1).error_on(:username) + end + + it "should require password" do + new_<%= user_singular_name %>(:password => '').should have(1).error_on(:password) + end + + it "should require well formed email" do + new_<%= user_singular_name %>(:email => 'foo@bar@example.com').should have(1).error_on(:email) + end + + it "should validate uniqueness of email" do + new_<%= user_singular_name %>(:email => 'bar@example.com').save! + new_<%= user_singular_name %>(:email => 'bar@example.com').should have(1).error_on(:email) + end + + it "should validate uniqueness of username" do + new_<%= user_singular_name %>(:username => 'uniquename').save! + new_<%= user_singular_name %>(:username => 'uniquename').should have(1).error_on(:username) + end + + it "should not allow odd characters in username" do + new_<%= user_singular_name %>(:username => 'odd ^&(@)').should have(1).error_on(:username) + end + + it "should validate password is longer than 3 characters" do + new_<%= user_singular_name %>(:password => 'bad').should have(1).error_on(:password) + end + + it "should require matching password confirmation" do + new_<%= user_singular_name %>(:password_confirmation => 'nonmatching').should have(1).error_on(:password) + end + + it "should generate password hash and salt on create" do + <%= user_singular_name %> = new_<%= user_singular_name %> + <%= user_singular_name %>.save! + <%= user_singular_name %>.password_hash.should_not be_nil + <%= user_singular_name %>.password_salt.should_not be_nil + end + + it "should authenticate by username" do + <%= user_singular_name %> = new_<%= user_singular_name %>(:username => 'foobar', :password => 'secret') + <%= user_singular_name %>.save! + <%= user_class_name %>.authenticate('foobar', 'secret').should == <%= user_singular_name %> + end + + it "should authenticate by email" do + <%= user_singular_name %> = new_<%= user_singular_name %>(:email => 'foo@bar.com', :password => 'secret') + <%= user_singular_name %>.save! + <%= user_class_name %>.authenticate('foo@bar.com', 'secret').should == <%= user_singular_name %> + end + + it "should not authenticate bad username" do + <%= user_class_name %>.authenticate('nonexisting', 'secret').should be_nil + end + + it "should not authenticate bad password" do + new_<%= user_singular_name %>(:username => 'foobar', :password => 'secret').save! + <%= user_class_name %>.authenticate('foobar', 'badpassword').should be_nil + end +<%- end -%> +end diff --git a/lib/generators/nifty/authentication/templates/tests/rspec/users_controller.rb b/lib/generators/nifty/authentication/templates/tests/rspec/users_controller.rb new file mode 100644 index 0000000..60bcff9 --- /dev/null +++ b/lib/generators/nifty/authentication/templates/tests/rspec/users_controller.rb @@ -0,0 +1,56 @@ +require File.dirname(__FILE__) + '/../spec_helper' + +describe <%= user_plural_class_name %>Controller do + fixtures :all + render_views + + it "new action should render new template" do + get :new + response.should render_template(:new) + end + + it "create action should render new template when model is invalid" do + <%= user_class_name %>.any_instance.stubs(:valid?).returns(false) + post :create + response.should render_template(:new) + end + + it "create action should redirect when model is valid" do + <%= user_class_name %>.any_instance.stubs(:valid?).returns(true) + post :create + response.should redirect_to(root_url) + <%- unless options[:authlogic] -%> + session['<%= user_singular_name %>_id'].should == assigns['<%= user_singular_name %>'].id + <%- end -%> + end + + it "edit action should redirect when not logged in" do + get :edit, :id => "ignored" + response.should redirect_to(login_url) + end + + it "edit action should render edit template" do + @controller.stubs(:current_<%= user_singular_name %>).returns(<%= user_class_name %>.first) + get :edit, :id => "ignored" + response.should render_template(:edit) + end + + it "update action should redirect when not logged in" do + put :update, :id => "ignored" + response.should redirect_to(login_url) + end + + it "update action should render edit template when <%= user_singular_name %> is invalid" do + @controller.stubs(:current_<%= user_singular_name %>).returns(<%= user_class_name %>.first) + <%= user_class_name %>.any_instance.stubs(:valid?).returns(false) + put :update, :id => "ignored" + response.should render_template(:edit) + end + + it "update action should redirect when <%= user_singular_name %> is valid" do + @controller.stubs(:current_<%= user_singular_name %>).returns(<%= user_class_name %>.first) + <%= user_class_name %>.any_instance.stubs(:valid?).returns(true) + put :update, :id => "ignored" + response.should redirect_to(root_url) + end +end diff --git a/lib/generators/nifty/authentication/templates/tests/shoulda/sessions_controller.rb b/lib/generators/nifty/authentication/templates/tests/shoulda/sessions_controller.rb new file mode 100644 index 0000000..e2f9005 --- /dev/null +++ b/lib/generators/nifty/authentication/templates/tests/shoulda/sessions_controller.rb @@ -0,0 +1,40 @@ +require 'test_helper' + +class <%= session_plural_class_name %>ControllerTest < ActionController::TestCase + context "new action" do + should "render new template" do + get :new + assert_template 'new' + end + end + + context "create action" do + <%- if options[:authlogic] -%> + should "render new template when authentication is invalid" do + post :create, :<%= session_singular_name %> => { :username => "foo", :password => "badpassword" } + assert_template 'new' + assert_nil <%= session_class_name %>.find + end + + should "redirect when authentication is valid" do + post :create, :<%= session_singular_name %> => { :username => "foo", :password => "secret" } + assert_redirected_to root_url + assert_equal <%= user_plural_name %>(:foo), <%= session_class_name %>.find.<%= user_singular_name %> + end + <%- else -%> + should "render new template when authentication is invalid" do + <%= user_class_name %>.stubs(:authenticate).returns(nil) + post :create + assert_template 'new' + assert_nil session['<%= user_singular_name %>_id'] + end + + should "redirect when authentication is valid" do + <%= user_class_name %>.stubs(:authenticate).returns(<%= user_class_name %>.first) + post :create + assert_redirected_to root_url + assert_equal <%= user_class_name %>.first.id, session['<%= user_singular_name %>_id'] + end + <%- end -%> + end +end diff --git a/lib/generators/nifty/authentication/templates/tests/shoulda/user.rb b/lib/generators/nifty/authentication/templates/tests/shoulda/user.rb new file mode 100644 index 0000000..beb8bf4 --- /dev/null +++ b/lib/generators/nifty/authentication/templates/tests/shoulda/user.rb @@ -0,0 +1,85 @@ +require 'test_helper' + +class <%= user_class_name %>Test < ActiveSupport::TestCase +<%- unless options[:authlogic] -%> + def new_<%= user_singular_name %>(attributes = {}) + attributes[:username] ||= 'foo' + attributes[:email] ||= 'foo@example.com' + attributes[:password] ||= 'abc123' + attributes[:password_confirmation] ||= attributes[:password] + <%= user_singular_name %> = <%= user_class_name %>.new(attributes) + <%= user_singular_name %>.valid? # run validations + <%= user_singular_name %> + end + + def setup + <%= user_class_name %>.delete_all + end + + should "be valid" do + assert new_<%= user_singular_name %>.valid? + end + + should "require username" do + assert_equal ["can't be blank"], new_<%= user_singular_name %>(:username => '').errors[:username] + end + + should "require password" do + assert_equal ["can't be blank"], new_<%= user_singular_name %>(:password => '').errors[:password] + end + + should "require well formed email" do + assert_equal ["is invalid"], new_<%= user_singular_name %>(:email => 'foo@bar@example.com').errors[:email] + end + + should "validate uniqueness of email" do + new_<%= user_singular_name %>(:email => 'bar@example.com').save! + assert_equal ["has already been taken"], new_<%= user_singular_name %>(:email => 'bar@example.com').errors[:email] + end + + should "validate uniqueness of username" do + new_<%= user_singular_name %>(:username => 'uniquename').save! + assert_equal ["has already been taken"], new_<%= user_singular_name %>(:username => 'uniquename').errors[:username] + end + + should "not allow odd characters in username" do + assert_equal ["should only contain letters, numbers, or .-_@"], new_<%= user_singular_name %>(:username => 'odd ^&(@)').errors[:username] + end + + should "validate password is longer than 3 characters" do + assert_equal ["is too short (minimum is 4 characters)"], new_<%= user_singular_name %>(:password => 'bad').errors[:password] + end + + should "require matching password confirmation" do + assert_equal ["doesn't match confirmation"], new_<%= user_singular_name %>(:password_confirmation => 'nonmatching').errors[:password] + end + + should "generate password hash and salt on create" do + <%= user_singular_name %> = new_<%= user_singular_name %> + <%= user_singular_name %>.save! + assert <%= user_singular_name %>.password_hash + assert <%= user_singular_name %>.password_salt + end + + should "authenticate by username" do + <%= user_singular_name %> = new_<%= user_singular_name %>(:username => 'foobar', :password => 'secret') + <%= user_singular_name %>.save! + assert_equal <%= user_singular_name %>, <%= user_class_name %>.authenticate('foobar', 'secret') + end + + should "authenticate by email" do + <%= user_singular_name %> = new_<%= user_singular_name %>(:email => 'foo@bar.com', :password => 'secret') + <%= user_singular_name %>.save! + assert_equal <%= user_singular_name %>, <%= user_class_name %>.authenticate('foo@bar.com', 'secret') + end + + should "not authenticate bad username" do + assert_nil <%= user_class_name %>.authenticate('nonexisting', 'secret') + end + + should "not authenticate bad password" do + new_<%= user_singular_name %>(:username => 'foobar', :password => 'secret').save! + assert_nil <%= user_class_name %>.authenticate('foobar', 'badpassword') + end +<%- end -%> +end diff --git a/lib/generators/nifty/authentication/templates/tests/shoulda/users_controller.rb b/lib/generators/nifty/authentication/templates/tests/shoulda/users_controller.rb new file mode 100644 index 0000000..1728329 --- /dev/null +++ b/lib/generators/nifty/authentication/templates/tests/shoulda/users_controller.rb @@ -0,0 +1,61 @@ +require 'test_helper' + +class <%= user_plural_class_name %>ControllerTest < ActionController::TestCase + context "new action" do + should "render new template" do + get :new + assert_template 'new' + end + end + + context "create action" do + should "render new template when <%= user_singular_name %> is invalid" do + <%= user_class_name %>.any_instance.stubs(:valid?).returns(false) + post :create + assert_template 'new' + end + + should "redirect when <%= user_singular_name %> is valid" do + <%= user_class_name %>.any_instance.stubs(:valid?).returns(true) + post :create + assert_redirected_to root_url + <%- unless options[:authlogic] -%> + assert_equal assigns['<%= user_singular_name %>'].id, session['<%= user_singular_name %>_id'] + <%- end -%> + end + end + + context "edit action" do + should "redirect when not logged in" do + get :edit, :id => "ignored" + assert_redirected_to login_url + end + + should "render edit template" do + @controller.stubs(:current_<%= user_singular_name %>).returns(<%= user_class_name %>.first) + get :edit, :id => "ignored" + assert_template 'edit' + end + end + + context "update action" do + should "redirect when not logged in" do + put :update, :id => "ignored" + assert_redirected_to login_url + end + + should "render edit template when <%= user_singular_name %> is invalid" do + @controller.stubs(:current_<%= user_singular_name %>).returns(<%= user_class_name %>.first) + <%= user_class_name %>.any_instance.stubs(:valid?).returns(false) + put :update, :id => "ignored" + assert_template 'edit' + end + + should "redirect when <%= user_singular_name %> is valid" do + @controller.stubs(:current_<%= user_singular_name %>).returns(<%= user_class_name %>.first) + <%= user_class_name %>.any_instance.stubs(:valid?).returns(true) + put :update, :id => "ignored" + assert_redirected_to root_url + end + end +end diff --git a/lib/generators/nifty/authentication/templates/tests/testunit/sessions_controller.rb b/lib/generators/nifty/authentication/templates/tests/testunit/sessions_controller.rb new file mode 100644 index 0000000..fe2a65b --- /dev/null +++ b/lib/generators/nifty/authentication/templates/tests/testunit/sessions_controller.rb @@ -0,0 +1,36 @@ +require 'test_helper' + +class <%= session_plural_class_name %>ControllerTest < ActionController::TestCase + def test_new + get :new + assert_template 'new' + end + +<%- if options[:authlogic] -%> + def test_create_invalid + post :create, :<%= session_singular_name %> => { :username => "foo", :password => "badpassword" } + assert_template 'new' + assert_nil <%= session_class_name %>.find + end + + def test_create_valid + post :create, :<%= session_singular_name %> => { :username => "foo", :password => "secret" } + assert_redirected_to root_url + assert_equal <%= user_plural_name %>(:foo), <%= session_class_name %>.find.<%= user_singular_name %> + end +<%- else -%> + def test_create_invalid + <%= user_class_name %>.stubs(:authenticate).returns(nil) + post :create + assert_template 'new' + assert_nil session['<%= user_singular_name %>_id'] + end + + def test_create_valid + <%= user_class_name %>.stubs(:authenticate).returns(<%= user_class_name %>.first) + post :create + assert_redirected_to root_url + assert_equal <%= user_class_name %>.first.id, session['<%= user_singular_name %>_id'] + end +<%- end -%> +end diff --git a/lib/generators/nifty/authentication/templates/tests/testunit/user.rb b/lib/generators/nifty/authentication/templates/tests/testunit/user.rb new file mode 100644 index 0000000..c036cf1 --- /dev/null +++ b/lib/generators/nifty/authentication/templates/tests/testunit/user.rb @@ -0,0 +1,88 @@ +require 'test_helper' + +class <%= user_class_name %>Test < ActiveSupport::TestCase +<%- unless options[:authlogic] -%> + def new_<%= user_singular_name %>(attributes = {}) + attributes[:username] ||= 'foo' + attributes[:email] ||= 'foo@example.com' + attributes[:password] ||= 'abc123' + attributes[:password_confirmation] ||= attributes[:password] + <%= user_singular_name %> = <%= user_class_name %>.new(attributes) + <%= user_singular_name %>.valid? # run validations + <%= user_singular_name %> + end + + def setup + <%= user_class_name %>.delete_all + end + + def test_valid + assert new_<%= user_singular_name %>.valid? + end + + def test_require_username + assert_equal ["can't be blank"], new_<%= user_singular_name %>(:username => '').errors[:username] + end + + def test_require_password + assert_equal ["can't be blank"], new_<%= user_singular_name %>(:password => '').errors[:password] + end + + def test_require_well_formed_email + assert_equal ["is invalid"], new_<%= user_singular_name %>(:email => 'foo@bar@example.com').errors[:email] + end + + def test_validate_uniqueness_of_email + new_<%= user_singular_name %>(:email => 'bar@example.com').save! + assert_equal ["has already been taken"], new_<%= user_singular_name %>(:email => 'bar@example.com').errors[:email] + end + + def test_validate_uniqueness_of_username + new_<%= user_singular_name %>(:username => 'uniquename').save! + assert_equal ["has already been taken"], new_<%= user_singular_name %>(:username => 'uniquename').errors[:username] + end + + def test_validate_odd_characters_in_username + assert_equal ["should only contain letters, numbers, or .-_@"], new_<%= user_singular_name %>(:username => 'odd ^&(@)').errors[:username] + end + + def test_validate_password_length + assert_equal ["is too short (minimum is 4 characters)"], new_<%= user_singular_name %>(:password => 'bad').errors[:password] + end + + def test_require_matching_password_confirmation + assert_equal ["doesn't match confirmation"], new_<%= user_singular_name %>(:password_confirmation => 'nonmatching').errors[:password] + end + + def test_generate_password_hash_and_salt_on_create + <%= user_singular_name %> = new_<%= user_singular_name %> + <%= user_singular_name %>.save! + assert <%= user_singular_name %>.password_hash + assert <%= user_singular_name %>.password_salt + end + + def test_authenticate_by_username + <%= user_class_name %>.delete_all + <%= user_singular_name %> = new_<%= user_singular_name %>(:username => 'foobar', :password => 'secret') + <%= user_singular_name %>.save! + assert_equal <%= user_singular_name %>, <%= user_class_name %>.authenticate('foobar', 'secret') + end + + def test_authenticate_by_email + <%= user_class_name %>.delete_all + <%= user_singular_name %> = new_<%= user_singular_name %>(:email => 'foo@bar.com', :password => 'secret') + <%= user_singular_name %>.save! + assert_equal <%= user_singular_name %>, <%= user_class_name %>.authenticate('foo@bar.com', 'secret') + end + + def test_authenticate_bad_username + assert_nil <%= user_class_name %>.authenticate('nonexisting', 'secret') + end + + def test_authenticate_bad_password + <%= user_class_name %>.delete_all + new_<%= user_singular_name %>(:username => 'foobar', :password => 'secret').save! + assert_nil <%= user_class_name %>.authenticate('foobar', 'badpassword') + end +<%- end -%> +end diff --git a/lib/generators/nifty/authentication/templates/tests/testunit/users_controller.rb b/lib/generators/nifty/authentication/templates/tests/testunit/users_controller.rb new file mode 100644 index 0000000..ef8a3f7 --- /dev/null +++ b/lib/generators/nifty/authentication/templates/tests/testunit/users_controller.rb @@ -0,0 +1,53 @@ +require 'test_helper' + +class <%= user_plural_class_name %>ControllerTest < ActionController::TestCase + def test_new + get :new + assert_template 'new' + end + + def test_create_invalid + <%= user_class_name %>.any_instance.stubs(:valid?).returns(false) + post :create + assert_template 'new' + end + + def test_create_valid + <%= user_class_name %>.any_instance.stubs(:valid?).returns(true) + post :create + assert_redirected_to root_url + <%- unless options[:authlogic] -%> + assert_equal assigns['<%= user_singular_name %>'].id, session['<%= user_singular_name %>_id'] + <%- end -%> + end + + def test_edit_without_user + get :edit, :id => "ignored" + assert_redirected_to login_url + end + + def test_edit + @controller.stubs(:current_<%= user_singular_name %>).returns(<%= user_class_name %>.first) + get :edit, :id => "ignored" + assert_template 'edit' + end + + def test_update_without_user + put :update, :id => "ignored" + assert_redirected_to login_url + end + + def test_update_invalid + @controller.stubs(:current_<%= user_singular_name %>).returns(<%= user_class_name %>.first) + <%= user_class_name %>.any_instance.stubs(:valid?).returns(false) + put :update, :id => "ignored" + assert_template 'edit' + end + + def test_update_valid + @controller.stubs(:current_<%= user_singular_name %>).returns(<%= user_class_name %>.first) + <%= user_class_name %>.any_instance.stubs(:valid?).returns(true) + put :update, :id => "ignored" + assert_redirected_to root_url + end +end diff --git a/lib/generators/nifty/authentication/templates/user.rb b/lib/generators/nifty/authentication/templates/user.rb new file mode 100644 index 0000000..ec18524 --- /dev/null +++ b/lib/generators/nifty/authentication/templates/user.rb @@ -0,0 +1,38 @@ +class <%= user_class_name %> < ActiveRecord::Base +<%- if options[:authlogic] -%> + acts_as_authentic +<%- else -%> + # new columns need to be added here to be writable through mass assignment + attr_accessible :username, :email, :password, :password_confirmation + + attr_accessor :password + before_save :prepare_password + + validates_presence_of :username + validates_uniqueness_of :username, :email, :allow_blank => true + validates_format_of :username, :with => /^[-\w\._@]+$/i, :allow_blank => true, :message => "should only contain letters, numbers, or .-_@" + validates_format_of :email, :with => /^[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i + validates_presence_of :password, :on => :create + validates_confirmation_of :password + validates_length_of :password, :minimum => 4, :allow_blank => true + + # login can be either username or email address + def self.authenticate(login, pass) + <%= user_singular_name %> = find_by_username(login) || find_by_email(login) + return <%= user_singular_name %> if <%= user_singular_name %> && <%= user_singular_name %>.password_hash == <%= user_singular_name %>.encrypt_password(pass) + end + + def encrypt_password(pass) + BCrypt::Engine.hash_secret(pass, password_salt) + end + + private + + def prepare_password + unless password.blank? + self.password_salt = BCrypt::Engine.generate_salt + self.password_hash = encrypt_password(password) + end + end +<%- end -%> +end diff --git a/lib/generators/nifty/authentication/templates/users_controller.rb b/lib/generators/nifty/authentication/templates/users_controller.rb new file mode 100644 index 0000000..2faed00 --- /dev/null +++ b/lib/generators/nifty/authentication/templates/users_controller.rb @@ -0,0 +1,32 @@ +class <%= user_plural_class_name %>Controller < ApplicationController + before_filter :login_required, :except => [:new, :create] + + def new + @<%= user_singular_name %> = <%= user_class_name %>.new + end + + def create + @<%= user_singular_name %> = <%= user_class_name %>.new(params[:<%= user_singular_name %>]) + if @<%= user_singular_name %>.save + <%- unless options[:authlogic] -%> + session[:<%= user_singular_name %>_id] = @<%= user_singular_name %>.id + <%- end -%> + redirect_to root_url, :notice => "Thank you for signing up! You are now logged in." + else + render :new + end + end + + def edit + @<%= user_singular_name %> = current_<%= user_singular_name %> + end + + def update + @<%= user_singular_name %> = current_<%= user_singular_name %> + if @<%= user_singular_name %>.update_attributes(params[:<%= user_singular_name %>]) + redirect_to root_url, :notice => "Your profile has been updated." + else + render :edit + end + end +end diff --git a/lib/generators/nifty/authentication/templates/users_helper.rb b/lib/generators/nifty/authentication/templates/users_helper.rb new file mode 100644 index 0000000..7eb9040 --- /dev/null +++ b/lib/generators/nifty/authentication/templates/users_helper.rb @@ -0,0 +1,2 @@ +module <%= user_plural_class_name %>Helper +end diff --git a/lib/generators/nifty/authentication/templates/views/erb/_form.html.erb b/lib/generators/nifty/authentication/templates/views/erb/_form.html.erb new file mode 100644 index 0000000..4e1c47d --- /dev/null +++ b/lib/generators/nifty/authentication/templates/views/erb/_form.html.erb @@ -0,0 +1,20 @@ +<%%= form_for @<%= user_singular_name %> do |f| %> + <%%= f.error_messages %> + <div class="field"> + <%%= f.label :username %> + <%%= f.text_field :username %> + </div> + <div class="field"> + <%%= f.label :email, "Email Address" %> + <%%= f.text_field :email %> + </div> + <div class="field"> + <%%= f.label :password %> + <%%= f.password_field :password %> + </div> + <div class="field"> + <%%= f.label :password_confirmation, "Confirm Password" %> + <%%= f.password_field :password_confirmation %> + </div> + <div class="actions"><%%= f.submit (@<%= user_singular_name %>.new_record? ? "Sign up" : "Update") %></div> +<%% end %> diff --git a/lib/generators/nifty/authentication/templates/views/erb/edit.html.erb b/lib/generators/nifty/authentication/templates/views/erb/edit.html.erb new file mode 100644 index 0000000..75de67e --- /dev/null +++ b/lib/generators/nifty/authentication/templates/views/erb/edit.html.erb @@ -0,0 +1,3 @@ +<%% title "Update Profile" %> + +<%%= render "form" %> diff --git a/lib/generators/nifty/authentication/templates/views/erb/login.html.erb b/lib/generators/nifty/authentication/templates/views/erb/login.html.erb new file mode 100644 index 0000000..1cbc428 --- /dev/null +++ b/lib/generators/nifty/authentication/templates/views/erb/login.html.erb @@ -0,0 +1,30 @@ +<%% title "Log in" %> + +<p>Don't have an account? <%%= link_to "Sign up!", signup_path %></p> + +<%- if options[:authlogic] -%> +<%%= form_for @<%= session_singular_name %> do |f| %> + <%%= f.error_messages %> + <div class="field"> + <%%= f.label :username %> + <%%= f.text_field :username %> + </div> + <div class="field"> + <%%= f.label :password %> + <%%= f.password_field :password %> + </div> + <div class="actions"><%%= f.submit "Log in" %></div> +<%% end %> +<%- else -%> +<%%= form_tag <%= session_plural_name %>_path do %> + <div class="field"> + <%%= label_tag :login, "Username or Email Address" %> + <%%= text_field_tag :login, params[:login] %> + </div> + <div class="field"> + <%%= label_tag :password %> + <%%= password_field_tag :password %> + </div> + <div class="actions"><%%= submit_tag "Log in" %></div> +<%% end %> +<%- end -%> diff --git a/lib/generators/nifty/authentication/templates/views/erb/signup.html.erb b/lib/generators/nifty/authentication/templates/views/erb/signup.html.erb new file mode 100644 index 0000000..6f282b5 --- /dev/null +++ b/lib/generators/nifty/authentication/templates/views/erb/signup.html.erb @@ -0,0 +1,5 @@ +<%% title "Sign up" %> + +<p>Already have an account? <%%= link_to "Log in", login_path %>.</p> + +<%%= render "form" %> diff --git a/lib/generators/nifty/authentication/templates/views/haml/_form.html.haml b/lib/generators/nifty/authentication/templates/views/haml/_form.html.haml new file mode 100644 index 0000000..992ee9c --- /dev/null +++ b/lib/generators/nifty/authentication/templates/views/haml/_form.html.haml @@ -0,0 +1,16 @@ += form_for @<%= user_singular_name %> do |f| + = f.error_messages + .field + = f.label :username + = f.text_field :username + .field + = f.label :email, "Email Address" + = f.text_field :email + .field + = f.label :password + = f.password_field :password + .field + = f.label :password_confirmation, "Confirm Password" + = f.password_field :password_confirmation + .actions + = f.submit (@<%= user_singular_name %>.new_record? ? "Sign up" : "Update") diff --git a/lib/generators/nifty/authentication/templates/views/haml/edit.html.haml b/lib/generators/nifty/authentication/templates/views/haml/edit.html.haml new file mode 100644 index 0000000..4b43a3b --- /dev/null +++ b/lib/generators/nifty/authentication/templates/views/haml/edit.html.haml @@ -0,0 +1,3 @@ +- title "Sign up" + += render "form" diff --git a/lib/generators/nifty/authentication/templates/views/haml/login.html.haml b/lib/generators/nifty/authentication/templates/views/haml/login.html.haml new file mode 100644 index 0000000..22fc95b --- /dev/null +++ b/lib/generators/nifty/authentication/templates/views/haml/login.html.haml @@ -0,0 +1,26 @@ +- title "Log in" + +%p== Don't have an account? #{link_to "Sign up!", signup_path} + +<%- if options[:authlogic] -%> += form_for @<%= session_singular_name %> do |f| + = f.error_messages + .field + = f.label :username + = f.text_field :username + .field + = f.label :password + = f.password_field :password + .actions + = f.submit "Log in" +<%- else -%> +- form_tag <%= session_plural_name %>_path do + .field + = label_tag :login, "Username or Email Address" + = text_field_tag :login, params[:login] + .field + = label_tag :password + = password_field_tag :password + .actions + = submit_tag "Log in" +<%- end -%> diff --git a/lib/generators/nifty/authentication/templates/views/haml/signup.html.haml b/lib/generators/nifty/authentication/templates/views/haml/signup.html.haml new file mode 100644 index 0000000..dc75c13 --- /dev/null +++ b/lib/generators/nifty/authentication/templates/views/haml/signup.html.haml @@ -0,0 +1,5 @@ +- title "Sign up" + +%p== Already have an account? #{link_to "Log in", login_path}. + += render "form" diff --git a/lib/generators/nifty/config/USAGE b/lib/generators/nifty/config/USAGE new file mode 100644 index 0000000..f98972c --- /dev/null +++ b/lib/generators/nifty/config/USAGE @@ -0,0 +1,23 @@ +Description: + The nifty_config generator creates YAML file in your config + directory and an initializer to load this config. The config has a + separate section for each environment. This is a great place to put + any config settings you don't want in your app. + + The config is loaded into a constant called APP_CONFIG by default, + this changes depending on the name you choose to pass the generator. + Use this constant to access the config settings like this. + + APP_CONFIG[:some_setting] + + +Examples: + rails generate nifty:config + + Config: config/app_config.yml + Initializer: config/initializers/load_app_config.rb + + rails generate nifty:config passwords + + Config: config/passwords_config.yml + Initializer: config/initializers/load_passwords_config.rb diff --git a/lib/generators/nifty/config/config_generator.rb b/lib/generators/nifty/config/config_generator.rb new file mode 100644 index 0000000..811c80e --- /dev/null +++ b/lib/generators/nifty/config/config_generator.rb @@ -0,0 +1,24 @@ +require 'generators/nifty' + +module Nifty + module Generators + class ConfigGenerator < Base + argument :config_name, :type => :string, :default => 'app', :banner => 'config_name' + + def create_config + template "load_config.rb", "config/initializers/load_#{file_name}_config.rb" + copy_file "config.yml", "config/#{file_name}_config.yml" + end + + private + + def file_name + config_name.underscore + end + + def constant_name + config_name.underscore.upcase + end + end + end +end diff --git a/lib/generators/nifty/config/templates/config.yml b/lib/generators/nifty/config/templates/config.yml new file mode 100644 index 0000000..3a26ebc --- /dev/null +++ b/lib/generators/nifty/config/templates/config.yml @@ -0,0 +1,8 @@ +development: + domain: localhost:3000 + +test: + domain: test.host + +production: + domain: example.com diff --git a/lib/generators/nifty/config/templates/load_config.rb b/lib/generators/nifty/config/templates/load_config.rb new file mode 100644 index 0000000..06e6a52 --- /dev/null +++ b/lib/generators/nifty/config/templates/load_config.rb @@ -0,0 +1,2 @@ +raw_config = File.read("#{Rails.root}/config/<%= file_name %>_config.yml") +<%= constant_name %>_CONFIG = YAML.load(raw_config)[Rails.env].symbolize_keys diff --git a/lib/generators/nifty/layout/USAGE b/lib/generators/nifty/layout/USAGE new file mode 100644 index 0000000..f94af0d --- /dev/null +++ b/lib/generators/nifty/layout/USAGE @@ -0,0 +1,25 @@ +Description: + The nifty_layout generator creates a basic layout, stylesheet and + helper which will give some structure to a starting Rails app. + + The generator takes one argument which will be the name of the + layout and stylesheet files. If no argument is passed then it defaults + to "application". + + The helper module includes some methods which can be called in any + template or partial to set variables to be used in the layout, such as + page title and javascript/stylesheet includes. + +Examples: + rails generate nifty:layout + + Layout: app/views/layouts/application.html.erb + Stylesheet: public/stylesheets/application.css + Helper: app/helpers/layout_helper.rb + + + rails generate nifty:layout admin + + Layout: app/views/layouts/admin.html.erb + Stylesheet: public/stylesheets/admin.css + Helper: app/helpers/layout_helper.rb diff --git a/lib/generators/nifty/layout/layout_generator.rb b/lib/generators/nifty/layout/layout_generator.rb new file mode 100644 index 0000000..fb7f9f4 --- /dev/null +++ b/lib/generators/nifty/layout/layout_generator.rb @@ -0,0 +1,29 @@ +require 'generators/nifty' + +module Nifty + module Generators + class LayoutGenerator < Base + argument :layout_name, :type => :string, :default => 'application', :banner => 'layout_name' + + class_option :haml, :desc => 'Generate HAML for view, and SASS for stylesheet.', :type => :boolean, :default => true + + def create_layout + if options.haml? + template 'layout.html.haml', "app/views/layouts/#{file_name}.html.haml" + copy_file 'stylesheet.sass', "public/stylesheets/sass/#{file_name}.sass" + else + template 'layout.html.erb', "app/views/layouts/#{file_name}.html.erb" + copy_file 'stylesheet.css', "public/stylesheets/#{file_name}.css" + end + copy_file 'layout_helper.rb', 'app/helpers/layout_helper.rb' + copy_file 'error_messages_helper.rb', 'app/helpers/error_messages_helper.rb' + end + + private + + def file_name + layout_name.underscore + end + end + end +end diff --git a/lib/generators/nifty/layout/templates/error_messages_helper.rb b/lib/generators/nifty/layout/templates/error_messages_helper.rb new file mode 100644 index 0000000..8e9c4d3 --- /dev/null +++ b/lib/generators/nifty/layout/templates/error_messages_helper.rb @@ -0,0 +1,23 @@ +module ErrorMessagesHelper + # Render error messages for the given objects. The :message and :header_message options are allowed. + def error_messages_for(*objects) + options = objects.extract_options! + options[:header_message] ||= I18n.t(:"activerecord.errors.header", :default => "Invalid Fields") + options[:message] ||= I18n.t(:"activerecord.errors.message", :default => "Correct the following errors and try again.") + messages = objects.compact.map { |o| o.errors.full_messages }.flatten + unless messages.empty? + content_tag(:div, :class => "error_messages") do + list_items = messages.map { |msg| content_tag(:li, msg.html_safe) } + content_tag(:h2, options[:header_message].html_safe) + content_tag(:p, options[:message].html_safe) + content_tag(:ul, list_items.join.html_safe) + end + end + end + + module FormBuilderAdditions + def error_messages(options = {}) + @template.error_messages_for(@object, options) + end + end +end + +ActionView::Helpers::FormBuilder.send(:include, ErrorMessagesHelper::FormBuilderAdditions) diff --git a/lib/generators/nifty/layout/templates/layout.html.haml b/lib/generators/nifty/layout/templates/layout.html.haml new file mode 100644 index 0000000..24ec1d6 --- /dev/null +++ b/lib/generators/nifty/layout/templates/layout.html.haml @@ -0,0 +1,21 @@ +!!! +%html + + %head + %title + = content_for?(:title) ? yield(:title) : "Untitled" + %meta{"http-equiv"=>"Content-Type", :content=>"text/html; charset=utf-8"}/ + = stylesheet_link_tag "application" + = javascript_include_tag "application" + = csrf_meta_tag + = yield(:head) + + %body + #container + - flash.each do |name, msg| + = content_tag :div, msg, :id => "flash_#{name}" + + - if show_title? + %h1= yield(:title) + + = yield diff --git a/lib/generators/nifty/layout/templates/layout_helper.rb b/lib/generators/nifty/layout/templates/layout_helper.rb new file mode 100644 index 0000000..93011d6 --- /dev/null +++ b/lib/generators/nifty/layout/templates/layout_helper.rb @@ -0,0 +1,38 @@ +# These helper methods can be called in your template to set variables to be used in the layout +# This module should be included in all views globally, +# to do so you may need to add this line to your ApplicationController +# helper :layout +module LayoutHelper + + def title(page_title, show_title = true) + content_for(:title) { strip_tags(page_title.to_s) } + @show_title = show_title + end + + def show_title? + @show_title + end + + def stylesheet(*args) + content_for(:head) { stylesheet_link_tag(*args) } + end + + def javascript(*args) + content_for(:head) { javascript_include_tag(*args) } + end + + def translation_missing?(output) + (output =~ /span/ or output.empty?) + end + + def conditional_hint(translation_key) + output = t(translation_key) + return output unless translation_missing?(output) + false + end + + def conditional_t(translation_key) + output = t(translation_key) + strip_tags(output) + end +end
\ No newline at end of file diff --git a/lib/generators/nifty/layout/templates/stylesheet.css b/lib/generators/nifty/layout/templates/stylesheet.css new file mode 100644 index 0000000..448a53f --- /dev/null +++ b/lib/generators/nifty/layout/templates/stylesheet.css @@ -0,0 +1,83 @@ +html, body { + background-color: #4B7399; + font-family: Verdana, Helvetica, Arial; + font-size: 14px; +} + +a img { + border: none; +} + +a { + color: #0000FF; +} + +.clear { + clear: both; + height: 0; + overflow: hidden; +} + +#container { + width: 75%; + margin: 0 auto; + background-color: #FFF; + padding: 20px 40px; + border: solid 1px black; + margin-top: 20px; +} + +#flash_notice, #flash_error, #flash_alert { + padding: 5px 8px; + margin: 10px 0; +} + +#flash_notice { + background-color: #CFC; + border: solid 1px #6C6; +} + +#flash_error, #flash_alert { + background-color: #FCC; + border: solid 1px #C66; +} + +.error_messages { + width: 400px; + border: 2px solid #CF0000; + padding: 0px; + padding-bottom: 12px; + margin-bottom: 20px; + background-color: #f0f0f0; + font-size: 12px; +} + +.error_messages h2 { + text-align: left; + font-weight: bold; + padding: 5px 10px; + font-size: 12px; + margin: 0; + background-color: #c00; + color: #fff; +} + +.error_messages p { + margin: 8px 10px; +} + +.error_messages ul { + margin: 0; +} + +.field_with_errors { + display: inline; +} + +form .field, form .actions { + margin: 10px 0; +} + +form label { + display: block; +} diff --git a/lib/generators/nifty/layout/templates/stylesheet.sass b/lib/generators/nifty/layout/templates/stylesheet.sass new file mode 100644 index 0000000..383bfd3 --- /dev/null +++ b/lib/generators/nifty/layout/templates/stylesheet.sass @@ -0,0 +1,73 @@ +$primary_color: #4b7399 + +body + background-color: $primary_color + font: + family: Verdana, Helvetica, Arial + size: 14px + +a + color: blue + img + border: none + +.clear + clear: both + height: 0 + overflow: hidden + +#container + width: 75% + margin: 0 auto + background: white + padding: 20px 40px + border: solid 1px black + margin-top: 20px + +#flash_notice, +#flash_error, +#flash_alert + padding: 5px 8px + margin: 10px 0 + +#flash_notice + background-color: #ccffcc + border: solid 1px #66cc66 + +#flash_error, +#flash_alert + background-color: #ffcccc + border: solid 1px #cc6666 + +.error_messages + width: 400px + border: 2px solid #cf0000 + padding: 0 + padding-bottom: 12px + margin-bottom: 20px + background-color: #f0f0f0 + font: + size: 12px + h2 + text-align: left + padding: 5px 5px 5px 15px + margin: 0 + font: + weight: bold + size: 12px + background-color: #cc0000 + color: white + p + margin: 8px 10px + ul + margin: 0 + +.field_with_errors + display: inline + +form .field, +form .actions + margin: 10px 0 + +form label + display: block diff --git a/lib/generators/nifty/scaffold/USAGE b/lib/generators/nifty/scaffold/USAGE new file mode 100644 index 0000000..363fd26 --- /dev/null +++ b/lib/generators/nifty/scaffold/USAGE @@ -0,0 +1,51 @@ +Description: + Scaffolds an entire resource, from model and migration to controller and + views. The resource is ready to use as a starting point for your restful, + resource-oriented application. Tests or specs are also generated depending + on if you have a "spec" directory or not. + + IMPORTANT: This generator uses the "title" helper method which is generated + by the nifty_layout generator. You may want to run that generator first. + +Usage: + Pass the name of the model, either CamelCased or under_scored, as the first + argument along with an optional list of attribute pairs and controller actions. + + If no controller actions are specified, they will default to index, show, + new, create, edit, update, and destroy. + + IMPORTANT: If no attribute pairs are specified, no model will be generated. + It will try to determine the attributes from an existing model. + + Attribute pairs are column_name:sql_type arguments specifying the + model's attributes. Timestamps are added by default, so you don't have to + specify them by hand as 'created_at:datetime updated_at:datetime'. + + For example, `nifty:scaffold post name:string content:text hidden:boolean` + gives you a model with those three attributes, a controller that handles + the create/show/update/destroy, forms to create and edit your posts, and + an index that lists them all, as well as a map.resources :posts + declaration in config/routes.rb. + + Adding an "!" in the mix of arguments will invert the passed controller + actions. This will include all 7 controller actitons except the ones + mentioned. This option doesn't affect model attributes. + +Examples: + rails generate nifty:scaffold post + + Will create a controller called "posts" it will contain all seven + CRUD actions along with the views. A model will NOT be created, + instead it will look for an existing model and use those attributes. + + rails generate nifty:scaffold post name:string content:text index new edit + + Will create a Post model and migration file with the name and content + attributes. It will also create a controller with index, new, create, + edit, and update actions. Notice the create and update actions are + added automatically with new and edit. + + rails generate nifty:scaffold post ! show new + + Creates a posts controller (no model) with index, edit, update, and + destroy actions. diff --git a/lib/generators/nifty/scaffold/scaffold_generator.rb b/lib/generators/nifty/scaffold/scaffold_generator.rb new file mode 100644 index 0000000..1283e17 --- /dev/null +++ b/lib/generators/nifty/scaffold/scaffold_generator.rb @@ -0,0 +1,344 @@ +require 'generators/nifty' +require 'rails/generators/migration' +require 'rails/generators/generated_attribute' + +module Nifty + module Generators + class ScaffoldGenerator < Base + include Rails::Generators::Migration + no_tasks { attr_accessor :scaffold_name, :model_attributes, :controller_actions } + + argument :scaffold_name, :type => :string, :required => true, :banner => 'ModelName' + argument :args_for_c_m, :type => :array, :default => [], :banner => 'controller_actions and model:attributes' + + class_option :skip_model, :desc => 'Don\'t generate a model or migration file.', :type => :boolean + class_option :skip_migration, :desc => 'Don\'t generate migration file for model.', :type => :boolean + class_option :skip_timestamps, :desc => 'Don\'t add timestamps to migration file.', :type => :boolean + class_option :skip_controller, :desc => 'Don\'t generate controller, helper, or views.', :type => :boolean + class_option :invert, :desc => 'Generate all controller actions except these mentioned.', :type => :boolean + class_option :namespace_model, :desc => 'If the resource is namespaced, include the model in the namespace.', :type => :boolean + class_option :haml, :desc => 'Generate HAML views instead of ERB.', :type => :boolean, :default => true + + class_option :testunit, :desc => 'Use test/unit for test files.', :group => 'Test framework', :type => :boolean + class_option :rspec, :desc => 'Use RSpec for test files.', :group => 'Test framework', :type => :boolean + class_option :shoulda, :desc => 'Use shoulda for test files.', :group => 'Test framework', :type => :boolean + + def initialize(*args, &block) + super + + print_usage unless scaffold_name.underscore =~ /^[a-z][a-z0-9_\/]+$/ + + @controller_actions = [] + @model_attributes = [] + @skip_model = options.skip_model? + @namespace_model = options.namespace_model? + @invert_actions = options.invert? + + args_for_c_m.each do |arg| + if arg == '!' + @invert_actions = true + elsif arg.include?(':') + @model_attributes << Rails::Generators::GeneratedAttribute.new(*arg.split(':')) + else + @controller_actions << arg + @controller_actions << 'create' if arg == 'new' + @controller_actions << 'update' if arg == 'edit' + end + end + + @controller_actions.uniq! + @model_attributes.uniq! + + if @invert_actions || @controller_actions.empty? + @controller_actions = all_actions - @controller_actions + end + + if @model_attributes.empty? + @skip_model = true # skip model if no attributes + if model_exists? + model_columns_for_attributes.each do |column| + @model_attributes << Rails::Generators::GeneratedAttribute.new(column.name.to_s, column.type.to_s) + end + else + @model_attributes << Rails::Generators::GeneratedAttribute.new('name', 'string') + end + end + end + + def add_gems + # add_gem "mocha", :group => :test + add_gem 'haml' + add_gem 'simple_form' + add_gem 'cancan' + end + + def create_model + unless @skip_model + template 'model.rb', "app/models/#{model_path}.rb" + if test_framework == :rspec + template "tests/rspec/model.rb", "spec/models/#{model_path}_spec.rb" + # FUCK YOU FIXTURES, WE USE FACTORY GIRL. + # template 'fixtures.yml', "spec/fixtures/#{model_path.pluralize}.yml" + else + template "tests/#{test_framework}/model.rb", "test/unit/#{model_path}_test.rb" + template 'fixtures.yml', "test/fixtures/#{model_path.pluralize}.yml" + end + end + end + + def create_migration + unless @skip_model || options.skip_migration? + migration_template 'migration.rb', "db/migrate/create_#{model_path.pluralize.gsub('/', '_')}.rb" + end + end + + def create_controller + unless options.skip_controller? + template 'controller.rb', "app/controllers/#{plural_name}_controller.rb" + + template 'helper.rb', "app/helpers/#{plural_name}_helper.rb" + + controller_actions.each do |action| + if %w[index show new edit].include?(action) # Actions with templates + template "views/#{view_language}/#{action}.html.#{view_language}", "app/views/#{plural_name}/#{action}.html.#{view_language}" + end + end + + # Move the index_core (can't do it on the top.) + template "views/#{view_language}/_index_core.html.#{view_language}", "app/views/#{plural_name}/_index_core.html.#{view_language}" + + + if form_partial? + template "views/#{view_language}/_form.html.#{view_language}", "app/views/#{plural_name}/_form.html.#{view_language}" + template "views/#{view_language}/_form_core.html.#{view_language}", "app/views/#{plural_name}/_form_core.html.#{view_language}" + end + + namespaces = plural_name.split('/') + resource = namespaces.pop + route namespaces.reverse.inject("resources :#{resource}") { |acc, namespace| + "namespace(:#{namespace}){ #{acc} }" + } + + if test_framework == :rspec + template "tests/#{test_framework}/controller.rb", "spec/controllers/#{plural_name}_controller_spec.rb" + else + template "tests/#{test_framework}/controller.rb", "test/functional/#{plural_name}_controller_test.rb" + end + end + end + + def create_locales + template 'locale.yml', "config/locales/views/#{plural_name}/en.yml" + template 'locale_de.yml', "config/locales/views/#{plural_name}/de.yml" + # template 'locale.yml', "config/locales/views/#{plural_name}/es.yml" + # gsub_file("config/locales/views/#{plural_name}/es.yml", 'en:', 'es:') + # gsub_file("config/locales/views/#{plural_name}/de.yml", 'en:', 'de:') + end + + def configuration + gsub_file('config/application.rb', "# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]", + "config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '*', '*', '*.{rb,yml}').to_s]") + end + + private + + def form_partial? + actions? :new, :edit + end + + def all_actions + %w[index show new create edit update destroy] + end + + def action?(name) + controller_actions.include? name.to_s + end + + def actions?(*names) + names.all? { |name| action? name } + end + + def singular_name + scaffold_name.underscore + end + + def plural_name + scaffold_name.underscore.pluralize + end + + def human_name + scaffold_name.humanize + end + + def table_name + if scaffold_name.include?('::') && @namespace_model + plural_name.gsub('/', '_') + end + end + + def class_name + if @namespace_model + scaffold_name.camelize + else + scaffold_name.split('::').last.camelize + end + end + + def model_path + class_name.underscore + end + + def plural_class_name + plural_name.camelize + end + + def instance_name + if @namespace_model + singular_name.gsub('/','_') + else + singular_name.split('/').last + end + end + + def instances_name + instance_name.pluralize + end + + def controller_methods(dir_name) + controller_actions.map do |action| + read_template("#{dir_name}/#{action}.rb") + end.join("\n").strip + end + + def render_form + if form_partial? + if options.haml? + "= render \"form\"" + else + "<%= render \"form\" %>" + end + else + read_template("views/#{view_language}/_form.html.#{view_language}") + end + end + + def item_resource + scaffold_name.underscore.gsub('/','_') + end + + def items_path + if action? :index + "#{item_resource.pluralize}_path" + else + "root_path" + end + end + + def item_path(options = {}) + name = options[:instance_variable] ? "@#{instance_name}" : instance_name + suffix = options[:full_url] ? "url" : "path" + if options[:action].to_s == "new" + "new_#{item_resource}_#{suffix}" + elsif options[:action].to_s == "edit" + "edit_#{item_resource}_#{suffix}(#{name})" + else + if scaffold_name.include?('::') && !@namespace_model + namespace = singular_name.split('/')[0..-2] + "[:#{namespace.join(', :')}, #{name}]" + else + name + end + end + end + + def item_url + if action? :show + item_path(:full_url => true, :instance_variable => true) + else + items_url + end + end + + def items_url + if action? :index + item_resource.pluralize + '_url' + else + "root_url" + end + end + + def item_path_for_spec(suffix = 'path') + if action? :show + "#{item_resource}_#{suffix}(assigns[:#{instance_name}])" + else + if suffix == 'path' + items_path + else + items_url + end + end + end + + def item_path_for_test(suffix = 'path') + if action? :show + "#{item_resource}_#{suffix}(assigns(:#{instance_name}))" + else + if suffix == 'path' + items_path + else + items_url + end + end + end + + def model_columns_for_attributes + class_name.constantize.columns.reject do |column| + column.name.to_s =~ /^(id|created_at|updated_at)$/ + end + end + + def view_language + options.haml? ? 'haml' : 'erb' + end + + def test_framework + return @test_framework if defined?(@test_framework) + if options.testunit? + return @test_framework = :testunit + elsif options.rspec? + return @test_framework = :rspec + elsif options.shoulda? + return @test_framework = :shoulda + else + return @test_framework = default_test_framework + end + end + + def default_test_framework + File.exist?(destination_path("spec")) ? :rspec : :testunit + end + + def model_exists? + File.exist? destination_path("app/models/#{singular_name}.rb") + end + + def read_template(relative_path) + ERB.new(File.read(find_in_source_paths(relative_path)), nil, '-').result(binding) + end + + def destination_path(path) + File.join(destination_root, path) + end + + # FIXME: Should be proxied to ActiveRecord::Generators::Base + # Implement the required interface for Rails::Generators::Migration. + def self.next_migration_number(dirname) #:nodoc: + if ActiveRecord::Base.timestamped_migrations + Time.now.utc.strftime("%Y%m%d%H%M%S") + else + "%.3d" % (current_migration_number(dirname) + 1) + end + end + end + end +end diff --git a/lib/generators/nifty/scaffold/templates/actions/create.rb b/lib/generators/nifty/scaffold/templates/actions/create.rb new file mode 100644 index 0000000..8365f0b --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/actions/create.rb @@ -0,0 +1,8 @@ + def create + @<%= instance_name %> = <%= class_name %>.new(params[:<%= instance_name %>]) + if @<%= instance_name %>.save + redirect_to <%= item_url %>, :notice => t('<%= plural_name %>.controller.successfuly_created') + else + render :new + end + end diff --git a/lib/generators/nifty/scaffold/templates/actions/destroy.rb b/lib/generators/nifty/scaffold/templates/actions/destroy.rb new file mode 100644 index 0000000..8a236e0 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/actions/destroy.rb @@ -0,0 +1,5 @@ + def destroy + @<%= instance_name %> = <%= class_name %>.find(params[:id]) + @<%= instance_name %>.destroy + redirect_to <%= items_url %>, :notice => t('<%= plural_name %>.controller.successfuly_destroyed') + end diff --git a/lib/generators/nifty/scaffold/templates/actions/edit.rb b/lib/generators/nifty/scaffold/templates/actions/edit.rb new file mode 100644 index 0000000..907e928 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/actions/edit.rb @@ -0,0 +1,3 @@ + def edit + @<%= instance_name %> = <%= class_name %>.find(params[:id]) + end diff --git a/lib/generators/nifty/scaffold/templates/actions/index.rb b/lib/generators/nifty/scaffold/templates/actions/index.rb new file mode 100644 index 0000000..0a8420c --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/actions/index.rb @@ -0,0 +1,3 @@ + def index + @<%= instances_name %> = <%= class_name %>.all + end diff --git a/lib/generators/nifty/scaffold/templates/actions/new.rb b/lib/generators/nifty/scaffold/templates/actions/new.rb new file mode 100644 index 0000000..c0991bc --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/actions/new.rb @@ -0,0 +1,3 @@ + def new + @<%= instance_name %> = <%= class_name %>.new + end diff --git a/lib/generators/nifty/scaffold/templates/actions/show.rb b/lib/generators/nifty/scaffold/templates/actions/show.rb new file mode 100644 index 0000000..27a0467 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/actions/show.rb @@ -0,0 +1,3 @@ + def show + @<%= instance_name %> = <%= class_name %>.find(params[:id]) + end diff --git a/lib/generators/nifty/scaffold/templates/actions/update.rb b/lib/generators/nifty/scaffold/templates/actions/update.rb new file mode 100644 index 0000000..a1473a6 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/actions/update.rb @@ -0,0 +1,8 @@ + def update + @<%= instance_name %> = <%= class_name %>.find(params[:id]) + if @<%= instance_name %>.update_attributes(params[:<%= instance_name %>]) + redirect_to <%= item_url %>, :notice => t('<%= plural_name %>.controller.successfuly_updated') + else + render :edit + end + end diff --git a/lib/generators/nifty/scaffold/templates/controller.rb b/lib/generators/nifty/scaffold/templates/controller.rb new file mode 100644 index 0000000..a54de70 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/controller.rb @@ -0,0 +1,3 @@ +class <%= plural_class_name %>Controller < ApplicationController + <%= controller_methods :actions %> +end diff --git a/lib/generators/nifty/scaffold/templates/fixtures.yml b/lib/generators/nifty/scaffold/templates/fixtures.yml new file mode 100644 index 0000000..447eaf9 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/fixtures.yml @@ -0,0 +1,9 @@ +one: +<%- for attribute in model_attributes -%> + <%= attribute.name %>: <%= attribute.default %> +<%- end -%> + +two: +<%- for attribute in model_attributes -%> + <%= attribute.name %>: <%= attribute.default %> +<%- end -%> diff --git a/lib/generators/nifty/scaffold/templates/helper.rb b/lib/generators/nifty/scaffold/templates/helper.rb new file mode 100644 index 0000000..084b485 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/helper.rb @@ -0,0 +1,2 @@ +module <%= plural_class_name %>Helper +end diff --git a/lib/generators/nifty/scaffold/templates/locale.yml b/lib/generators/nifty/scaffold/templates/locale.yml new file mode 100644 index 0000000..46e4ce3 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/locale.yml @@ -0,0 +1,46 @@ +en: + <%= plural_name %>: + name: '<%= human_name %>' + controller: + successfuly_created: 'Successfully created <%= human_name %>.' + successfuly_updated: 'Successfully updated <%= human_name %>.' + successfuly_destroyed: 'Successfully destroyed <%= human_name %>.' + index: + page_title: 'Listing <%= human_name %>' + <%- for attribute in model_attributes -%> + <%= attribute.name %>: '<%= attribute.human_name %>' + <%- end -%> + actions: + confirm: 'Are you sure you want to delete this <%= human_name %>?' + destroy: 'Delete' + edit: 'Edit' + show: 'View' + create: 'New' + create_for: 'New <%= human_name %> for %{resource}' + show: + page_title: 'Show <%= human_name %>' + <%- for attribute in model_attributes -%> + <%= attribute.name %>: '<%= attribute.human_name %>' + <%- end -%> + actions: + confirm: 'Are you sure you want to delete this element?' + destroy: 'Delete' + edit: 'Edit' + view_all: 'View All' + new: + page_title: 'New <%= human_name %>' + actions: + back_to_list: 'Back to Index' + edit: + page_title: 'Editing <%= human_name %>' + actions: + back_to_list: 'Back to Index' + edit: 'Edit' + view_all: 'View All' + form: + <%- for attribute in model_attributes -%> + <%= attribute.name %>: + label: '<%= attribute.human_name %>' + hint: '' + <%- end -%> + button: 'Submit'
\ No newline at end of file diff --git a/lib/generators/nifty/scaffold/templates/locale_de.yml b/lib/generators/nifty/scaffold/templates/locale_de.yml new file mode 100644 index 0000000..027d36d --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/locale_de.yml @@ -0,0 +1,46 @@ +de: + <%= plural_name %>: + name: '<%= human_name %>' + controller: + successfuly_created: '<%= human_name %> wurde angelegt.' + successfuly_updated: '<%= human_name %> wurde aktualisiert.' + successfuly_destroyed: '<%= human_name %> wurde gelöscht.' + index: + page_title: 'Übersicht von <%= human_name %>' + <%- for attribute in model_attributes -%> + <%= attribute.name %>: '<%= attribute.human_name %>' + <%- end -%> + actions: + confirm: 'Sind Sie sicher, dass Sie folgendes löschen möchten: <%= human_name %>' + destroy: 'Löschen' + edit: 'Bearbeiten' + show: 'Anzeigen' + create: 'Neu anlegen' + create_for: '<%= human_name %> neu anlegen für %{resource}' + show: + page_title: '<%= human_name %> bearbeiten' + <%- for attribute in model_attributes -%> + <%= attribute.name %>: '<%= attribute.human_name %>' + <%- end -%> + actions: + confirm: 'Sind Sie sicher, dass die dieses Element löschen möchten?' + destroy: 'Löschen' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + new: + page_title: '<%= human_name %> neu anlegen' + actions: + back_to_list: 'Zurück zur Übersicht' + edit: + page_title: '<%= human_name %> bearbeiten' + actions: + back_to_list: 'Zurück zur Übersicht' + edit: 'Bearbeiten' + view_all: 'Alle anzeigen' + form: + <%- for attribute in model_attributes -%> + <%= attribute.name %>: + label: '<%= attribute.human_name %>' + hint: '' + <%- end -%> + button: 'Absenden'
\ No newline at end of file diff --git a/lib/generators/nifty/scaffold/templates/migration.rb b/lib/generators/nifty/scaffold/templates/migration.rb new file mode 100644 index 0000000..02fd6bd --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/migration.rb @@ -0,0 +1,16 @@ +class Create<%= class_name.pluralize.delete('::') %> < ActiveRecord::Migration + def self.up + create_table :<%= table_name || plural_name.split('/').last %> do |t| + <%- for attribute in model_attributes -%> + t.<%= attribute.type %> :<%= attribute.name %> + <%- end -%> + <%- unless options[:skip_timestamps] -%> + t.timestamps + <%- end -%> + end + end + + def self.down + drop_table :<%= table_name || plural_name.split('/').last %> + end +end diff --git a/lib/generators/nifty/scaffold/templates/model.rb b/lib/generators/nifty/scaffold/templates/model.rb new file mode 100644 index 0000000..fe5f980 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/model.rb @@ -0,0 +1,4 @@ +class <%= class_name %> < ActiveRecord::Base +<%= " set_table_name :#{table_name}\n" if table_name -%> + attr_accessible <%= model_attributes.map { |a| ":#{a.name}" }.join(", ") %> +end diff --git a/lib/generators/nifty/scaffold/templates/tests/rspec/actions/create.rb b/lib/generators/nifty/scaffold/templates/tests/rspec/actions/create.rb new file mode 100644 index 0000000..ef5a906 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/rspec/actions/create.rb @@ -0,0 +1,11 @@ + it "create action should render new template when model is invalid" do + <%= class_name %>.any_instance.stubs(:valid?).returns(false) + post :create + response.should render_template(:new) + end + + it "create action should redirect when model is valid" do + <%= class_name %>.any_instance.stubs(:valid?).returns(true) + post :create + response.should redirect_to(<%= item_path_for_spec('url') %>) + end diff --git a/lib/generators/nifty/scaffold/templates/tests/rspec/actions/destroy.rb b/lib/generators/nifty/scaffold/templates/tests/rspec/actions/destroy.rb new file mode 100644 index 0000000..8372953 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/rspec/actions/destroy.rb @@ -0,0 +1,6 @@ + it "destroy action should destroy model and redirect to index action" do + <%= instance_name %> = <%= class_name %>.first + delete :destroy, :id => <%= instance_name %> + response.should redirect_to(<%= items_url %>) + <%= class_name %>.exists?(<%= instance_name %>.id).should be_false + end diff --git a/lib/generators/nifty/scaffold/templates/tests/rspec/actions/edit.rb b/lib/generators/nifty/scaffold/templates/tests/rspec/actions/edit.rb new file mode 100644 index 0000000..e144904 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/rspec/actions/edit.rb @@ -0,0 +1,4 @@ + it "edit action should render edit template" do + get :edit, :id => <%= class_name %>.first + response.should render_template(:edit) + end diff --git a/lib/generators/nifty/scaffold/templates/tests/rspec/actions/index.rb b/lib/generators/nifty/scaffold/templates/tests/rspec/actions/index.rb new file mode 100644 index 0000000..a40ea16 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/rspec/actions/index.rb @@ -0,0 +1,4 @@ + it "index action should render index template" do + get :index + response.should render_template(:index) + end diff --git a/lib/generators/nifty/scaffold/templates/tests/rspec/actions/new.rb b/lib/generators/nifty/scaffold/templates/tests/rspec/actions/new.rb new file mode 100644 index 0000000..22e1b40 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/rspec/actions/new.rb @@ -0,0 +1,4 @@ + it "new action should render new template" do + get :new + response.should render_template(:new) + end diff --git a/lib/generators/nifty/scaffold/templates/tests/rspec/actions/show.rb b/lib/generators/nifty/scaffold/templates/tests/rspec/actions/show.rb new file mode 100644 index 0000000..eaa3d93 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/rspec/actions/show.rb @@ -0,0 +1,4 @@ + it "show action should render show template" do + get :show, :id => <%= class_name %>.first + response.should render_template(:show) + end diff --git a/lib/generators/nifty/scaffold/templates/tests/rspec/actions/update.rb b/lib/generators/nifty/scaffold/templates/tests/rspec/actions/update.rb new file mode 100644 index 0000000..c919962 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/rspec/actions/update.rb @@ -0,0 +1,11 @@ + it "update action should render edit template when model is invalid" do + <%= class_name %>.any_instance.stubs(:valid?).returns(false) + put :update, :id => <%= class_name %>.first + response.should render_template(:edit) + end + + it "update action should redirect when model is valid" do + <%= class_name %>.any_instance.stubs(:valid?).returns(true) + put :update, :id => <%= class_name %>.first + response.should redirect_to(<%= item_path_for_spec('url') %>) + end diff --git a/lib/generators/nifty/scaffold/templates/tests/rspec/controller.rb b/lib/generators/nifty/scaffold/templates/tests/rspec/controller.rb new file mode 100644 index 0000000..5d97f63 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/rspec/controller.rb @@ -0,0 +1,8 @@ +require File.dirname(__FILE__) + '/../spec_helper' + +describe <%= plural_class_name %>Controller do + fixtures :all + render_views + + <%= controller_methods 'tests/rspec/actions' %> +end diff --git a/lib/generators/nifty/scaffold/templates/tests/rspec/model.rb b/lib/generators/nifty/scaffold/templates/tests/rspec/model.rb new file mode 100644 index 0000000..25c3cc4 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/rspec/model.rb @@ -0,0 +1,7 @@ +require File.dirname(__FILE__) + '/../spec_helper' + +describe <%= class_name %> do + it "should be valid" do + <%= class_name %>.new.should be_valid + end +end diff --git a/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/create.rb b/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/create.rb new file mode 100644 index 0000000..f305367 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/create.rb @@ -0,0 +1,13 @@ + context "create action" do + should "render new template when model is invalid" do + <%= class_name %>.any_instance.stubs(:valid?).returns(false) + post :create + assert_template 'new' + end + + should "redirect when model is valid" do + <%= class_name %>.any_instance.stubs(:valid?).returns(true) + post :create + assert_redirected_to <%= item_path_for_test('url') %> + end + end diff --git a/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/destroy.rb b/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/destroy.rb new file mode 100644 index 0000000..ea20133 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/destroy.rb @@ -0,0 +1,8 @@ + context "destroy action" do + should "destroy model and redirect to index action" do + <%= instance_name %> = <%= class_name %>.first + delete :destroy, :id => <%= instance_name %> + assert_redirected_to <%= items_url %> + assert !<%= class_name %>.exists?(<%= instance_name %>.id) + end + end diff --git a/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/edit.rb b/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/edit.rb new file mode 100644 index 0000000..47597d0 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/edit.rb @@ -0,0 +1,6 @@ + context "edit action" do + should "render edit template" do + get :edit, :id => <%= class_name %>.first + assert_template 'edit' + end + end diff --git a/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/index.rb b/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/index.rb new file mode 100644 index 0000000..44b056b --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/index.rb @@ -0,0 +1,6 @@ + context "index action" do + should "render index template" do + get :index + assert_template 'index' + end + end diff --git a/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/new.rb b/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/new.rb new file mode 100644 index 0000000..5857a55 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/new.rb @@ -0,0 +1,6 @@ + context "new action" do + should "render new template" do + get :new + assert_template 'new' + end + end diff --git a/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/show.rb b/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/show.rb new file mode 100644 index 0000000..75b37f6 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/show.rb @@ -0,0 +1,6 @@ + context "show action" do + should "render show template" do + get :show, :id => <%= class_name %>.first + assert_template 'show' + end + end diff --git a/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/update.rb b/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/update.rb new file mode 100644 index 0000000..fc46f72 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/shoulda/actions/update.rb @@ -0,0 +1,13 @@ + context "update action" do + should "render edit template when model is invalid" do + <%= class_name %>.any_instance.stubs(:valid?).returns(false) + put :update, :id => <%= class_name %>.first + assert_template 'edit' + end + + should "redirect when model is valid" do + <%= class_name %>.any_instance.stubs(:valid?).returns(true) + put :update, :id => <%= class_name %>.first + assert_redirected_to <%= item_path_for_test('url') %> + end + end diff --git a/lib/generators/nifty/scaffold/templates/tests/shoulda/controller.rb b/lib/generators/nifty/scaffold/templates/tests/shoulda/controller.rb new file mode 100644 index 0000000..e9f9764 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/shoulda/controller.rb @@ -0,0 +1,5 @@ +require 'test_helper' + +class <%= plural_class_name %>ControllerTest < ActionController::TestCase + <%= controller_methods 'tests/shoulda/actions' %> +end diff --git a/lib/generators/nifty/scaffold/templates/tests/shoulda/model.rb b/lib/generators/nifty/scaffold/templates/tests/shoulda/model.rb new file mode 100644 index 0000000..9065963 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/shoulda/model.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class <%= class_name %>Test < ActiveSupport::TestCase + should "be valid" do + assert <%= class_name %>.new.valid? + end +end diff --git a/lib/generators/nifty/scaffold/templates/tests/testunit/actions/create.rb b/lib/generators/nifty/scaffold/templates/tests/testunit/actions/create.rb new file mode 100644 index 0000000..5a6d2ac --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/testunit/actions/create.rb @@ -0,0 +1,11 @@ + def test_create_invalid + <%= class_name %>.any_instance.stubs(:valid?).returns(false) + post :create + assert_template 'new' + end + + def test_create_valid + <%= class_name %>.any_instance.stubs(:valid?).returns(true) + post :create + assert_redirected_to <%= item_path_for_test('url') %> + end diff --git a/lib/generators/nifty/scaffold/templates/tests/testunit/actions/destroy.rb b/lib/generators/nifty/scaffold/templates/tests/testunit/actions/destroy.rb new file mode 100644 index 0000000..adedf91 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/testunit/actions/destroy.rb @@ -0,0 +1,6 @@ + def test_destroy + <%= instance_name %> = <%= class_name %>.first + delete :destroy, :id => <%= instance_name %> + assert_redirected_to <%= items_url %> + assert !<%= class_name %>.exists?(<%= instance_name %>.id) + end diff --git a/lib/generators/nifty/scaffold/templates/tests/testunit/actions/edit.rb b/lib/generators/nifty/scaffold/templates/tests/testunit/actions/edit.rb new file mode 100644 index 0000000..55ed24a --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/testunit/actions/edit.rb @@ -0,0 +1,4 @@ + def test_edit + get :edit, :id => <%= class_name %>.first + assert_template 'edit' + end diff --git a/lib/generators/nifty/scaffold/templates/tests/testunit/actions/index.rb b/lib/generators/nifty/scaffold/templates/tests/testunit/actions/index.rb new file mode 100644 index 0000000..22d3bad --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/testunit/actions/index.rb @@ -0,0 +1,4 @@ + def test_index + get :index + assert_template 'index' + end diff --git a/lib/generators/nifty/scaffold/templates/tests/testunit/actions/new.rb b/lib/generators/nifty/scaffold/templates/tests/testunit/actions/new.rb new file mode 100644 index 0000000..c551327 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/testunit/actions/new.rb @@ -0,0 +1,4 @@ + def test_new + get :new + assert_template 'new' + end diff --git a/lib/generators/nifty/scaffold/templates/tests/testunit/actions/show.rb b/lib/generators/nifty/scaffold/templates/tests/testunit/actions/show.rb new file mode 100644 index 0000000..b74f371 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/testunit/actions/show.rb @@ -0,0 +1,4 @@ + def test_show + get :show, :id => <%= class_name %>.first + assert_template 'show' + end diff --git a/lib/generators/nifty/scaffold/templates/tests/testunit/actions/update.rb b/lib/generators/nifty/scaffold/templates/tests/testunit/actions/update.rb new file mode 100644 index 0000000..b588bfc --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/testunit/actions/update.rb @@ -0,0 +1,11 @@ + def test_update_invalid + <%= class_name %>.any_instance.stubs(:valid?).returns(false) + put :update, :id => <%= class_name %>.first + assert_template 'edit' + end + + def test_update_valid + <%= class_name %>.any_instance.stubs(:valid?).returns(true) + put :update, :id => <%= class_name %>.first + assert_redirected_to <%= item_path_for_test('url') %> + end diff --git a/lib/generators/nifty/scaffold/templates/tests/testunit/controller.rb b/lib/generators/nifty/scaffold/templates/tests/testunit/controller.rb new file mode 100644 index 0000000..8b99836 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/testunit/controller.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class <%= plural_class_name %>ControllerTest < ActionController::TestCase + setup do + <%= item_path :instance_variable => true %> = <%= plural_name %>(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:<%= plural_name %>) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create <%= item_path %>" do + assert_difference('<%= class_name %>.count') do + post :create, <%= item_path %>: @<%= item_path %>.attributes + end + + assert_redirected_to <%= item_path %>_path(assigns(:<%= item_path %>)) + end + + test "should show <%= item_path %>" do + get :show, id: @<%= item_path %>.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @<%= item_path %>.to_param + assert_response :success + end + + test "should update <%= item_path %>" do + put :update, id: @<%= item_path %>.to_param, <%= item_path %>: @<%= item_path %>.attributes + assert_redirected_to <%= item_path %>_path(assigns(:<%= item_path %>)) + end + + test "should destroy <%= item_path %>" do + assert_difference('<%= class_name %>.count', -1) do + delete :destroy, id: @<%= item_path %>.to_param + end + + assert_redirected_to <%= plural_name %>_path + end +end diff --git a/lib/generators/nifty/scaffold/templates/tests/testunit/model.rb b/lib/generators/nifty/scaffold/templates/tests/testunit/model.rb new file mode 100644 index 0000000..09b835c --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/tests/testunit/model.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class <%= class_name %>Test < ActiveSupport::TestCase + def test_should_be_valid + assert <%= class_name %>.new.valid? + end +end diff --git a/lib/generators/nifty/scaffold/templates/views/haml/_form.html.haml b/lib/generators/nifty/scaffold/templates/views/haml/_form.html.haml new file mode 100644 index 0000000..57cb828 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/views/haml/_form.html.haml @@ -0,0 +1,7 @@ += simple_form_for(<%= item_path :instance_variable => true %>) do |f| + = f.error_notification + + = render "form_core", :f => f + + .actions + = f.button :submit, conditional_t('<%= plural_name %>.form.submit')
\ No newline at end of file diff --git a/lib/generators/nifty/scaffold/templates/views/haml/_form_core.html.haml b/lib/generators/nifty/scaffold/templates/views/haml/_form_core.html.haml new file mode 100644 index 0000000..ca7f253 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/views/haml/_form_core.html.haml @@ -0,0 +1,4 @@ +.inputs +<%- model_attributes.each do |attribute| -%> + = f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %>, :label => t('<%= plural_name %>.form.<%= attribute.name %>.label'), :hint => conditional_hint('<%= plural_name %>.form.<%= attribute.name %>.hint') +<%- end -%> diff --git a/lib/generators/nifty/scaffold/templates/views/haml/_index_core.html.haml b/lib/generators/nifty/scaffold/templates/views/haml/_index_core.html.haml new file mode 100644 index 0000000..9cbea63 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/views/haml/_index_core.html.haml @@ -0,0 +1,13 @@ +%table + %tr + <%- for attribute in model_attributes -%> + %th= t('<%= plural_name %>.index.<%= attribute.name %>') + <%- end -%> + + - reset_cycle + - for <%= instance_name %> in <%= instances_name %> + %tr{:class => cycle('odd', 'even')} + <%- for attribute in model_attributes -%> + %td= <%= instance_name %>.<%= attribute.name %> + <%- end -%> + =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:child => <%= instance_name %>}
\ No newline at end of file diff --git a/lib/generators/nifty/scaffold/templates/views/haml/edit.html.haml b/lib/generators/nifty/scaffold/templates/views/haml/edit.html.haml new file mode 100644 index 0000000..dc7de62 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/views/haml/edit.html.haml @@ -0,0 +1,3 @@ +- title t("<%= plural_name %>.edit.page_title") + +<%= render_form %>
\ No newline at end of file diff --git a/lib/generators/nifty/scaffold/templates/views/haml/index.html.haml b/lib/generators/nifty/scaffold/templates/views/haml/index.html.haml new file mode 100644 index 0000000..86c6b9e --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/views/haml/index.html.haml @@ -0,0 +1,6 @@ +- title t("<%= plural_name %>.index.page_title") + +- if @<%= instances_name %> && @<%= instances_name %>.count > 0 + = render "index_core", :<%= instances_name %> => @<%= instances_name %> + += render :partial => 'shared/create_link', :locals => {:child_class => <%= instances_name.classify %>}
\ No newline at end of file diff --git a/lib/generators/nifty/scaffold/templates/views/haml/new.html.haml b/lib/generators/nifty/scaffold/templates/views/haml/new.html.haml new file mode 100644 index 0000000..4e7f871 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/views/haml/new.html.haml @@ -0,0 +1,3 @@ +- title t("<%= plural_name %>.new.page_title") + +<%= render_form %>
\ No newline at end of file diff --git a/lib/generators/nifty/scaffold/templates/views/haml/show.html.haml b/lib/generators/nifty/scaffold/templates/views/haml/show.html.haml new file mode 100644 index 0000000..3d01340 --- /dev/null +++ b/lib/generators/nifty/scaffold/templates/views/haml/show.html.haml @@ -0,0 +1,9 @@ +- title t("<%= plural_name %>.show.page_title") + +<%- for attribute in model_attributes -%> +%p + %strong= t('<%= plural_name %>.show.<%= attribute.name %>') + ":" + = @<%= instance_name %>.<%= attribute.name %> +<%- end -%> + += render :partial => 'shared/show_edit_destroy_part', :locals => { :child => @<%= instance_name %> }
\ No newline at end of file diff --git a/lib/phone_controllers/snom_phone.rb b/lib/phone_controllers/snom_phone.rb new file mode 100644 index 0000000..9c41e02 --- /dev/null +++ b/lib/phone_controllers/snom_phone.rb @@ -0,0 +1,23 @@ +class SnomPhone + + attr_accessor :phone, :destination_number + + def initialize( attributes = {} ) + @phone = attributes[:phone] + @destination_number = attributes[:destination_number] + end + + def initiate_call + # TODO Initiate a new call to the destination_number. + # Do what ever it takes. + 42 + end + + # persisted is important not to get "undefined method + # `to_key' for" error + # -- Huh? #TODO Add a better description. + def persisted? + false + end + +end diff --git a/lib/tasks/.gitkeep b/lib/tasks/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/tasks/.gitkeep diff --git a/lib/tasks/cvs_user_import.rake b/lib/tasks/cvs_user_import.rake new file mode 100644 index 0000000..2475a43 --- /dev/null +++ b/lib/tasks/cvs_user_import.rake @@ -0,0 +1,331 @@ +namespace :user_import do + desc "Import users from a CSV file." + task :csv => :environment do + require 'csv' + require 'digest/md5' + + # Example CSV format in the file: + # UserName,LastName,FirstName,AcademicTitel,PhoneOffice,VoipNr1,VoipNr2,VoipNr3,VoipTelco,CellPhone,HardFax,SoftFax,Email,PIN + # 123456,Mustermann,Max,Dr.,+49 123 1001234,20,21,22,23,+49 160 12345678,+49 123 1001238,29,max.mustermann@example.com,1324 + + # A generic hook to manipulate each given phone number with Ruby tools. + # + def regex_convert_phone_numbers(phone_number = nil) + if phone_number.class == String && !phone_number.blank? + # 123 Example + # + # if !phone_number.match(/^123(..)$/).nil? + # phone_number = phone_number.gsub(/^123(..)$/,'123'+$1) + # end + end + + phone_number + end + + # Read the CSV data and store them in the new_users hash. + # + csv_data = CSV.read(IMPORT_CSV_FILE, encoding: IMPORT_CSV_ENCODING) + headers = csv_data.shift.map {|i| i.to_s } + string_data = csv_data.map {|row| row.map {|cell| cell.to_s } } + new_users = string_data.map {|row| Hash[*headers.zip(row).flatten] } + gs_node_id = GsNode.where(:ip_address => HOMEBASE_IP_ADDRESS).first.try(:id) + + if File.exists?(DOUBLE_CHECK_POSITIVE_USERS_CSV) + csv_data = CSV.read(DOUBLE_CHECK_POSITIVE_USERS_CSV, encoding: IMPORT_CSV_ENCODING) + if csv_data.blank? + double_checked_user_names = [] + else + headers = csv_data.shift.map {|i| i.to_s } + string_data = csv_data.map {|row| row.map {|cell| cell.to_s } } + double_check_positiv_users = string_data.map {|row| Hash[*headers.zip(row).flatten] } + + double_checked_user_names = double_check_positiv_users.map{|user| user['UserName']} + end + else + double_checked_user_names = new_users.map{|user| user['UserName']} + end + + tenant = Tenant.find(DEFAULT_API_TENANT_ID) + + # Destroy deleted user by making a diff of where(:importer_checksum) + # and users in the CSV file. + # + if defined?(USER_NAME_PREFIX) && !USER_NAME_PREFIX.blank? + new_users_user_names = new_users.map{|x| USER_NAME_PREFIX.to_s + x['UserName'].to_s} + else + new_users_user_names = new_users.map{|x| x['UserName']} + end + csv_imported_user_names_in_the_database = User.where('importer_checksum != ?', '').pluck(:user_name) + + + to_be_destroyed_user_names = csv_imported_user_names_in_the_database - new_users_user_names + + User.where(:user_name => to_be_destroyed_user_names, :gs_node_id => gs_node_id).destroy_all + + # Loop through all entries in the CSV file. + # + new_users.each do |csv_user| + if !(csv_user['UserName'].blank? || csv_user['Email'].blank?) && double_checked_user_names.include?(csv_user['UserName']) + csv_user['Email'] = csv_user['Email'].downcase + if defined?(USER_NAME_PREFIX) && !USER_NAME_PREFIX.blank? + csv_user['UserName'] = USER_NAME_PREFIX.to_s + csv_user['UserName'] + end + + md5_sum = Digest::MD5.hexdigest(csv_user.to_yaml) + user = nil + + # Check if this user already exists and has a changed checksum. + # + if tenant.users.where(:user_name => csv_user['UserName']).first.try(:importer_checksum).to_s != md5_sum.to_s + # Find or create the user + # + if tenant.users.where(:user_name => csv_user['UserName']).count > 0 + user = tenant.users.where(:user_name => csv_user['UserName']).first + if defined? IMPORT_IGNORE_PIN_ON_UPDATE && IMPORT_IGNORE_PIN_ON_UPDATE == true + user.update_attributes( + :last_name => csv_user['LastName'], + :first_name => csv_user['FirstName'], + :email => csv_user['Email'], + :importer_checksum => md5_sum, + :gs_node_id => gs_node_id, + ) + else + user.update_attributes( + :last_name => csv_user['LastName'], + :first_name => csv_user['FirstName'], + :email => csv_user['Email'], + :new_pin => csv_user['PIN'], + :new_pin_confirmation => csv_user['PIN'], + :password => csv_user['PIN'], + :password_confirmation => csv_user['PIN'], + :importer_checksum => md5_sum, + :gs_node_id => gs_node_id, + ) + end + else + if csv_user['PIN'].blank? + csv_user['PIN'] = (1..6).map{|i| (0 .. 9).to_a.sample}.join + end + user = tenant.users.create( + :user_name => csv_user['UserName'], + :last_name => csv_user['LastName'], + :first_name => csv_user['FirstName'], + :email => csv_user['Email'], + :new_pin => csv_user['PIN'], + :new_pin_confirmation => csv_user['PIN'], + :password => csv_user['PIN'], + :password_confirmation => csv_user['PIN'], + :language_id => tenant.language_id, + :importer_checksum => md5_sum, + :gs_node_id => gs_node_id, + :send_voicemail_as_email_attachment => true, + ) + end + + # Create group membership + if tenant.user_groups.exists?(:name => 'Users') + tenant.user_groups.where(:name => 'Users').first.user_group_memberships.create(:user => user) + end + end + + if user + # Find or create a sip_accounts + ['VoipNr1', 'VoipNr2', 'VoipNr3'].each_with_index do |phone_number_type, index| + + if index > 0 + auth_name = "#{csv_user['UserName']}_#{index}" + else + auth_name = csv_user['UserName'].to_s + end + + if !phone_number_type.blank? && !csv_user[phone_number_type].blank? + sip_account = user.sip_accounts.where(:auth_name => auth_name).first + if sip_account + if sip_account.caller_name.to_s != user.to_s + sip_account.update_attributes(:caller_name => user.to_s) + end + else + sip_account = user.sip_accounts.create( + :caller_name => user.to_s, + :voicemail_pin => csv_user['PIN'], + :auth_name => auth_name, + :password => csv_user['PIN'], + :clip => true, + :hotdeskable => true, + :callforward_rules_act_per_sip_account => true, + :gs_node_id => gs_node_id, + ) + end + + phone_numbers = Array.new() + phone_numbers.push(csv_user[phone_number_type].to_s.gsub(/[^0-9\+]/, '')) + + # Find or create phone numbers + converted_phone_number = regex_convert_phone_numbers(csv_user[phone_number_type]) + if converted_phone_number != csv_user[phone_number_type] + phone_numbers.push(converted_phone_number.gsub(/[^0-9\+]/, '')) + end + phone_numbers_count = sip_account.phone_numbers.count + phone_numbers.each do |phone_number_number| + phone_number = sip_account.phone_numbers.where(:number => phone_number_number).first + if !phone_number + phone_number = sip_account.phone_numbers.create(:number => phone_number_number, :gs_node_id => gs_node_id) + end + end + + # Create default call forwarding entries + if phone_numbers_count < sip_account.phone_numbers.count + call_forward_case_offline = CallForwardCase.find_by_value('offline') + if call_forward_case_offline + sip_account.phone_numbers.first.call_forwards.create(:call_forward_case_id => call_forward_case_offline.id, :call_forwardable_type => 'Voicemail', :active => true, :depth => DEFAULT_CALL_FORWARD_DEPTH) + end + end + else + user.sip_accounts.where(:auth_name => auth_name).destroy_all + end + end + + if !csv_user['SoftFax'].blank? + phone_numbers = Array.new() + phone_numbers.push(csv_user['SoftFax'].to_s.gsub(/[^0-9\+]/, '')) + converted_phone_number = regex_convert_phone_numbers(csv_user['SoftFax']) + if converted_phone_number != csv_user['SoftFax'] + phone_numbers.push(converted_phone_number.gsub(/[^0-9\+]/, '')) + end + + fax_account = user.fax_accounts.first + if fax_account + if fax_account.name != user.to_s || fax_account.email != user.email + fax_account.update_attributes(:name => user.to_s, :email => user.email) + end + else + fax_account = user.fax_accounts.create( + :name => user.to_s, + :station_id => converted_phone_number.gsub(/[^0-9\+]/,''), + :email => user.email, + :days_till_auto_delete => 90, + :retries => 3, + ) + end + + if fax_account + fax_account.phone_numbers.each do |phone_number| + if !phone_numbers.include?(phone_number.number) + phone_number.delete + end + end + phone_numbers.each do |phone_number_number| + phone_number = fax_account.phone_numbers.where(:number => phone_number_number).first + if !phone_number + phone_number = fax_account.phone_numbers.create(:number => phone_number_number) + end + end + end + else + user.fax_accounts.destroy_all + end + + if !csv_user['HardFax'].blank? + phone_numbers = Array.new() + phone_numbers.push(csv_user['HardFax'].to_s.gsub(/[^0-9\+]/, '')) + converted_phone_number = regex_convert_phone_numbers(csv_user['HardFax']) + if converted_phone_number != csv_user['HardFax'] + phone_numbers.push(converted_phone_number.gsub(/[^0-9\+]/, '')) + end + + # Create a sip_account for a hardware fax. + # + fax_sip_account = user.sip_accounts.where(:description => 'Hardware-Fax').first + if fax_sip_account + if fax_sip_account.caller_name != "Hardware Fax of #{user.to_s}" + fax_sip_account.update_attributes(:caller_name => "Hardware Fax of #{user.to_s}") + end + else + fax_sip_account = user.sip_accounts.create( + :caller_name => "Hardware Fax of #{user.to_s}", + :description => 'Hardware-Fax', + :auth_name => 'HARDFAX' + csv_user['UserName'], + :password => csv_user['PIN'], + :clip => true, + :hotdeskable => false, + :callforward_rules_act_per_sip_account => true, + :gs_node_id => gs_node_id, + ) + end + + if fax_sip_account + fax_sip_account.phone_numbers.each do |phone_number| + if !phone_numbers.include?(phone_number.number) + phone_number.delete + end + end + phone_numbers.each do |phone_number_number| + phone_number = fax_sip_account.phone_numbers.where(:number => phone_number_number).first + if !phone_number + phone_number = fax_sip_account.phone_numbers.create(:number => phone_number_number) + end + end + end + else + user.sip_accounts.where(:description => 'Hardware-Fax').destroy_all + end + + if !csv_user['VoipTelco'].blank? + conference = user.conferences.first + if conference.nil? + # Create a conference room for this user. + # + conference = user.conferences.build + conference.name = "Default Conference for #{user.to_s}" + conference.start = nil + conference.end = nil + conference.open_for_anybody = true + conference.max_members = DEFAULT_MAX_CONFERENCE_MEMBERS + conference.pin = (1..MINIMUM_PIN_LENGTH).map{|i| (0 .. 9).to_a.sample}.join + conference.save + end + + phone_numbers = Array.new() + phone_numbers.push(csv_user['VoipTelco'].to_s.gsub(/[^0-9\+]/, '')) + converted_phone_number = regex_convert_phone_numbers(csv_user['VoipTelco']) + if converted_phone_number != csv_user['VoipTelco'] + phone_numbers.push(converted_phone_number.gsub(/[^0-9\+]/, '')) + end + + if conference + conference.phone_numbers.each do |phone_number| + if !phone_numbers.include?(phone_number.number) + phone_number.delete + end + end + phone_numbers.each do |phone_number_number| + phone_number = conference.phone_numbers.where(:number => phone_number_number).first + if !phone_number + phone_number = conference.phone_numbers.create(:number => phone_number_number) + end + end + end + else + user.conferences.destroy_all + end + + # Create Whitelist Entry for default Callthrough + # + cell_phone_number = csv_user['CellPhone'].to_s.gsub(/[^0-9\+]/, '') + + if !cell_phone_number.blank? + callthrough = tenant.callthroughs.find_or_create_by_name(CALLTHROUGH_NAME_TO_BE_USED_FOR_DEFAULT_ACTIVATION) + + access_authorization = callthrough.access_authorizations.find_or_create_by_name('Cellphones') + + new_phone_number = access_authorization.phone_numbers.find_or_create_by_number(cell_phone_number, :name => user.to_s, :access_authorization_user_id => user.id) + end + end + else + # puts "#{csv_user['UserName']} (#{csv_user['Email']}) has not changed." + end + end + + + end +end
\ No newline at end of file diff --git a/lib/tasks/fax.rake b/lib/tasks/fax.rake new file mode 100644 index 0000000..1f1c282 --- /dev/null +++ b/lib/tasks/fax.rake @@ -0,0 +1,74 @@ +# encoding: UTF-8 + +desc "Import inbound fax" + +task :import_inbound_fax, [ + :fax_account_id, + :result_code, + :document_total_pages, + :document_transferred_pages, + :ecm_requested, + :ecm_used, + :image_resolution, + :remote_station_id, + :transfer_rate, + :transmission_time, + :document, + :caller_id_number, + :caller_id_name, + ] => :environment do |t, a| + + TIFF_FUFFIX = ".tiff" + PDF_SUFFIX = ".pdf" + + fax_arguments = a.to_hash + + tiff_file = fax_arguments[:document] + + if !tiff_file or !File.exists?( tiff_file ) + $stderr.puts "File \"#{tiff_file}\" does not exist" + exit 1 + end + + paper_size = "letter" + pdf_file = "#{File.dirname(tiff_file)}/#{File.basename(tiff_file, TIFF_FUFFIX)}#{PDF_SUFFIX}" + + system "tiff2pdf \\ + -o \"#{pdf_file}\" \\ + -p #{paper_size} \\ + -a \"#{fax_arguments[:remote_station_id]}\" \\ + -c \"AMOOMA Gemeinschaft version #{GEMEINSCHAFT_VERSION}\" \\ + -t \"#{fax_arguments[:remote_station_id]}\" \"#{tiff_file}\"" + + if !File.exists?( pdf_file ) + $stderr.puts "File \"#{pdf_file}\" does not exist" + exit 1 + end + + fax_account = FaxAccount.find(fax_arguments[:fax_account_id]) + if !fax_account + $stderr.puts "Fax account \"#{fax_arguments[:fax_account_id]}\" does not exist" + exit 1 + end + + fax_arguments[:document] = nil + fax_arguments[:success] = true + fax_arguments[:inbound] = true + fax_arguments[:sent_at] = Time.now + fax_arguments[:local_station_id] = fax_account.station_id + fax_arguments[:retry_counter] = 0 + fax_arguments[:fax_resolution_id] = FaxResolution.first.id + fax_arguments[:image_size] = File.size(tiff_file) + fax_arguments[:ecm_used] = fax_arguments[:ecm_used] == "on" ? true : false + fax_document = fax_account.fax_documents.build(fax_arguments) + fax_document.document = File.open(pdf_file) + + if fax_document.save + fax_document.mark_as_inbound! + exit 0 + else + $stderr.puts "Error(s) creating fax document:" + $stderr.puts fax_document.errors.inspect + exit 1 + end +end diff --git a/lib/tasks/gs_cluster.rake b/lib/tasks/gs_cluster.rake new file mode 100644 index 0000000..565fd83 --- /dev/null +++ b/lib/tasks/gs_cluster.rake @@ -0,0 +1,333 @@ +namespace :gs_cluster do + desc "Sync local data to other gs cluster nodes." + task :push_waiting_data_to_other_nodes => :environment do + infinity_loop_protection_counter = GsClusterSyncLogEntry.where(:homebase_ip_address => HOMEBASE_IP_ADDRESS, + :waiting_to_be_synced => true).count + 10 + + # One bite at a time. + # + while GsClusterSyncLogEntry.where(:homebase_ip_address => HOMEBASE_IP_ADDRESS, + :waiting_to_be_synced => true).any? && + infinity_loop_protection_counter > 0 + GsClusterSyncLogEntry.where(:homebase_ip_address => HOMEBASE_IP_ADDRESS, + :waiting_to_be_synced => true).first.populate_other_cluster_nodes + infinity_loop_protection_counter -= 1 + end + + end + + desc "Reset gs_cluster_sync_log." + task :reset_sync_log => :environment do + GsClusterSyncLogEntry.destroy_all + + User.where('is_native IS NOT FALSE').each do |user| + puts("Processing User=#{user.id}/#{user.uuid} - #{user.user_name}") + user.create_on_other_gs_nodes + end + + SipAccount.where('is_native IS NOT FALSE').each do |sip_account| + puts("Processing SipAccount=#{sip_account.id}/#{sip_account.uuid} - #{sip_account.auth_name}"); + sip_account.create_on_other_gs_nodes + end + + PhoneNumber.where('is_native IS NOT FALSE AND phone_numberable_type IN ("SipAccount", "Conference", "FaxAccount", "Callthrough", "HuntGroup", "AutomaticCallDistributor")').each do |phone_number| + puts("Processing PhoneNumber=#{phone_number.id}/#{phone_number.uuid} - #{phone_number.number}"); + phone_number.create_on_other_gs_nodes + end + end + + desc "Pull objects from nodes." + task :pull => :environment do + local_node = GsNode.where(:ip_address => HOMEBASE_IP_ADDRESS).first + GsNode.where(:accepts_updates_from => true).each do |remote_node| + if remote_node.id == local_node.id + next + end + + puts "Processing node: #{remote_node.name}" + pull_node(remote_node, local_node) + end + end + + def pull_node(remote_node, local_node) + require 'nokogiri' + require 'open-uri' + + is_native = false + remote_site = remote_node.site + local_node_id = local_node.id + last_sync = remote_node.last_sync.to_i + + remote_objects(remote_site, local_node_id, last_sync, Tenant).each do |tenant| + puts "Processing Tenant: #{tenant[:name]}" + end + + remote_objects(remote_site, local_node_id, last_sync, UserGroup).each do |remote_object| + attributes = make_hash(remote_object.attributes) + tenant = Tenant.where(:name => attributes[:tenant]).first + process_object(UserGroup, tenant.user_groups, UserGroup.where(:name => attributes[:name], :tenant_id => tenant.try(:id)).first, attributes) + end + + remote_objects(remote_site, local_node_id, last_sync, User).each do |remote_object| + attributes = make_hash(remote_object.attributes) + + tenant = Tenant.where(:name => attributes[:current_tenant]).first + attributes[:language_id] = Language.where(:code => attributes[:language]).first.try(:id) + attributes.delete(:language) + attributes.delete(:current_tenant) + + if tenant + if ! attributes[:gs_node].blank? + attributes[:gs_node_id] = GsNode.where(:name => attributes[:gs_node]).first.try(:id) + attributes.delete(:gs_node) + end + + process_object(User, tenant.users, User.where(:uuid => attributes[:uuid]).first, attributes, { :is_native => is_native }) + else + $stderr.puts "NO_PROCESSING User #{attributes[:uuid]} - no current tenant" + end + end + + remote_objects(remote_site, local_node_id, last_sync, UserGroupMembership).each do |remote_object| + attributes = make_hash(remote_object.attributes) + attributes[:user_id] = User.where(:uuid => attributes[:user_uuid]).first.try(:id) + attributes[:user_group_id] = UserGroup.where(:name => attributes[:user_group]).first.try(:id) + attributes.delete(:user_uuid) + attributes.delete(:user_group) + + if attributes[:user_id] && attributes[:user_group_id] + process_object(UserGroupMembership, UserGroupMembership, UserGroupMembership.where(:user_id => attributes[:user_id], :user_group_id => attributes[:user_group_id]).first, attributes) + end + end + + remote_objects(remote_site, local_node_id, last_sync, SipAccount).each do |remote_object| + attributes = make_hash(remote_object.attributes) + attributes[:tenant_id] = Tenant.where(:name => attributes[:tenant]).first.try(:id) + attributes[:sip_domain] = SipDomain.where(:host => attributes[:sip_domain]).first + + if ! attributes[:sip_accountable_uuid].blank? + attributes[:sip_accountable_id] = attributes[:sip_accountable_type].constantize.where(:uuid => attributes[:sip_accountable_uuid]).first.try(:id) + end + + if attributes[:sip_accountable_id] + if ! attributes[:gs_node].blank? + attributes[:gs_node_id] = GsNode.where(:name => attributes[:gs_node]).first.try(:id) + attributes.delete(:gs_node) + end + + attributes.delete(:sip_accountable_uuid) + attributes.delete(:tenant) + process_object(SipAccount, SipAccount, SipAccount.where(:uuid => attributes[:uuid]).first, attributes, { :is_native => is_native }) + end + end + + remote_objects(remote_site, local_node_id, last_sync, Conference).each do |remote_object| + attributes = make_hash(remote_object.attributes) + + if ! attributes[:conferenceable_uuid].blank? + attributes[:conferenceable_id] = attributes[:conferenceable_type].constantize.where(:uuid => attributes[:conferenceable_uuid]).first.try(:id) + end + attributes.delete(:conferenceable_uuid) + + process_object(Conference, Conference, Conference.where(:uuid => attributes[:uuid]).first, attributes) + end + + remote_objects(remote_site, local_node_id, last_sync, FaxAccount).each do |remote_object| + attributes = make_hash(remote_object.attributes) + attributes[:tenant_id] = Tenant.where(:name => attributes[:tenant]).first.try(:id) + if ! attributes[:fax_accountable_uuid].blank? + attributes[:fax_accountable_id] = attributes[:fax_accountable_type].constantize.where(:uuid => attributes[:fax_accountable_uuid]).first.try(:id) + end + attributes.delete(:fax_accountable_uuid) + attributes.delete(:tenant) + process_object(FaxAccount, FaxAccount, FaxAccount.where(:uuid => attributes[:uuid]).first, attributes) + end + + remote_objects(remote_site, local_node_id, last_sync, PhoneBook).each do |remote_object| + attributes = make_hash(remote_object.attributes) + + if ! attributes[:phone_bookable_uuid].blank? + attributes[:phone_bookable_id] = attributes[:phone_bookable_type].constantize.where(:uuid => attributes[:phone_bookable_uuid]).first.try(:id) + end + attributes.delete(:phone_bookable_uuid) + process_object(PhoneBook, PhoneBook, PhoneBook.where(:uuid => attributes[:uuid]).first, attributes) + end + + remote_objects(remote_site, local_node_id, last_sync, PhoneBookEntry).each do |remote_object| + attributes = make_hash(remote_object.attributes) + attributes[:phone_book_id] = PhoneBook.where(:uuid => attributes[:phone_book_uuid]).first.try(:id) + attributes.delete(:phone_book_uuid) + process_object(PhoneBookEntry, PhoneBookEntry, PhoneBookEntry.where(:uuid => attributes[:uuid]).first, attributes) + end + + remote_objects(remote_site, local_node_id, last_sync, PhoneNumber).each do |remote_object| + attributes = make_hash(remote_object.attributes) + + if ! attributes[:phone_numberable_uuid].blank? + attributes[:phone_numberable_id] = attributes[:phone_numberable_type].constantize.where(:uuid => attributes[:phone_numberable_uuid]).first.try(:id) + end + + if ! attributes[:gs_node].blank? + attributes[:gs_node_id] = GsNode.where(:name => attributes[:gs_node]).first.try(:id) + attributes.delete(:gs_node) + end + + if !attributes[:phone_numberable_id] + puts "WARNING PhoneNumber #{attributes[:number]} has no local parent object #{attributes[:phone_numberable_type]}/#{attributes[:phone_numberable_uuid]}" + end + + attributes.delete(:phone_numberable_uuid) + process_object(PhoneNumber, PhoneNumber, PhoneNumber.where(:uuid => attributes[:uuid]).first, attributes, { :is_native => is_native }) + end + + remote_objects(remote_site, local_node_id, last_sync, CallForward).each do |remote_object| + attributes = make_hash(remote_object.attributes) + + attributes[:phone_number_id] = PhoneNumber.where(:uuid => attributes[:phone_number_uuid]).first.try(:id) + + if ! attributes[:call_forwardable_uuid].blank? + attributes[:call_forwardable_id] = attributes[:call_forwardable_type].constantize.where(:uuid => attributes[:call_forwardable_uuid]).first.try(:id) + attributes.delete(:call_forwardable_uuid) + end + + attributes[:call_forward_case_id] = CallForwardCase.where(:value => attributes[:service]).first.try(:id) + + attributes.delete(:phone_number_uuid) + attributes.delete(:service) + + process_object(CallForward, CallForward, CallForward.where(:uuid => attributes[:uuid]).first, attributes) + end + + remote_objects(remote_site, local_node_id, last_sync, Softkey).each do |remote_object| + attributes = make_hash(remote_object.attributes) + attributes[:sip_account_id] = SipAccount.where(:uuid => attributes[:sip_account_uuid]).first.try(:id) + attributes[:call_forward_id] = CallForward.where(:uuid => attributes[:call_forward_uuid]).first.try(:id) + attributes[:softkey_function_id] = SoftkeyFunction.where(:name => attributes[:function]).first.try(:id) + attributes.delete(:sip_account_uuid) + attributes.delete(:call_forward_uuid) + attributes.delete(:softkey_function) + process_object(Softkey, Softkey, Softkey.where(:uuid => attributes[:uuid]).first, attributes) + end + + remote_objects(remote_site, local_node_id, last_sync, Ringtone).each do |remote_object| + attributes = make_hash(remote_object.attributes) + if ! attributes[:ringtoneable_uuid].blank? + attributes[:ringtoneable_id] = attributes[:ringtoneable_type].constantize.where(:uuid => attributes[:ringtoneable_uuid]).first.try(:id) + end + + if !attributes[:ringtoneable_id] + puts "WARNING Ringtone #{attributes[:number]} has no local parent object #{attributes[:ringtoneable_type]}/#{attributes[:ringtoneable_uuid]}" + else + attributes.delete(:ringtoneable_uuid) + process_object(Ringtone, Ringtone, Ringtone.where(:ringtoneable_type => attributes[:ringtoneable_type], :ringtoneable_id => attributes[:ringtoneable_id]).first, attributes) + end + end + + remote_objects(remote_site, local_node_id, last_sync, ConferenceInvitee).each do |remote_object| + attributes = make_hash(remote_object.attributes) + attributes[:conference_id] = Conference.where(:uuid => attributes[:conference_uuid]).first.try(:id) + attributes[:phone_number] = PhoneNumber.where(:uuid => attributes[:phone_number_uuid]).first + if !attributes[:conference_id] + puts "WARNING ConferenceInvitee #{attributes[:uuid]} has no local Conference object #{attributes[:conference_uuid]}" + else + attributes[:phone_book_entry_id] = PhoneBookEntry.where(:uuid => attributes[:phone_book_entry_uuid]).first.try(:id) + attributes.delete(:conference_uuid) + attributes.delete(:phone_number_uuid) + attributes.delete(:phone_book_entry_uuid) + process_object(ConferenceInvitee, ConferenceInvitee, ConferenceInvitee.where(:uuid => attributes[:uuid]).first, attributes) + end + end + + #remote_objects(remote_site, local_node_id, last_sync, FaxDocument).each do |remote_object| + # attributes = make_hash(remote_object.attributes) + # attributes[:fax_account_id] = FaxAccount.where(:uuid => attributes[:fax_account_uuid]).first.try(:id) + # attributes[:fax_resolution_id] = FaxResolution.where(:resolution_value => attributes[:fax_resolution]).first.try(:id) + # attributes.delete(:fax_account_uuid) + # attributes.delete(:fax_resolution) + # process_object(FaxDocument, FaxDocument, FaxDocument.where(:uuid => attributes[:uuid]).first, attributes) + #end + + #remote_objects(remote_site, local_node_id, last_sync, CallHistory).each do |remote_object| + # attributes = make_hash(remote_object.attributes) + # process_object(CallHistory, CallHistory, CallHistory.where(:caller_channel_uuid => attributes[:caller_channel_uuid], :call_historyable_type => attributes[:caller_channel_type], :call_historyable_id => call_historyable.try(:id)).first, attributes) + #end + + remote_objects(remote_site, local_node_id, last_sync, DeletedItem).each do |remote_object| + attributes = make_hash(remote_object.attributes) + deleted_item = remote_object[:class_name].constantize.where(:uuid => attributes[:uuid]).first + + if deleted_item + print "DELETE #{deleted_item.class.to_s} #{deleted_item.to_s} : " + + if deleted_item.destroy + puts "OK" + else + $stderr.puts "Couldn't delete #{deleted_item.class.to_s}. #{deleted_item.errors.inspect}" + end + else + puts "NO_DELETE #{remote_object[:class_name]} #{remote_object[:uuid]}" + end + end + + if ! remote_node.synced + $stderr.puts "Errors updating node #{remote_node.name}. #{remote_node.errors.inspect}" + end + end + + def make_hash(attributes, new_hash = Hash.new) + attributes.each do |key, value| + new_hash[key.to_sym] = value.to_s + end + return new_hash + end + + def remote_objects(remote_site, local_node_id, last_sync, object_class) + class_name = object_class.to_s.underscore + section_name = class_name.pluralize + doc = Nokogiri::XML(open("#{remote_site}/gs_nodes/#{local_node_id}/sync.xml?newer=#{last_sync}&image=false&class=#{section_name}", :proxy => nil, :read_timeout => 120)) + return doc.xpath("//gemeinschaft_sync/#{section_name}/#{class_name}") + end + + def process_object(object_class, belongs_to, local_object, attributes, local_attributes = Hash.new) + if local_object + if local_object.updated_at < attributes[:updated_at] + print "UPDATE #{object_class.to_s} #{local_object.to_s} : " + update_object(local_object, attributes, local_attributes) + else + print "NO_UPDATE #{object_class.to_s}: #{local_object.to_s} - last update: #{local_object.updated_at}, remote: #{attributes[:updated_at]}" + end + else + print "CREATE #{object_class.to_s} #{attributes[:name].to_s} #{attributes[:uuid].to_s} : " + create_object(belongs_to, attributes, local_attributes) + end + puts "." + end + + def create_object(object_class, attributes, local_attributes) + attributes = attributes.merge(local_attributes) + + new_local_copy = object_class.create(attributes, :without_protection => true) + if new_local_copy && new_local_copy.errors.count == 0 + print "Created object, #{new_local_copy.class.to_s} #{new_local_copy.to_s}" + return true + else + $stderr.print "Couldn't create object. #{new_local_copy.errors.messages.inspect}" + return false + end + end + + def update_object(local_object, attributes, local_attributes) + attributes = attributes.merge(local_attributes) + + if local_object.update_attributes(attributes, :without_protection => true) + print "Updated #{local_object.class.to_s}, ID #{local_object.id}." + return true + else + $stderr.print "Couldn't update UserGroup. #{local_user_group.errors.inspect}" + return true + end + end + + class DeletedItem + end +end diff --git a/lib/tasks/originate.rake b/lib/tasks/originate.rake new file mode 100644 index 0000000..5e7dfe3 --- /dev/null +++ b/lib/tasks/originate.rake @@ -0,0 +1,13 @@ +desc "Originate call" + +task :originate, [ + :sip_account_id, + :extension, + ] => :environment do |t, a| + + extension = a.extension.to_s + sip_account = SipAccount.where(:id => a.sip_account_id.to_i).first + + print "Originate #{sip_account} -> #{extension} ... " + puts sip_account.call(extension) +end
\ No newline at end of file diff --git a/lib/tasks/populate_area_codes_de.rake b/lib/tasks/populate_area_codes_de.rake new file mode 100644 index 0000000..7237f5d --- /dev/null +++ b/lib/tasks/populate_area_codes_de.rake @@ -0,0 +1,5211 @@ +# encoding: UTF-8 + +desc "Populate database with German area codes" +task :areacodes_germany => :environment do + if ! germany = Country.where( :name => 'Germany' ).first + $stderr.puts "Error. Country not found." + exit 1 + end + + AreaCode.create(:country => germany, :name => "Essen", :area_code => "201") + AreaCode.create(:country => germany, :name => "Wuppertal", :area_code => "202") + AreaCode.create(:country => germany, :name => "Duisburg", :area_code => "203") + AreaCode.create(:country => germany, :name => "Bottrop", :area_code => "2041") + AreaCode.create(:country => germany, :name => "Gladbeck", :area_code => "2043") + AreaCode.create(:country => germany, :name => "Bottrop-Kirchhellen", :area_code => "2045") + AreaCode.create(:country => germany, :name => "Velbert", :area_code => "2051") + AreaCode.create(:country => germany, :name => "Velbert-Langenberg", :area_code => "2052") + AreaCode.create(:country => germany, :name => "Velbert-Neviges", :area_code => "2053") + AreaCode.create(:country => germany, :name => "Essen-Kettwig", :area_code => "2054") + AreaCode.create(:country => germany, :name => "Heiligenhaus", :area_code => "2056") + AreaCode.create(:country => germany, :name => "Wülfrath", :area_code => "2058") + AreaCode.create(:country => germany, :name => "Dinslaken", :area_code => "2064") + AreaCode.create(:country => germany, :name => "Duisburg-Rheinhausen", :area_code => "2065") + AreaCode.create(:country => germany, :name => "Duisburg-Homberg", :area_code => "2066") + AreaCode.create(:country => germany, :name => "Oberhausen Rheinl", :area_code => "208") + AreaCode.create(:country => germany, :name => "Gelsenkirchen", :area_code => "209") + AreaCode.create(:country => germany, :name => "Ratingen", :area_code => "2102") + AreaCode.create(:country => germany, :name => "Hilden", :area_code => "2103") + AreaCode.create(:country => germany, :name => "Mettmann", :area_code => "2104") + AreaCode.create(:country => germany, :name => "Düsseldorf", :area_code => "211") + AreaCode.create(:country => germany, :name => "Solingen", :area_code => "212") + AreaCode.create(:country => germany, :name => "Haan Rheinl", :area_code => "2129") + AreaCode.create(:country => germany, :name => "Neuss", :area_code => "2131") + AreaCode.create(:country => germany, :name => "Meerbusch-Büderich", :area_code => "2132") + AreaCode.create(:country => germany, :name => "Dormagen", :area_code => "2133") + AreaCode.create(:country => germany, :name => "Neuss-Norf", :area_code => "2137") + AreaCode.create(:country => germany, :name => "Leverkusen", :area_code => "214") + AreaCode.create(:country => germany, :name => "Meerbusch-Lank", :area_code => "2150") + AreaCode.create(:country => germany, :name => "Krefeld", :area_code => "2151") + AreaCode.create(:country => germany, :name => "Kempen", :area_code => "2152") + AreaCode.create(:country => germany, :name => "Nettetal-Lobberich", :area_code => "2153") + AreaCode.create(:country => germany, :name => "Willich", :area_code => "2154") + AreaCode.create(:country => germany, :name => "Willich-Anrath", :area_code => "2156") + AreaCode.create(:country => germany, :name => "Nettetal-Kaldenkirchen", :area_code => "2157") + AreaCode.create(:country => germany, :name => "Grefrath b Krefeld", :area_code => "2158") + AreaCode.create(:country => germany, :name => "Meerbusch-Osterath", :area_code => "2159") + AreaCode.create(:country => germany, :name => "Mönchengladbach", :area_code => "2161") + AreaCode.create(:country => germany, :name => "Viersen", :area_code => "2162") + AreaCode.create(:country => germany, :name => "Schwalmtal Niederrhein", :area_code => "2163") + AreaCode.create(:country => germany, :name => "Jüchen-Otzenrath", :area_code => "2164") + AreaCode.create(:country => germany, :name => "Jüchen", :area_code => "2165") + AreaCode.create(:country => germany, :name => "Mönchengladbach-Rheydt", :area_code => "2166") + AreaCode.create(:country => germany, :name => "Leverkusen-Opladen", :area_code => "2171") + AreaCode.create(:country => germany, :name => "Langenfeld Rheinland", :area_code => "2173") + AreaCode.create(:country => germany, :name => "Burscheid Rheinl", :area_code => "2174") + AreaCode.create(:country => germany, :name => "Leichlingen Rheinland", :area_code => "2175") + AreaCode.create(:country => germany, :name => "Grevenbroich", :area_code => "2181") + AreaCode.create(:country => germany, :name => "Grevenbroich-Kapellen", :area_code => "2182") + AreaCode.create(:country => germany, :name => "Rommerskirchen", :area_code => "2183") + AreaCode.create(:country => germany, :name => "Remscheid", :area_code => "2191") + AreaCode.create(:country => germany, :name => "Hückeswagen", :area_code => "2192") + AreaCode.create(:country => germany, :name => "Dabringhausen", :area_code => "2193") + AreaCode.create(:country => germany, :name => "Radevormwald", :area_code => "2195") + AreaCode.create(:country => germany, :name => "Wermelskirchen", :area_code => "2196") + AreaCode.create(:country => germany, :name => "Bergisch Gladbach", :area_code => "2202") + AreaCode.create(:country => germany, :name => "Köln-Porz", :area_code => "2203") + AreaCode.create(:country => germany, :name => "Bensberg", :area_code => "2204") + AreaCode.create(:country => germany, :name => "Rösrath", :area_code => "2205") + AreaCode.create(:country => germany, :name => "Overath", :area_code => "2206") + AreaCode.create(:country => germany, :name => "Kürten-Dürscheid", :area_code => "2207") + AreaCode.create(:country => germany, :name => "Niederkassel", :area_code => "2208") + AreaCode.create(:country => germany, :name => "Köln", :area_code => "221") + AreaCode.create(:country => germany, :name => "Bornheim Rheinl", :area_code => "2222") + AreaCode.create(:country => germany, :name => "Königswinter", :area_code => "2223") + AreaCode.create(:country => germany, :name => "Bad Honnef", :area_code => "2224") + AreaCode.create(:country => germany, :name => "Meckenheim Rheinl", :area_code => "2225") + AreaCode.create(:country => germany, :name => "Rheinbach", :area_code => "2226") + AreaCode.create(:country => germany, :name => "Bornheim-Merten", :area_code => "2227") + AreaCode.create(:country => germany, :name => "Remagen-Rolandseck", :area_code => "2228") + AreaCode.create(:country => germany, :name => "Brühl Rheinl", :area_code => "2232") + AreaCode.create(:country => germany, :name => "Hürth Rheinl", :area_code => "2233") + AreaCode.create(:country => germany, :name => "Frechen", :area_code => "2234") + AreaCode.create(:country => germany, :name => "Erftstadt", :area_code => "2235") + AreaCode.create(:country => germany, :name => "Wesseling Rheinl", :area_code => "2236") + AreaCode.create(:country => germany, :name => "Kerpen Rheinl-Türnich", :area_code => "2237") + AreaCode.create(:country => germany, :name => "Pulheim", :area_code => "2238") + AreaCode.create(:country => germany, :name => "Siegburg", :area_code => "2241") + AreaCode.create(:country => germany, :name => "Hennef Sieg", :area_code => "2242") + AreaCode.create(:country => germany, :name => "Eitorf", :area_code => "2243") + AreaCode.create(:country => germany, :name => "Königswinter-Oberpleis", :area_code => "2244") + AreaCode.create(:country => germany, :name => "Much", :area_code => "2245") + AreaCode.create(:country => germany, :name => "Lohmar", :area_code => "2246") + AreaCode.create(:country => germany, :name => "Neunkirchen-Seelscheid", :area_code => "2247") + AreaCode.create(:country => germany, :name => "Hennef-Uckerath", :area_code => "2248") + AreaCode.create(:country => germany, :name => "Euskirchen", :area_code => "2251") + AreaCode.create(:country => germany, :name => "Zülpich", :area_code => "2252") + AreaCode.create(:country => germany, :name => "Bad Münstereifel", :area_code => "2253") + AreaCode.create(:country => germany, :name => "Weilerswist", :area_code => "2254") + AreaCode.create(:country => germany, :name => "Euskirchen-Flamersheim", :area_code => "2255") + AreaCode.create(:country => germany, :name => "Mechernich-Satzvey", :area_code => "2256") + AreaCode.create(:country => germany, :name => "Reckerscheid", :area_code => "2257") + AreaCode.create(:country => germany, :name => "Gummersbach", :area_code => "2261") + AreaCode.create(:country => germany, :name => "Wiehl", :area_code => "2262") + AreaCode.create(:country => germany, :name => "Engelskirchen", :area_code => "2263") + AreaCode.create(:country => germany, :name => "Marienheide", :area_code => "2264") + AreaCode.create(:country => germany, :name => "Reichshof-Eckenhagen", :area_code => "2265") + AreaCode.create(:country => germany, :name => "Lindlar", :area_code => "2266") + AreaCode.create(:country => germany, :name => "Wipperfürth", :area_code => "2267") + AreaCode.create(:country => germany, :name => "Kürten", :area_code => "2268") + AreaCode.create(:country => germany, :name => "Kierspe-Rönsahl", :area_code => "2269") + AreaCode.create(:country => germany, :name => "Bergheim Erft", :area_code => "2271") + AreaCode.create(:country => germany, :name => "Bedburg Erft", :area_code => "2272") + AreaCode.create(:country => germany, :name => "Kerpen-Horrem", :area_code => "2273") + AreaCode.create(:country => germany, :name => "Elsdorf Rheinl", :area_code => "2274") + AreaCode.create(:country => germany, :name => "Kerpen-Buir", :area_code => "2275") + AreaCode.create(:country => germany, :name => "Bonn", :area_code => "228") + AreaCode.create(:country => germany, :name => "Waldbröl", :area_code => "2291") + AreaCode.create(:country => germany, :name => "Windeck Sieg", :area_code => "2292") + AreaCode.create(:country => germany, :name => "Nümbrecht", :area_code => "2293") + AreaCode.create(:country => germany, :name => "Morsbach Sieg", :area_code => "2294") + AreaCode.create(:country => germany, :name => "Ruppichteroth", :area_code => "2295") + AreaCode.create(:country => germany, :name => "Reichshof-Brüchermühle", :area_code => "2296") + AreaCode.create(:country => germany, :name => "Wildbergerhütte", :area_code => "2297") + AreaCode.create(:country => germany, :name => "Holzwickede", :area_code => "2301") + AreaCode.create(:country => germany, :name => "Witten", :area_code => "2302") + AreaCode.create(:country => germany, :name => "Unna", :area_code => "2303") + AreaCode.create(:country => germany, :name => "Schwerte", :area_code => "2304") + AreaCode.create(:country => germany, :name => "Castrop-Rauxel", :area_code => "2305") + AreaCode.create(:country => germany, :name => "Lünen", :area_code => "2306") + AreaCode.create(:country => germany, :name => "Kamen", :area_code => "2307") + AreaCode.create(:country => germany, :name => "Unna-Hemmerde", :area_code => "2308") + AreaCode.create(:country => germany, :name => "Waltrop", :area_code => "2309") + AreaCode.create(:country => germany, :name => "Dortmund", :area_code => "231") + AreaCode.create(:country => germany, :name => "Herne", :area_code => "2323") + AreaCode.create(:country => germany, :name => "Hattingen Ruhr", :area_code => "2324") + AreaCode.create(:country => germany, :name => "Wanne-Eickel", :area_code => "2325") + AreaCode.create(:country => germany, :name => "Bochum-Wattenscheid", :area_code => "2327") + AreaCode.create(:country => germany, :name => "Herdecke", :area_code => "2330") + AreaCode.create(:country => germany, :name => "Hagen Westf", :area_code => "2331") + AreaCode.create(:country => germany, :name => "Gevelsberg", :area_code => "2332") + AreaCode.create(:country => germany, :name => "Ennepetal", :area_code => "2333") + AreaCode.create(:country => germany, :name => "Hagen-Hohenlimburg", :area_code => "2334") + AreaCode.create(:country => germany, :name => "Wetter Ruhr", :area_code => "2335") + AreaCode.create(:country => germany, :name => "Schwelm", :area_code => "2336") + AreaCode.create(:country => germany, :name => "Hagen-Dahl", :area_code => "2337") + AreaCode.create(:country => germany, :name => "Breckerfeld", :area_code => "2338") + AreaCode.create(:country => germany, :name => "Sprockhövel-Haßlinghausen", :area_code => "2339") + AreaCode.create(:country => germany, :name => "Bochum", :area_code => "234") + AreaCode.create(:country => germany, :name => "Lüdenscheid", :area_code => "2351") + AreaCode.create(:country => germany, :name => "Altena Westf", :area_code => "2352") + AreaCode.create(:country => germany, :name => "Halver", :area_code => "2353") + AreaCode.create(:country => germany, :name => "Meinerzhagen", :area_code => "2354") + AreaCode.create(:country => germany, :name => "Schalksmühle", :area_code => "2355") + AreaCode.create(:country => germany, :name => "Herscheid Westf", :area_code => "2357") + AreaCode.create(:country => germany, :name => "Meinerzhagen-Valbert", :area_code => "2358") + AreaCode.create(:country => germany, :name => "Kierspe", :area_code => "2359") + AreaCode.create(:country => germany, :name => "Haltern-Lippramsdorf", :area_code => "2360") + AreaCode.create(:country => germany, :name => "Recklinghausen", :area_code => "2361") + AreaCode.create(:country => germany, :name => "Dorsten", :area_code => "2362") + AreaCode.create(:country => germany, :name => "Datteln", :area_code => "2363") + AreaCode.create(:country => germany, :name => "Haltern Westf", :area_code => "2364") + AreaCode.create(:country => germany, :name => "Marl", :area_code => "2365") + AreaCode.create(:country => germany, :name => "Herten Westf", :area_code => "2366") + AreaCode.create(:country => germany, :name => "Henrichenburg", :area_code => "2367") + AreaCode.create(:country => germany, :name => "Oer-Erkenschwick", :area_code => "2368") + AreaCode.create(:country => germany, :name => "Dorsten-Wulfen", :area_code => "2369") + AreaCode.create(:country => germany, :name => "Iserlohn", :area_code => "2371") + AreaCode.create(:country => germany, :name => "Hemer", :area_code => "2372") + AreaCode.create(:country => germany, :name => "Menden Sauerland", :area_code => "2373") + AreaCode.create(:country => germany, :name => "Iserlohn-Letmathe", :area_code => "2374") + AreaCode.create(:country => germany, :name => "Balve", :area_code => "2375") + AreaCode.create(:country => germany, :name => "Wickede Ruhr", :area_code => "2377") + AreaCode.create(:country => germany, :name => "Fröndenberg-Langschede", :area_code => "2378") + AreaCode.create(:country => germany, :name => "Menden-Asbeck", :area_code => "2379") + AreaCode.create(:country => germany, :name => "Hamm Westf", :area_code => "2381") + AreaCode.create(:country => germany, :name => "Ahlen Westf", :area_code => "2382") + AreaCode.create(:country => germany, :name => "Bönen", :area_code => "2383") + AreaCode.create(:country => germany, :name => "Welver", :area_code => "2384") + AreaCode.create(:country => germany, :name => "Hamm-Rhynern", :area_code => "2385") + AreaCode.create(:country => germany, :name => "Drensteinfurt-Walstedde", :area_code => "2387") + AreaCode.create(:country => germany, :name => "Hamm-Uentrop", :area_code => "2388") + AreaCode.create(:country => germany, :name => "Werne", :area_code => "2389") + AreaCode.create(:country => germany, :name => "Plettenberg", :area_code => "2391") + AreaCode.create(:country => germany, :name => "Werdohl", :area_code => "2392") + AreaCode.create(:country => germany, :name => "Sundern-Allendorf", :area_code => "2393") + AreaCode.create(:country => germany, :name => "Neuenrade-Affeln", :area_code => "2394") + AreaCode.create(:country => germany, :name => "Finnentrop-Rönkhausen", :area_code => "2395") + AreaCode.create(:country => germany, :name => "Baesweiler", :area_code => "2401") + AreaCode.create(:country => germany, :name => "Stolberg Rheinl", :area_code => "2402") + AreaCode.create(:country => germany, :name => "Eschweiler Rheinl", :area_code => "2403") + AreaCode.create(:country => germany, :name => "Alsdorf Rheinl", :area_code => "2404") + AreaCode.create(:country => germany, :name => "Würselen", :area_code => "2405") + AreaCode.create(:country => germany, :name => "Herzogenrath", :area_code => "2406") + AreaCode.create(:country => germany, :name => "Herzogenrath-Kohlscheid", :area_code => "2407") + AreaCode.create(:country => germany, :name => "Aachen-Kornelimünster", :area_code => "2408") + AreaCode.create(:country => germany, :name => "Stolberg-Gressenich", :area_code => "2409") + AreaCode.create(:country => germany, :name => "Aachen", :area_code => "241") + AreaCode.create(:country => germany, :name => "Düren", :area_code => "2421") + AreaCode.create(:country => germany, :name => "Kreuzau", :area_code => "2422") + AreaCode.create(:country => germany, :name => "Langerwehe", :area_code => "2423") + AreaCode.create(:country => germany, :name => "Vettweiss", :area_code => "2424") + AreaCode.create(:country => germany, :name => "Nideggen-Embken", :area_code => "2425") + AreaCode.create(:country => germany, :name => "Nörvenich", :area_code => "2426") + AreaCode.create(:country => germany, :name => "Nideggen", :area_code => "2427") + AreaCode.create(:country => germany, :name => "Niederzier", :area_code => "2428") + AreaCode.create(:country => germany, :name => "Hürtgenwald", :area_code => "2429") + AreaCode.create(:country => germany, :name => "Erkelenz", :area_code => "2431") + AreaCode.create(:country => germany, :name => "Wassenberg", :area_code => "2432") + AreaCode.create(:country => germany, :name => "Hückelhoven", :area_code => "2433") + AreaCode.create(:country => germany, :name => "Wegberg", :area_code => "2434") + AreaCode.create(:country => germany, :name => "Erkelenz-Lövenich", :area_code => "2435") + AreaCode.create(:country => germany, :name => "Wegberg-Rödgen", :area_code => "2436") + AreaCode.create(:country => germany, :name => "Nettersheim-Tondorf", :area_code => "2440") + AreaCode.create(:country => germany, :name => "Kall", :area_code => "2441") + AreaCode.create(:country => germany, :name => "Mechernich", :area_code => "2443") + AreaCode.create(:country => germany, :name => "Schleiden-Gemünd", :area_code => "2444") + AreaCode.create(:country => germany, :name => "Schleiden Eifel", :area_code => "2445") + AreaCode.create(:country => germany, :name => "Heimbach Eifel", :area_code => "2446") + AreaCode.create(:country => germany, :name => "Dahlem b Kall", :area_code => "2447") + AreaCode.create(:country => germany, :name => "Hellenthal-Rescheid", :area_code => "2448") + AreaCode.create(:country => germany, :name => "Blankenheim Ahr", :area_code => "2449") + AreaCode.create(:country => germany, :name => "Geilenkirchen", :area_code => "2451") + AreaCode.create(:country => germany, :name => "Heinsberg Rheinl", :area_code => "2452") + AreaCode.create(:country => germany, :name => "Heinsberg-Randerath", :area_code => "2453") + AreaCode.create(:country => germany, :name => "Gangelt", :area_code => "2454") + AreaCode.create(:country => germany, :name => "Waldfeucht", :area_code => "2455") + AreaCode.create(:country => germany, :name => "Selfkant", :area_code => "2456") + AreaCode.create(:country => germany, :name => "Jülich", :area_code => "2461") + AreaCode.create(:country => germany, :name => "Linnich", :area_code => "2462") + AreaCode.create(:country => germany, :name => "Titz", :area_code => "2463") + AreaCode.create(:country => germany, :name => "Aldenhoven b Jülich", :area_code => "2464") + AreaCode.create(:country => germany, :name => "Inden", :area_code => "2465") + AreaCode.create(:country => germany, :name => "Roetgen Eifel", :area_code => "2471") + AreaCode.create(:country => germany, :name => "Monschau", :area_code => "2472") + AreaCode.create(:country => germany, :name => "Simmerath", :area_code => "2473") + AreaCode.create(:country => germany, :name => "Nideggen-Schmidt", :area_code => "2474") + AreaCode.create(:country => germany, :name => "Hellenthal", :area_code => "2482") + AreaCode.create(:country => germany, :name => "Mechernich-Eiserfey", :area_code => "2484") + AreaCode.create(:country => germany, :name => "Schleiden-Dreiborn", :area_code => "2485") + AreaCode.create(:country => germany, :name => "Nettersheim", :area_code => "2486") + AreaCode.create(:country => germany, :name => "Münster-Hiltrup", :area_code => "2501") + AreaCode.create(:country => germany, :name => "Nottuln", :area_code => "2502") + AreaCode.create(:country => germany, :name => "Telgte", :area_code => "2504") + AreaCode.create(:country => germany, :name => "Altenberge Westf", :area_code => "2505") + AreaCode.create(:country => germany, :name => "Münster-Wolbeck", :area_code => "2506") + AreaCode.create(:country => germany, :name => "Havixbeck", :area_code => "2507") + AreaCode.create(:country => germany, :name => "Drensteinfurt", :area_code => "2508") + AreaCode.create(:country => germany, :name => "Nottuln-Appelhülsen", :area_code => "2509") + AreaCode.create(:country => germany, :name => "Münster", :area_code => "251") + AreaCode.create(:country => germany, :name => "Wadersloh-Diestedde", :area_code => "2520") + AreaCode.create(:country => germany, :name => "Beckum", :area_code => "2521") + AreaCode.create(:country => germany, :name => "Oelde", :area_code => "2522") + AreaCode.create(:country => germany, :name => "Wadersloh", :area_code => "2523") + AreaCode.create(:country => germany, :name => "Ennigerloh", :area_code => "2524") + AreaCode.create(:country => germany, :name => "Beckum-Neubeckum", :area_code => "2525") + AreaCode.create(:country => germany, :name => "Sendenhorst", :area_code => "2526") + AreaCode.create(:country => germany, :name => "Lippetal-Lippborg", :area_code => "2527") + AreaCode.create(:country => germany, :name => "Ennigerloh-Enniger", :area_code => "2528") + AreaCode.create(:country => germany, :name => "Oelde-Stromberg", :area_code => "2529") + AreaCode.create(:country => germany, :name => "Ostbevern", :area_code => "2532") + AreaCode.create(:country => germany, :name => "Münster-Nienberge", :area_code => "2533") + AreaCode.create(:country => germany, :name => "Münster-Roxel", :area_code => "2534") + AreaCode.create(:country => germany, :name => "Sendenhorst-Albersloh", :area_code => "2535") + AreaCode.create(:country => germany, :name => "Münster-Albachten", :area_code => "2536") + AreaCode.create(:country => germany, :name => "Drensteinfurt-Rinkerode", :area_code => "2538") + AreaCode.create(:country => germany, :name => "Coesfeld", :area_code => "2541") + AreaCode.create(:country => germany, :name => "Gescher", :area_code => "2542") + AreaCode.create(:country => germany, :name => "Billerbeck Westf", :area_code => "2543") + AreaCode.create(:country => germany, :name => "Rosendahl-Darfeld", :area_code => "2545") + AreaCode.create(:country => germany, :name => "Coesfeld-Lette", :area_code => "2546") + AreaCode.create(:country => germany, :name => "Rosendahl-Osterwick", :area_code => "2547") + AreaCode.create(:country => germany, :name => "Dülmen-Rorup", :area_code => "2548") + AreaCode.create(:country => germany, :name => "Steinfurt-Burgsteinfurt", :area_code => "2551") + AreaCode.create(:country => germany, :name => "Steinfurt-Borghorst", :area_code => "2552") + AreaCode.create(:country => germany, :name => "Ochtrup", :area_code => "2553") + AreaCode.create(:country => germany, :name => "Laer Kr Steinfurt", :area_code => "2554") + AreaCode.create(:country => germany, :name => "Schöppingen", :area_code => "2555") + AreaCode.create(:country => germany, :name => "Metelen", :area_code => "2556") + AreaCode.create(:country => germany, :name => "Wettringen Kr Steinfurt", :area_code => "2557") + AreaCode.create(:country => germany, :name => "Horstmar", :area_code => "2558") + AreaCode.create(:country => germany, :name => "Ahaus", :area_code => "2561") + AreaCode.create(:country => germany, :name => "Gronau Westfalen", :area_code => "2562") + AreaCode.create(:country => germany, :name => "Stadtlohn", :area_code => "2563") + AreaCode.create(:country => germany, :name => "Vreden", :area_code => "2564") + AreaCode.create(:country => germany, :name => "Gronau-Epe", :area_code => "2565") + AreaCode.create(:country => germany, :name => "Legden", :area_code => "2566") + AreaCode.create(:country => germany, :name => "Ahaus-Alstätte", :area_code => "2567") + AreaCode.create(:country => germany, :name => "Heek", :area_code => "2568") + AreaCode.create(:country => germany, :name => "Greven Westf", :area_code => "2571") + AreaCode.create(:country => germany, :name => "Emsdetten", :area_code => "2572") + AreaCode.create(:country => germany, :name => "Nordwalde", :area_code => "2573") + AreaCode.create(:country => germany, :name => "Saerbeck", :area_code => "2574") + AreaCode.create(:country => germany, :name => "Greven-Reckenfeld", :area_code => "2575") + AreaCode.create(:country => germany, :name => "Warendorf", :area_code => "2581") + AreaCode.create(:country => germany, :name => "Everswinkel", :area_code => "2582") + AreaCode.create(:country => germany, :name => "Sassenberg", :area_code => "2583") + AreaCode.create(:country => germany, :name => "Warendorf-Milte", :area_code => "2584") + AreaCode.create(:country => germany, :name => "Warendorf-Hoetmar", :area_code => "2585") + AreaCode.create(:country => germany, :name => "Beelen", :area_code => "2586") + AreaCode.create(:country => germany, :name => "Ennigerloh-Westkirchen", :area_code => "2587") + AreaCode.create(:country => germany, :name => "Harsewinkel-Greffen", :area_code => "2588") + AreaCode.create(:country => germany, :name => "Dülmen-Buldern", :area_code => "2590") + AreaCode.create(:country => germany, :name => "Lüdinghausen", :area_code => "2591") + AreaCode.create(:country => germany, :name => "Selm", :area_code => "2592") + AreaCode.create(:country => germany, :name => "Ascheberg Westf", :area_code => "2593") + AreaCode.create(:country => germany, :name => "Dülmen", :area_code => "2594") + AreaCode.create(:country => germany, :name => "Olfen", :area_code => "2595") + AreaCode.create(:country => germany, :name => "Nordkirchen", :area_code => "2596") + AreaCode.create(:country => germany, :name => "Senden Westf", :area_code => "2597") + AreaCode.create(:country => germany, :name => "Senden-Ottmarsbocholt", :area_code => "2598") + AreaCode.create(:country => germany, :name => "Ascheberg-Herbern", :area_code => "2599") + AreaCode.create(:country => germany, :name => "Nauort", :area_code => "2601") + AreaCode.create(:country => germany, :name => "Montabaur", :area_code => "2602") + AreaCode.create(:country => germany, :name => "Bad Ems", :area_code => "2603") + AreaCode.create(:country => germany, :name => "Nassau Lahn", :area_code => "2604") + AreaCode.create(:country => germany, :name => "Löf", :area_code => "2605") + AreaCode.create(:country => germany, :name => "Winningen Mosel", :area_code => "2606") + AreaCode.create(:country => germany, :name => "Kobern-Gondorf", :area_code => "2607") + AreaCode.create(:country => germany, :name => "Welschneudorf", :area_code => "2608") + AreaCode.create(:country => germany, :name => "Koblenz a Rhein", :area_code => "261") + AreaCode.create(:country => germany, :name => "Neuhäusel Westerw", :area_code => "2620") + AreaCode.create(:country => germany, :name => "Lahnstein", :area_code => "2621") + AreaCode.create(:country => germany, :name => "Bendorf Rhein", :area_code => "2622") + AreaCode.create(:country => germany, :name => "Ransbach-Baumbach", :area_code => "2623") + AreaCode.create(:country => germany, :name => "Höhr-Grenzhausen", :area_code => "2624") + AreaCode.create(:country => germany, :name => "Ochtendung", :area_code => "2625") + AreaCode.create(:country => germany, :name => "Selters Westerwald", :area_code => "2626") + AreaCode.create(:country => germany, :name => "Braubach", :area_code => "2627") + AreaCode.create(:country => germany, :name => "Rhens", :area_code => "2628") + AreaCode.create(:country => germany, :name => "Mülheim-Kärlich", :area_code => "2630") + AreaCode.create(:country => germany, :name => "Neuwied", :area_code => "2631") + AreaCode.create(:country => germany, :name => "Andernach", :area_code => "2632") + AreaCode.create(:country => germany, :name => "Brohl-Lützing", :area_code => "2633") + AreaCode.create(:country => germany, :name => "Rengsdorf", :area_code => "2634") + AreaCode.create(:country => germany, :name => "Rheinbrohl", :area_code => "2635") + AreaCode.create(:country => germany, :name => "Burgbrohl", :area_code => "2636") + AreaCode.create(:country => germany, :name => "Weissenthurm", :area_code => "2637") + AreaCode.create(:country => germany, :name => "Waldbreitbach", :area_code => "2638") + AreaCode.create(:country => germany, :name => "Anhausen Kr Neuwied", :area_code => "2639") + AreaCode.create(:country => germany, :name => "Bad Neuenahr-Ahrweiler", :area_code => "2641") + AreaCode.create(:country => germany, :name => "Remagen", :area_code => "2642") + AreaCode.create(:country => germany, :name => "Altenahr", :area_code => "2643") + AreaCode.create(:country => germany, :name => "Linz am Rhein", :area_code => "2644") + AreaCode.create(:country => germany, :name => "Vettelschoss", :area_code => "2645") + AreaCode.create(:country => germany, :name => "Königsfeld Eifel", :area_code => "2646") + AreaCode.create(:country => germany, :name => "Kesseling", :area_code => "2647") + AreaCode.create(:country => germany, :name => "Mayen", :area_code => "2651") + AreaCode.create(:country => germany, :name => "Mendig", :area_code => "2652") + AreaCode.create(:country => germany, :name => "Kaisersesch", :area_code => "2653") + AreaCode.create(:country => germany, :name => "Polch", :area_code => "2654") + AreaCode.create(:country => germany, :name => "Weibern", :area_code => "2655") + AreaCode.create(:country => germany, :name => "Virneburg", :area_code => "2656") + AreaCode.create(:country => germany, :name => "Uersfeld", :area_code => "2657") + AreaCode.create(:country => germany, :name => "Bad Marienberg Westerwald", :area_code => "2661") + AreaCode.create(:country => germany, :name => "Hachenburg", :area_code => "2662") + AreaCode.create(:country => germany, :name => "Westerburg Westerw", :area_code => "2663") + AreaCode.create(:country => germany, :name => "Rennerod", :area_code => "2664") + AreaCode.create(:country => germany, :name => "Freilingen Westerw", :area_code => "2666") + AreaCode.create(:country => germany, :name => "Stein-Neukirch", :area_code => "2667") + AreaCode.create(:country => germany, :name => "Cochem", :area_code => "2671") + AreaCode.create(:country => germany, :name => "Treis-Karden", :area_code => "2672") + AreaCode.create(:country => germany, :name => "Ellenz-Poltersdorf", :area_code => "2673") + AreaCode.create(:country => germany, :name => "Bad Bertrich", :area_code => "2674") + AreaCode.create(:country => germany, :name => "Ediger-Eller", :area_code => "2675") + AreaCode.create(:country => germany, :name => "Ulmen", :area_code => "2676") + AreaCode.create(:country => germany, :name => "Lutzerath", :area_code => "2677") + AreaCode.create(:country => germany, :name => "Büchel b Cochem", :area_code => "2678") + AreaCode.create(:country => germany, :name => "Mündersbach", :area_code => "2680") + AreaCode.create(:country => germany, :name => "Altenkirchen Westerwald", :area_code => "2681") + AreaCode.create(:country => germany, :name => "Hamm Sieg", :area_code => "2682") + AreaCode.create(:country => germany, :name => "Asbach Westerw", :area_code => "2683") + AreaCode.create(:country => germany, :name => "Puderbach Westerw", :area_code => "2684") + AreaCode.create(:country => germany, :name => "Flammersfeld", :area_code => "2685") + AreaCode.create(:country => germany, :name => "Weyerbusch", :area_code => "2686") + AreaCode.create(:country => germany, :name => "Horhausen Westerwald", :area_code => "2687") + AreaCode.create(:country => germany, :name => "Kroppach", :area_code => "2688") + AreaCode.create(:country => germany, :name => "Dierdorf", :area_code => "2689") + AreaCode.create(:country => germany, :name => "Adenau", :area_code => "2691") + AreaCode.create(:country => germany, :name => "Kelberg", :area_code => "2692") + AreaCode.create(:country => germany, :name => "Antweiler", :area_code => "2693") + AreaCode.create(:country => germany, :name => "Wershofen", :area_code => "2694") + AreaCode.create(:country => germany, :name => "Insul", :area_code => "2695") + AreaCode.create(:country => germany, :name => "Nohn Eifel", :area_code => "2696") + AreaCode.create(:country => germany, :name => "Blankenheim-Ahrhütte", :area_code => "2697") + AreaCode.create(:country => germany, :name => "Siegen", :area_code => "271") + AreaCode.create(:country => germany, :name => "Lennestadt", :area_code => "2721") + AreaCode.create(:country => germany, :name => "Attendorn", :area_code => "2722") + AreaCode.create(:country => germany, :name => "Kirchhundem", :area_code => "2723") + AreaCode.create(:country => germany, :name => "Finnentrop-Serkenrode", :area_code => "2724") + AreaCode.create(:country => germany, :name => "Lennestadt-Oedingen", :area_code => "2725") + AreaCode.create(:country => germany, :name => "Kreuztal", :area_code => "2732") + AreaCode.create(:country => germany, :name => "Hilchenbach", :area_code => "2733") + AreaCode.create(:country => germany, :name => "Freudenberg Westf", :area_code => "2734") + AreaCode.create(:country => germany, :name => "Neunkirchen Siegerl", :area_code => "2735") + AreaCode.create(:country => germany, :name => "Burbach Siegerl", :area_code => "2736") + AreaCode.create(:country => germany, :name => "Netphen-Deuz", :area_code => "2737") + AreaCode.create(:country => germany, :name => "Netphen", :area_code => "2738") + AreaCode.create(:country => germany, :name => "Wilnsdorf", :area_code => "2739") + AreaCode.create(:country => germany, :name => "Betzdorf", :area_code => "2741") + AreaCode.create(:country => germany, :name => "Wissen", :area_code => "2742") + AreaCode.create(:country => germany, :name => "Daaden", :area_code => "2743") + AreaCode.create(:country => germany, :name => "Herdorf", :area_code => "2744") + AreaCode.create(:country => germany, :name => "Brachbach Sieg", :area_code => "2745") + AreaCode.create(:country => germany, :name => "Molzhain", :area_code => "2747") + AreaCode.create(:country => germany, :name => "Diedenshausen", :area_code => "2750") + AreaCode.create(:country => germany, :name => "Bad Berleburg", :area_code => "2751") + AreaCode.create(:country => germany, :name => "Bad Laasphe", :area_code => "2752") + AreaCode.create(:country => germany, :name => "Erndtebrück", :area_code => "2753") + AreaCode.create(:country => germany, :name => "Bad Laasphe-Feudingen", :area_code => "2754") + AreaCode.create(:country => germany, :name => "Bad Berleburg-Schwarzenau", :area_code => "2755") + AreaCode.create(:country => germany, :name => "Bad Berleburg-Girkhausen", :area_code => "2758") + AreaCode.create(:country => germany, :name => "Bad Berleburg-Aue", :area_code => "2759") + AreaCode.create(:country => germany, :name => "Olpe Biggesee", :area_code => "2761") + AreaCode.create(:country => germany, :name => "Wenden Südsauerland", :area_code => "2762") + AreaCode.create(:country => germany, :name => "Drolshagen-Bleche", :area_code => "2763") + AreaCode.create(:country => germany, :name => "Welschen Ennest", :area_code => "2764") + AreaCode.create(:country => germany, :name => "Eschenburg", :area_code => "2770") + AreaCode.create(:country => germany, :name => "Dillenburg", :area_code => "2771") + AreaCode.create(:country => germany, :name => "Herborn Hess", :area_code => "2772") + AreaCode.create(:country => germany, :name => "Haiger", :area_code => "2773") + AreaCode.create(:country => germany, :name => "Dietzhölztal", :area_code => "2774") + AreaCode.create(:country => germany, :name => "Driedorf", :area_code => "2775") + AreaCode.create(:country => germany, :name => "Bad Endbach-Hartenrod", :area_code => "2776") + AreaCode.create(:country => germany, :name => "Breitscheid Hess", :area_code => "2777") + AreaCode.create(:country => germany, :name => "Siegbach", :area_code => "2778") + AreaCode.create(:country => germany, :name => "Greifenstein-Beilstein", :area_code => "2779") + AreaCode.create(:country => germany, :name => "Xanten", :area_code => "2801") + AreaCode.create(:country => germany, :name => "Alpen", :area_code => "2802") + AreaCode.create(:country => germany, :name => "Wesel-Büderich", :area_code => "2803") + AreaCode.create(:country => germany, :name => "Xanten-Marienbaum", :area_code => "2804") + AreaCode.create(:country => germany, :name => "Wesel", :area_code => "281") + AreaCode.create(:country => germany, :name => "Kleve Niederrhein", :area_code => "2821") + AreaCode.create(:country => germany, :name => "Emmerich", :area_code => "2822") + AreaCode.create(:country => germany, :name => "Goch", :area_code => "2823") + AreaCode.create(:country => germany, :name => "Kalkar", :area_code => "2824") + AreaCode.create(:country => germany, :name => "Uedem", :area_code => "2825") + AreaCode.create(:country => germany, :name => "Kranenburg Niederrhein", :area_code => "2826") + AreaCode.create(:country => germany, :name => "Goch-Hassum", :area_code => "2827") + AreaCode.create(:country => germany, :name => "Emmerich-Elten", :area_code => "2828") + AreaCode.create(:country => germany, :name => "Geldern", :area_code => "2831") + AreaCode.create(:country => germany, :name => "Kevelaer", :area_code => "2832") + AreaCode.create(:country => germany, :name => "Kerken", :area_code => "2833") + AreaCode.create(:country => germany, :name => "Straelen", :area_code => "2834") + AreaCode.create(:country => germany, :name => "Issum", :area_code => "2835") + AreaCode.create(:country => germany, :name => "Wachtendonk", :area_code => "2836") + AreaCode.create(:country => germany, :name => "Weeze", :area_code => "2837") + AreaCode.create(:country => germany, :name => "Sonsbeck", :area_code => "2838") + AreaCode.create(:country => germany, :name => "Straelen-Herongen", :area_code => "2839") + AreaCode.create(:country => germany, :name => "Moers", :area_code => "2841") + AreaCode.create(:country => germany, :name => "Kamp-Lintfort", :area_code => "2842") + AreaCode.create(:country => germany, :name => "Rheinberg", :area_code => "2843") + AreaCode.create(:country => germany, :name => "Rheinberg-Orsoy", :area_code => "2844") + AreaCode.create(:country => germany, :name => "Neukirchen-Vluyn", :area_code => "2845") + AreaCode.create(:country => germany, :name => "Rees-Haldern", :area_code => "2850") + AreaCode.create(:country => germany, :name => "Rees", :area_code => "2851") + AreaCode.create(:country => germany, :name => "Hamminkeln", :area_code => "2852") + AreaCode.create(:country => germany, :name => "Schermbeck", :area_code => "2853") + AreaCode.create(:country => germany, :name => "Voerde Niederrhein", :area_code => "2855") + AreaCode.create(:country => germany, :name => "Hamminkeln-Brünen", :area_code => "2856") + AreaCode.create(:country => germany, :name => "Rees-Mehr", :area_code => "2857") + AreaCode.create(:country => germany, :name => "Hünxe", :area_code => "2858") + AreaCode.create(:country => germany, :name => "Wesel-Bislich", :area_code => "2859") + AreaCode.create(:country => germany, :name => "Borken Westf", :area_code => "2861") + AreaCode.create(:country => germany, :name => "Südlohn", :area_code => "2862") + AreaCode.create(:country => germany, :name => "Velen", :area_code => "2863") + AreaCode.create(:country => germany, :name => "Reken", :area_code => "2864") + AreaCode.create(:country => germany, :name => "Raesfeld", :area_code => "2865") + AreaCode.create(:country => germany, :name => "Dorsten-Rhade", :area_code => "2866") + AreaCode.create(:country => germany, :name => "Heiden Kr Borken", :area_code => "2867") + AreaCode.create(:country => germany, :name => "Bocholt", :area_code => "2871") + AreaCode.create(:country => germany, :name => "Rhede Westf", :area_code => "2872") + AreaCode.create(:country => germany, :name => "Isselburg-Werth", :area_code => "2873") + AreaCode.create(:country => germany, :name => "Isselburg", :area_code => "2874") + AreaCode.create(:country => germany, :name => "Warstein", :area_code => "2902") + AreaCode.create(:country => germany, :name => "Meschede-Freienohl", :area_code => "2903") + AreaCode.create(:country => germany, :name => "Bestwig", :area_code => "2904") + AreaCode.create(:country => germany, :name => "Bestwig-Ramsbeck", :area_code => "2905") + AreaCode.create(:country => germany, :name => "Meschede", :area_code => "291") + AreaCode.create(:country => germany, :name => "Soest", :area_code => "2921") + AreaCode.create(:country => germany, :name => "Werl", :area_code => "2922") + AreaCode.create(:country => germany, :name => "Lippetal-Herzfeld", :area_code => "2923") + AreaCode.create(:country => germany, :name => "Möhnesee", :area_code => "2924") + AreaCode.create(:country => germany, :name => "Warstein-Allagen", :area_code => "2925") + AreaCode.create(:country => germany, :name => "Neuengeseke", :area_code => "2927") + AreaCode.create(:country => germany, :name => "Soest-Ostönnen", :area_code => "2928") + AreaCode.create(:country => germany, :name => "Arnsberg", :area_code => "2931") + AreaCode.create(:country => germany, :name => "Neheim-Hüsten", :area_code => "2932") + AreaCode.create(:country => germany, :name => "Sundern Sauerland", :area_code => "2933") + AreaCode.create(:country => germany, :name => "Sundern-Altenhellefeld", :area_code => "2934") + AreaCode.create(:country => germany, :name => "Sundern-Hachen", :area_code => "2935") + AreaCode.create(:country => germany, :name => "Arnsberg-Oeventrop", :area_code => "2937") + AreaCode.create(:country => germany, :name => "Ense", :area_code => "2938") + AreaCode.create(:country => germany, :name => "Lippstadt", :area_code => "2941") + AreaCode.create(:country => germany, :name => "Geseke", :area_code => "2942") + AreaCode.create(:country => germany, :name => "Erwitte", :area_code => "2943") + AreaCode.create(:country => germany, :name => "Rietberg-Mastholte", :area_code => "2944") + AreaCode.create(:country => germany, :name => "Lippstadt-Benninghausen", :area_code => "2945") + AreaCode.create(:country => germany, :name => "Anröchte", :area_code => "2947") + AreaCode.create(:country => germany, :name => "Lippstadt-Rebbeke", :area_code => "2948") + AreaCode.create(:country => germany, :name => "Büren", :area_code => "2951") + AreaCode.create(:country => germany, :name => "Rüthen", :area_code => "2952") + AreaCode.create(:country => germany, :name => "Wünnenberg", :area_code => "2953") + AreaCode.create(:country => germany, :name => "Rüthen-Oestereiden", :area_code => "2954") + AreaCode.create(:country => germany, :name => "Büren-Wewelsburg", :area_code => "2955") + AreaCode.create(:country => germany, :name => "Wünnenberg-Haaren", :area_code => "2957") + AreaCode.create(:country => germany, :name => "Büren-Harth", :area_code => "2958") + AreaCode.create(:country => germany, :name => "Brilon", :area_code => "2961") + AreaCode.create(:country => germany, :name => "Olsberg", :area_code => "2962") + AreaCode.create(:country => germany, :name => "Brilon-Messinghausen", :area_code => "2963") + AreaCode.create(:country => germany, :name => "Brilon-Alme", :area_code => "2964") + AreaCode.create(:country => germany, :name => "Schmallenberg-Dorlar", :area_code => "2971") + AreaCode.create(:country => germany, :name => "Schmallenberg", :area_code => "2972") + AreaCode.create(:country => germany, :name => "Eslohe Sauerland", :area_code => "2973") + AreaCode.create(:country => germany, :name => "Schmallenberg-Fredeburg", :area_code => "2974") + AreaCode.create(:country => germany, :name => "Schmallenberg-Oberkirchen", :area_code => "2975") + AreaCode.create(:country => germany, :name => "Schmallenberg-Bödefeld", :area_code => "2977") + AreaCode.create(:country => germany, :name => "Winterberg Westf", :area_code => "2981") + AreaCode.create(:country => germany, :name => "Medebach", :area_code => "2982") + AreaCode.create(:country => germany, :name => "Winterberg-Siedlinghausen", :area_code => "2983") + AreaCode.create(:country => germany, :name => "Hallenberg", :area_code => "2984") + AreaCode.create(:country => germany, :name => "Winterberg-Niedersfeld", :area_code => "2985") + AreaCode.create(:country => germany, :name => "Marsberg-Bredelar", :area_code => "2991") + AreaCode.create(:country => germany, :name => "Marsberg", :area_code => "2992") + AreaCode.create(:country => germany, :name => "Marsberg-Canstein", :area_code => "2993") + AreaCode.create(:country => germany, :name => "Marsberg-Westheim", :area_code => "2994") + AreaCode.create(:country => germany, :name => "Berlin", :area_code => "30") + AreaCode.create(:country => germany, :name => "Oranienburg", :area_code => "3301") + AreaCode.create(:country => germany, :name => "Hennigsdorf", :area_code => "3302") + AreaCode.create(:country => germany, :name => "Birkenwerder", :area_code => "3303") + AreaCode.create(:country => germany, :name => "Velten", :area_code => "3304") + AreaCode.create(:country => germany, :name => "Nassenheide", :area_code => "33051") + AreaCode.create(:country => germany, :name => "Zehlendorf Kr Oberhavel", :area_code => "33053") + AreaCode.create(:country => germany, :name => "Liebenwalde", :area_code => "33054") + AreaCode.create(:country => germany, :name => "Kremmen", :area_code => "33055") + AreaCode.create(:country => germany, :name => "Mühlenbeck Kr Oberhavel", :area_code => "33056") + AreaCode.create(:country => germany, :name => "Gransee", :area_code => "3306") + AreaCode.create(:country => germany, :name => "Zehdenick", :area_code => "3307") + AreaCode.create(:country => germany, :name => "Marienthal Kr Oberhavel", :area_code => "33080") + AreaCode.create(:country => germany, :name => "Menz Kr Oberhavel", :area_code => "33082") + AreaCode.create(:country => germany, :name => "Schulzendorf Kr Oberhavel", :area_code => "33083") + AreaCode.create(:country => germany, :name => "Gutengermendorf", :area_code => "33084") + AreaCode.create(:country => germany, :name => "Seilershof", :area_code => "33085") + AreaCode.create(:country => germany, :name => "Grieben Kr Oberhavel", :area_code => "33086") + AreaCode.create(:country => germany, :name => "Bredereiche", :area_code => "33087") + AreaCode.create(:country => germany, :name => "Falkenthal", :area_code => "33088") + AreaCode.create(:country => germany, :name => "Himmelpfort", :area_code => "33089") + AreaCode.create(:country => germany, :name => "Fürstenberg Havel", :area_code => "33093") + AreaCode.create(:country => germany, :name => "Löwenberg", :area_code => "33094") + AreaCode.create(:country => germany, :name => "Potsdam", :area_code => "331") + AreaCode.create(:country => germany, :name => "Bergholz-Rehbrücke", :area_code => "33200") + AreaCode.create(:country => germany, :name => "Gross Glienicke", :area_code => "33201") + AreaCode.create(:country => germany, :name => "Töplitz", :area_code => "33202") + AreaCode.create(:country => germany, :name => "Kleinmachnow", :area_code => "33203") + AreaCode.create(:country => germany, :name => "Beelitz Mark", :area_code => "33204") + AreaCode.create(:country => germany, :name => "Michendorf", :area_code => "33205") + AreaCode.create(:country => germany, :name => "Fichtenwalde", :area_code => "33206") + AreaCode.create(:country => germany, :name => "Gross Kreutz", :area_code => "33207") + AreaCode.create(:country => germany, :name => "Fahrland", :area_code => "33208") + AreaCode.create(:country => germany, :name => "Caputh", :area_code => "33209") + AreaCode.create(:country => germany, :name => "Nauen Brandenb", :area_code => "3321") + AreaCode.create(:country => germany, :name => "Falkensee", :area_code => "3322") + AreaCode.create(:country => germany, :name => "Börnicke Kr Havelland", :area_code => "33230") + AreaCode.create(:country => germany, :name => "Pausin", :area_code => "33231") + AreaCode.create(:country => germany, :name => "Brieselang", :area_code => "33232") + AreaCode.create(:country => germany, :name => "Ketzin", :area_code => "33233") + AreaCode.create(:country => germany, :name => "Wustermark", :area_code => "33234") + AreaCode.create(:country => germany, :name => "Friesack", :area_code => "33235") + AreaCode.create(:country => germany, :name => "Paulinenaue", :area_code => "33237") + AreaCode.create(:country => germany, :name => "Senzke", :area_code => "33238") + AreaCode.create(:country => germany, :name => "Gross Behnitz", :area_code => "33239") + AreaCode.create(:country => germany, :name => "Werder Havel", :area_code => "3327") + AreaCode.create(:country => germany, :name => "Teltow", :area_code => "3328") + AreaCode.create(:country => germany, :name => "Stahnsdorf", :area_code => "3329") + AreaCode.create(:country => germany, :name => "Angermünde", :area_code => "3331") + AreaCode.create(:country => germany, :name => "Schwedt/Oder", :area_code => "3332") + AreaCode.create(:country => germany, :name => "Casekow", :area_code => "33331") + AreaCode.create(:country => germany, :name => "Gartz Oder", :area_code => "33332") + AreaCode.create(:country => germany, :name => "Tantow", :area_code => "33333") + AreaCode.create(:country => germany, :name => "Greiffenberg", :area_code => "33334") + AreaCode.create(:country => germany, :name => "Pinnow Kr Uckermark", :area_code => "33335") + AreaCode.create(:country => germany, :name => "Passow Kr Uckermark", :area_code => "33336") + AreaCode.create(:country => germany, :name => "Altkünkendorf", :area_code => "33337") + AreaCode.create(:country => germany, :name => "Stolpe/Oder", :area_code => "33338") + AreaCode.create(:country => germany, :name => "Eberswalde", :area_code => "3334") + AreaCode.create(:country => germany, :name => "Finowfurt", :area_code => "3335") + AreaCode.create(:country => germany, :name => "Joachimsthal", :area_code => "33361") + AreaCode.create(:country => germany, :name => "Liepe Kr Barnim", :area_code => "33362") + AreaCode.create(:country => germany, :name => "Altenhof Kr Barnim", :area_code => "33363") + AreaCode.create(:country => germany, :name => "Gross Ziethen Kr Barnim", :area_code => "33364") + AreaCode.create(:country => germany, :name => "Lüdersdorf Kr Barnim", :area_code => "33365") + AreaCode.create(:country => germany, :name => "Chorin", :area_code => "33366") + AreaCode.create(:country => germany, :name => "Friedrichswalde Brandenb", :area_code => "33367") + AreaCode.create(:country => germany, :name => "Hohensaaten", :area_code => "33368") + AreaCode.create(:country => germany, :name => "Oderberg", :area_code => "33369") + AreaCode.create(:country => germany, :name => "Biesenthal Brandenb", :area_code => "3337") + AreaCode.create(:country => germany, :name => "Bernau Brandenb", :area_code => "3338") + AreaCode.create(:country => germany, :name => "Gross Schönebeck Kr Barnim", :area_code => "33393") + AreaCode.create(:country => germany, :name => "Blumberg Kr Barnim", :area_code => "33394") + AreaCode.create(:country => germany, :name => "Zerpenschleuse", :area_code => "33395") + AreaCode.create(:country => germany, :name => "Klosterfelde", :area_code => "33396") + AreaCode.create(:country => germany, :name => "Wandlitz", :area_code => "33397") + AreaCode.create(:country => germany, :name => "Werneuchen", :area_code => "33398") + AreaCode.create(:country => germany, :name => "Strausberg", :area_code => "3341") + AreaCode.create(:country => germany, :name => "Neuenhagen b Berlin", :area_code => "3342") + AreaCode.create(:country => germany, :name => "Müncheberg", :area_code => "33432") + AreaCode.create(:country => germany, :name => "Buckow Märk Schweiz", :area_code => "33433") + AreaCode.create(:country => germany, :name => "Herzfelde b Strausberg", :area_code => "33434") + AreaCode.create(:country => germany, :name => "Rehfelde", :area_code => "33435") + AreaCode.create(:country => germany, :name => "Prötzel", :area_code => "33436") + AreaCode.create(:country => germany, :name => "Reichenberg b Strausberg", :area_code => "33437") + AreaCode.create(:country => germany, :name => "Altlandsberg", :area_code => "33438") + AreaCode.create(:country => germany, :name => "Fredersdorf-Vogelsdorf", :area_code => "33439") + AreaCode.create(:country => germany, :name => "Bad Freienwalde", :area_code => "3344") + AreaCode.create(:country => germany, :name => "Heckelberg", :area_code => "33451") + AreaCode.create(:country => germany, :name => "Neulewin", :area_code => "33452") + AreaCode.create(:country => germany, :name => "Wölsickendorf/Wollenberg", :area_code => "33454") + AreaCode.create(:country => germany, :name => "Wriezen", :area_code => "33456") + AreaCode.create(:country => germany, :name => "Altreetz", :area_code => "33457") + AreaCode.create(:country => germany, :name => "Falkenberg Mark", :area_code => "33458") + AreaCode.create(:country => germany, :name => "Seelow", :area_code => "3346") + AreaCode.create(:country => germany, :name => "Lietzen", :area_code => "33470") + AreaCode.create(:country => germany, :name => "Golzow b Seelow", :area_code => "33472") + AreaCode.create(:country => germany, :name => "Zechin", :area_code => "33473") + AreaCode.create(:country => germany, :name => "Neutrebbin", :area_code => "33474") + AreaCode.create(:country => germany, :name => "Letschin", :area_code => "33475") + AreaCode.create(:country => germany, :name => "Neuhardenberg", :area_code => "33476") + AreaCode.create(:country => germany, :name => "Trebnitz b Müncheberg", :area_code => "33477") + AreaCode.create(:country => germany, :name => "Gross Neuendorf", :area_code => "33478") + AreaCode.create(:country => germany, :name => "Küstrin-Kietz", :area_code => "33479") + AreaCode.create(:country => germany, :name => "Frankfurt (Oder)", :area_code => "335") + AreaCode.create(:country => germany, :name => "Podelzig", :area_code => "33601") + AreaCode.create(:country => germany, :name => "Alt Zeschdorf", :area_code => "33602") + AreaCode.create(:country => germany, :name => "Falkenhagen b Seelow", :area_code => "33603") + AreaCode.create(:country => germany, :name => "Lebus", :area_code => "33604") + AreaCode.create(:country => germany, :name => "Boossen", :area_code => "33605") + AreaCode.create(:country => germany, :name => "Müllrose", :area_code => "33606") + AreaCode.create(:country => germany, :name => "Briesen Mark", :area_code => "33607") + AreaCode.create(:country => germany, :name => "Jacobsdorf Mark", :area_code => "33608") + AreaCode.create(:country => germany, :name => "Brieskow-Finkenheerd", :area_code => "33609") + AreaCode.create(:country => germany, :name => "Fürstenwalde Spree", :area_code => "3361") + AreaCode.create(:country => germany, :name => "Erkner", :area_code => "3362") + AreaCode.create(:country => germany, :name => "Bad Saarow-Pieskow", :area_code => "33631") + AreaCode.create(:country => germany, :name => "Hangelsberg", :area_code => "33632") + AreaCode.create(:country => germany, :name => "Spreenhagen", :area_code => "33633") + AreaCode.create(:country => germany, :name => "Berkenbrück Kr Oder-Spree", :area_code => "33634") + AreaCode.create(:country => germany, :name => "Arensdorf Kr Oder-Spree", :area_code => "33635") + AreaCode.create(:country => germany, :name => "Steinhöfel Kr Oder-Spree", :area_code => "33636") + AreaCode.create(:country => germany, :name => "Beerfelde", :area_code => "33637") + AreaCode.create(:country => germany, :name => "Rüdersdorf b Berlin", :area_code => "33638") + AreaCode.create(:country => germany, :name => "Eisenhüttenstadt", :area_code => "3364") + AreaCode.create(:country => germany, :name => "Neuzelle", :area_code => "33652") + AreaCode.create(:country => germany, :name => "Ziltendorf", :area_code => "33653") + AreaCode.create(:country => germany, :name => "Fünfeichen", :area_code => "33654") + AreaCode.create(:country => germany, :name => "Grunow Kr Oder-Spree", :area_code => "33655") + AreaCode.create(:country => germany, :name => "Bahro", :area_code => "33656") + AreaCode.create(:country => germany, :name => "Steinsdorf Brandenb", :area_code => "33657") + AreaCode.create(:country => germany, :name => "Beeskow", :area_code => "3366") + AreaCode.create(:country => germany, :name => "Lieberose", :area_code => "33671") + AreaCode.create(:country => germany, :name => "Pfaffendorf b Beeskow", :area_code => "33672") + AreaCode.create(:country => germany, :name => "Weichensdorf", :area_code => "33673") + AreaCode.create(:country => germany, :name => "Trebatsch", :area_code => "33674") + AreaCode.create(:country => germany, :name => "Tauche", :area_code => "33675") + AreaCode.create(:country => germany, :name => "Friedland b Beeskow", :area_code => "33676") + AreaCode.create(:country => germany, :name => "Glienicke b Beeskow", :area_code => "33677") + AreaCode.create(:country => germany, :name => "Storkow Mark", :area_code => "33678") + AreaCode.create(:country => germany, :name => "Wendisch Rietz", :area_code => "33679") + AreaCode.create(:country => germany, :name => "Grossbeeren", :area_code => "33701") + AreaCode.create(:country => germany, :name => "Wünsdorf", :area_code => "33702") + AreaCode.create(:country => germany, :name => "Sperenberg", :area_code => "33703") + AreaCode.create(:country => germany, :name => "Baruth Mark", :area_code => "33704") + AreaCode.create(:country => germany, :name => "Rangsdorf", :area_code => "33708") + AreaCode.create(:country => germany, :name => "Luckenwalde", :area_code => "3371") + AreaCode.create(:country => germany, :name => "Jüterbog", :area_code => "3372") + AreaCode.create(:country => germany, :name => "Trebbin", :area_code => "33731") + AreaCode.create(:country => germany, :name => "Hennickendorf b Luckenwalde", :area_code => "33732") + AreaCode.create(:country => germany, :name => "Stülpe", :area_code => "33733") + AreaCode.create(:country => germany, :name => "Felgentreu", :area_code => "33734") + AreaCode.create(:country => germany, :name => "Niedergörsdorf", :area_code => "33741") + AreaCode.create(:country => germany, :name => "Oehna Brandenb", :area_code => "33742") + AreaCode.create(:country => germany, :name => "Blönsdorf", :area_code => "33743") + AreaCode.create(:country => germany, :name => "Hohenseefeld", :area_code => "33744") + AreaCode.create(:country => germany, :name => "Petkus", :area_code => "33745") + AreaCode.create(:country => germany, :name => "Werbig b Jüterbog", :area_code => "33746") + AreaCode.create(:country => germany, :name => "Marzahna", :area_code => "33747") + AreaCode.create(:country => germany, :name => "Treuenbrietzen", :area_code => "33748") + AreaCode.create(:country => germany, :name => "Königs Wusterhausen", :area_code => "3375") + AreaCode.create(:country => germany, :name => "Münchehofe Kr Dahme-Spreewald", :area_code => "33760") + AreaCode.create(:country => germany, :name => "Zeuthen", :area_code => "33762") + AreaCode.create(:country => germany, :name => "Bestensee", :area_code => "33763") + AreaCode.create(:country => germany, :name => "Mittenwalde Mark", :area_code => "33764") + AreaCode.create(:country => germany, :name => "Märkisch Buchholz", :area_code => "33765") + AreaCode.create(:country => germany, :name => "Teupitz", :area_code => "33766") + AreaCode.create(:country => germany, :name => "Friedersdorf b Berlin", :area_code => "33767") + AreaCode.create(:country => germany, :name => "Prieros", :area_code => "33768") + AreaCode.create(:country => germany, :name => "Töpchin", :area_code => "33769") + AreaCode.create(:country => germany, :name => "Zossen Brandenb", :area_code => "3377") + AreaCode.create(:country => germany, :name => "Ludwigsfelde", :area_code => "3378") + AreaCode.create(:country => germany, :name => "Mahlow", :area_code => "3379") + AreaCode.create(:country => germany, :name => "Brandenburg an der Havel", :area_code => "3381") + AreaCode.create(:country => germany, :name => "Lehnin", :area_code => "3382") + AreaCode.create(:country => germany, :name => "Ziesar", :area_code => "33830") + AreaCode.create(:country => germany, :name => "Weseram", :area_code => "33831") + AreaCode.create(:country => germany, :name => "Rogäsen", :area_code => "33832") + AreaCode.create(:country => germany, :name => "Wollin b Brandenburg", :area_code => "33833") + AreaCode.create(:country => germany, :name => "Pritzerbe", :area_code => "33834") + AreaCode.create(:country => germany, :name => "Golzow b Brandenburg", :area_code => "33835") + AreaCode.create(:country => germany, :name => "Butzow b Brandenburg", :area_code => "33836") + AreaCode.create(:country => germany, :name => "Brielow", :area_code => "33837") + AreaCode.create(:country => germany, :name => "Päwesin", :area_code => "33838") + AreaCode.create(:country => germany, :name => "Wusterwitz", :area_code => "33839") + AreaCode.create(:country => germany, :name => "Belzig", :area_code => "33841") + AreaCode.create(:country => germany, :name => "Niemegk", :area_code => "33843") + AreaCode.create(:country => germany, :name => "Brück Brandenb", :area_code => "33844") + AreaCode.create(:country => germany, :name => "Borkheide", :area_code => "33845") + AreaCode.create(:country => germany, :name => "Dippmannsdorf", :area_code => "33846") + AreaCode.create(:country => germany, :name => "Görzke", :area_code => "33847") + AreaCode.create(:country => germany, :name => "Raben", :area_code => "33848") + AreaCode.create(:country => germany, :name => "Wiesenburg Mark", :area_code => "33849") + AreaCode.create(:country => germany, :name => "Rathenow", :area_code => "3385") + AreaCode.create(:country => germany, :name => "Premnitz", :area_code => "3386") + AreaCode.create(:country => germany, :name => "Zollchow b Rathenow", :area_code => "33870") + AreaCode.create(:country => germany, :name => "Hohennauen", :area_code => "33872") + AreaCode.create(:country => germany, :name => "Grosswudicke", :area_code => "33873") + AreaCode.create(:country => germany, :name => "Stechow Brandenb", :area_code => "33874") + AreaCode.create(:country => germany, :name => "Rhinow", :area_code => "33875") + AreaCode.create(:country => germany, :name => "Buschow", :area_code => "33876") + AreaCode.create(:country => germany, :name => "Nitzahn", :area_code => "33877") + AreaCode.create(:country => germany, :name => "Nennhausen", :area_code => "33878") + AreaCode.create(:country => germany, :name => "Neuruppin", :area_code => "3391") + AreaCode.create(:country => germany, :name => "Walsleben b Neuruppin", :area_code => "33920") + AreaCode.create(:country => germany, :name => "Zechlinerhütte", :area_code => "33921") + AreaCode.create(:country => germany, :name => "Karwesee", :area_code => "33922") + AreaCode.create(:country => germany, :name => "Flecken Zechlin", :area_code => "33923") + AreaCode.create(:country => germany, :name => "Rägelin", :area_code => "33924") + AreaCode.create(:country => germany, :name => "Wustrau-Altfriesack", :area_code => "33925") + AreaCode.create(:country => germany, :name => "Herzberg Mark", :area_code => "33926") + AreaCode.create(:country => germany, :name => "Wildberg Brandenb", :area_code => "33928") + AreaCode.create(:country => germany, :name => "Gühlen-Glienicke", :area_code => "33929") + AreaCode.create(:country => germany, :name => "Rheinsberg Mark", :area_code => "33931") + AreaCode.create(:country => germany, :name => "Fehrbellin", :area_code => "33932") + AreaCode.create(:country => germany, :name => "Lindow Mark", :area_code => "33933") + AreaCode.create(:country => germany, :name => "Wittstock Dosse", :area_code => "3394") + AreaCode.create(:country => germany, :name => "Pritzwalk", :area_code => "3395") + AreaCode.create(:country => germany, :name => "Heiligengrabe", :area_code => "33962") + AreaCode.create(:country => germany, :name => "Wulfersdorf b Wittstock", :area_code => "33963") + AreaCode.create(:country => germany, :name => "Fretzdorf", :area_code => "33964") + AreaCode.create(:country => germany, :name => "Herzsprung b Wittstock", :area_code => "33965") + AreaCode.create(:country => germany, :name => "Dranse", :area_code => "33966") + AreaCode.create(:country => germany, :name => "Freyenstein", :area_code => "33967") + AreaCode.create(:country => germany, :name => "Meyenburg Kr Prignitz", :area_code => "33968") + AreaCode.create(:country => germany, :name => "Stepenitz", :area_code => "33969") + AreaCode.create(:country => germany, :name => "Neustadt Dosse", :area_code => "33970") + AreaCode.create(:country => germany, :name => "Kyritz Brandenb", :area_code => "33971") + AreaCode.create(:country => germany, :name => "Breddin", :area_code => "33972") + AreaCode.create(:country => germany, :name => "Zernitz b Neustadt Dosse", :area_code => "33973") + AreaCode.create(:country => germany, :name => "Dessow", :area_code => "33974") + AreaCode.create(:country => germany, :name => "Dannenwalde Kr Prignitz", :area_code => "33975") + AreaCode.create(:country => germany, :name => "Wutike", :area_code => "33976") + AreaCode.create(:country => germany, :name => "Gumtow", :area_code => "33977") + AreaCode.create(:country => germany, :name => "Segeletz", :area_code => "33978") + AreaCode.create(:country => germany, :name => "Wusterhausen Dosse", :area_code => "33979") + AreaCode.create(:country => germany, :name => "Putlitz", :area_code => "33981") + AreaCode.create(:country => germany, :name => "Hoppenrade Kr Prignitz", :area_code => "33982") + AreaCode.create(:country => germany, :name => "Gross Pankow Kr Prignitz", :area_code => "33983") + AreaCode.create(:country => germany, :name => "Blumenthal b Pritzwalk", :area_code => "33984") + AreaCode.create(:country => germany, :name => "Falkenhagen Kr Prignitz", :area_code => "33986") + AreaCode.create(:country => germany, :name => "Sadenbeck", :area_code => "33989") + AreaCode.create(:country => germany, :name => "Dessau Anh", :area_code => "340") + AreaCode.create(:country => germany, :name => "Leipzig", :area_code => "341") + AreaCode.create(:country => germany, :name => "Delitzsch", :area_code => "34202") + AreaCode.create(:country => germany, :name => "Zwenkau", :area_code => "34203") + AreaCode.create(:country => germany, :name => "Schkeuditz", :area_code => "34204") + AreaCode.create(:country => germany, :name => "Markranstädt", :area_code => "34205") + AreaCode.create(:country => germany, :name => "Rötha", :area_code => "34206") + AreaCode.create(:country => germany, :name => "Zwochau", :area_code => "34207") + AreaCode.create(:country => germany, :name => "Löbnitz b Delitzsch", :area_code => "34208") + AreaCode.create(:country => germany, :name => "Torgau", :area_code => "3421") + AreaCode.create(:country => germany, :name => "Schildau Gneisenaustadt", :area_code => "34221") + AreaCode.create(:country => germany, :name => "Arzberg b Torgau", :area_code => "34222") + AreaCode.create(:country => germany, :name => "Dommitzsch", :area_code => "34223") + AreaCode.create(:country => germany, :name => "Belgern Sachs", :area_code => "34224") + AreaCode.create(:country => germany, :name => "Eilenburg", :area_code => "3423") + AreaCode.create(:country => germany, :name => "Jesewitz", :area_code => "34241") + AreaCode.create(:country => germany, :name => "Hohenpriessnitz", :area_code => "34242") + AreaCode.create(:country => germany, :name => "Bad Düben", :area_code => "34243") + AreaCode.create(:country => germany, :name => "Mockrehna", :area_code => "34244") + AreaCode.create(:country => germany, :name => "Wurzen", :area_code => "3425") + AreaCode.create(:country => germany, :name => "Kühren b Wurzen", :area_code => "34261") + AreaCode.create(:country => germany, :name => "Falkenhain b Wurzen", :area_code => "34262") + AreaCode.create(:country => germany, :name => "Hohburg", :area_code => "34263") + AreaCode.create(:country => germany, :name => "Borsdorf", :area_code => "34291") + AreaCode.create(:country => germany, :name => "Brandis b Wurzen", :area_code => "34292") + AreaCode.create(:country => germany, :name => "Naunhof b Grimma", :area_code => "34293") + AreaCode.create(:country => germany, :name => "Rackwitz", :area_code => "34294") + AreaCode.create(:country => germany, :name => "Krensitz", :area_code => "34295") + AreaCode.create(:country => germany, :name => "Groitzsch b Pegau", :area_code => "34296") + AreaCode.create(:country => germany, :name => "Liebertwolkwitz", :area_code => "34297") + AreaCode.create(:country => germany, :name => "Taucha b Leipzig", :area_code => "34298") + AreaCode.create(:country => germany, :name => "Gaschwitz", :area_code => "34299") + AreaCode.create(:country => germany, :name => "Döbeln", :area_code => "3431") + AreaCode.create(:country => germany, :name => "Leisnig", :area_code => "34321") + AreaCode.create(:country => germany, :name => "Rosswein", :area_code => "34322") + AreaCode.create(:country => germany, :name => "Ostrau Sachs", :area_code => "34324") + AreaCode.create(:country => germany, :name => "Mochau-Lüttewitz", :area_code => "34325") + AreaCode.create(:country => germany, :name => "Waldheim Sachs", :area_code => "34327") + AreaCode.create(:country => germany, :name => "Hartha b Döbeln", :area_code => "34328") + AreaCode.create(:country => germany, :name => "Borna Stadt", :area_code => "3433") + AreaCode.create(:country => germany, :name => "Geithain", :area_code => "34341") + AreaCode.create(:country => germany, :name => "Neukieritzsch", :area_code => "34342") + AreaCode.create(:country => germany, :name => "Regis-Breitingen", :area_code => "34343") + AreaCode.create(:country => germany, :name => "Kohren-Sahlis", :area_code => "34344") + AreaCode.create(:country => germany, :name => "Bad Lausick", :area_code => "34345") + AreaCode.create(:country => germany, :name => "Narsdorf", :area_code => "34346") + AreaCode.create(:country => germany, :name => "Oelzschau b Borna", :area_code => "34347") + AreaCode.create(:country => germany, :name => "Frohburg", :area_code => "34348") + AreaCode.create(:country => germany, :name => "Oschatz", :area_code => "3435") + AreaCode.create(:country => germany, :name => "Dahlen Sachs", :area_code => "34361") + AreaCode.create(:country => germany, :name => "Mügeln b Oschatz", :area_code => "34362") + AreaCode.create(:country => germany, :name => "Cavertitz", :area_code => "34363") + AreaCode.create(:country => germany, :name => "Wermsdorf", :area_code => "34364") + AreaCode.create(:country => germany, :name => "Grimma", :area_code => "3437") + AreaCode.create(:country => germany, :name => "Colditz", :area_code => "34381") + AreaCode.create(:country => germany, :name => "Nerchau", :area_code => "34382") + AreaCode.create(:country => germany, :name => "Trebsen Mulde", :area_code => "34383") + AreaCode.create(:country => germany, :name => "Grossbothen", :area_code => "34384") + AreaCode.create(:country => germany, :name => "Mutzschen", :area_code => "34385") + AreaCode.create(:country => germany, :name => "Dürrweitzschen b Grimma", :area_code => "34386") + AreaCode.create(:country => germany, :name => "Zeitz", :area_code => "3441") + AreaCode.create(:country => germany, :name => "Osterfeld", :area_code => "34422") + AreaCode.create(:country => germany, :name => "Heuckewalde", :area_code => "34423") + AreaCode.create(:country => germany, :name => "Reuden b Zeitz", :area_code => "34424") + AreaCode.create(:country => germany, :name => "Droyssig", :area_code => "34425") + AreaCode.create(:country => germany, :name => "Kayna", :area_code => "34426") + AreaCode.create(:country => germany, :name => "Weissenfels Sachs-Anh", :area_code => "3443") + AreaCode.create(:country => germany, :name => "Hohenmölsen", :area_code => "34441") + AreaCode.create(:country => germany, :name => "Teuchern", :area_code => "34443") + AreaCode.create(:country => germany, :name => "Lützen", :area_code => "34444") + AreaCode.create(:country => germany, :name => "Stößen", :area_code => "34445") + AreaCode.create(:country => germany, :name => "Grosskorbetha", :area_code => "34446") + AreaCode.create(:country => germany, :name => "Naumburg Saale", :area_code => "3445") + AreaCode.create(:country => germany, :name => "Nebra Unstrut", :area_code => "34461") + AreaCode.create(:country => germany, :name => "Laucha Unstrut", :area_code => "34462") + AreaCode.create(:country => germany, :name => "Bad Kösen", :area_code => "34463") + AreaCode.create(:country => germany, :name => "Freyburg Unstrut", :area_code => "34464") + AreaCode.create(:country => germany, :name => "Bad Bibra", :area_code => "34465") + AreaCode.create(:country => germany, :name => "Janisroda", :area_code => "34466") + AreaCode.create(:country => germany, :name => "Eckartsberga", :area_code => "34467") + AreaCode.create(:country => germany, :name => "Altenburg Thür", :area_code => "3447") + AreaCode.create(:country => germany, :name => "Meuselwitz Thür", :area_code => "3448") + AreaCode.create(:country => germany, :name => "Schmölln Thür", :area_code => "34491") + AreaCode.create(:country => germany, :name => "Lucka", :area_code => "34492") + AreaCode.create(:country => germany, :name => "Gößnitz Thür", :area_code => "34493") + AreaCode.create(:country => germany, :name => "Ehrenhain", :area_code => "34494") + AreaCode.create(:country => germany, :name => "Dobitschen", :area_code => "34495") + AreaCode.create(:country => germany, :name => "Nöbdenitz", :area_code => "34496") + AreaCode.create(:country => germany, :name => "Langenleuba-Niederhain", :area_code => "34497") + AreaCode.create(:country => germany, :name => "Rositz", :area_code => "34498") + AreaCode.create(:country => germany, :name => "Halle Saale", :area_code => "345") + AreaCode.create(:country => germany, :name => "Ostrau Saalkreis", :area_code => "34600") + AreaCode.create(:country => germany, :name => "Teutschenthal", :area_code => "34601") + AreaCode.create(:country => germany, :name => "Landsberg Sachs-Anh", :area_code => "34602") + AreaCode.create(:country => germany, :name => "Nauendorf Sachs-Anh", :area_code => "34603") + AreaCode.create(:country => germany, :name => "Niemberg", :area_code => "34604") + AreaCode.create(:country => germany, :name => "Gröbers", :area_code => "34605") + AreaCode.create(:country => germany, :name => "Teicha Sachs-Anh", :area_code => "34606") + AreaCode.create(:country => germany, :name => "Wettin", :area_code => "34607") + AreaCode.create(:country => germany, :name => "Salzmünde", :area_code => "34609") + AreaCode.create(:country => germany, :name => "Merseburg Saale", :area_code => "3461") + AreaCode.create(:country => germany, :name => "Bad Dürrenberg", :area_code => "3462") + AreaCode.create(:country => germany, :name => "Mücheln Geiseltal", :area_code => "34632") + AreaCode.create(:country => germany, :name => "Braunsbedra", :area_code => "34633") + AreaCode.create(:country => germany, :name => "Bad Lauchstädt", :area_code => "34635") + AreaCode.create(:country => germany, :name => "Schafstädt", :area_code => "34636") + AreaCode.create(:country => germany, :name => "Frankleben", :area_code => "34637") + AreaCode.create(:country => germany, :name => "Zöschen", :area_code => "34638") + AreaCode.create(:country => germany, :name => "Wallendorf Luppe", :area_code => "34639") + AreaCode.create(:country => germany, :name => "Sangerhausen", :area_code => "3464") + AreaCode.create(:country => germany, :name => "Rossla", :area_code => "34651") + AreaCode.create(:country => germany, :name => "Allstedt", :area_code => "34652") + AreaCode.create(:country => germany, :name => "Rottleberode", :area_code => "34653") + AreaCode.create(:country => germany, :name => "Stolberg Harz", :area_code => "34654") + AreaCode.create(:country => germany, :name => "Wallhausen Sachs-Anh", :area_code => "34656") + AreaCode.create(:country => germany, :name => "Hayn Harz", :area_code => "34658") + AreaCode.create(:country => germany, :name => "Blankenheim b Sangerhausen", :area_code => "34659") + AreaCode.create(:country => germany, :name => "Artern Unstrut", :area_code => "3466") + AreaCode.create(:country => germany, :name => "Bad Frankenhausen Kyffhäuser", :area_code => "34671") + AreaCode.create(:country => germany, :name => "Rossleben", :area_code => "34672") + AreaCode.create(:country => germany, :name => "Heldrungen", :area_code => "34673") + AreaCode.create(:country => germany, :name => "Könnern", :area_code => "34691") + AreaCode.create(:country => germany, :name => "Alsleben Saale", :area_code => "34692") + AreaCode.create(:country => germany, :name => "Bernburg Saale", :area_code => "3471") + AreaCode.create(:country => germany, :name => "Nienburg Saale", :area_code => "34721") + AreaCode.create(:country => germany, :name => "Preusslitz", :area_code => "34722") + AreaCode.create(:country => germany, :name => "Aschersleben Sachs-Anh", :area_code => "3473") + AreaCode.create(:country => germany, :name => "Frose", :area_code => "34741") + AreaCode.create(:country => germany, :name => "Sylda", :area_code => "34742") + AreaCode.create(:country => germany, :name => "Ermsleben", :area_code => "34743") + AreaCode.create(:country => germany, :name => "Winningen Sachs-Anh", :area_code => "34745") + AreaCode.create(:country => germany, :name => "Giersleben", :area_code => "34746") + AreaCode.create(:country => germany, :name => "Lutherstadt Eisleben", :area_code => "3475") + AreaCode.create(:country => germany, :name => "Hettstedt Sachs-Anh", :area_code => "3476") + AreaCode.create(:country => germany, :name => "Querfurt", :area_code => "34771") + AreaCode.create(:country => germany, :name => "Helbra", :area_code => "34772") + AreaCode.create(:country => germany, :name => "Schwittersdorf", :area_code => "34773") + AreaCode.create(:country => germany, :name => "Röblingen am See", :area_code => "34774") + AreaCode.create(:country => germany, :name => "Wippra", :area_code => "34775") + AreaCode.create(:country => germany, :name => "Rothenschirmbach", :area_code => "34776") + AreaCode.create(:country => germany, :name => "Abberode", :area_code => "34779") + AreaCode.create(:country => germany, :name => "Greifenhagen", :area_code => "34781") + AreaCode.create(:country => germany, :name => "Mansfeld Südharz", :area_code => "34782") + AreaCode.create(:country => germany, :name => "Gerbstedt", :area_code => "34783") + AreaCode.create(:country => germany, :name => "Sandersleben", :area_code => "34785") + AreaCode.create(:country => germany, :name => "Roßlau Elbe", :area_code => "34901") + AreaCode.create(:country => germany, :name => "Coswig Anhalt", :area_code => "34903") + AreaCode.create(:country => germany, :name => "Oranienbaum", :area_code => "34904") + AreaCode.create(:country => germany, :name => "Wörlitz", :area_code => "34905") + AreaCode.create(:country => germany, :name => "Raguhn", :area_code => "34906") + AreaCode.create(:country => germany, :name => "Jeber-Bergfrieden", :area_code => "34907") + AreaCode.create(:country => germany, :name => "Aken Elbe", :area_code => "34909") + AreaCode.create(:country => germany, :name => "Lutherstadt Wittenberg", :area_code => "3491") + AreaCode.create(:country => germany, :name => "Kropstädt", :area_code => "34920") + AreaCode.create(:country => germany, :name => "Kemberg", :area_code => "34921") + AreaCode.create(:country => germany, :name => "Mühlanger", :area_code => "34922") + AreaCode.create(:country => germany, :name => "Cobbelsdorf", :area_code => "34923") + AreaCode.create(:country => germany, :name => "Zahna", :area_code => "34924") + AreaCode.create(:country => germany, :name => "Bad Schmiedeberg", :area_code => "34925") + AreaCode.create(:country => germany, :name => "Pretzsch Elbe", :area_code => "34926") + AreaCode.create(:country => germany, :name => "Globig-Bleddin", :area_code => "34927") + AreaCode.create(:country => germany, :name => "Seegrehna", :area_code => "34928") + AreaCode.create(:country => germany, :name => "Straach", :area_code => "34929") + AreaCode.create(:country => germany, :name => "Bitterfeld", :area_code => "3493") + AreaCode.create(:country => germany, :name => "Wolfen", :area_code => "3494") + AreaCode.create(:country => germany, :name => "Gräfenhainichen", :area_code => "34953") + AreaCode.create(:country => germany, :name => "Roitzsch b Bitterfeld", :area_code => "34954") + AreaCode.create(:country => germany, :name => "Gossa", :area_code => "34955") + AreaCode.create(:country => germany, :name => "Zörbig", :area_code => "34956") + AreaCode.create(:country => germany, :name => "Köthen Anhalt", :area_code => "3496") + AreaCode.create(:country => germany, :name => "Osternienburg", :area_code => "34973") + AreaCode.create(:country => germany, :name => "Görzig Kr Köthen", :area_code => "34975") + AreaCode.create(:country => germany, :name => "Gröbzig", :area_code => "34976") + AreaCode.create(:country => germany, :name => "Quellendorf", :area_code => "34977") + AreaCode.create(:country => germany, :name => "Radegast Kr Köthen", :area_code => "34978") + AreaCode.create(:country => germany, :name => "Wulfen Sachs-Anh", :area_code => "34979") + AreaCode.create(:country => germany, :name => "Pirna", :area_code => "3501") + AreaCode.create(:country => germany, :name => "Struppen", :area_code => "35020") + AreaCode.create(:country => germany, :name => "Königstein Sächs Schweiz", :area_code => "35021") + AreaCode.create(:country => germany, :name => "Bad Schandau", :area_code => "35022") + AreaCode.create(:country => germany, :name => "Bad Gottleuba", :area_code => "35023") + AreaCode.create(:country => germany, :name => "Stadt Wehlen", :area_code => "35024") + AreaCode.create(:country => germany, :name => "Liebstadt", :area_code => "35025") + AreaCode.create(:country => germany, :name => "Dürrröhrsdorf-Dittersbach", :area_code => "35026") + AreaCode.create(:country => germany, :name => "Weesenstein", :area_code => "35027") + AreaCode.create(:country => germany, :name => "Krippen", :area_code => "35028") + AreaCode.create(:country => germany, :name => "Langenhennersdorf", :area_code => "35032") + AreaCode.create(:country => germany, :name => "Rosenthal Sächs Schweiz", :area_code => "35033") + AreaCode.create(:country => germany, :name => "Dippoldiswalde", :area_code => "3504") + AreaCode.create(:country => germany, :name => "Kipsdorf Kurort", :area_code => "35052") + AreaCode.create(:country => germany, :name => "Glashütte Sachs", :area_code => "35053") + AreaCode.create(:country => germany, :name => "Lauenstein Sachs", :area_code => "35054") + AreaCode.create(:country => germany, :name => "Höckendorf b Dippoldiswalde", :area_code => "35055") + AreaCode.create(:country => germany, :name => "Altenberg Sachs", :area_code => "35056") + AreaCode.create(:country => germany, :name => "Hermsdorf Erzgeb", :area_code => "35057") + AreaCode.create(:country => germany, :name => "Pretzschendorf", :area_code => "35058") + AreaCode.create(:country => germany, :name => "Dresden", :area_code => "351") + AreaCode.create(:country => germany, :name => "Arnsdorf b Dresden", :area_code => "35200") + AreaCode.create(:country => germany, :name => "Langebrück", :area_code => "35201") + AreaCode.create(:country => germany, :name => "Klingenberg Sachs", :area_code => "35202") + AreaCode.create(:country => germany, :name => "Tharandt", :area_code => "35203") + AreaCode.create(:country => germany, :name => "Wilsdruff", :area_code => "35204") + AreaCode.create(:country => germany, :name => "Ottendorf-Okrilla", :area_code => "35205") + AreaCode.create(:country => germany, :name => "Kreischa b Dresden", :area_code => "35206") + AreaCode.create(:country => germany, :name => "Moritzburg", :area_code => "35207") + AreaCode.create(:country => germany, :name => "Radeburg", :area_code => "35208") + AreaCode.create(:country => germany, :name => "Mohorn", :area_code => "35209") + AreaCode.create(:country => germany, :name => "Meissen", :area_code => "3521") + AreaCode.create(:country => germany, :name => "Grossenhain Sachs", :area_code => "3522") + AreaCode.create(:country => germany, :name => "Coswig b Dresden", :area_code => "3523") + AreaCode.create(:country => germany, :name => "Tauscha b Großenhain", :area_code => "35240") + AreaCode.create(:country => germany, :name => "Lommatzsch", :area_code => "35241") + AreaCode.create(:country => germany, :name => "Nossen", :area_code => "35242") + AreaCode.create(:country => germany, :name => "Weinböhla", :area_code => "35243") + AreaCode.create(:country => germany, :name => "Krögis", :area_code => "35244") + AreaCode.create(:country => germany, :name => "Burkhardswalde-Munzig", :area_code => "35245") + AreaCode.create(:country => germany, :name => "Ziegenhain Sachs", :area_code => "35246") + AreaCode.create(:country => germany, :name => "Zehren Sachs", :area_code => "35247") + AreaCode.create(:country => germany, :name => "Schönfeld b Großenhain", :area_code => "35248") + AreaCode.create(:country => germany, :name => "Basslitz", :area_code => "35249") + AreaCode.create(:country => germany, :name => "Riesa", :area_code => "3525") + AreaCode.create(:country => germany, :name => "Gröditz b Riesa", :area_code => "35263") + AreaCode.create(:country => germany, :name => "Strehla", :area_code => "35264") + AreaCode.create(:country => germany, :name => "Glaubitz", :area_code => "35265") + AreaCode.create(:country => germany, :name => "Heyda b Riesa", :area_code => "35266") + AreaCode.create(:country => germany, :name => "Diesbar-Seusslitz", :area_code => "35267") + AreaCode.create(:country => germany, :name => "Stauchitz", :area_code => "35268") + AreaCode.create(:country => germany, :name => "Radeberg", :area_code => "3528") + AreaCode.create(:country => germany, :name => "Heidenau Sachs", :area_code => "3529") + AreaCode.create(:country => germany, :name => "Finsterwalde", :area_code => "3531") + AreaCode.create(:country => germany, :name => "Doberlug-Kirchhain", :area_code => "35322") + AreaCode.create(:country => germany, :name => "Sonnewalde", :area_code => "35323") + AreaCode.create(:country => germany, :name => "Crinitz", :area_code => "35324") + AreaCode.create(:country => germany, :name => "Rückersdorf b Finsterwalde", :area_code => "35325") + AreaCode.create(:country => germany, :name => "Schönborn Kr Elbe-Elster", :area_code => "35326") + AreaCode.create(:country => germany, :name => "Priessen", :area_code => "35327") + AreaCode.create(:country => germany, :name => "Dollenchen", :area_code => "35329") + AreaCode.create(:country => germany, :name => "Elsterwerda", :area_code => "3533") + AreaCode.create(:country => germany, :name => "Bad Liebenwerda", :area_code => "35341") + AreaCode.create(:country => germany, :name => "Mühlberg Elbe", :area_code => "35342") + AreaCode.create(:country => germany, :name => "Hirschfeld b Elsterwerda", :area_code => "35343") + AreaCode.create(:country => germany, :name => "Herzberg Elster", :area_code => "3535") + AreaCode.create(:country => germany, :name => "Schlieben", :area_code => "35361") + AreaCode.create(:country => germany, :name => "Schönewalde b Herzberg", :area_code => "35362") + AreaCode.create(:country => germany, :name => "Fermerswalde", :area_code => "35363") + AreaCode.create(:country => germany, :name => "Lebusa", :area_code => "35364") + AreaCode.create(:country => germany, :name => "Falkenberg Elster", :area_code => "35365") + AreaCode.create(:country => germany, :name => "Jessen Elster", :area_code => "3537") + AreaCode.create(:country => germany, :name => "Elster Elbe", :area_code => "35383") + AreaCode.create(:country => germany, :name => "Steinsdorf b Jessen", :area_code => "35384") + AreaCode.create(:country => germany, :name => "Annaburg", :area_code => "35385") + AreaCode.create(:country => germany, :name => "Prettin", :area_code => "35386") + AreaCode.create(:country => germany, :name => "Seyda", :area_code => "35387") + AreaCode.create(:country => germany, :name => "Klöden", :area_code => "35388") + AreaCode.create(:country => germany, :name => "Holzdorf Elster", :area_code => "35389") + AreaCode.create(:country => germany, :name => "Calau", :area_code => "3541") + AreaCode.create(:country => germany, :name => "Lübbenau Spreewald", :area_code => "3542") + AreaCode.create(:country => germany, :name => "Vetschau", :area_code => "35433") + AreaCode.create(:country => germany, :name => "Altdöbern", :area_code => "35434") + AreaCode.create(:country => germany, :name => "Gollmitz b Calau", :area_code => "35435") + AreaCode.create(:country => germany, :name => "Laasow b Calau", :area_code => "35436") + AreaCode.create(:country => germany, :name => "Zinnitz", :area_code => "35439") + AreaCode.create(:country => germany, :name => "Luckau Brandenb", :area_code => "3544") + AreaCode.create(:country => germany, :name => "Dahme Brandenb", :area_code => "35451") + AreaCode.create(:country => germany, :name => "Golssen", :area_code => "35452") + AreaCode.create(:country => germany, :name => "Drahnsdorf", :area_code => "35453") + AreaCode.create(:country => germany, :name => "Uckro", :area_code => "35454") + AreaCode.create(:country => germany, :name => "Walddrehna", :area_code => "35455") + AreaCode.create(:country => germany, :name => "Terpt", :area_code => "35456") + AreaCode.create(:country => germany, :name => "Lübben Spreewald", :area_code => "3546") + AreaCode.create(:country => germany, :name => "Birkenhainchen", :area_code => "35471") + AreaCode.create(:country => germany, :name => "Schlepzig", :area_code => "35472") + AreaCode.create(:country => germany, :name => "Neu Lübbenau", :area_code => "35473") + AreaCode.create(:country => germany, :name => "Schönwalde b Lübben", :area_code => "35474") + AreaCode.create(:country => germany, :name => "Straupitz", :area_code => "35475") + AreaCode.create(:country => germany, :name => "Wittmannsdorf-Bückchen", :area_code => "35476") + AreaCode.create(:country => germany, :name => "Rietzneuendorf-Friedrichshof", :area_code => "35477") + AreaCode.create(:country => germany, :name => "Goyatz", :area_code => "35478") + AreaCode.create(:country => germany, :name => "Cottbus", :area_code => "355") + AreaCode.create(:country => germany, :name => "Döbern NL", :area_code => "35600") + AreaCode.create(:country => germany, :name => "Peitz", :area_code => "35601") + AreaCode.create(:country => germany, :name => "Drebkau", :area_code => "35602") + AreaCode.create(:country => germany, :name => "Burg Spreewald", :area_code => "35603") + AreaCode.create(:country => germany, :name => "Krieschow", :area_code => "35604") + AreaCode.create(:country => germany, :name => "Komptendorf", :area_code => "35605") + AreaCode.create(:country => germany, :name => "Briesen b Cottbus", :area_code => "35606") + AreaCode.create(:country => germany, :name => "Jänschwalde", :area_code => "35607") + AreaCode.create(:country => germany, :name => "Gross Ossnig", :area_code => "35608") + AreaCode.create(:country => germany, :name => "Drachhausen", :area_code => "35609") + AreaCode.create(:country => germany, :name => "Guben", :area_code => "3561") + AreaCode.create(:country => germany, :name => "Forst Lausitz", :area_code => "3562") + AreaCode.create(:country => germany, :name => "Spremberg", :area_code => "3563") + AreaCode.create(:country => germany, :name => "Schwarze Pumpe", :area_code => "3564") + AreaCode.create(:country => germany, :name => "Bärenklau NL", :area_code => "35691") + AreaCode.create(:country => germany, :name => "Kerkwitz", :area_code => "35692") + AreaCode.create(:country => germany, :name => "Lauschütz", :area_code => "35693") + AreaCode.create(:country => germany, :name => "Gosda b Klinge", :area_code => "35694") + AreaCode.create(:country => germany, :name => "Simmersdorf", :area_code => "35695") + AreaCode.create(:country => germany, :name => "Briesnig", :area_code => "35696") + AreaCode.create(:country => germany, :name => "Bagenz", :area_code => "35697") + AreaCode.create(:country => germany, :name => "Hornow", :area_code => "35698") + AreaCode.create(:country => germany, :name => "Hoyerswerda", :area_code => "3571") + AreaCode.create(:country => germany, :name => "Lauta b Hoyerswerda", :area_code => "35722") + AreaCode.create(:country => germany, :name => "Bernsdorf OL", :area_code => "35723") + AreaCode.create(:country => germany, :name => "Lohsa", :area_code => "35724") + AreaCode.create(:country => germany, :name => "Wittichenau", :area_code => "35725") + AreaCode.create(:country => germany, :name => "Groß Särchen", :area_code => "35726") + AreaCode.create(:country => germany, :name => "Burghammer", :area_code => "35727") + AreaCode.create(:country => germany, :name => "Uhyst Spree", :area_code => "35728") + AreaCode.create(:country => germany, :name => "Senftenberg", :area_code => "3573") + AreaCode.create(:country => germany, :name => "Lauchhammer", :area_code => "3574") + AreaCode.create(:country => germany, :name => "Welzow", :area_code => "35751") + AreaCode.create(:country => germany, :name => "Ruhland", :area_code => "35752") + AreaCode.create(:country => germany, :name => "Großräschen", :area_code => "35753") + AreaCode.create(:country => germany, :name => "Klettwitz", :area_code => "35754") + AreaCode.create(:country => germany, :name => "Ortrand", :area_code => "35755") + AreaCode.create(:country => germany, :name => "Hosena", :area_code => "35756") + AreaCode.create(:country => germany, :name => "Weisswasser", :area_code => "3576") + AreaCode.create(:country => germany, :name => "Bad Muskau", :area_code => "35771") + AreaCode.create(:country => germany, :name => "Rietschen", :area_code => "35772") + AreaCode.create(:country => germany, :name => "Schleife", :area_code => "35773") + AreaCode.create(:country => germany, :name => "Boxberg Sachs", :area_code => "35774") + AreaCode.create(:country => germany, :name => "Pechern", :area_code => "35775") + AreaCode.create(:country => germany, :name => "Kamenz", :area_code => "3578") + AreaCode.create(:country => germany, :name => "Ossling", :area_code => "35792") + AreaCode.create(:country => germany, :name => "Elstra", :area_code => "35793") + AreaCode.create(:country => germany, :name => "Königsbrück", :area_code => "35795") + AreaCode.create(:country => germany, :name => "Panschwitz-Kuckau", :area_code => "35796") + AreaCode.create(:country => germany, :name => "Schwepnitz", :area_code => "35797") + AreaCode.create(:country => germany, :name => "Görlitz", :area_code => "3581") + AreaCode.create(:country => germany, :name => "Zodel", :area_code => "35820") + AreaCode.create(:country => germany, :name => "Hagenwerder", :area_code => "35822") + AreaCode.create(:country => germany, :name => "Ostritz", :area_code => "35823") + AreaCode.create(:country => germany, :name => "Kodersdorf", :area_code => "35825") + AreaCode.create(:country => germany, :name => "Königshain b Görlitz", :area_code => "35826") + AreaCode.create(:country => germany, :name => "Nieder-Seifersdorf", :area_code => "35827") + AreaCode.create(:country => germany, :name => "Reichenbach OL", :area_code => "35828") + AreaCode.create(:country => germany, :name => "Gersdorf b Görlitz", :area_code => "35829") + AreaCode.create(:country => germany, :name => "Zittau", :area_code => "3583") + AreaCode.create(:country => germany, :name => "Großschönau Sachs", :area_code => "35841") + AreaCode.create(:country => germany, :name => "Oderwitz", :area_code => "35842") + AreaCode.create(:country => germany, :name => "Hirschfelde b Zittau", :area_code => "35843") + AreaCode.create(:country => germany, :name => "Oybin Kurort", :area_code => "35844") + AreaCode.create(:country => germany, :name => "Löbau", :area_code => "3585") + AreaCode.create(:country => germany, :name => "Neugersdorf ,Sachs", :area_code => "3586") + AreaCode.create(:country => germany, :name => "Neusalza-Spremberg", :area_code => "35872") + AreaCode.create(:country => germany, :name => "Herrnhut", :area_code => "35873") + AreaCode.create(:country => germany, :name => "Bernstadt a d Eigen", :area_code => "35874") + AreaCode.create(:country => germany, :name => "Obercunnersdorf b Löbau", :area_code => "35875") + AreaCode.create(:country => germany, :name => "Weissenberg Sachs", :area_code => "35876") + AreaCode.create(:country => germany, :name => "Cunewalde", :area_code => "35877") + AreaCode.create(:country => germany, :name => "Niesky", :area_code => "3588") + AreaCode.create(:country => germany, :name => "Rothenburg OL", :area_code => "35891") + AreaCode.create(:country => germany, :name => "Horka OL", :area_code => "35892") + AreaCode.create(:country => germany, :name => "Mücka", :area_code => "35893") + AreaCode.create(:country => germany, :name => "Hähnichen", :area_code => "35894") + AreaCode.create(:country => germany, :name => "Klitten", :area_code => "35895") + AreaCode.create(:country => germany, :name => "Bautzen", :area_code => "3591") + AreaCode.create(:country => germany, :name => "Kirschau", :area_code => "3592") + AreaCode.create(:country => germany, :name => "Seitschen", :area_code => "35930") + AreaCode.create(:country => germany, :name => "Königswartha", :area_code => "35931") + AreaCode.create(:country => germany, :name => "Guttau", :area_code => "35932") + AreaCode.create(:country => germany, :name => "Neschwitz", :area_code => "35933") + AreaCode.create(:country => germany, :name => "Grossdubrau", :area_code => "35934") + AreaCode.create(:country => germany, :name => "Kleinwelka", :area_code => "35935") + AreaCode.create(:country => germany, :name => "Sohland Spree", :area_code => "35936") + AreaCode.create(:country => germany, :name => "Prischwitz", :area_code => "35937") + AreaCode.create(:country => germany, :name => "Großpostwitz OL", :area_code => "35938") + AreaCode.create(:country => germany, :name => "Hochkirch", :area_code => "35939") + AreaCode.create(:country => germany, :name => "Bischofswerda", :area_code => "3594") + AreaCode.create(:country => germany, :name => "Neukirch Lausitz", :area_code => "35951") + AreaCode.create(:country => germany, :name => "Großröhrsdorf OL", :area_code => "35952") + AreaCode.create(:country => germany, :name => "Burkau", :area_code => "35953") + AreaCode.create(:country => germany, :name => "Grossharthau", :area_code => "35954") + AreaCode.create(:country => germany, :name => "Pulsnitz", :area_code => "35955") + AreaCode.create(:country => germany, :name => "Neustadt i Sa", :area_code => "3596") + AreaCode.create(:country => germany, :name => "Sebnitz", :area_code => "35971") + AreaCode.create(:country => germany, :name => "Stolpen", :area_code => "35973") + AreaCode.create(:country => germany, :name => "Hinterhermsdorf", :area_code => "35974") + AreaCode.create(:country => germany, :name => "Hohnstein", :area_code => "35975") + AreaCode.create(:country => germany, :name => "Mühlhausen Thür", :area_code => "3601") + AreaCode.create(:country => germany, :name => "Ebeleben", :area_code => "36020") + AreaCode.create(:country => germany, :name => "Schlotheim", :area_code => "36021") + AreaCode.create(:country => germany, :name => "Grossengottern", :area_code => "36022") + AreaCode.create(:country => germany, :name => "Horsmar", :area_code => "36023") + AreaCode.create(:country => germany, :name => "Diedorf b Mühlhausen Thür", :area_code => "36024") + AreaCode.create(:country => germany, :name => "Körner", :area_code => "36025") + AreaCode.create(:country => germany, :name => "Struth b Mühlhausen Thür", :area_code => "36026") + AreaCode.create(:country => germany, :name => "Lengenfeld Unterm Stein", :area_code => "36027") + AreaCode.create(:country => germany, :name => "Kammerforst Thür", :area_code => "36028") + AreaCode.create(:country => germany, :name => "Menteroda", :area_code => "36029") + AreaCode.create(:country => germany, :name => "Bad Langensalza", :area_code => "3603") + AreaCode.create(:country => germany, :name => "Bad Tennstedt", :area_code => "36041") + AreaCode.create(:country => germany, :name => "Tonna", :area_code => "36042") + AreaCode.create(:country => germany, :name => "Kirchheilingen", :area_code => "36043") + AreaCode.create(:country => germany, :name => "Leinefelde", :area_code => "3605") + AreaCode.create(:country => germany, :name => "Heiligenstadt Heilbad", :area_code => "3606") + AreaCode.create(:country => germany, :name => "Teistungen", :area_code => "36071") + AreaCode.create(:country => germany, :name => "Weißenborn-Lüderode", :area_code => "36072") + AreaCode.create(:country => germany, :name => "Worbis", :area_code => "36074") + AreaCode.create(:country => germany, :name => "Dingelstädt Eichsfeld", :area_code => "36075") + AreaCode.create(:country => germany, :name => "Niederorschel", :area_code => "36076") + AreaCode.create(:country => germany, :name => "Grossbodungen", :area_code => "36077") + AreaCode.create(:country => germany, :name => "Arenshausen", :area_code => "36081") + AreaCode.create(:country => germany, :name => "Ershausen", :area_code => "36082") + AreaCode.create(:country => germany, :name => "Uder", :area_code => "36083") + AreaCode.create(:country => germany, :name => "Heuthen", :area_code => "36084") + AreaCode.create(:country => germany, :name => "Reinholterode", :area_code => "36085") + AreaCode.create(:country => germany, :name => "Wüstheuterode", :area_code => "36087") + AreaCode.create(:country => germany, :name => "Erfurt", :area_code => "361") + AreaCode.create(:country => germany, :name => "Elxleben b Arnstadt", :area_code => "36200") + AreaCode.create(:country => germany, :name => "Walschleben", :area_code => "36201") + AreaCode.create(:country => germany, :name => "Neudietendorf", :area_code => "36202") + AreaCode.create(:country => germany, :name => "Vieselbach", :area_code => "36203") + AreaCode.create(:country => germany, :name => "Stotternheim", :area_code => "36204") + AreaCode.create(:country => germany, :name => "Gräfenroda", :area_code => "36205") + AreaCode.create(:country => germany, :name => "Grossfahner", :area_code => "36206") + AreaCode.create(:country => germany, :name => "Plaue Thür", :area_code => "36207") + AreaCode.create(:country => germany, :name => "Ermstedt", :area_code => "36208") + AreaCode.create(:country => germany, :name => "Klettbach", :area_code => "36209") + AreaCode.create(:country => germany, :name => "Gotha Thür", :area_code => "3621") + AreaCode.create(:country => germany, :name => "Waltershausen Thür", :area_code => "3622") + AreaCode.create(:country => germany, :name => "Friedrichroda", :area_code => "3623") + AreaCode.create(:country => germany, :name => "Ohrdruf", :area_code => "3624") + AreaCode.create(:country => germany, :name => "Tambach-Dietharz Thür Wald", :area_code => "36252") + AreaCode.create(:country => germany, :name => "Georgenthal Thür Wald", :area_code => "36253") + AreaCode.create(:country => germany, :name => "Friedrichswerth", :area_code => "36254") + AreaCode.create(:country => germany, :name => "Goldbach b Gotha", :area_code => "36255") + AreaCode.create(:country => germany, :name => "Wechmar", :area_code => "36256") + AreaCode.create(:country => germany, :name => "Luisenthal Thür", :area_code => "36257") + AreaCode.create(:country => germany, :name => "Friemar", :area_code => "36258") + AreaCode.create(:country => germany, :name => "Tabarz Thür Wald", :area_code => "36259") + AreaCode.create(:country => germany, :name => "Arnstadt", :area_code => "3628") + AreaCode.create(:country => germany, :name => "Stadtilm", :area_code => "3629") + AreaCode.create(:country => germany, :name => "Nordhausen Thür", :area_code => "3631") + AreaCode.create(:country => germany, :name => "Sondershausen", :area_code => "3632") + AreaCode.create(:country => germany, :name => "Grossberndten", :area_code => "36330") + AreaCode.create(:country => germany, :name => "Ilfeld", :area_code => "36331") + AreaCode.create(:country => germany, :name => "Ellrich", :area_code => "36332") + AreaCode.create(:country => germany, :name => "Heringen Helme", :area_code => "36333") + AreaCode.create(:country => germany, :name => "Wolkramshausen", :area_code => "36334") + AreaCode.create(:country => germany, :name => "Grosswechsungen", :area_code => "36335") + AreaCode.create(:country => germany, :name => "Klettenberg", :area_code => "36336") + AreaCode.create(:country => germany, :name => "Schiedungen", :area_code => "36337") + AreaCode.create(:country => germany, :name => "Bleicherode", :area_code => "36338") + AreaCode.create(:country => germany, :name => "Sömmerda", :area_code => "3634") + AreaCode.create(:country => germany, :name => "Kölleda", :area_code => "3635") + AreaCode.create(:country => germany, :name => "Greussen", :area_code => "3636") + AreaCode.create(:country => germany, :name => "Grossenehrich", :area_code => "36370") + AreaCode.create(:country => germany, :name => "Schlossvippach", :area_code => "36371") + AreaCode.create(:country => germany, :name => "Kleinneuhausen", :area_code => "36372") + AreaCode.create(:country => germany, :name => "Buttstädt", :area_code => "36373") + AreaCode.create(:country => germany, :name => "Weissensee", :area_code => "36374") + AreaCode.create(:country => germany, :name => "Kindelbrück", :area_code => "36375") + AreaCode.create(:country => germany, :name => "Straussfurt", :area_code => "36376") + AreaCode.create(:country => germany, :name => "Rastenberg", :area_code => "36377") + AreaCode.create(:country => germany, :name => "Ostramondra", :area_code => "36378") + AreaCode.create(:country => germany, :name => "Holzengel", :area_code => "36379") + AreaCode.create(:country => germany, :name => "Jena", :area_code => "3641") + AreaCode.create(:country => germany, :name => "Camburg", :area_code => "36421") + AreaCode.create(:country => germany, :name => "Reinstädt Thür", :area_code => "36422") + AreaCode.create(:country => germany, :name => "Orlamünde", :area_code => "36423") + AreaCode.create(:country => germany, :name => "Kahla Thür", :area_code => "36424") + AreaCode.create(:country => germany, :name => "Isserstedt", :area_code => "36425") + AreaCode.create(:country => germany, :name => "Ottendorf b Stadtroda", :area_code => "36426") + AreaCode.create(:country => germany, :name => "Dornburg Saale", :area_code => "36427") + AreaCode.create(:country => germany, :name => "Stadtroda", :area_code => "36428") + AreaCode.create(:country => germany, :name => "Weimar Thür", :area_code => "3643") + AreaCode.create(:country => germany, :name => "Apolda", :area_code => "3644") + AreaCode.create(:country => germany, :name => "Kranichfeld", :area_code => "36450") + AreaCode.create(:country => germany, :name => "Buttelstedt", :area_code => "36451") + AreaCode.create(:country => germany, :name => "Berlstedt", :area_code => "36452") + AreaCode.create(:country => germany, :name => "Mellingen", :area_code => "36453") + AreaCode.create(:country => germany, :name => "Magdala", :area_code => "36454") + AreaCode.create(:country => germany, :name => "Bad Berka", :area_code => "36458") + AreaCode.create(:country => germany, :name => "Blankenhain Thür", :area_code => "36459") + AreaCode.create(:country => germany, :name => "Bad Sulza", :area_code => "36461") + AreaCode.create(:country => germany, :name => "Ossmannstedt", :area_code => "36462") + AreaCode.create(:country => germany, :name => "Gebstedt", :area_code => "36463") + AreaCode.create(:country => germany, :name => "Wormstedt", :area_code => "36464") + AreaCode.create(:country => germany, :name => "Oberndorf b Apolda", :area_code => "36465") + AreaCode.create(:country => germany, :name => "Pößneck", :area_code => "3647") + AreaCode.create(:country => germany, :name => "Neustadt an der Orla", :area_code => "36481") + AreaCode.create(:country => germany, :name => "Triptis", :area_code => "36482") + AreaCode.create(:country => germany, :name => "Ziegenrück", :area_code => "36483") + AreaCode.create(:country => germany, :name => "Knau b Pößneck", :area_code => "36484") + AreaCode.create(:country => germany, :name => "Gera", :area_code => "365") + AreaCode.create(:country => germany, :name => "Hermsdorf Thür", :area_code => "36601") + AreaCode.create(:country => germany, :name => "Ronneburg Thür", :area_code => "36602") + AreaCode.create(:country => germany, :name => "Weida", :area_code => "36603") + AreaCode.create(:country => germany, :name => "Münchenbernsdorf", :area_code => "36604") + AreaCode.create(:country => germany, :name => "Bad Köstritz", :area_code => "36605") + AreaCode.create(:country => germany, :name => "Kraftsdorf", :area_code => "36606") + AreaCode.create(:country => germany, :name => "Niederpöllnitz", :area_code => "36607") + AreaCode.create(:country => germany, :name => "Seelingstädt b Gera", :area_code => "36608") + AreaCode.create(:country => germany, :name => "Greiz", :area_code => "3661") + AreaCode.create(:country => germany, :name => "Elsterberg b Plauen", :area_code => "36621") + AreaCode.create(:country => germany, :name => "Triebes", :area_code => "36622") + AreaCode.create(:country => germany, :name => "Berga Elster", :area_code => "36623") + AreaCode.create(:country => germany, :name => "Teichwolframsdorf", :area_code => "36624") + AreaCode.create(:country => germany, :name => "Langenwetzendorf", :area_code => "36625") + AreaCode.create(:country => germany, :name => "Auma", :area_code => "36626") + AreaCode.create(:country => germany, :name => "Zeulenroda", :area_code => "36628") + AreaCode.create(:country => germany, :name => "Schleiz", :area_code => "3663") + AreaCode.create(:country => germany, :name => "Remptendorf", :area_code => "36640") + AreaCode.create(:country => germany, :name => "Harra", :area_code => "36642") + AreaCode.create(:country => germany, :name => "Thimmendorf", :area_code => "36643") + AreaCode.create(:country => germany, :name => "Hirschberg Saale", :area_code => "36644") + AreaCode.create(:country => germany, :name => "Mühltroff", :area_code => "36645") + AreaCode.create(:country => germany, :name => "Tanna b Schleiz", :area_code => "36646") + AreaCode.create(:country => germany, :name => "Saalburg Thür", :area_code => "36647") + AreaCode.create(:country => germany, :name => "Dittersdorf b Schleiz", :area_code => "36648") + AreaCode.create(:country => germany, :name => "Gefell b Schleiz", :area_code => "36649") + AreaCode.create(:country => germany, :name => "Lobenstein", :area_code => "36651") + AreaCode.create(:country => germany, :name => "Wurzbach", :area_code => "36652") + AreaCode.create(:country => germany, :name => "Lehesten Thür Wald", :area_code => "36653") + AreaCode.create(:country => germany, :name => "Eisenberg Thür", :area_code => "36691") + AreaCode.create(:country => germany, :name => "Bürgel", :area_code => "36692") + AreaCode.create(:country => germany, :name => "Crossen an der Elster", :area_code => "36693") + AreaCode.create(:country => germany, :name => "Schkölen Thür", :area_code => "36694") + AreaCode.create(:country => germany, :name => "Söllmnitz", :area_code => "36695") + AreaCode.create(:country => germany, :name => "Lichte", :area_code => "36701") + AreaCode.create(:country => germany, :name => "Lauscha", :area_code => "36702") + AreaCode.create(:country => germany, :name => "Gräfenthal", :area_code => "36703") + AreaCode.create(:country => germany, :name => "Steinheid", :area_code => "36704") + AreaCode.create(:country => germany, :name => "Oberweißbach Thür Wald", :area_code => "36705") + AreaCode.create(:country => germany, :name => "Saalfeld Saale", :area_code => "3671") + AreaCode.create(:country => germany, :name => "Rudolstadt", :area_code => "3672") + AreaCode.create(:country => germany, :name => "Sitzendorf", :area_code => "36730") + AreaCode.create(:country => germany, :name => "Unterloquitz", :area_code => "36731") + AreaCode.create(:country => germany, :name => "Könitz", :area_code => "36732") + AreaCode.create(:country => germany, :name => "Kaulsdorf", :area_code => "36733") + AreaCode.create(:country => germany, :name => "Leutenberg", :area_code => "36734") + AreaCode.create(:country => germany, :name => "Probstzella", :area_code => "36735") + AreaCode.create(:country => germany, :name => "Arnsgereuth", :area_code => "36736") + AreaCode.create(:country => germany, :name => "Drognitz", :area_code => "36737") + AreaCode.create(:country => germany, :name => "Königsee", :area_code => "36738") + AreaCode.create(:country => germany, :name => "Rottenbach", :area_code => "36739") + AreaCode.create(:country => germany, :name => "Bad Blankenburg", :area_code => "36741") + AreaCode.create(:country => germany, :name => "Uhlstädt", :area_code => "36742") + AreaCode.create(:country => germany, :name => "Teichel", :area_code => "36743") + AreaCode.create(:country => germany, :name => "Remda", :area_code => "36744") + AreaCode.create(:country => germany, :name => "Sonneberg Thür", :area_code => "3675") + AreaCode.create(:country => germany, :name => "Heubisch", :area_code => "36761") + AreaCode.create(:country => germany, :name => "Steinach Thür", :area_code => "36762") + AreaCode.create(:country => germany, :name => "Neuhaus-Schierschnitz", :area_code => "36764") + AreaCode.create(:country => germany, :name => "Schalkau", :area_code => "36766") + AreaCode.create(:country => germany, :name => "Ilmenau Thür", :area_code => "3677") + AreaCode.create(:country => germany, :name => "Grossbreitenbach", :area_code => "36781") + AreaCode.create(:country => germany, :name => "Schmiedefeld a Rennsteig", :area_code => "36782") + AreaCode.create(:country => germany, :name => "Gehren Thür", :area_code => "36783") + AreaCode.create(:country => germany, :name => "Stützerbach", :area_code => "36784") + AreaCode.create(:country => germany, :name => "Gräfinau-Angstedt", :area_code => "36785") + AreaCode.create(:country => germany, :name => "Neuhaus a Rennweg", :area_code => "3679") + AreaCode.create(:country => germany, :name => "Suhl", :area_code => "3681") + AreaCode.create(:country => germany, :name => "Zella-Mehlis", :area_code => "3682") + AreaCode.create(:country => germany, :name => "Schmalkalden", :area_code => "3683") + AreaCode.create(:country => germany, :name => "Trusetal", :area_code => "36840") + AreaCode.create(:country => germany, :name => "Schleusingen", :area_code => "36841") + AreaCode.create(:country => germany, :name => "Oberhof Thür", :area_code => "36842") + AreaCode.create(:country => germany, :name => "Benshausen", :area_code => "36843") + AreaCode.create(:country => germany, :name => "Rohr Thür", :area_code => "36844") + AreaCode.create(:country => germany, :name => "Gehlberg", :area_code => "36845") + AreaCode.create(:country => germany, :name => "Suhl-Dietzhausen", :area_code => "36846") + AreaCode.create(:country => germany, :name => "Steinbach-Hallenberg", :area_code => "36847") + AreaCode.create(:country => germany, :name => "Wernshausen", :area_code => "36848") + AreaCode.create(:country => germany, :name => "Kleinschmalkalden", :area_code => "36849") + AreaCode.create(:country => germany, :name => "Hildburghausen", :area_code => "3685") + AreaCode.create(:country => germany, :name => "Eisfeld", :area_code => "3686") + AreaCode.create(:country => germany, :name => "Masserberg", :area_code => "36870") + AreaCode.create(:country => germany, :name => "Bad Colberg-Heldburg", :area_code => "36871") + AreaCode.create(:country => germany, :name => "Themar", :area_code => "36873") + AreaCode.create(:country => germany, :name => "Schönbrunn b Hildburghaus", :area_code => "36874") + AreaCode.create(:country => germany, :name => "Straufhain-Streufdorf", :area_code => "36875") + AreaCode.create(:country => germany, :name => "Oberland", :area_code => "36878") + AreaCode.create(:country => germany, :name => "Eisenach Thür", :area_code => "3691") + AreaCode.create(:country => germany, :name => "Grossenlupnitz", :area_code => "36920") + AreaCode.create(:country => germany, :name => "Wutha-Farnroda", :area_code => "36921") + AreaCode.create(:country => germany, :name => "Gerstungen", :area_code => "36922") + AreaCode.create(:country => germany, :name => "Treffurt", :area_code => "36923") + AreaCode.create(:country => germany, :name => "Mihla", :area_code => "36924") + AreaCode.create(:country => germany, :name => "Marksuhl", :area_code => "36925") + AreaCode.create(:country => germany, :name => "Creuzburg", :area_code => "36926") + AreaCode.create(:country => germany, :name => "Unterellen", :area_code => "36927") + AreaCode.create(:country => germany, :name => "Neuenhof Thür", :area_code => "36928") + AreaCode.create(:country => germany, :name => "Ruhla", :area_code => "36929") + AreaCode.create(:country => germany, :name => "Meiningen", :area_code => "3693") + AreaCode.create(:country => germany, :name => "Oepfershausen", :area_code => "36940") + AreaCode.create(:country => germany, :name => "Wasungen", :area_code => "36941") + AreaCode.create(:country => germany, :name => "Bettenhausen Thür", :area_code => "36943") + AreaCode.create(:country => germany, :name => "Rentwertshausen", :area_code => "36944") + AreaCode.create(:country => germany, :name => "Henneberg", :area_code => "36945") + AreaCode.create(:country => germany, :name => "Erbenhausen Thür", :area_code => "36946") + AreaCode.create(:country => germany, :name => "Jüchsen", :area_code => "36947") + AreaCode.create(:country => germany, :name => "Römhild", :area_code => "36948") + AreaCode.create(:country => germany, :name => "Obermaßfeld-Grimmenthal", :area_code => "36949") + AreaCode.create(:country => germany, :name => "Bad Salzungen", :area_code => "3695") + AreaCode.create(:country => germany, :name => "Bad Liebenstein", :area_code => "36961") + AreaCode.create(:country => germany, :name => "Vacha", :area_code => "36962") + AreaCode.create(:country => germany, :name => "Dorndorf Rhön", :area_code => "36963") + AreaCode.create(:country => germany, :name => "Dermbach Rhön", :area_code => "36964") + AreaCode.create(:country => germany, :name => "Stadtlengsfeld", :area_code => "36965") + AreaCode.create(:country => germany, :name => "Kaltennordheim", :area_code => "36966") + AreaCode.create(:country => germany, :name => "Geisa", :area_code => "36967") + AreaCode.create(:country => germany, :name => "Rossdorf Rhön", :area_code => "36968") + AreaCode.create(:country => germany, :name => "Merkers", :area_code => "36969") + AreaCode.create(:country => germany, :name => "Chemnitz Sachs", :area_code => "371") + AreaCode.create(:country => germany, :name => "Wittgensdorf b Chemnitz", :area_code => "37200") + AreaCode.create(:country => germany, :name => "Claussnitz b Chemnitz", :area_code => "37202") + AreaCode.create(:country => germany, :name => "Gersdorf b Chemnitz", :area_code => "37203") + AreaCode.create(:country => germany, :name => "Lichtenstein Sachs", :area_code => "37204") + AreaCode.create(:country => germany, :name => "Frankenberg", :area_code => "37206") + AreaCode.create(:country => germany, :name => "Hainichen", :area_code => "37207") + AreaCode.create(:country => germany, :name => "Auerswalde", :area_code => "37208") + AreaCode.create(:country => germany, :name => "Einsiedel b Chemnitz", :area_code => "37209") + AreaCode.create(:country => germany, :name => "Meinersdorf", :area_code => "3721") + AreaCode.create(:country => germany, :name => "Limbach-Oberfrohna", :area_code => "3722") + AreaCode.create(:country => germany, :name => "Hohenstein-Ernstthal", :area_code => "3723") + AreaCode.create(:country => germany, :name => "Burgstädt", :area_code => "3724") + AreaCode.create(:country => germany, :name => "Zschopau", :area_code => "3725") + AreaCode.create(:country => germany, :name => "Flöha", :area_code => "3726") + AreaCode.create(:country => germany, :name => "Mittweida", :area_code => "3727") + AreaCode.create(:country => germany, :name => "Augustusburg", :area_code => "37291") + AreaCode.create(:country => germany, :name => "Oederan", :area_code => "37292") + AreaCode.create(:country => germany, :name => "Eppendorf Sachs", :area_code => "37293") + AreaCode.create(:country => germany, :name => "Grünhainichen", :area_code => "37294") + AreaCode.create(:country => germany, :name => "Lugau Erzgeb", :area_code => "37295") + AreaCode.create(:country => germany, :name => "Stollberg Erzgeb", :area_code => "37296") + AreaCode.create(:country => germany, :name => "Thum Sachs", :area_code => "37297") + AreaCode.create(:country => germany, :name => "Oelsnitz Erzgeb", :area_code => "37298") + AreaCode.create(:country => germany, :name => "Freiberg Sachs", :area_code => "3731") + AreaCode.create(:country => germany, :name => "Mulda Sachs", :area_code => "37320") + AreaCode.create(:country => germany, :name => "Frankenstein Sachs", :area_code => "37321") + AreaCode.create(:country => germany, :name => "Brand-Erbisdorf", :area_code => "37322") + AreaCode.create(:country => germany, :name => "Lichtenberg Erzgeb", :area_code => "37323") + AreaCode.create(:country => germany, :name => "Reinsberg Sachs", :area_code => "37324") + AreaCode.create(:country => germany, :name => "Niederbobritzsch", :area_code => "37325") + AreaCode.create(:country => germany, :name => "Frauenstein Sachs", :area_code => "37326") + AreaCode.create(:country => germany, :name => "Rechenberg-Bienenmühle", :area_code => "37327") + AreaCode.create(:country => germany, :name => "Grossschirma", :area_code => "37328") + AreaCode.create(:country => germany, :name => "Grosshartmannsdorf", :area_code => "37329") + AreaCode.create(:country => germany, :name => "Annaberg-Buchholz", :area_code => "3733") + AreaCode.create(:country => germany, :name => "Ehrenfriedersdorf", :area_code => "37341") + AreaCode.create(:country => germany, :name => "Cranzahl", :area_code => "37342") + AreaCode.create(:country => germany, :name => "Jöhstadt", :area_code => "37343") + AreaCode.create(:country => germany, :name => "Crottendorf Sachs", :area_code => "37344") + AreaCode.create(:country => germany, :name => "Geyer", :area_code => "37346") + AreaCode.create(:country => germany, :name => "Bärenstein Kr Annaberg", :area_code => "37347") + AreaCode.create(:country => germany, :name => "Oberwiesenthal Kurort", :area_code => "37348") + AreaCode.create(:country => germany, :name => "Scheibenberg", :area_code => "37349") + AreaCode.create(:country => germany, :name => "Marienberg Sachs", :area_code => "3735") + AreaCode.create(:country => germany, :name => "Olbernhau", :area_code => "37360") + AreaCode.create(:country => germany, :name => "Neuhausen Erzgeb", :area_code => "37361") + AreaCode.create(:country => germany, :name => "Seiffen Erzgeb", :area_code => "37362") + AreaCode.create(:country => germany, :name => "Zöblitz", :area_code => "37363") + AreaCode.create(:country => germany, :name => "Reitzenhain Erzgeb", :area_code => "37364") + AreaCode.create(:country => germany, :name => "Sayda", :area_code => "37365") + AreaCode.create(:country => germany, :name => "Rübenau", :area_code => "37366") + AreaCode.create(:country => germany, :name => "Lengefeld Erzgeb", :area_code => "37367") + AreaCode.create(:country => germany, :name => "Deutschneudorf", :area_code => "37368") + AreaCode.create(:country => germany, :name => "Wolkenstein", :area_code => "37369") + AreaCode.create(:country => germany, :name => "Rochlitz", :area_code => "3737") + AreaCode.create(:country => germany, :name => "Penig", :area_code => "37381") + AreaCode.create(:country => germany, :name => "Geringswalde", :area_code => "37382") + AreaCode.create(:country => germany, :name => "Lunzenau", :area_code => "37383") + AreaCode.create(:country => germany, :name => "Wechselburg", :area_code => "37384") + AreaCode.create(:country => germany, :name => "Plauen", :area_code => "3741") + AreaCode.create(:country => germany, :name => "Oelsnitz Vogtl", :area_code => "37421") + AreaCode.create(:country => germany, :name => "Markneukirchen", :area_code => "37422") + AreaCode.create(:country => germany, :name => "Adorf Vogtl", :area_code => "37423") + AreaCode.create(:country => germany, :name => "Eichigt", :area_code => "37430") + AreaCode.create(:country => germany, :name => "Mehltheuer Vogtl", :area_code => "37431") + AreaCode.create(:country => germany, :name => "Pausa Vogtl", :area_code => "37432") + AreaCode.create(:country => germany, :name => "Gutenfürst", :area_code => "37433") + AreaCode.create(:country => germany, :name => "Bobenneukirchen", :area_code => "37434") + AreaCode.create(:country => germany, :name => "Reuth b Plauen", :area_code => "37435") + AreaCode.create(:country => germany, :name => "Weischlitz", :area_code => "37436") + AreaCode.create(:country => germany, :name => "Bad Elster", :area_code => "37437") + AreaCode.create(:country => germany, :name => "Bad Brambach", :area_code => "37438") + AreaCode.create(:country => germany, :name => "Jocketa", :area_code => "37439") + AreaCode.create(:country => germany, :name => "Auerbach Vogtl.", :area_code => "3744") + AreaCode.create(:country => germany, :name => "Falkenstein Vogtl", :area_code => "3745") + AreaCode.create(:country => germany, :name => "Rothenkirchen Vogtl", :area_code => "37462") + AreaCode.create(:country => germany, :name => "Bergen Vogtl", :area_code => "37463") + AreaCode.create(:country => germany, :name => "Schöneck Vogtl", :area_code => "37464") + AreaCode.create(:country => germany, :name => "Tannenbergsthal Vogtl", :area_code => "37465") + AreaCode.create(:country => germany, :name => "Klingenthal Sachs", :area_code => "37467") + AreaCode.create(:country => germany, :name => "Treuen Vogtl", :area_code => "37468") + AreaCode.create(:country => germany, :name => "Zwickau", :area_code => "375") + AreaCode.create(:country => germany, :name => "Neumark Sachs", :area_code => "37600") + AreaCode.create(:country => germany, :name => "Mülsen Skt Jacob", :area_code => "37601") + AreaCode.create(:country => germany, :name => "Kirchberg Sachs", :area_code => "37602") + AreaCode.create(:country => germany, :name => "Wildenfels", :area_code => "37603") + AreaCode.create(:country => germany, :name => "Mosel", :area_code => "37604") + AreaCode.create(:country => germany, :name => "Hartenstein Sachs", :area_code => "37605") + AreaCode.create(:country => germany, :name => "Lengenfeld Vogtl", :area_code => "37606") + AreaCode.create(:country => germany, :name => "Ebersbrunn Sachs", :area_code => "37607") + AreaCode.create(:country => germany, :name => "Waldenburg Sachs", :area_code => "37608") + AreaCode.create(:country => germany, :name => "Wolkenburg Mulde", :area_code => "37609") + AreaCode.create(:country => germany, :name => "Werdau Sachs", :area_code => "3761") + AreaCode.create(:country => germany, :name => "Crimmitschau", :area_code => "3762") + AreaCode.create(:country => germany, :name => "Glauchau", :area_code => "3763") + AreaCode.create(:country => germany, :name => "Meerane", :area_code => "3764") + AreaCode.create(:country => germany, :name => "Reichenbach Vogtl", :area_code => "3765") + AreaCode.create(:country => germany, :name => "Aue Sachs", :area_code => "3771") + AreaCode.create(:country => germany, :name => "Schneeberg Erzgeb", :area_code => "3772") + AreaCode.create(:country => germany, :name => "Johanngeorgenstadt", :area_code => "3773") + AreaCode.create(:country => germany, :name => "Schwarzenberg", :area_code => "3774") + AreaCode.create(:country => germany, :name => "Eibenstock", :area_code => "37752") + AreaCode.create(:country => germany, :name => "Zwönitz", :area_code => "37754") + AreaCode.create(:country => germany, :name => "Schönheide Erzgeb", :area_code => "37755") + AreaCode.create(:country => germany, :name => "Breitenbrunn Erzgeb", :area_code => "37756") + AreaCode.create(:country => germany, :name => "Rittersgrün", :area_code => "37757") + AreaCode.create(:country => germany, :name => "Rostock", :area_code => "381") + AreaCode.create(:country => germany, :name => "Gelbensande", :area_code => "38201") + AreaCode.create(:country => germany, :name => "Volkenshagen", :area_code => "38202") + AreaCode.create(:country => germany, :name => "Bad Doberan", :area_code => "38203") + AreaCode.create(:country => germany, :name => "Broderstorf", :area_code => "38204") + AreaCode.create(:country => germany, :name => "Tessin b Rostock", :area_code => "38205") + AreaCode.create(:country => germany, :name => "Graal-Müritz Seeheilbad", :area_code => "38206") + AreaCode.create(:country => germany, :name => "Stäbelow", :area_code => "38207") + AreaCode.create(:country => germany, :name => "Kavelstorf", :area_code => "38208") + AreaCode.create(:country => germany, :name => "Sanitz b Rostock", :area_code => "38209") + AreaCode.create(:country => germany, :name => "Ribnitz-Damgarten", :area_code => "3821") + AreaCode.create(:country => germany, :name => "Wustrow Ostseebad", :area_code => "38220") + AreaCode.create(:country => germany, :name => "Marlow", :area_code => "38221") + AreaCode.create(:country => germany, :name => "Semlow", :area_code => "38222") + AreaCode.create(:country => germany, :name => "Saal Vorpom", :area_code => "38223") + AreaCode.create(:country => germany, :name => "Gresenhorst", :area_code => "38224") + AreaCode.create(:country => germany, :name => "Trinwillershagen", :area_code => "38225") + AreaCode.create(:country => germany, :name => "Dierhagen Ostseebad", :area_code => "38226") + AreaCode.create(:country => germany, :name => "Lüdershagen b Barth", :area_code => "38227") + AreaCode.create(:country => germany, :name => "Dettmannsdorf-Kölzow", :area_code => "38228") + AreaCode.create(:country => germany, :name => "Bad Sülze", :area_code => "38229") + AreaCode.create(:country => germany, :name => "Barth", :area_code => "38231") + AreaCode.create(:country => germany, :name => "Zingst Ostseebad", :area_code => "38232") + AreaCode.create(:country => germany, :name => "Prerow Ostseebad", :area_code => "38233") + AreaCode.create(:country => germany, :name => "Born a Darß", :area_code => "38234") + AreaCode.create(:country => germany, :name => "Kröpelin", :area_code => "38292") + AreaCode.create(:country => germany, :name => "Kühlungsborn Ostseebad", :area_code => "38293") + AreaCode.create(:country => germany, :name => "Neubukow", :area_code => "38294") + AreaCode.create(:country => germany, :name => "Satow b Bad Doberan", :area_code => "38295") + AreaCode.create(:country => germany, :name => "Rerik Ostseebad", :area_code => "38296") + AreaCode.create(:country => germany, :name => "Moitin", :area_code => "38297") + AreaCode.create(:country => germany, :name => "Insel Hiddensee", :area_code => "38300") + AreaCode.create(:country => germany, :name => "Putbus", :area_code => "38301") + AreaCode.create(:country => germany, :name => "Sagard", :area_code => "38302") + AreaCode.create(:country => germany, :name => "Sellin Ostseebad", :area_code => "38303") + AreaCode.create(:country => germany, :name => "Garz Rügen", :area_code => "38304") + AreaCode.create(:country => germany, :name => "Gingst", :area_code => "38305") + AreaCode.create(:country => germany, :name => "Samtens", :area_code => "38306") + AreaCode.create(:country => germany, :name => "Poseritz", :area_code => "38307") + AreaCode.create(:country => germany, :name => "Göhren Rügen", :area_code => "38308") + AreaCode.create(:country => germany, :name => "Trent", :area_code => "38309") + AreaCode.create(:country => germany, :name => "Stralsund", :area_code => "3831") + AreaCode.create(:country => germany, :name => "Tribsees", :area_code => "38320") + AreaCode.create(:country => germany, :name => "Martensdorf b Stralsund", :area_code => "38321") + AreaCode.create(:country => germany, :name => "Richtenberg", :area_code => "38322") + AreaCode.create(:country => germany, :name => "Prohn", :area_code => "38323") + AreaCode.create(:country => germany, :name => "Velgast", :area_code => "38324") + AreaCode.create(:country => germany, :name => "Rolofshagen", :area_code => "38325") + AreaCode.create(:country => germany, :name => "Grimmen", :area_code => "38326") + AreaCode.create(:country => germany, :name => "Elmenhorst Vorpom", :area_code => "38327") + AreaCode.create(:country => germany, :name => "Miltzow", :area_code => "38328") + AreaCode.create(:country => germany, :name => "Rakow Vorpom", :area_code => "38331") + AreaCode.create(:country => germany, :name => "Gross Bisdorf", :area_code => "38332") + AreaCode.create(:country => germany, :name => "Horst b Grimmen", :area_code => "38333") + AreaCode.create(:country => germany, :name => "Grammendorf", :area_code => "38334") + AreaCode.create(:country => germany, :name => "Greifswald", :area_code => "3834") + AreaCode.create(:country => germany, :name => "Mesekenhagen", :area_code => "38351") + AreaCode.create(:country => germany, :name => "Kemnitz b Greifswald", :area_code => "38352") + AreaCode.create(:country => germany, :name => "Gützkow b Greifswald", :area_code => "38353") + AreaCode.create(:country => germany, :name => "Wusterhusen", :area_code => "38354") + AreaCode.create(:country => germany, :name => "Züssow", :area_code => "38355") + AreaCode.create(:country => germany, :name => "Behrenhoff", :area_code => "38356") + AreaCode.create(:country => germany, :name => "Wolgast", :area_code => "3836") + AreaCode.create(:country => germany, :name => "Kröslin", :area_code => "38370") + AreaCode.create(:country => germany, :name => "Karlshagen", :area_code => "38371") + AreaCode.create(:country => germany, :name => "Usedom", :area_code => "38372") + AreaCode.create(:country => germany, :name => "Katzow", :area_code => "38373") + AreaCode.create(:country => germany, :name => "Lassan b Wolgast", :area_code => "38374") + AreaCode.create(:country => germany, :name => "Koserow", :area_code => "38375") + AreaCode.create(:country => germany, :name => "Zirchow", :area_code => "38376") + AreaCode.create(:country => germany, :name => "Zinnowitz", :area_code => "38377") + AreaCode.create(:country => germany, :name => "Heringsdorf Seebad", :area_code => "38378") + AreaCode.create(:country => germany, :name => "Benz Usedom", :area_code => "38379") + AreaCode.create(:country => germany, :name => "Bergen auf Rügen", :area_code => "3838") + AreaCode.create(:country => germany, :name => "Altenkirchen Rügen", :area_code => "38391") + AreaCode.create(:country => germany, :name => "Sassnitz", :area_code => "38392") + AreaCode.create(:country => germany, :name => "Binz Ostseebad", :area_code => "38393") + AreaCode.create(:country => germany, :name => "Wismar Meckl", :area_code => "3841") + AreaCode.create(:country => germany, :name => "Neukloster", :area_code => "38422") + AreaCode.create(:country => germany, :name => "Bad Kleinen", :area_code => "38423") + AreaCode.create(:country => germany, :name => "Bobitz", :area_code => "38424") + AreaCode.create(:country => germany, :name => "Kirchdorf Poel", :area_code => "38425") + AreaCode.create(:country => germany, :name => "Neuburg-Steinhausen", :area_code => "38426") + AreaCode.create(:country => germany, :name => "Blowatz", :area_code => "38427") + AreaCode.create(:country => germany, :name => "Hohenkirchen b Wismar", :area_code => "38428") + AreaCode.create(:country => germany, :name => "Glasin", :area_code => "38429") + AreaCode.create(:country => germany, :name => "Güstrow", :area_code => "3843") + AreaCode.create(:country => germany, :name => "Schwaan", :area_code => "3844") + AreaCode.create(:country => germany, :name => "Tarnow b Bützow", :area_code => "38450") + AreaCode.create(:country => germany, :name => "Hoppenrade b Güstrow", :area_code => "38451") + AreaCode.create(:country => germany, :name => "Lalendorf", :area_code => "38452") + AreaCode.create(:country => germany, :name => "Mistorf", :area_code => "38453") + AreaCode.create(:country => germany, :name => "Kritzkow", :area_code => "38454") + AreaCode.create(:country => germany, :name => "Plaaz", :area_code => "38455") + AreaCode.create(:country => germany, :name => "Langhagen b Güstrow", :area_code => "38456") + AreaCode.create(:country => germany, :name => "Krakow am See", :area_code => "38457") + AreaCode.create(:country => germany, :name => "Zehna", :area_code => "38458") + AreaCode.create(:country => germany, :name => "Laage", :area_code => "38459") + AreaCode.create(:country => germany, :name => "Bützow", :area_code => "38461") + AreaCode.create(:country => germany, :name => "Baumgarten Meckl", :area_code => "38462") + AreaCode.create(:country => germany, :name => "Bernitt", :area_code => "38464") + AreaCode.create(:country => germany, :name => "Jürgenshagen", :area_code => "38466") + AreaCode.create(:country => germany, :name => "Sternberg", :area_code => "3847") + AreaCode.create(:country => germany, :name => "Witzin", :area_code => "38481") + AreaCode.create(:country => germany, :name => "Warin", :area_code => "38482") + AreaCode.create(:country => germany, :name => "Brüel", :area_code => "38483") + AreaCode.create(:country => germany, :name => "Ventschow", :area_code => "38484") + AreaCode.create(:country => germany, :name => "Dabel", :area_code => "38485") + AreaCode.create(:country => germany, :name => "Gustävel", :area_code => "38486") + AreaCode.create(:country => germany, :name => "Demen", :area_code => "38488") + AreaCode.create(:country => germany, :name => "Schwerin Meckl", :area_code => "385") + AreaCode.create(:country => germany, :name => "Raben Steinfeld", :area_code => "3860") + AreaCode.create(:country => germany, :name => "Plate", :area_code => "3861") + AreaCode.create(:country => germany, :name => "Crivitz", :area_code => "3863") + AreaCode.create(:country => germany, :name => "Holthusen", :area_code => "3865") + AreaCode.create(:country => germany, :name => "Cambs", :area_code => "3866") + AreaCode.create(:country => germany, :name => "Lübstorf", :area_code => "3867") + AreaCode.create(:country => germany, :name => "Rastow", :area_code => "3868") + AreaCode.create(:country => germany, :name => "Dümmer", :area_code => "3869") + AreaCode.create(:country => germany, :name => "Parchim", :area_code => "3871") + AreaCode.create(:country => germany, :name => "Grebbin", :area_code => "38720") + AreaCode.create(:country => germany, :name => "Ziegendorf", :area_code => "38721") + AreaCode.create(:country => germany, :name => "Raduhn", :area_code => "38722") + AreaCode.create(:country => germany, :name => "Kladrum", :area_code => "38723") + AreaCode.create(:country => germany, :name => "Siggelkow", :area_code => "38724") + AreaCode.create(:country => germany, :name => "Gross Godems", :area_code => "38725") + AreaCode.create(:country => germany, :name => "Spornitz", :area_code => "38726") + AreaCode.create(:country => germany, :name => "Mestlin", :area_code => "38727") + AreaCode.create(:country => germany, :name => "Domsühl", :area_code => "38728") + AreaCode.create(:country => germany, :name => "Marnitz", :area_code => "38729") + AreaCode.create(:country => germany, :name => "Lübz", :area_code => "38731") + AreaCode.create(:country => germany, :name => "Gallin b Lübz", :area_code => "38732") + AreaCode.create(:country => germany, :name => "Karbow-Vietlübbe", :area_code => "38733") + AreaCode.create(:country => germany, :name => "Plau am See", :area_code => "38735") + AreaCode.create(:country => germany, :name => "Goldberg Meckl", :area_code => "38736") + AreaCode.create(:country => germany, :name => "Ganzlin", :area_code => "38737") + AreaCode.create(:country => germany, :name => "Karow b Lübz", :area_code => "38738") + AreaCode.create(:country => germany, :name => "Ludwigslust Meckl", :area_code => "3874") + AreaCode.create(:country => germany, :name => "Malliss", :area_code => "38750") + AreaCode.create(:country => germany, :name => "Picher", :area_code => "38751") + AreaCode.create(:country => germany, :name => "Zierzow b Ludwigslust", :area_code => "38752") + AreaCode.create(:country => germany, :name => "Wöbbelin", :area_code => "38753") + AreaCode.create(:country => germany, :name => "Leussow b Ludwigslust", :area_code => "38754") + AreaCode.create(:country => germany, :name => "Eldena", :area_code => "38755") + AreaCode.create(:country => germany, :name => "Grabow Meckl", :area_code => "38756") + AreaCode.create(:country => germany, :name => "Neustadt-Glewe", :area_code => "38757") + AreaCode.create(:country => germany, :name => "Dömitz", :area_code => "38758") + AreaCode.create(:country => germany, :name => "Tewswoos", :area_code => "38759") + AreaCode.create(:country => germany, :name => "Perleberg", :area_code => "3876") + AreaCode.create(:country => germany, :name => "Wittenberge", :area_code => "3877") + AreaCode.create(:country => germany, :name => "Lanz Brandenb", :area_code => "38780") + AreaCode.create(:country => germany, :name => "Mellen", :area_code => "38781") + AreaCode.create(:country => germany, :name => "Reetz b Perleberg", :area_code => "38782") + AreaCode.create(:country => germany, :name => "Dallmin", :area_code => "38783") + AreaCode.create(:country => germany, :name => "Kleinow Kr Prignitz", :area_code => "38784") + AreaCode.create(:country => germany, :name => "Berge b Perleberg", :area_code => "38785") + AreaCode.create(:country => germany, :name => "Glöwen", :area_code => "38787") + AreaCode.create(:country => germany, :name => "Gross Warnow", :area_code => "38788") + AreaCode.create(:country => germany, :name => "Wolfshagen b Perleberg", :area_code => "38789") + AreaCode.create(:country => germany, :name => "Bad Wilsnack", :area_code => "38791") + AreaCode.create(:country => germany, :name => "Lenzen (Elbe)", :area_code => "38792") + AreaCode.create(:country => germany, :name => "Dergenthin", :area_code => "38793") + AreaCode.create(:country => germany, :name => "Cumlosen", :area_code => "38794") + AreaCode.create(:country => germany, :name => "Viesecke", :area_code => "38796") + AreaCode.create(:country => germany, :name => "Karstädt Kr Prignitz", :area_code => "38797") + AreaCode.create(:country => germany, :name => "Grevesmühlen", :area_code => "3881") + AreaCode.create(:country => germany, :name => "Lüdersdorf Meckl", :area_code => "38821") + AreaCode.create(:country => germany, :name => "Diedrichshagen b Grevesmühlen", :area_code => "38822") + AreaCode.create(:country => germany, :name => "Selmsdorf", :area_code => "38823") + AreaCode.create(:country => germany, :name => "Mallentin", :area_code => "38824") + AreaCode.create(:country => germany, :name => "Klütz", :area_code => "38825") + AreaCode.create(:country => germany, :name => "Dassow", :area_code => "38826") + AreaCode.create(:country => germany, :name => "Kalkhorst", :area_code => "38827") + AreaCode.create(:country => germany, :name => "Schönberg Meckl", :area_code => "38828") + AreaCode.create(:country => germany, :name => "Hagenow", :area_code => "3883") + AreaCode.create(:country => germany, :name => "Neuhaus Elbe", :area_code => "38841") + AreaCode.create(:country => germany, :name => "Lüttenmark", :area_code => "38842") + AreaCode.create(:country => germany, :name => "Bennin", :area_code => "38843") + AreaCode.create(:country => germany, :name => "Gülze", :area_code => "38844") + AreaCode.create(:country => germany, :name => "Kaarssen", :area_code => "38845") + AreaCode.create(:country => germany, :name => "Boizenburg Elbe", :area_code => "38847") + AreaCode.create(:country => germany, :name => "Vellahn", :area_code => "38848") + AreaCode.create(:country => germany, :name => "Gammelin", :area_code => "38850") + AreaCode.create(:country => germany, :name => "Zarrentin Meckl", :area_code => "38851") + AreaCode.create(:country => germany, :name => "Wittenburg", :area_code => "38852") + AreaCode.create(:country => germany, :name => "Drönnewitz b Hagenow", :area_code => "38853") + AreaCode.create(:country => germany, :name => "Redefin", :area_code => "38854") + AreaCode.create(:country => germany, :name => "Lübtheen", :area_code => "38855") + AreaCode.create(:country => germany, :name => "Pritzier b Hagenow", :area_code => "38856") + AreaCode.create(:country => germany, :name => "Lassahn", :area_code => "38858") + AreaCode.create(:country => germany, :name => "Alt Zachun", :area_code => "38859") + AreaCode.create(:country => germany, :name => "Gadebusch", :area_code => "3886") + AreaCode.create(:country => germany, :name => "Mühlen Eichsen", :area_code => "38871") + AreaCode.create(:country => germany, :name => "Rehna", :area_code => "38872") + AreaCode.create(:country => germany, :name => "Carlow", :area_code => "38873") + AreaCode.create(:country => germany, :name => "Lützow", :area_code => "38874") + AreaCode.create(:country => germany, :name => "Schlagsdorf b Gadebusch", :area_code => "38875") + AreaCode.create(:country => germany, :name => "Roggendorf", :area_code => "38876") + AreaCode.create(:country => germany, :name => "Beetzendorf", :area_code => "39000") + AreaCode.create(:country => germany, :name => "Apenburg", :area_code => "39001") + AreaCode.create(:country => germany, :name => "Oebisfelde", :area_code => "39002") + AreaCode.create(:country => germany, :name => "Jübar", :area_code => "39003") + AreaCode.create(:country => germany, :name => "Köckte b Gardelegen", :area_code => "39004") + AreaCode.create(:country => germany, :name => "Kusey", :area_code => "39005") + AreaCode.create(:country => germany, :name => "Miesterhorst", :area_code => "39006") + AreaCode.create(:country => germany, :name => "Tangeln", :area_code => "39007") + AreaCode.create(:country => germany, :name => "Kunrau", :area_code => "39008") + AreaCode.create(:country => germany, :name => "Badel", :area_code => "39009") + AreaCode.create(:country => germany, :name => "Salzwedel", :area_code => "3901") + AreaCode.create(:country => germany, :name => "Diesdorf Altm", :area_code => "3902") + AreaCode.create(:country => germany, :name => "Brunau", :area_code => "39030") + AreaCode.create(:country => germany, :name => "Dähre", :area_code => "39031") + AreaCode.create(:country => germany, :name => "Mahlsdorf b Salzwedel", :area_code => "39032") + AreaCode.create(:country => germany, :name => "Wallstawe", :area_code => "39033") + AreaCode.create(:country => germany, :name => "Fleetmark", :area_code => "39034") + AreaCode.create(:country => germany, :name => "Kuhfelde", :area_code => "39035") + AreaCode.create(:country => germany, :name => "Binde", :area_code => "39036") + AreaCode.create(:country => germany, :name => "Pretzier", :area_code => "39037") + AreaCode.create(:country => germany, :name => "Henningen", :area_code => "39038") + AreaCode.create(:country => germany, :name => "Bonese", :area_code => "39039") + AreaCode.create(:country => germany, :name => "Haldensleben", :area_code => "3904") + AreaCode.create(:country => germany, :name => "Bartensleben", :area_code => "39050") + AreaCode.create(:country => germany, :name => "Calvörde", :area_code => "39051") + AreaCode.create(:country => germany, :name => "Erxleben b Haldensleben", :area_code => "39052") + AreaCode.create(:country => germany, :name => "Süplingen", :area_code => "39053") + AreaCode.create(:country => germany, :name => "Flechtingen", :area_code => "39054") + AreaCode.create(:country => germany, :name => "Hörsingen", :area_code => "39055") + AreaCode.create(:country => germany, :name => "Klüden", :area_code => "39056") + AreaCode.create(:country => germany, :name => "Rätzlingen Sachs-Anh", :area_code => "39057") + AreaCode.create(:country => germany, :name => "Uthmöden", :area_code => "39058") + AreaCode.create(:country => germany, :name => "Wegenstedt", :area_code => "39059") + AreaCode.create(:country => germany, :name => "Weferlingen", :area_code => "39061") + AreaCode.create(:country => germany, :name => "Bebertal", :area_code => "39062") + AreaCode.create(:country => germany, :name => "Gardelegen", :area_code => "3907") + AreaCode.create(:country => germany, :name => "Kalbe Milde", :area_code => "39080") + AreaCode.create(:country => germany, :name => "Kakerbeck Sachs-Anh", :area_code => "39081") + AreaCode.create(:country => germany, :name => "Mieste", :area_code => "39082") + AreaCode.create(:country => germany, :name => "Messdorf", :area_code => "39083") + AreaCode.create(:country => germany, :name => "Lindstedt", :area_code => "39084") + AreaCode.create(:country => germany, :name => "Zichtau", :area_code => "39085") + AreaCode.create(:country => germany, :name => "Jävenitz", :area_code => "39086") + AreaCode.create(:country => germany, :name => "Jerchel Altmark", :area_code => "39087") + AreaCode.create(:country => germany, :name => "Letzlingen", :area_code => "39088") + AreaCode.create(:country => germany, :name => "Bismark Altmark", :area_code => "39089") + AreaCode.create(:country => germany, :name => "Klötze Altmark", :area_code => "3909") + AreaCode.create(:country => germany, :name => "Magdeburg", :area_code => "391") + AreaCode.create(:country => germany, :name => "Gommern", :area_code => "39200") + AreaCode.create(:country => germany, :name => "Wolmirstedt", :area_code => "39201") + AreaCode.create(:country => germany, :name => "Gross Ammensleben", :area_code => "39202") + AreaCode.create(:country => germany, :name => "Barleben", :area_code => "39203") + AreaCode.create(:country => germany, :name => "Niederndodeleben", :area_code => "39204") + AreaCode.create(:country => germany, :name => "Langenweddingen", :area_code => "39205") + AreaCode.create(:country => germany, :name => "Eichenbarleben", :area_code => "39206") + AreaCode.create(:country => germany, :name => "Colbitz", :area_code => "39207") + AreaCode.create(:country => germany, :name => "Loitsche", :area_code => "39208") + AreaCode.create(:country => germany, :name => "Wanzleben", :area_code => "39209") + AreaCode.create(:country => germany, :name => "Burg b Magdeburg", :area_code => "3921") + AreaCode.create(:country => germany, :name => "Möckern b Magdeburg", :area_code => "39221") + AreaCode.create(:country => germany, :name => "Möser", :area_code => "39222") + AreaCode.create(:country => germany, :name => "Theessen", :area_code => "39223") + AreaCode.create(:country => germany, :name => "Büden", :area_code => "39224") + AreaCode.create(:country => germany, :name => "Altengrabow", :area_code => "39225") + AreaCode.create(:country => germany, :name => "Hohenziatz", :area_code => "39226") + AreaCode.create(:country => germany, :name => "Zerbst", :area_code => "3923") + AreaCode.create(:country => germany, :name => "Leitzkau", :area_code => "39241") + AreaCode.create(:country => germany, :name => "Prödel", :area_code => "39242") + AreaCode.create(:country => germany, :name => "Nedlitz b Zerbst", :area_code => "39243") + AreaCode.create(:country => germany, :name => "Steutz", :area_code => "39244") + AreaCode.create(:country => germany, :name => "Loburg", :area_code => "39245") + AreaCode.create(:country => germany, :name => "Lindau Anh", :area_code => "39246") + AreaCode.create(:country => germany, :name => "Güterglück", :area_code => "39247") + AreaCode.create(:country => germany, :name => "Dobritz", :area_code => "39248") + AreaCode.create(:country => germany, :name => "Stassfurt", :area_code => "3925") + AreaCode.create(:country => germany, :name => "Güsten Anh", :area_code => "39262") + AreaCode.create(:country => germany, :name => "Unseburg", :area_code => "39263") + AreaCode.create(:country => germany, :name => "Kroppenstedt", :area_code => "39264") + AreaCode.create(:country => germany, :name => "Löderburg", :area_code => "39265") + AreaCode.create(:country => germany, :name => "Förderstedt", :area_code => "39266") + AreaCode.create(:country => germany, :name => "Schneidlingen", :area_code => "39267") + AreaCode.create(:country => germany, :name => "Egeln", :area_code => "39268") + AreaCode.create(:country => germany, :name => "Schönebeck Elbe", :area_code => "3928") + AreaCode.create(:country => germany, :name => "Calbe Saale", :area_code => "39291") + AreaCode.create(:country => germany, :name => "Biederitz", :area_code => "39292") + AreaCode.create(:country => germany, :name => "Dreileben", :area_code => "39293") + AreaCode.create(:country => germany, :name => "Gross Rosenburg", :area_code => "39294") + AreaCode.create(:country => germany, :name => "Zuchau", :area_code => "39295") + AreaCode.create(:country => germany, :name => "Welsleben", :area_code => "39296") + AreaCode.create(:country => germany, :name => "Eickendorf Kr Schönebeck", :area_code => "39297") + AreaCode.create(:country => germany, :name => "Barby Elbe", :area_code => "39298") + AreaCode.create(:country => germany, :name => "Stendal", :area_code => "3931") + AreaCode.create(:country => germany, :name => "Schinne", :area_code => "39320") + AreaCode.create(:country => germany, :name => "Arneburg", :area_code => "39321") + AreaCode.create(:country => germany, :name => "Tangermünde", :area_code => "39322") + AreaCode.create(:country => germany, :name => "Schönhausen Elbe", :area_code => "39323") + AreaCode.create(:country => germany, :name => "Kläden b Stendal", :area_code => "39324") + AreaCode.create(:country => germany, :name => "Vinzelberg", :area_code => "39325") + AreaCode.create(:country => germany, :name => "Klietz", :area_code => "39327") + AreaCode.create(:country => germany, :name => "Rochau", :area_code => "39328") + AreaCode.create(:country => germany, :name => "Möringen", :area_code => "39329") + AreaCode.create(:country => germany, :name => "Genthin", :area_code => "3933") + AreaCode.create(:country => germany, :name => "Redekin", :area_code => "39341") + AreaCode.create(:country => germany, :name => "Gladau", :area_code => "39342") + AreaCode.create(:country => germany, :name => "Jerichow", :area_code => "39343") + AreaCode.create(:country => germany, :name => "Güsen", :area_code => "39344") + AreaCode.create(:country => germany, :name => "Parchen", :area_code => "39345") + AreaCode.create(:country => germany, :name => "Tucheim", :area_code => "39346") + AreaCode.create(:country => germany, :name => "Kade", :area_code => "39347") + AreaCode.create(:country => germany, :name => "Klitsche", :area_code => "39348") + AreaCode.create(:country => germany, :name => "Parey Elbe", :area_code => "39349") + AreaCode.create(:country => germany, :name => "Tangerhütte", :area_code => "3935") + AreaCode.create(:country => germany, :name => "Lüderitz", :area_code => "39361") + AreaCode.create(:country => germany, :name => "Grieben b Tangerhütte", :area_code => "39362") + AreaCode.create(:country => germany, :name => "Angern", :area_code => "39363") + AreaCode.create(:country => germany, :name => "Dolle", :area_code => "39364") + AreaCode.create(:country => germany, :name => "Bellingen b Stendal", :area_code => "39365") + AreaCode.create(:country => germany, :name => "Kehnert", :area_code => "39366") + AreaCode.create(:country => germany, :name => "Osterburg Altmark", :area_code => "3937") + AreaCode.create(:country => germany, :name => "Kamern", :area_code => "39382") + AreaCode.create(:country => germany, :name => "Sandau Elbe", :area_code => "39383") + AreaCode.create(:country => germany, :name => "Arendsee Altmark", :area_code => "39384") + AreaCode.create(:country => germany, :name => "Seehausen Altmark", :area_code => "39386") + AreaCode.create(:country => germany, :name => "Havelberg", :area_code => "39387") + AreaCode.create(:country => germany, :name => "Goldbeck Altm", :area_code => "39388") + AreaCode.create(:country => germany, :name => "Schollene", :area_code => "39389") + AreaCode.create(:country => germany, :name => "Iden", :area_code => "39390") + AreaCode.create(:country => germany, :name => "Lückstedt", :area_code => "39391") + AreaCode.create(:country => germany, :name => "Rönnebeck Sachs-Ahn", :area_code => "39392") + AreaCode.create(:country => germany, :name => "Werben Elbe", :area_code => "39393") + AreaCode.create(:country => germany, :name => "Hohenberg-Krusemark", :area_code => "39394") + AreaCode.create(:country => germany, :name => "Wanzer", :area_code => "39395") + AreaCode.create(:country => germany, :name => "Neukirchen Altmark", :area_code => "39396") + AreaCode.create(:country => germany, :name => "Geestgottberg", :area_code => "39397") + AreaCode.create(:country => germany, :name => "Gross Garz", :area_code => "39398") + AreaCode.create(:country => germany, :name => "Kleinau", :area_code => "39399") + AreaCode.create(:country => germany, :name => "Wefensleben", :area_code => "39400") + AreaCode.create(:country => germany, :name => "Neuwegersleben", :area_code => "39401") + AreaCode.create(:country => germany, :name => "Völpke", :area_code => "39402") + AreaCode.create(:country => germany, :name => "Gröningen Sachs-Ahn", :area_code => "39403") + AreaCode.create(:country => germany, :name => "Ausleben", :area_code => "39404") + AreaCode.create(:country => germany, :name => "Hötensleben", :area_code => "39405") + AreaCode.create(:country => germany, :name => "Harbke", :area_code => "39406") + AreaCode.create(:country => germany, :name => "Seehausen Börde", :area_code => "39407") + AreaCode.create(:country => germany, :name => "Hadmersleben", :area_code => "39408") + AreaCode.create(:country => germany, :name => "Eilsleben", :area_code => "39409") + AreaCode.create(:country => germany, :name => "Halberstadt", :area_code => "3941") + AreaCode.create(:country => germany, :name => "Osterwieck", :area_code => "39421") + AreaCode.create(:country => germany, :name => "Badersleben", :area_code => "39422") + AreaCode.create(:country => germany, :name => "Wegeleben", :area_code => "39423") + AreaCode.create(:country => germany, :name => "Schwanebeck Sachs-Anh", :area_code => "39424") + AreaCode.create(:country => germany, :name => "Dingelstedt a Huy", :area_code => "39425") + AreaCode.create(:country => germany, :name => "Hessen", :area_code => "39426") + AreaCode.create(:country => germany, :name => "Ströbeck", :area_code => "39427") + AreaCode.create(:country => germany, :name => "Pabstorf", :area_code => "39428") + AreaCode.create(:country => germany, :name => "Wernigerode", :area_code => "3943") + AreaCode.create(:country => germany, :name => "Blankenburg Harz", :area_code => "3944") + AreaCode.create(:country => germany, :name => "Wasserleben", :area_code => "39451") + AreaCode.create(:country => germany, :name => "Ilsenburg", :area_code => "39452") + AreaCode.create(:country => germany, :name => "Derenburg", :area_code => "39453") + AreaCode.create(:country => germany, :name => "Elbingerode Harz", :area_code => "39454") + AreaCode.create(:country => germany, :name => "Schierke", :area_code => "39455") + AreaCode.create(:country => germany, :name => "Altenbrak", :area_code => "39456") + AreaCode.create(:country => germany, :name => "Benneckenstein Harz", :area_code => "39457") + AreaCode.create(:country => germany, :name => "Heudeber", :area_code => "39458") + AreaCode.create(:country => germany, :name => "Hasselfelde", :area_code => "39459") + AreaCode.create(:country => germany, :name => "Quedlinburg", :area_code => "3946") + AreaCode.create(:country => germany, :name => "Thale", :area_code => "3947") + AreaCode.create(:country => germany, :name => "Hedersleben b Aschersleben", :area_code => "39481") + AreaCode.create(:country => germany, :name => "Gatersleben", :area_code => "39482") + AreaCode.create(:country => germany, :name => "Ballenstedt", :area_code => "39483") + AreaCode.create(:country => germany, :name => "Harzgerode", :area_code => "39484") + AreaCode.create(:country => germany, :name => "Gernrode Harz", :area_code => "39485") + AreaCode.create(:country => germany, :name => "Friedrichsbrunn", :area_code => "39487") + AreaCode.create(:country => germany, :name => "Güntersberge", :area_code => "39488") + AreaCode.create(:country => germany, :name => "Strassberg Harz", :area_code => "39489") + AreaCode.create(:country => germany, :name => "Oschersleben Bode", :area_code => "3949") + AreaCode.create(:country => germany, :name => "Neubrandenburg", :area_code => "395") + AreaCode.create(:country => germany, :name => "Zwiedorf", :area_code => "39600") + AreaCode.create(:country => germany, :name => "Friedland Meckl", :area_code => "39601") + AreaCode.create(:country => germany, :name => "Kleeth", :area_code => "39602") + AreaCode.create(:country => germany, :name => "Burg Stargard", :area_code => "39603") + AreaCode.create(:country => germany, :name => "Wildberg b Altentreptow", :area_code => "39604") + AreaCode.create(:country => germany, :name => "Gross Nemerow", :area_code => "39605") + AreaCode.create(:country => germany, :name => "Glienke", :area_code => "39606") + AreaCode.create(:country => germany, :name => "Kotelow", :area_code => "39607") + AreaCode.create(:country => germany, :name => "Staven", :area_code => "39608") + AreaCode.create(:country => germany, :name => "Altentreptow", :area_code => "3961") + AreaCode.create(:country => germany, :name => "Penzlin b Waren", :area_code => "3962") + AreaCode.create(:country => germany, :name => "Woldegk", :area_code => "3963") + AreaCode.create(:country => germany, :name => "Bredenfelde b Strasburg", :area_code => "3964") + AreaCode.create(:country => germany, :name => "Burow b Altentreptow", :area_code => "3965") + AreaCode.create(:country => germany, :name => "Cölpin", :area_code => "3966") + AreaCode.create(:country => germany, :name => "Oertzenhof b Strasburg", :area_code => "3967") + AreaCode.create(:country => germany, :name => "Schönbeck Meckl", :area_code => "3968") + AreaCode.create(:country => germany, :name => "Siedenbollentin", :area_code => "3969") + AreaCode.create(:country => germany, :name => "Anklam", :area_code => "3971") + AreaCode.create(:country => germany, :name => "Liepen b Anklam", :area_code => "39721") + AreaCode.create(:country => germany, :name => "Sarnow b Anklam", :area_code => "39722") + AreaCode.create(:country => germany, :name => "Krien", :area_code => "39723") + AreaCode.create(:country => germany, :name => "Klein Bünzow", :area_code => "39724") + AreaCode.create(:country => germany, :name => "Ducherow", :area_code => "39726") + AreaCode.create(:country => germany, :name => "Spantekow", :area_code => "39727") + AreaCode.create(:country => germany, :name => "Medow b Anklam", :area_code => "39728") + AreaCode.create(:country => germany, :name => "Pasewalk", :area_code => "3973") + AreaCode.create(:country => germany, :name => "Nechlin", :area_code => "39740") + AreaCode.create(:country => germany, :name => "Jatznick", :area_code => "39741") + AreaCode.create(:country => germany, :name => "Brüssow b Pasewalk", :area_code => "39742") + AreaCode.create(:country => germany, :name => "Zerrenthin", :area_code => "39743") + AreaCode.create(:country => germany, :name => "Rothenklempenow", :area_code => "39744") + AreaCode.create(:country => germany, :name => "Hetzdorf b Strasburg", :area_code => "39745") + AreaCode.create(:country => germany, :name => "Krackow", :area_code => "39746") + AreaCode.create(:country => germany, :name => "Züsedom", :area_code => "39747") + AreaCode.create(:country => germany, :name => "Viereck", :area_code => "39748") + AreaCode.create(:country => germany, :name => "Grambow b Pasewalk", :area_code => "39749") + AreaCode.create(:country => germany, :name => "Penkun", :area_code => "39751") + AreaCode.create(:country => germany, :name => "Blumenhagen b Strasburg", :area_code => "39752") + AreaCode.create(:country => germany, :name => "Strasburg", :area_code => "39753") + AreaCode.create(:country => germany, :name => "Löcknitz Vorpom", :area_code => "39754") + AreaCode.create(:country => germany, :name => "Torgelow b Ueckermünde", :area_code => "3976") + AreaCode.create(:country => germany, :name => "Ueckermünde", :area_code => "39771") + AreaCode.create(:country => germany, :name => "Rothemühl", :area_code => "39772") + AreaCode.create(:country => germany, :name => "Altwarp", :area_code => "39773") + AreaCode.create(:country => germany, :name => "Mönkebude", :area_code => "39774") + AreaCode.create(:country => germany, :name => "Ahlbeck b Torgelow", :area_code => "39775") + AreaCode.create(:country => germany, :name => "Hintersee", :area_code => "39776") + AreaCode.create(:country => germany, :name => "Borkenfriede", :area_code => "39777") + AreaCode.create(:country => germany, :name => "Ferdinandshof b Torgelow", :area_code => "39778") + AreaCode.create(:country => germany, :name => "Eggesin", :area_code => "39779") + AreaCode.create(:country => germany, :name => "Neustrelitz", :area_code => "3981") + AreaCode.create(:country => germany, :name => "Triepkendorf", :area_code => "39820") + AreaCode.create(:country => germany, :name => "Carpin", :area_code => "39821") + AreaCode.create(:country => germany, :name => "Kratzeburg", :area_code => "39822") + AreaCode.create(:country => germany, :name => "Rechlin", :area_code => "39823") + AreaCode.create(:country => germany, :name => "Hohenzieritz", :area_code => "39824") + AreaCode.create(:country => germany, :name => "Wokuhl", :area_code => "39825") + AreaCode.create(:country => germany, :name => "Blankensee b Neustrelitz", :area_code => "39826") + AreaCode.create(:country => germany, :name => "Schwarz b Neustrelitz", :area_code => "39827") + AreaCode.create(:country => germany, :name => "Wustrow Kr Mecklenburg-Strelitz", :area_code => "39828") + AreaCode.create(:country => germany, :name => "Blankenförde", :area_code => "39829") + AreaCode.create(:country => germany, :name => "Feldberg Meckl", :area_code => "39831") + AreaCode.create(:country => germany, :name => "Wesenberg Meckl", :area_code => "39832") + AreaCode.create(:country => germany, :name => "Mirow Kr Neustrelitz", :area_code => "39833") + AreaCode.create(:country => germany, :name => "Prenzlau", :area_code => "3984") + AreaCode.create(:country => germany, :name => "Göritz b Prenzlau", :area_code => "39851") + AreaCode.create(:country => germany, :name => "Schönermark b Prenzlau", :area_code => "39852") + AreaCode.create(:country => germany, :name => "Holzendorf b Prenzlau", :area_code => "39853") + AreaCode.create(:country => germany, :name => "Kleptow", :area_code => "39854") + AreaCode.create(:country => germany, :name => "Parmen-Weggun", :area_code => "39855") + AreaCode.create(:country => germany, :name => "Beenz b Prenzlau", :area_code => "39856") + AreaCode.create(:country => germany, :name => "Drense", :area_code => "39857") + AreaCode.create(:country => germany, :name => "Bietikow", :area_code => "39858") + AreaCode.create(:country => germany, :name => "Fürstenwerder", :area_code => "39859") + AreaCode.create(:country => germany, :name => "Gramzow b Prenzlau", :area_code => "39861") + AreaCode.create(:country => germany, :name => "Schmölln b Prenzlau", :area_code => "39862") + AreaCode.create(:country => germany, :name => "Seehausen b Prenzlau", :area_code => "39863") + AreaCode.create(:country => germany, :name => "Templin", :area_code => "3987") + AreaCode.create(:country => germany, :name => "Ringenwalde b Templin", :area_code => "39881") + AreaCode.create(:country => germany, :name => "Gollin", :area_code => "39882") + AreaCode.create(:country => germany, :name => "Groß Dölln", :area_code => "39883") + AreaCode.create(:country => germany, :name => "Hassleben b Prenzlau", :area_code => "39884") + AreaCode.create(:country => germany, :name => "Jakobshagen", :area_code => "39885") + AreaCode.create(:country => germany, :name => "Milmersdorf", :area_code => "39886") + AreaCode.create(:country => germany, :name => "Gerswalde", :area_code => "39887") + AreaCode.create(:country => germany, :name => "Lychen", :area_code => "39888") + AreaCode.create(:country => germany, :name => "Boitzenburg", :area_code => "39889") + AreaCode.create(:country => germany, :name => "Waren Müritz", :area_code => "3991") + AreaCode.create(:country => germany, :name => "Ankershagen", :area_code => "39921") + AreaCode.create(:country => germany, :name => "Dambeck b Röbel", :area_code => "39922") + AreaCode.create(:country => germany, :name => "Priborn", :area_code => "39923") + AreaCode.create(:country => germany, :name => "Stuer", :area_code => "39924") + AreaCode.create(:country => germany, :name => "Wredenhagen", :area_code => "39925") + AreaCode.create(:country => germany, :name => "Grabowhöfe", :area_code => "39926") + AreaCode.create(:country => germany, :name => "Nossentiner Hütte", :area_code => "39927") + AreaCode.create(:country => germany, :name => "Möllenhagen", :area_code => "39928") + AreaCode.create(:country => germany, :name => "Jabel b Waren", :area_code => "39929") + AreaCode.create(:country => germany, :name => "Röbel Müritz", :area_code => "39931") + AreaCode.create(:country => germany, :name => "Malchow b Waren", :area_code => "39932") + AreaCode.create(:country => germany, :name => "Vollrathsruhe", :area_code => "39933") + AreaCode.create(:country => germany, :name => "Groß Plasten", :area_code => "39934") + AreaCode.create(:country => germany, :name => "Malchin", :area_code => "3994") + AreaCode.create(:country => germany, :name => "Faulenrost", :area_code => "39951") + AreaCode.create(:country => germany, :name => "Grammentin", :area_code => "39952") + AreaCode.create(:country => germany, :name => "Schwinkendorf", :area_code => "39953") + AreaCode.create(:country => germany, :name => "Stavenhagen Reuterstadt", :area_code => "39954") + AreaCode.create(:country => germany, :name => "Jürgenstorf Meckl", :area_code => "39955") + AreaCode.create(:country => germany, :name => "Neukalen", :area_code => "39956") + AreaCode.create(:country => germany, :name => "Gielow", :area_code => "39957") + AreaCode.create(:country => germany, :name => "Dargun", :area_code => "39959") + AreaCode.create(:country => germany, :name => "Teterow", :area_code => "3996") + AreaCode.create(:country => germany, :name => "Gnoien", :area_code => "39971") + AreaCode.create(:country => germany, :name => "Walkendorf", :area_code => "39972") + AreaCode.create(:country => germany, :name => "Altkalen", :area_code => "39973") + AreaCode.create(:country => germany, :name => "Thürkow", :area_code => "39975") + AreaCode.create(:country => germany, :name => "Groß Bützin", :area_code => "39976") + AreaCode.create(:country => germany, :name => "Jördenstorf", :area_code => "39977") + AreaCode.create(:country => germany, :name => "Gross Roge", :area_code => "39978") + AreaCode.create(:country => germany, :name => "Demmin", :area_code => "3998") + AreaCode.create(:country => germany, :name => "Daberkow", :area_code => "39991") + AreaCode.create(:country => germany, :name => "Görmin", :area_code => "39992") + AreaCode.create(:country => germany, :name => "Hohenmocker", :area_code => "39993") + AreaCode.create(:country => germany, :name => "Metschow", :area_code => "39994") + AreaCode.create(:country => germany, :name => "Nossendorf", :area_code => "39995") + AreaCode.create(:country => germany, :name => "Törpin", :area_code => "39996") + AreaCode.create(:country => germany, :name => "Jarmen", :area_code => "39997") + AreaCode.create(:country => germany, :name => "Loitz b Demmin", :area_code => "39998") + AreaCode.create(:country => germany, :name => "Tutow", :area_code => "39999") + AreaCode.create(:country => germany, :name => "Hamburg", :area_code => "40") + AreaCode.create(:country => germany, :name => "Pinneberg", :area_code => "4101") + AreaCode.create(:country => germany, :name => "Ahrensburg", :area_code => "4102") + AreaCode.create(:country => germany, :name => "Wedel", :area_code => "4103") + AreaCode.create(:country => germany, :name => "Aumühle b Hamburg", :area_code => "4104") + AreaCode.create(:country => germany, :name => "Seevetal", :area_code => "4105") + AreaCode.create(:country => germany, :name => "Quickborn Kr Pinneberg", :area_code => "4106") + AreaCode.create(:country => germany, :name => "Siek Kr Stormarn", :area_code => "4107") + AreaCode.create(:country => germany, :name => "Rosengarten Kr Harburg", :area_code => "4108") + AreaCode.create(:country => germany, :name => "Tangstedt Bz Hamburg", :area_code => "4109") + AreaCode.create(:country => germany, :name => "Ellerhoop", :area_code => "4120") + AreaCode.create(:country => germany, :name => "Elmshorn", :area_code => "4121") + AreaCode.create(:country => germany, :name => "Uetersen", :area_code => "4122") + AreaCode.create(:country => germany, :name => "Barmstedt", :area_code => "4123") + AreaCode.create(:country => germany, :name => "Glückstadt", :area_code => "4124") + AreaCode.create(:country => germany, :name => "Seestermühe", :area_code => "4125") + AreaCode.create(:country => germany, :name => "Horst Holstein", :area_code => "4126") + AreaCode.create(:country => germany, :name => "Westerhorn", :area_code => "4127") + AreaCode.create(:country => germany, :name => "Kollmar", :area_code => "4128") + AreaCode.create(:country => germany, :name => "Haseldorf", :area_code => "4129") + AreaCode.create(:country => germany, :name => "Lüneburg", :area_code => "4131") + AreaCode.create(:country => germany, :name => "Amelinghausen", :area_code => "4132") + AreaCode.create(:country => germany, :name => "Wittorf Kr Lüneburg", :area_code => "4133") + AreaCode.create(:country => germany, :name => "Embsen Kr Lünebeburg", :area_code => "4134") + AreaCode.create(:country => germany, :name => "Kirchgellersen", :area_code => "4135") + AreaCode.create(:country => germany, :name => "Scharnebeck", :area_code => "4136") + AreaCode.create(:country => germany, :name => "Barendorf", :area_code => "4137") + AreaCode.create(:country => germany, :name => "Betzendorf Kr Lünebeburg", :area_code => "4138") + AreaCode.create(:country => germany, :name => "Hohnstorf Elbe", :area_code => "4139") + AreaCode.create(:country => germany, :name => "Estorf Kr Stade", :area_code => "4140") + AreaCode.create(:country => germany, :name => "Stade", :area_code => "4141") + AreaCode.create(:country => germany, :name => "Steinkirchen Kr Stade", :area_code => "4142") + AreaCode.create(:country => germany, :name => "Drochtersen", :area_code => "4143") + AreaCode.create(:country => germany, :name => "Himmelpforten", :area_code => "4144") + AreaCode.create(:country => germany, :name => "Stade-Bützfleth", :area_code => "4146") + AreaCode.create(:country => germany, :name => "Drochtersen-Assel", :area_code => "4148") + AreaCode.create(:country => germany, :name => "Fredenbeck", :area_code => "4149") + AreaCode.create(:country => germany, :name => "Schwarzenbek", :area_code => "4151") + AreaCode.create(:country => germany, :name => "Geesthacht", :area_code => "4152") + AreaCode.create(:country => germany, :name => "Lauenburg Elbe", :area_code => "4153") + AreaCode.create(:country => germany, :name => "Trittau", :area_code => "4154") + AreaCode.create(:country => germany, :name => "Büchen", :area_code => "4155") + AreaCode.create(:country => germany, :name => "Talkau", :area_code => "4156") + AreaCode.create(:country => germany, :name => "Roseburg", :area_code => "4158") + AreaCode.create(:country => germany, :name => "Basthorst", :area_code => "4159") + AreaCode.create(:country => germany, :name => "Buxtehude", :area_code => "4161") + AreaCode.create(:country => germany, :name => "Jork", :area_code => "4162") + AreaCode.create(:country => germany, :name => "Horneburg Niederelbe", :area_code => "4163") + AreaCode.create(:country => germany, :name => "Harsefeld", :area_code => "4164") + AreaCode.create(:country => germany, :name => "Hollenstedt Nordheide", :area_code => "4165") + AreaCode.create(:country => germany, :name => "Ahlerstedt", :area_code => "4166") + AreaCode.create(:country => germany, :name => "Apensen", :area_code => "4167") + AreaCode.create(:country => germany, :name => "Neu Wulmstorf-Elstorf", :area_code => "4168") + AreaCode.create(:country => germany, :name => "Sauensiek", :area_code => "4169") + AreaCode.create(:country => germany, :name => "Winsen Luhe", :area_code => "4171") + AreaCode.create(:country => germany, :name => "Salzhausen", :area_code => "4172") + AreaCode.create(:country => germany, :name => "Wulfsen", :area_code => "4173") + AreaCode.create(:country => germany, :name => "Stelle Kr Harburg", :area_code => "4174") + AreaCode.create(:country => germany, :name => "Egestorf Nordheide", :area_code => "4175") + AreaCode.create(:country => germany, :name => "Marschacht", :area_code => "4176") + AreaCode.create(:country => germany, :name => "Drage Elbe", :area_code => "4177") + AreaCode.create(:country => germany, :name => "Radbruch", :area_code => "4178") + AreaCode.create(:country => germany, :name => "Winsen-Tönnhausen", :area_code => "4179") + AreaCode.create(:country => germany, :name => "Königsmoor", :area_code => "4180") + AreaCode.create(:country => germany, :name => "Buchholz in der Nordheide", :area_code => "4181") + AreaCode.create(:country => germany, :name => "Tostedt", :area_code => "4182") + AreaCode.create(:country => germany, :name => "Jesteburg", :area_code => "4183") + AreaCode.create(:country => germany, :name => "Hanstedt Nordheide", :area_code => "4184") + AreaCode.create(:country => germany, :name => "Marxen Auetal", :area_code => "4185") + AreaCode.create(:country => germany, :name => "Buchholz-Trelde", :area_code => "4186") + AreaCode.create(:country => germany, :name => "Holm-Seppensen", :area_code => "4187") + AreaCode.create(:country => germany, :name => "Welle Nordheide", :area_code => "4188") + AreaCode.create(:country => germany, :name => "Undeloh", :area_code => "4189") + AreaCode.create(:country => germany, :name => "Kaltenkirchen Holst", :area_code => "4191") + AreaCode.create(:country => germany, :name => "Bad Bramstedt", :area_code => "4192") + AreaCode.create(:country => germany, :name => "Henstedt-Ulzburg", :area_code => "4193") + AreaCode.create(:country => germany, :name => "Sievershütten", :area_code => "4194") + AreaCode.create(:country => germany, :name => "Hartenholm", :area_code => "4195") + AreaCode.create(:country => germany, :name => "Achim b Bremen", :area_code => "4202") + AreaCode.create(:country => germany, :name => "Weyhe b Bremen", :area_code => "4203") + AreaCode.create(:country => germany, :name => "Thedinghausen", :area_code => "4204") + AreaCode.create(:country => germany, :name => "Ottersberg", :area_code => "4205") + AreaCode.create(:country => germany, :name => "Stuhr-Heiligenrode", :area_code => "4206") + AreaCode.create(:country => germany, :name => "Oyten", :area_code => "4207") + AreaCode.create(:country => germany, :name => "Grasberg", :area_code => "4208") + AreaCode.create(:country => germany, :name => "Schwanewede", :area_code => "4209") + AreaCode.create(:country => germany, :name => "Bremen", :area_code => "421") + AreaCode.create(:country => germany, :name => "Delmenhorst", :area_code => "4221") + AreaCode.create(:country => germany, :name => "Ganderkesee", :area_code => "4222") + AreaCode.create(:country => germany, :name => "Ganderkesee-Bookholzberg", :area_code => "4223") + AreaCode.create(:country => germany, :name => "Gross Ippener", :area_code => "4224") + AreaCode.create(:country => germany, :name => "Verden-Walle", :area_code => "4230") + AreaCode.create(:country => germany, :name => "Verden Aller", :area_code => "4231") + AreaCode.create(:country => germany, :name => "Langwedel Kr Verden", :area_code => "4232") + AreaCode.create(:country => germany, :name => "Blender", :area_code => "4233") + AreaCode.create(:country => germany, :name => "Dörverden", :area_code => "4234") + AreaCode.create(:country => germany, :name => "Langwedel-Etelsen", :area_code => "4235") + AreaCode.create(:country => germany, :name => "Kirchlinteln", :area_code => "4236") + AreaCode.create(:country => germany, :name => "Bendingbostel", :area_code => "4237") + AreaCode.create(:country => germany, :name => "Neddenaverbergen", :area_code => "4238") + AreaCode.create(:country => germany, :name => "Dörverden-Westen", :area_code => "4239") + AreaCode.create(:country => germany, :name => "Syke-Heiligenfelde", :area_code => "4240") + AreaCode.create(:country => germany, :name => "Bassum", :area_code => "4241") + AreaCode.create(:country => germany, :name => "Syke", :area_code => "4242") + AreaCode.create(:country => germany, :name => "Twistringen", :area_code => "4243") + AreaCode.create(:country => germany, :name => "Harpstedt", :area_code => "4244") + AreaCode.create(:country => germany, :name => "Neuenkirchen b Bassum", :area_code => "4245") + AreaCode.create(:country => germany, :name => "Twistringen-Heiligenloh", :area_code => "4246") + AreaCode.create(:country => germany, :name => "Affinghausen", :area_code => "4247") + AreaCode.create(:country => germany, :name => "Bassum-Neubruchhausen", :area_code => "4248") + AreaCode.create(:country => germany, :name => "Bassum-Nordwohlde", :area_code => "4249") + AreaCode.create(:country => germany, :name => "Hoya", :area_code => "4251") + AreaCode.create(:country => germany, :name => "Bruchhausen-Vilsen", :area_code => "4252") + AreaCode.create(:country => germany, :name => "Asendorf Kr Diepholz", :area_code => "4253") + AreaCode.create(:country => germany, :name => "Eystrup", :area_code => "4254") + AreaCode.create(:country => germany, :name => "Martfeld", :area_code => "4255") + AreaCode.create(:country => germany, :name => "Hilgermissen", :area_code => "4256") + AreaCode.create(:country => germany, :name => "Schweringen", :area_code => "4257") + AreaCode.create(:country => germany, :name => "Schwarme", :area_code => "4258") + AreaCode.create(:country => germany, :name => "Visselhövede-Wittorf", :area_code => "4260") + AreaCode.create(:country => germany, :name => "Rotenburg Wümme", :area_code => "4261") + AreaCode.create(:country => germany, :name => "Visselhövede", :area_code => "4262") + AreaCode.create(:country => germany, :name => "Scheessel", :area_code => "4263") + AreaCode.create(:country => germany, :name => "Sottrum Kr Rotenburg", :area_code => "4264") + AreaCode.create(:country => germany, :name => "Fintel", :area_code => "4265") + AreaCode.create(:country => germany, :name => "Brockel", :area_code => "4266") + AreaCode.create(:country => germany, :name => "Lauenbrück", :area_code => "4267") + AreaCode.create(:country => germany, :name => "Bötersen", :area_code => "4268") + AreaCode.create(:country => germany, :name => "Ahausen-Kirchwalsede", :area_code => "4269") + AreaCode.create(:country => germany, :name => "Sulingen", :area_code => "4271") + AreaCode.create(:country => germany, :name => "Siedenburg", :area_code => "4272") + AreaCode.create(:country => germany, :name => "Kirchdorf b Sulingen", :area_code => "4273") + AreaCode.create(:country => germany, :name => "Varrel b Sulingen", :area_code => "4274") + AreaCode.create(:country => germany, :name => "Ehrenburg", :area_code => "4275") + AreaCode.create(:country => germany, :name => "Borstel b Sulingen", :area_code => "4276") + AreaCode.create(:country => germany, :name => "Schwaförden", :area_code => "4277") + AreaCode.create(:country => germany, :name => "Zeven", :area_code => "4281") + AreaCode.create(:country => germany, :name => "Sittensen", :area_code => "4282") + AreaCode.create(:country => germany, :name => "Tarmstedt", :area_code => "4283") + AreaCode.create(:country => germany, :name => "Selsingen", :area_code => "4284") + AreaCode.create(:country => germany, :name => "Rhade b Zeven", :area_code => "4285") + AreaCode.create(:country => germany, :name => "Gyhum", :area_code => "4286") + AreaCode.create(:country => germany, :name => "Heeslingen-Boitzen", :area_code => "4287") + AreaCode.create(:country => germany, :name => "Horstedt Kr Rotenburg", :area_code => "4288") + AreaCode.create(:country => germany, :name => "Kirchtimke", :area_code => "4289") + AreaCode.create(:country => germany, :name => "Ritterhude", :area_code => "4292") + AreaCode.create(:country => germany, :name => "Ottersberg-Fischerhude", :area_code => "4293") + AreaCode.create(:country => germany, :name => "Riede Kr Verden", :area_code => "4294") + AreaCode.create(:country => germany, :name => "Emtinghausen", :area_code => "4295") + AreaCode.create(:country => germany, :name => "Schwanewede-Aschwarden", :area_code => "4296") + AreaCode.create(:country => germany, :name => "Ottersberg-Posthausen", :area_code => "4297") + AreaCode.create(:country => germany, :name => "Lilienthal", :area_code => "4298") + AreaCode.create(:country => germany, :name => "Kirchbarkau", :area_code => "4302") + AreaCode.create(:country => germany, :name => "Schlesen", :area_code => "4303") + AreaCode.create(:country => germany, :name => "Westensee", :area_code => "4305") + AreaCode.create(:country => germany, :name => "Raisdorf", :area_code => "4307") + AreaCode.create(:country => germany, :name => "Schwedeneck", :area_code => "4308") + AreaCode.create(:country => germany, :name => "Kiel", :area_code => "431") + AreaCode.create(:country => germany, :name => "Heidmühlen", :area_code => "4320") + AreaCode.create(:country => germany, :name => "Neumünster", :area_code => "4321") + AreaCode.create(:country => germany, :name => "Bordesholm", :area_code => "4322") + AreaCode.create(:country => germany, :name => "Bornhöved", :area_code => "4323") + AreaCode.create(:country => germany, :name => "Brokstedt", :area_code => "4324") + AreaCode.create(:country => germany, :name => "Wankendorf", :area_code => "4326") + AreaCode.create(:country => germany, :name => "Grossenaspe", :area_code => "4327") + AreaCode.create(:country => germany, :name => "Rickling", :area_code => "4328") + AreaCode.create(:country => germany, :name => "Langwedel Holst", :area_code => "4329") + AreaCode.create(:country => germany, :name => "Emkendorf", :area_code => "4330") + AreaCode.create(:country => germany, :name => "Rendsburg", :area_code => "4331") + AreaCode.create(:country => germany, :name => "Hamdorf b Rendsburg", :area_code => "4332") + AreaCode.create(:country => germany, :name => "Erfde", :area_code => "4333") + AreaCode.create(:country => germany, :name => "Bredenbek b Rendsburg", :area_code => "4334") + AreaCode.create(:country => germany, :name => "Hohn b Rendsburg", :area_code => "4335") + AreaCode.create(:country => germany, :name => "Owschlag", :area_code => "4336") + AreaCode.create(:country => germany, :name => "Jevenstedt", :area_code => "4337") + AreaCode.create(:country => germany, :name => "Alt Duvenstedt", :area_code => "4338") + AreaCode.create(:country => germany, :name => "Christiansholm", :area_code => "4339") + AreaCode.create(:country => germany, :name => "Achterwehr", :area_code => "4340") + AreaCode.create(:country => germany, :name => "Preetz Kr Plön", :area_code => "4342") + AreaCode.create(:country => germany, :name => "Laboe", :area_code => "4343") + AreaCode.create(:country => germany, :name => "Schönberg Holstein", :area_code => "4344") + AreaCode.create(:country => germany, :name => "Gettorf", :area_code => "4346") + AreaCode.create(:country => germany, :name => "Flintbek", :area_code => "4347") + AreaCode.create(:country => germany, :name => "Schönkirchen", :area_code => "4348") + AreaCode.create(:country => germany, :name => "Dänischenhagen", :area_code => "4349") + AreaCode.create(:country => germany, :name => "Eckernförde", :area_code => "4351") + AreaCode.create(:country => germany, :name => "Damp", :area_code => "4352") + AreaCode.create(:country => germany, :name => "Ascheffel", :area_code => "4353") + AreaCode.create(:country => germany, :name => "Fleckeby", :area_code => "4354") + AreaCode.create(:country => germany, :name => "Rieseby", :area_code => "4355") + AreaCode.create(:country => germany, :name => "Gross Wittensee", :area_code => "4356") + AreaCode.create(:country => germany, :name => "Sehestedt Eider", :area_code => "4357") + AreaCode.create(:country => germany, :name => "Loose b Eckernförde", :area_code => "4358") + AreaCode.create(:country => germany, :name => "Oldenburg in Holstein", :area_code => "4361") + AreaCode.create(:country => germany, :name => "Heiligenhafen", :area_code => "4362") + AreaCode.create(:country => germany, :name => "Lensahn", :area_code => "4363") + AreaCode.create(:country => germany, :name => "Dahme Kr Ostholstein", :area_code => "4364") + AreaCode.create(:country => germany, :name => "Heringsdorf Holst", :area_code => "4365") + AreaCode.create(:country => germany, :name => "Grömitz-Cismar", :area_code => "4366") + AreaCode.create(:country => germany, :name => "Grossenbrode", :area_code => "4367") + AreaCode.create(:country => germany, :name => "Burg auf Fehmarn", :area_code => "4371") + AreaCode.create(:country => germany, :name => "Westfehmarn", :area_code => "4372") + AreaCode.create(:country => germany, :name => "Lütjenburg", :area_code => "4381") + AreaCode.create(:country => germany, :name => "Wangels", :area_code => "4382") + AreaCode.create(:country => germany, :name => "Grebin", :area_code => "4383") + AreaCode.create(:country => germany, :name => "Selent", :area_code => "4384") + AreaCode.create(:country => germany, :name => "Hohenfelde b Kiel", :area_code => "4385") + AreaCode.create(:country => germany, :name => "Nortorf b Neumünster", :area_code => "4392") + AreaCode.create(:country => germany, :name => "Boostedt", :area_code => "4393") + AreaCode.create(:country => germany, :name => "Bokhorst", :area_code => "4394") + AreaCode.create(:country => germany, :name => "Brake Unterweser", :area_code => "4401") + AreaCode.create(:country => germany, :name => "Rastede", :area_code => "4402") + AreaCode.create(:country => germany, :name => "Bad Zwischenahn", :area_code => "4403") + AreaCode.create(:country => germany, :name => "Elsfleth", :area_code => "4404") + AreaCode.create(:country => germany, :name => "Edewecht", :area_code => "4405") + AreaCode.create(:country => germany, :name => "Berne", :area_code => "4406") + AreaCode.create(:country => germany, :name => "Wardenburg", :area_code => "4407") + AreaCode.create(:country => germany, :name => "Hude Oldenburg", :area_code => "4408") + AreaCode.create(:country => germany, :name => "Westerstede-Ocholt", :area_code => "4409") + AreaCode.create(:country => germany, :name => "Oldenburg (Oldb)", :area_code => "441") + AreaCode.create(:country => germany, :name => "Wilhelmshaven", :area_code => "4421") + AreaCode.create(:country => germany, :name => "Sande Kr Friesl", :area_code => "4422") + AreaCode.create(:country => germany, :name => "Fedderwarden", :area_code => "4423") + AreaCode.create(:country => germany, :name => "Wangerland-Hooksiel", :area_code => "4425") + AreaCode.create(:country => germany, :name => "Wangerland-Horumersiel", :area_code => "4426") + AreaCode.create(:country => germany, :name => "Wildeshausen", :area_code => "4431") + AreaCode.create(:country => germany, :name => "Dötlingen-Brettorf", :area_code => "4432") + AreaCode.create(:country => germany, :name => "Dötlingen", :area_code => "4433") + AreaCode.create(:country => germany, :name => "Colnrade", :area_code => "4434") + AreaCode.create(:country => germany, :name => "Grossenkneten", :area_code => "4435") + AreaCode.create(:country => germany, :name => "Vechta", :area_code => "4441") + AreaCode.create(:country => germany, :name => "Lohne Oldenburg", :area_code => "4442") + AreaCode.create(:country => germany, :name => "Dinklage", :area_code => "4443") + AreaCode.create(:country => germany, :name => "Goldenstedt", :area_code => "4444") + AreaCode.create(:country => germany, :name => "Visbek Kr Vechta", :area_code => "4445") + AreaCode.create(:country => germany, :name => "Bakum Kr Vechta", :area_code => "4446") + AreaCode.create(:country => germany, :name => "Vechta-Langförden", :area_code => "4447") + AreaCode.create(:country => germany, :name => "Varel Jadebusen", :area_code => "4451") + AreaCode.create(:country => germany, :name => "Zetel-Neuenburg", :area_code => "4452") + AreaCode.create(:country => germany, :name => "Zetel", :area_code => "4453") + AreaCode.create(:country => germany, :name => "Jade", :area_code => "4454") + AreaCode.create(:country => germany, :name => "Jade-Schweiburg", :area_code => "4455") + AreaCode.create(:country => germany, :name => "Varel-Altjührden", :area_code => "4456") + AreaCode.create(:country => germany, :name => "Wiefelstede-Spohle", :area_code => "4458") + AreaCode.create(:country => germany, :name => "Jever", :area_code => "4461") + AreaCode.create(:country => germany, :name => "Wittmund", :area_code => "4462") + AreaCode.create(:country => germany, :name => "Wangerland", :area_code => "4463") + AreaCode.create(:country => germany, :name => "Wittmund-Carolinensiel", :area_code => "4464") + AreaCode.create(:country => germany, :name => "Friedeburg Ostfriesl", :area_code => "4465") + AreaCode.create(:country => germany, :name => "Wittmund-Ardorf", :area_code => "4466") + AreaCode.create(:country => germany, :name => "Wittmund-Funnix", :area_code => "4467") + AreaCode.create(:country => germany, :name => "Friedeburg-Reepsholt", :area_code => "4468") + AreaCode.create(:country => germany, :name => "Wangerooge", :area_code => "4469") + AreaCode.create(:country => germany, :name => "Cloppenburg", :area_code => "4471") + AreaCode.create(:country => germany, :name => "Lastrup", :area_code => "4472") + AreaCode.create(:country => germany, :name => "Emstek", :area_code => "4473") + AreaCode.create(:country => germany, :name => "Garrel", :area_code => "4474") + AreaCode.create(:country => germany, :name => "Molbergen", :area_code => "4475") + AreaCode.create(:country => germany, :name => "Lastrup-Hemmelte", :area_code => "4477") + AreaCode.create(:country => germany, :name => "Cappeln Oldenburg", :area_code => "4478") + AreaCode.create(:country => germany, :name => "Molbergen-Peheim", :area_code => "4479") + AreaCode.create(:country => germany, :name => "Ovelgönne-Strückhausen", :area_code => "4480") + AreaCode.create(:country => germany, :name => "Hatten-Sandkrug", :area_code => "4481") + AreaCode.create(:country => germany, :name => "Hatten", :area_code => "4482") + AreaCode.create(:country => germany, :name => "Ovelgönne-Großenmeer", :area_code => "4483") + AreaCode.create(:country => germany, :name => "Hude-Wüsting", :area_code => "4484") + AreaCode.create(:country => germany, :name => "Elsfleth-Huntorf", :area_code => "4485") + AreaCode.create(:country => germany, :name => "Edewecht-Friedrichsfehn", :area_code => "4486") + AreaCode.create(:country => germany, :name => "Grossenkneten-Huntlosen", :area_code => "4487") + AreaCode.create(:country => germany, :name => "Westerstede", :area_code => "4488") + AreaCode.create(:country => germany, :name => "Apen", :area_code => "4489") + AreaCode.create(:country => germany, :name => "Friesoythe", :area_code => "4491") + AreaCode.create(:country => germany, :name => "Saterland", :area_code => "4492") + AreaCode.create(:country => germany, :name => "Friesoythe-Gehlenberg", :area_code => "4493") + AreaCode.create(:country => germany, :name => "Bösel Oldenburg", :area_code => "4494") + AreaCode.create(:country => germany, :name => "Friesoythe-Thüle", :area_code => "4495") + AreaCode.create(:country => germany, :name => "Friesoythe-Markhausen", :area_code => "4496") + AreaCode.create(:country => germany, :name => "Barßel-Harkebrügge", :area_code => "4497") + AreaCode.create(:country => germany, :name => "Saterland-Ramsloh", :area_code => "4498") + AreaCode.create(:country => germany, :name => "Barssel", :area_code => "4499") + AreaCode.create(:country => germany, :name => "Kastorf Holst", :area_code => "4501") + AreaCode.create(:country => germany, :name => "Lübeck-Travemünde", :area_code => "4502") + AreaCode.create(:country => germany, :name => "Timmendorfer Strand", :area_code => "4503") + AreaCode.create(:country => germany, :name => "Ratekau", :area_code => "4504") + AreaCode.create(:country => germany, :name => "Stockelsdorf-Curau", :area_code => "4505") + AreaCode.create(:country => germany, :name => "Stockelsdorf-Krumbeck", :area_code => "4506") + AreaCode.create(:country => germany, :name => "Krummesse", :area_code => "4508") + AreaCode.create(:country => germany, :name => "Groß Grönau", :area_code => "4509") + AreaCode.create(:country => germany, :name => "Lübeck", :area_code => "451") + AreaCode.create(:country => germany, :name => "Eutin", :area_code => "4521") + AreaCode.create(:country => germany, :name => "Plön", :area_code => "4522") + AreaCode.create(:country => germany, :name => "Malente", :area_code => "4523") + AreaCode.create(:country => germany, :name => "Scharbeutz-Pönitz", :area_code => "4524") + AreaCode.create(:country => germany, :name => "Ahrensbök", :area_code => "4525") + AreaCode.create(:country => germany, :name => "Ascheberg Holstein", :area_code => "4526") + AreaCode.create(:country => germany, :name => "Bosau", :area_code => "4527") + AreaCode.create(:country => germany, :name => "Schönwalde am Bungsberg", :area_code => "4528") + AreaCode.create(:country => germany, :name => "Süsel-Bujendorf", :area_code => "4529") + AreaCode.create(:country => germany, :name => "Bad Oldesloe", :area_code => "4531") + AreaCode.create(:country => germany, :name => "Bargteheide", :area_code => "4532") + AreaCode.create(:country => germany, :name => "Reinfeld Holstein", :area_code => "4533") + AreaCode.create(:country => germany, :name => "Steinburg Kr Storman", :area_code => "4534") + AreaCode.create(:country => germany, :name => "Nahe", :area_code => "4535") + AreaCode.create(:country => germany, :name => "Steinhorst Lauenb", :area_code => "4536") + AreaCode.create(:country => germany, :name => "Sülfeld Holst", :area_code => "4537") + AreaCode.create(:country => germany, :name => "Westerau", :area_code => "4539") + AreaCode.create(:country => germany, :name => "Ratzeburg", :area_code => "4541") + AreaCode.create(:country => germany, :name => "Mölln Lauenb", :area_code => "4542") + AreaCode.create(:country => germany, :name => "Nusse", :area_code => "4543") + AreaCode.create(:country => germany, :name => "Berkenthin", :area_code => "4544") + AreaCode.create(:country => germany, :name => "Seedorf Lauenb", :area_code => "4545") + AreaCode.create(:country => germany, :name => "Mustin Lauenburg", :area_code => "4546") + AreaCode.create(:country => germany, :name => "Gudow Lauenb", :area_code => "4547") + AreaCode.create(:country => germany, :name => "Bühnsdorf", :area_code => "4550") + AreaCode.create(:country => germany, :name => "Bad Segeberg", :area_code => "4551") + AreaCode.create(:country => germany, :name => "Leezen", :area_code => "4552") + AreaCode.create(:country => germany, :name => "Geschendorf", :area_code => "4553") + AreaCode.create(:country => germany, :name => "Wahlstedt", :area_code => "4554") + AreaCode.create(:country => germany, :name => "Seedorf b Bad Segeberg", :area_code => "4555") + AreaCode.create(:country => germany, :name => "Ahrensbök-Gnissau", :area_code => "4556") + AreaCode.create(:country => germany, :name => "Blunk", :area_code => "4557") + AreaCode.create(:country => germany, :name => "Todesfelde", :area_code => "4558") + AreaCode.create(:country => germany, :name => "Wensin", :area_code => "4559") + AreaCode.create(:country => germany, :name => "Neustadt in Holstein", :area_code => "4561") + AreaCode.create(:country => germany, :name => "Grömitz", :area_code => "4562") + AreaCode.create(:country => germany, :name => "Scharbeutz-Haffkrug", :area_code => "4563") + AreaCode.create(:country => germany, :name => "Schashagen", :area_code => "4564") + AreaCode.create(:country => germany, :name => "Freienwill", :area_code => "4602") + AreaCode.create(:country => germany, :name => "Havetoft", :area_code => "4603") + AreaCode.create(:country => germany, :name => "Grossenwiehe", :area_code => "4604") + AreaCode.create(:country => germany, :name => "Medelby", :area_code => "4605") + AreaCode.create(:country => germany, :name => "Wanderup", :area_code => "4606") + AreaCode.create(:country => germany, :name => "Janneby", :area_code => "4607") + AreaCode.create(:country => germany, :name => "Handewitt", :area_code => "4608") + AreaCode.create(:country => germany, :name => "Eggebek", :area_code => "4609") + AreaCode.create(:country => germany, :name => "Flensburg", :area_code => "461") + AreaCode.create(:country => germany, :name => "Schleswig", :area_code => "4621") + AreaCode.create(:country => germany, :name => "Taarstedt", :area_code => "4622") + AreaCode.create(:country => germany, :name => "Böklund", :area_code => "4623") + AreaCode.create(:country => germany, :name => "Kropp", :area_code => "4624") + AreaCode.create(:country => germany, :name => "Jübek", :area_code => "4625") + AreaCode.create(:country => germany, :name => "Treia", :area_code => "4626") + AreaCode.create(:country => germany, :name => "Dörpstedt", :area_code => "4627") + AreaCode.create(:country => germany, :name => "Barderup", :area_code => "4630") + AreaCode.create(:country => germany, :name => "Glücksburg Ostsee", :area_code => "4631") + AreaCode.create(:country => germany, :name => "Steinbergkirche", :area_code => "4632") + AreaCode.create(:country => germany, :name => "Satrup", :area_code => "4633") + AreaCode.create(:country => germany, :name => "Husby", :area_code => "4634") + AreaCode.create(:country => germany, :name => "Sörup", :area_code => "4635") + AreaCode.create(:country => germany, :name => "Langballig", :area_code => "4636") + AreaCode.create(:country => germany, :name => "Sterup", :area_code => "4637") + AreaCode.create(:country => germany, :name => "Tarp", :area_code => "4638") + AreaCode.create(:country => germany, :name => "Schafflund", :area_code => "4639") + AreaCode.create(:country => germany, :name => "Süderbrarup", :area_code => "4641") + AreaCode.create(:country => germany, :name => "Kappeln Schlei", :area_code => "4642") + AreaCode.create(:country => germany, :name => "Gelting Angeln", :area_code => "4643") + AreaCode.create(:country => germany, :name => "Karby", :area_code => "4644") + AreaCode.create(:country => germany, :name => "Mohrkirch", :area_code => "4646") + AreaCode.create(:country => germany, :name => "Sylt", :area_code => "4651") + AreaCode.create(:country => germany, :name => "Niebüll", :area_code => "4661") + AreaCode.create(:country => germany, :name => "Leck", :area_code => "4662") + AreaCode.create(:country => germany, :name => "Süderlügum", :area_code => "4663") + AreaCode.create(:country => germany, :name => "Neukirchen b Niebüll", :area_code => "4664") + AreaCode.create(:country => germany, :name => "Emmelsbüll-Horsbüll", :area_code => "4665") + AreaCode.create(:country => germany, :name => "Ladelund", :area_code => "4666") + AreaCode.create(:country => germany, :name => "Dagebüll", :area_code => "4667") + AreaCode.create(:country => germany, :name => "Klanxbüll", :area_code => "4668") + AreaCode.create(:country => germany, :name => "Bredstedt", :area_code => "4671") + AreaCode.create(:country => germany, :name => "Langenhorn", :area_code => "4672") + AreaCode.create(:country => germany, :name => "Joldelund", :area_code => "4673") + AreaCode.create(:country => germany, :name => "Ockholm", :area_code => "4674") + AreaCode.create(:country => germany, :name => "Wyk auf Föhr", :area_code => "4681") + AreaCode.create(:country => germany, :name => "Amrum", :area_code => "4682") + AreaCode.create(:country => germany, :name => "Oldsum", :area_code => "4683") + AreaCode.create(:country => germany, :name => "Langeneß Hallig", :area_code => "4684") + AreaCode.create(:country => germany, :name => "Sandstedt", :area_code => "4702") + AreaCode.create(:country => germany, :name => "Loxstedt-Donnern", :area_code => "4703") + AreaCode.create(:country => germany, :name => "Drangstedt", :area_code => "4704") + AreaCode.create(:country => germany, :name => "Wremen", :area_code => "4705") + AreaCode.create(:country => germany, :name => "Schiffdorf", :area_code => "4706") + AreaCode.create(:country => germany, :name => "Langen-Neuenwalde", :area_code => "4707") + AreaCode.create(:country => germany, :name => "Ringstedt", :area_code => "4708") + AreaCode.create(:country => germany, :name => "Bremerhaven", :area_code => "471") + AreaCode.create(:country => germany, :name => "Cuxhaven", :area_code => "4721") + AreaCode.create(:country => germany, :name => "Cuxhaven-Altenbruch", :area_code => "4722") + AreaCode.create(:country => germany, :name => "Cuxhaven-Altenwalde", :area_code => "4723") + AreaCode.create(:country => germany, :name => "Cuxhaven-Lüdingworth", :area_code => "4724") + AreaCode.create(:country => germany, :name => "Helgoland", :area_code => "4725") + AreaCode.create(:country => germany, :name => "Nordenham", :area_code => "4731") + AreaCode.create(:country => germany, :name => "Stadland-Rodenkirchen", :area_code => "4732") + AreaCode.create(:country => germany, :name => "Butjadingen-Burhave", :area_code => "4733") + AreaCode.create(:country => germany, :name => "Stadland-Seefeld", :area_code => "4734") + AreaCode.create(:country => germany, :name => "Butjadingen-Stollhamm", :area_code => "4735") + AreaCode.create(:country => germany, :name => "Butjadingen-Tossens", :area_code => "4736") + AreaCode.create(:country => germany, :name => "Stadland-Schwei", :area_code => "4737") + AreaCode.create(:country => germany, :name => "Loxstedt-Dedesdorf", :area_code => "4740") + AreaCode.create(:country => germany, :name => "Nordholz b Bremerhaven", :area_code => "4741") + AreaCode.create(:country => germany, :name => "Dorum", :area_code => "4742") + AreaCode.create(:country => germany, :name => "Langen b Bremerhaven", :area_code => "4743") + AreaCode.create(:country => germany, :name => "Loxstedt", :area_code => "4744") + AreaCode.create(:country => germany, :name => "Bad Bederkesa", :area_code => "4745") + AreaCode.create(:country => germany, :name => "Hagen b Bremerhaven", :area_code => "4746") + AreaCode.create(:country => germany, :name => "Beverstedt", :area_code => "4747") + AreaCode.create(:country => germany, :name => "Stubben b Bremerhaven", :area_code => "4748") + AreaCode.create(:country => germany, :name => "Schiffdorf-Geestenseth", :area_code => "4749") + AreaCode.create(:country => germany, :name => "Otterndorf", :area_code => "4751") + AreaCode.create(:country => germany, :name => "Neuhaus Oste", :area_code => "4752") + AreaCode.create(:country => germany, :name => "Balje", :area_code => "4753") + AreaCode.create(:country => germany, :name => "Bülkau", :area_code => "4754") + AreaCode.create(:country => germany, :name => "Ihlienworth", :area_code => "4755") + AreaCode.create(:country => germany, :name => "Odisheim", :area_code => "4756") + AreaCode.create(:country => germany, :name => "Wanna", :area_code => "4757") + AreaCode.create(:country => germany, :name => "Nordleda", :area_code => "4758") + AreaCode.create(:country => germany, :name => "Bremervörde", :area_code => "4761") + AreaCode.create(:country => germany, :name => "Kutenholz", :area_code => "4762") + AreaCode.create(:country => germany, :name => "Gnarrenburg", :area_code => "4763") + AreaCode.create(:country => germany, :name => "Gnarrenburg-Klenkendorf", :area_code => "4764") + AreaCode.create(:country => germany, :name => "Ebersdorf b Bremervörde", :area_code => "4765") + AreaCode.create(:country => germany, :name => "Basdahl", :area_code => "4766") + AreaCode.create(:country => germany, :name => "Bremervörde-Bevern", :area_code => "4767") + AreaCode.create(:country => germany, :name => "Hipstedt", :area_code => "4768") + AreaCode.create(:country => germany, :name => "Bremervörde-Iselersheim", :area_code => "4769") + AreaCode.create(:country => germany, :name => "Wischhafen", :area_code => "4770") + AreaCode.create(:country => germany, :name => "Hemmoor", :area_code => "4771") + AreaCode.create(:country => germany, :name => "Oberndorf Oste", :area_code => "4772") + AreaCode.create(:country => germany, :name => "Lamstedt", :area_code => "4773") + AreaCode.create(:country => germany, :name => "Hechthausen", :area_code => "4774") + AreaCode.create(:country => germany, :name => "Grossenwörden", :area_code => "4775") + AreaCode.create(:country => germany, :name => "Osten-Altendorf", :area_code => "4776") + AreaCode.create(:country => germany, :name => "Cadenberge", :area_code => "4777") + AreaCode.create(:country => germany, :name => "Wingst", :area_code => "4778") + AreaCode.create(:country => germany, :name => "Freiburg Elbe", :area_code => "4779") + AreaCode.create(:country => germany, :name => "Osterholz-Scharmbeck", :area_code => "4791") + AreaCode.create(:country => germany, :name => "Worpswede", :area_code => "4792") + AreaCode.create(:country => germany, :name => "Hambergen", :area_code => "4793") + AreaCode.create(:country => germany, :name => "Worpswede-Ostersode", :area_code => "4794") + AreaCode.create(:country => germany, :name => "Garlstedt", :area_code => "4795") + AreaCode.create(:country => germany, :name => "Teufelsmoor", :area_code => "4796") + AreaCode.create(:country => germany, :name => "Wrohm", :area_code => "4802") + AreaCode.create(:country => germany, :name => "Pahlen", :area_code => "4803") + AreaCode.create(:country => germany, :name => "Nordhastedt", :area_code => "4804") + AreaCode.create(:country => germany, :name => "Schafstedt", :area_code => "4805") + AreaCode.create(:country => germany, :name => "Sarzbüttel", :area_code => "4806") + AreaCode.create(:country => germany, :name => "Heide Holst", :area_code => "481") + AreaCode.create(:country => germany, :name => "Itzehoe", :area_code => "4821") + AreaCode.create(:country => germany, :name => "Kellinghusen", :area_code => "4822") + AreaCode.create(:country => germany, :name => "Wilster", :area_code => "4823") + AreaCode.create(:country => germany, :name => "Krempe", :area_code => "4824") + AreaCode.create(:country => germany, :name => "Burg Dithmarschen", :area_code => "4825") + AreaCode.create(:country => germany, :name => "Hohenlockstedt", :area_code => "4826") + AreaCode.create(:country => germany, :name => "Wacken", :area_code => "4827") + AreaCode.create(:country => germany, :name => "Lägerdorf", :area_code => "4828") + AreaCode.create(:country => germany, :name => "Wewelsfleth", :area_code => "4829") + AreaCode.create(:country => germany, :name => "Süderhastedt", :area_code => "4830") + AreaCode.create(:country => germany, :name => "Meldorf", :area_code => "4832") + AreaCode.create(:country => germany, :name => "Wesselburen", :area_code => "4833") + AreaCode.create(:country => germany, :name => "Büsum", :area_code => "4834") + AreaCode.create(:country => germany, :name => "Albersdorf Holst", :area_code => "4835") + AreaCode.create(:country => germany, :name => "Hennstedt Dithm", :area_code => "4836") + AreaCode.create(:country => germany, :name => "Neuenkirchen Dithm", :area_code => "4837") + AreaCode.create(:country => germany, :name => "Tellingstedt", :area_code => "4838") + AreaCode.create(:country => germany, :name => "Wöhrden Dithm", :area_code => "4839") + AreaCode.create(:country => germany, :name => "Husum Nordsee", :area_code => "4841") + AreaCode.create(:country => germany, :name => "Nordstrand", :area_code => "4842") + AreaCode.create(:country => germany, :name => "Viöl", :area_code => "4843") + AreaCode.create(:country => germany, :name => "Pellworm", :area_code => "4844") + AreaCode.create(:country => germany, :name => "Ostenfeld Husum", :area_code => "4845") + AreaCode.create(:country => germany, :name => "Hattstedt", :area_code => "4846") + AreaCode.create(:country => germany, :name => "Oster-Ohrstedt", :area_code => "4847") + AreaCode.create(:country => germany, :name => "Rantrum", :area_code => "4848") + AreaCode.create(:country => germany, :name => "Hooge", :area_code => "4849") + AreaCode.create(:country => germany, :name => "Marne", :area_code => "4851") + AreaCode.create(:country => germany, :name => "Brunsbüttel", :area_code => "4852") + AreaCode.create(:country => germany, :name => "Sankt Michaelisdonn", :area_code => "4853") + AreaCode.create(:country => germany, :name => "Friedrichskoog", :area_code => "4854") + AreaCode.create(:country => germany, :name => "Eddelak", :area_code => "4855") + AreaCode.create(:country => germany, :name => "Kronprinzenkoog", :area_code => "4856") + AreaCode.create(:country => germany, :name => "Barlt", :area_code => "4857") + AreaCode.create(:country => germany, :name => "Sankt Margarethen Holst", :area_code => "4858") + AreaCode.create(:country => germany, :name => "Windbergen", :area_code => "4859") + AreaCode.create(:country => germany, :name => "Tönning", :area_code => "4861") + AreaCode.create(:country => germany, :name => "Garding", :area_code => "4862") + AreaCode.create(:country => germany, :name => "Sankt Peter-Ording", :area_code => "4863") + AreaCode.create(:country => germany, :name => "Oldenswort", :area_code => "4864") + AreaCode.create(:country => germany, :name => "Osterhever", :area_code => "4865") + AreaCode.create(:country => germany, :name => "Hohenwestedt", :area_code => "4871") + AreaCode.create(:country => germany, :name => "Hanerau-Hademarschen", :area_code => "4872") + AreaCode.create(:country => germany, :name => "Aukrug", :area_code => "4873") + AreaCode.create(:country => germany, :name => "Todenbüttel", :area_code => "4874") + AreaCode.create(:country => germany, :name => "Stafstedt", :area_code => "4875") + AreaCode.create(:country => germany, :name => "Reher Holst", :area_code => "4876") + AreaCode.create(:country => germany, :name => "Hennstedt b Itzehoe", :area_code => "4877") + AreaCode.create(:country => germany, :name => "Friedrichstadt", :area_code => "4881") + AreaCode.create(:country => germany, :name => "Lunden", :area_code => "4882") + AreaCode.create(:country => germany, :name => "Süderstapel", :area_code => "4883") + AreaCode.create(:country => germany, :name => "Schwabstedt", :area_code => "4884") + AreaCode.create(:country => germany, :name => "Bergenhusen", :area_code => "4885") + AreaCode.create(:country => germany, :name => "Schenefeld Mittelholst", :area_code => "4892") + AreaCode.create(:country => germany, :name => "Hohenaspe", :area_code => "4893") + AreaCode.create(:country => germany, :name => "Jemgum-Ditzum", :area_code => "4902") + AreaCode.create(:country => germany, :name => "Wymeer", :area_code => "4903") + AreaCode.create(:country => germany, :name => "Leer Ostfriesland", :area_code => "491") + AreaCode.create(:country => germany, :name => "Wirdum", :area_code => "4920") + AreaCode.create(:country => germany, :name => "Emden Stadt", :area_code => "4921") + AreaCode.create(:country => germany, :name => "Borkum", :area_code => "4922") + AreaCode.create(:country => germany, :name => "Krummhörn-Pewsum", :area_code => "4923") + AreaCode.create(:country => germany, :name => "Moormerland-Oldersum", :area_code => "4924") + AreaCode.create(:country => germany, :name => "Hinte", :area_code => "4925") + AreaCode.create(:country => germany, :name => "Krummhörn-Greetsiel", :area_code => "4926") + AreaCode.create(:country => germany, :name => "Krummhörn-Loquard", :area_code => "4927") + AreaCode.create(:country => germany, :name => "Ihlow-Riepe", :area_code => "4928") + AreaCode.create(:country => germany, :name => "Ihlow Kr Aurich", :area_code => "4929") + AreaCode.create(:country => germany, :name => "Norden", :area_code => "4931") + AreaCode.create(:country => germany, :name => "Norderney", :area_code => "4932") + AreaCode.create(:country => germany, :name => "Dornum Ostfriesl", :area_code => "4933") + AreaCode.create(:country => germany, :name => "Marienhafe", :area_code => "4934") + AreaCode.create(:country => germany, :name => "Juist", :area_code => "4935") + AreaCode.create(:country => germany, :name => "Grossheide", :area_code => "4936") + AreaCode.create(:country => germany, :name => "Hagermarsch", :area_code => "4938") + AreaCode.create(:country => germany, :name => "Baltrum", :area_code => "4939") + AreaCode.create(:country => germany, :name => "Aurich", :area_code => "4941") + AreaCode.create(:country => germany, :name => "Südbrookmerland", :area_code => "4942") + AreaCode.create(:country => germany, :name => "Grossefehn", :area_code => "4943") + AreaCode.create(:country => germany, :name => "Wiesmoor", :area_code => "4944") + AreaCode.create(:country => germany, :name => "Grossefehn-Timmel", :area_code => "4945") + AreaCode.create(:country => germany, :name => "Grossefehn-Bagband", :area_code => "4946") + AreaCode.create(:country => germany, :name => "Aurich-Ogenbargen", :area_code => "4947") + AreaCode.create(:country => germany, :name => "Wiesmoor-Marcardsmoor", :area_code => "4948") + AreaCode.create(:country => germany, :name => "Holtland", :area_code => "4950") + AreaCode.create(:country => germany, :name => "Weener", :area_code => "4951") + AreaCode.create(:country => germany, :name => "Rhauderfehn", :area_code => "4952") + AreaCode.create(:country => germany, :name => "Bunde", :area_code => "4953") + AreaCode.create(:country => germany, :name => "Moormerland", :area_code => "4954") + AreaCode.create(:country => germany, :name => "Westoverledingen", :area_code => "4955") + AreaCode.create(:country => germany, :name => "Uplengen", :area_code => "4956") + AreaCode.create(:country => germany, :name => "Detern", :area_code => "4957") + AreaCode.create(:country => germany, :name => "Jemgum", :area_code => "4958") + AreaCode.create(:country => germany, :name => "Dollart", :area_code => "4959") + AreaCode.create(:country => germany, :name => "Papenburg", :area_code => "4961") + AreaCode.create(:country => germany, :name => "Papenburg-Aschendorf", :area_code => "4962") + AreaCode.create(:country => germany, :name => "Dörpen", :area_code => "4963") + AreaCode.create(:country => germany, :name => "Rhede Ems", :area_code => "4964") + AreaCode.create(:country => germany, :name => "Surwold", :area_code => "4965") + AreaCode.create(:country => germany, :name => "Neubörger", :area_code => "4966") + AreaCode.create(:country => germany, :name => "Rhauderfehn-Burlage", :area_code => "4967") + AreaCode.create(:country => germany, :name => "Neulehe", :area_code => "4968") + AreaCode.create(:country => germany, :name => "Esens", :area_code => "4971") + AreaCode.create(:country => germany, :name => "Langeoog", :area_code => "4972") + AreaCode.create(:country => germany, :name => "Wittmund-Burhafe", :area_code => "4973") + AreaCode.create(:country => germany, :name => "Neuharlingersiel", :area_code => "4974") + AreaCode.create(:country => germany, :name => "Westerholt Ostfriesl", :area_code => "4975") + AreaCode.create(:country => germany, :name => "Spiekeroog", :area_code => "4976") + AreaCode.create(:country => germany, :name => "Blomberg Ostfriesl", :area_code => "4977") + AreaCode.create(:country => germany, :name => "Nienburg Weser", :area_code => "5021") + AreaCode.create(:country => germany, :name => "Wietzen", :area_code => "5022") + AreaCode.create(:country => germany, :name => "Liebenau Kr Nienburg Weser", :area_code => "5023") + AreaCode.create(:country => germany, :name => "Rohrsen Kr Nienburg Weser", :area_code => "5024") + AreaCode.create(:country => germany, :name => "Estorf Weser", :area_code => "5025") + AreaCode.create(:country => germany, :name => "Steimbke", :area_code => "5026") + AreaCode.create(:country => germany, :name => "Linsburg", :area_code => "5027") + AreaCode.create(:country => germany, :name => "Pennigsehl", :area_code => "5028") + AreaCode.create(:country => germany, :name => "Wunstorf", :area_code => "5031") + AreaCode.create(:country => germany, :name => "Neustadt am Rübenberge", :area_code => "5032") + AreaCode.create(:country => germany, :name => "Wunstorf-Grossenheidorn", :area_code => "5033") + AreaCode.create(:country => germany, :name => "Neustadt-Hagen", :area_code => "5034") + AreaCode.create(:country => germany, :name => "Gross Munzel", :area_code => "5035") + AreaCode.create(:country => germany, :name => "Neustadt-Schneeren", :area_code => "5036") + AreaCode.create(:country => germany, :name => "Bad Rehburg", :area_code => "5037") + AreaCode.create(:country => germany, :name => "Springe Deister", :area_code => "5041") + AreaCode.create(:country => germany, :name => "Bad Münder am Deister", :area_code => "5042") + AreaCode.create(:country => germany, :name => "Lauenau", :area_code => "5043") + AreaCode.create(:country => germany, :name => "Springe-Eldagsen", :area_code => "5044") + AreaCode.create(:country => germany, :name => "Springe-Bennigsen", :area_code => "5045") + AreaCode.create(:country => germany, :name => "Bergen Kr Celle", :area_code => "5051") + AreaCode.create(:country => germany, :name => "Hermannsburg", :area_code => "5052") + AreaCode.create(:country => germany, :name => "Faßberg-Müden", :area_code => "5053") + AreaCode.create(:country => germany, :name => "Bergen-Sülze", :area_code => "5054") + AreaCode.create(:country => germany, :name => "Fassberg", :area_code => "5055") + AreaCode.create(:country => germany, :name => "Winsen-Meissendorf", :area_code => "5056") + AreaCode.create(:country => germany, :name => "Bodenburg", :area_code => "5060") + AreaCode.create(:country => germany, :name => "Holle b Hildesheim", :area_code => "5062") + AreaCode.create(:country => germany, :name => "Bad Salzdetfurth", :area_code => "5063") + AreaCode.create(:country => germany, :name => "Groß Düngen", :area_code => "5064") + AreaCode.create(:country => germany, :name => "Sibbesse", :area_code => "5065") + AreaCode.create(:country => germany, :name => "Sarstedt", :area_code => "5066") + AreaCode.create(:country => germany, :name => "Bockenem", :area_code => "5067") + AreaCode.create(:country => germany, :name => "Elze Leine", :area_code => "5068") + AreaCode.create(:country => germany, :name => "Nordstemmen", :area_code => "5069") + AreaCode.create(:country => germany, :name => "Schwarmstedt", :area_code => "5071") + AreaCode.create(:country => germany, :name => "Neustadt-Mandelsloh", :area_code => "5072") + AreaCode.create(:country => germany, :name => "Neustadt-Esperke", :area_code => "5073") + AreaCode.create(:country => germany, :name => "Rodewald", :area_code => "5074") + AreaCode.create(:country => germany, :name => "Langlingen", :area_code => "5082") + AreaCode.create(:country => germany, :name => "Hohne b Celle", :area_code => "5083") + AreaCode.create(:country => germany, :name => "Hambühren", :area_code => "5084") + AreaCode.create(:country => germany, :name => "Burgdorf-Ehlershausen", :area_code => "5085") + AreaCode.create(:country => germany, :name => "Celle-Scheuen", :area_code => "5086") + AreaCode.create(:country => germany, :name => "Pattensen", :area_code => "5101") + AreaCode.create(:country => germany, :name => "Laatzen", :area_code => "5102") + AreaCode.create(:country => germany, :name => "Wennigsen Deister", :area_code => "5103") + AreaCode.create(:country => germany, :name => "Barsinghausen", :area_code => "5105") + AreaCode.create(:country => germany, :name => "Gehrden Han", :area_code => "5108") + AreaCode.create(:country => germany, :name => "Ronnenberg", :area_code => "5109") + AreaCode.create(:country => germany, :name => "Hannover", :area_code => "511") + AreaCode.create(:country => germany, :name => "Hildesheim", :area_code => "5121") + AreaCode.create(:country => germany, :name => "Schellerten", :area_code => "5123") + AreaCode.create(:country => germany, :name => "Algermissen", :area_code => "5126") + AreaCode.create(:country => germany, :name => "Harsum", :area_code => "5127") + AreaCode.create(:country => germany, :name => "Hohenhameln", :area_code => "5128") + AreaCode.create(:country => germany, :name => "Söhlde", :area_code => "5129") + AreaCode.create(:country => germany, :name => "Wedemark", :area_code => "5130") + AreaCode.create(:country => germany, :name => "Garbsen", :area_code => "5131") + AreaCode.create(:country => germany, :name => "Lehrte", :area_code => "5132") + AreaCode.create(:country => germany, :name => "Burgwedel-Fuhrberg", :area_code => "5135") + AreaCode.create(:country => germany, :name => "Burgdorf Kr Hannover", :area_code => "5136") + AreaCode.create(:country => germany, :name => "Seelze", :area_code => "5137") + AreaCode.create(:country => germany, :name => "Sehnde", :area_code => "5138") + AreaCode.create(:country => germany, :name => "Burgwedel", :area_code => "5139") + AreaCode.create(:country => germany, :name => "Celle", :area_code => "5141") + AreaCode.create(:country => germany, :name => "Eschede", :area_code => "5142") + AreaCode.create(:country => germany, :name => "Winsen Aller", :area_code => "5143") + AreaCode.create(:country => germany, :name => "Wathlingen", :area_code => "5144") + AreaCode.create(:country => germany, :name => "Beedenbostel", :area_code => "5145") + AreaCode.create(:country => germany, :name => "Wietze", :area_code => "5146") + AreaCode.create(:country => germany, :name => "Uetze-Hänigsen", :area_code => "5147") + AreaCode.create(:country => germany, :name => "Steinhorst Niedersachs", :area_code => "5148") + AreaCode.create(:country => germany, :name => "Wienhausen", :area_code => "5149") + AreaCode.create(:country => germany, :name => "Hameln", :area_code => "5151") + AreaCode.create(:country => germany, :name => "Hessisch Oldendorf", :area_code => "5152") + AreaCode.create(:country => germany, :name => "Salzhemmendorf", :area_code => "5153") + AreaCode.create(:country => germany, :name => "Aerzen", :area_code => "5154") + AreaCode.create(:country => germany, :name => "Emmerthal", :area_code => "5155") + AreaCode.create(:country => germany, :name => "Coppenbrügge", :area_code => "5156") + AreaCode.create(:country => germany, :name => "Emmerthal-Börry", :area_code => "5157") + AreaCode.create(:country => germany, :name => "Hemeringen", :area_code => "5158") + AreaCode.create(:country => germany, :name => "Coppenbrügge-Bisperode", :area_code => "5159") + AreaCode.create(:country => germany, :name => "Walsrode", :area_code => "5161") + AreaCode.create(:country => germany, :name => "Fallingbostel", :area_code => "5162") + AreaCode.create(:country => germany, :name => "Fallingbostel-Dorfmark", :area_code => "5163") + AreaCode.create(:country => germany, :name => "Hodenhagen", :area_code => "5164") + AreaCode.create(:country => germany, :name => "Rethem Aller", :area_code => "5165") + AreaCode.create(:country => germany, :name => "Walsrode-Kirchboitzen", :area_code => "5166") + AreaCode.create(:country => germany, :name => "Walsrode-Westenholz", :area_code => "5167") + AreaCode.create(:country => germany, :name => "Walsrode-Stellichte", :area_code => "5168") + AreaCode.create(:country => germany, :name => "Peine", :area_code => "5171") + AreaCode.create(:country => germany, :name => "Ilsede", :area_code => "5172") + AreaCode.create(:country => germany, :name => "Uetze", :area_code => "5173") + AreaCode.create(:country => germany, :name => "Lahstedt", :area_code => "5174") + AreaCode.create(:country => germany, :name => "Lehrte-Arpke", :area_code => "5175") + AreaCode.create(:country => germany, :name => "Edemissen", :area_code => "5176") + AreaCode.create(:country => germany, :name => "Edemissen-Abbensen", :area_code => "5177") + AreaCode.create(:country => germany, :name => "Alfeld Leine", :area_code => "5181") + AreaCode.create(:country => germany, :name => "Gronau Leine", :area_code => "5182") + AreaCode.create(:country => germany, :name => "Lamspringe", :area_code => "5183") + AreaCode.create(:country => germany, :name => "Freden Leine", :area_code => "5184") + AreaCode.create(:country => germany, :name => "Duingen", :area_code => "5185") + AreaCode.create(:country => germany, :name => "Salzhemmendorf-Wallensen", :area_code => "5186") + AreaCode.create(:country => germany, :name => "Delligsen", :area_code => "5187") + AreaCode.create(:country => germany, :name => "Soltau-Emmingen", :area_code => "5190") + AreaCode.create(:country => germany, :name => "Soltau", :area_code => "5191") + AreaCode.create(:country => germany, :name => "Munster", :area_code => "5192") + AreaCode.create(:country => germany, :name => "Schneverdingen", :area_code => "5193") + AreaCode.create(:country => germany, :name => "Bispingen", :area_code => "5194") + AreaCode.create(:country => germany, :name => "Neuenkirchen b Soltau", :area_code => "5195") + AreaCode.create(:country => germany, :name => "Wietzendorf", :area_code => "5196") + AreaCode.create(:country => germany, :name => "Soltau-Frielingen", :area_code => "5197") + AreaCode.create(:country => germany, :name => "Schneverdingen-Wintermoor", :area_code => "5198") + AreaCode.create(:country => germany, :name => "Schneverdingen-Heber", :area_code => "5199") + AreaCode.create(:country => germany, :name => "Halle Westf", :area_code => "5201") + AreaCode.create(:country => germany, :name => "Oerlinghausen", :area_code => "5202") + AreaCode.create(:country => germany, :name => "Werther Westf", :area_code => "5203") + AreaCode.create(:country => germany, :name => "Steinhagen Westf", :area_code => "5204") + AreaCode.create(:country => germany, :name => "Bielefeld-Sennestadt", :area_code => "5205") + AreaCode.create(:country => germany, :name => "Bielefeld-Jöllenbeck", :area_code => "5206") + AreaCode.create(:country => germany, :name => "Schloss Holte-Stukenbrock", :area_code => "5207") + AreaCode.create(:country => germany, :name => "Leopoldshöhe", :area_code => "5208") + AreaCode.create(:country => germany, :name => "Gütersloh-Friedrichsdorf", :area_code => "5209") + AreaCode.create(:country => germany, :name => "Bielefeld", :area_code => "521") + AreaCode.create(:country => germany, :name => "Herford", :area_code => "5221") + AreaCode.create(:country => germany, :name => "Bad Salzuflen", :area_code => "5222") + AreaCode.create(:country => germany, :name => "Bünde", :area_code => "5223") + AreaCode.create(:country => germany, :name => "Enger Westf", :area_code => "5224") + AreaCode.create(:country => germany, :name => "Spenge", :area_code => "5225") + AreaCode.create(:country => germany, :name => "Bruchmühlen Westf", :area_code => "5226") + AreaCode.create(:country => germany, :name => "Vlotho-Exter", :area_code => "5228") + AreaCode.create(:country => germany, :name => "Detmold", :area_code => "5231") + AreaCode.create(:country => germany, :name => "Lage Lippe", :area_code => "5232") + AreaCode.create(:country => germany, :name => "Steinheim Westf", :area_code => "5233") + AreaCode.create(:country => germany, :name => "Horn-Bad Meinberg", :area_code => "5234") + AreaCode.create(:country => germany, :name => "Blomberg Lippe", :area_code => "5235") + AreaCode.create(:country => germany, :name => "Blomberg-Grossenmarpe", :area_code => "5236") + AreaCode.create(:country => germany, :name => "Augustdorf", :area_code => "5237") + AreaCode.create(:country => germany, :name => "Nieheim-Himmighausen", :area_code => "5238") + AreaCode.create(:country => germany, :name => "Gütersloh", :area_code => "5241") + AreaCode.create(:country => germany, :name => "Rheda-Wiedenbrück", :area_code => "5242") + AreaCode.create(:country => germany, :name => "Rietberg", :area_code => "5244") + AreaCode.create(:country => germany, :name => "Herzebrock-Clarholz", :area_code => "5245") + AreaCode.create(:country => germany, :name => "Verl", :area_code => "5246") + AreaCode.create(:country => germany, :name => "Harsewinkel", :area_code => "5247") + AreaCode.create(:country => germany, :name => "Langenberg Kr Gütersloh", :area_code => "5248") + AreaCode.create(:country => germany, :name => "Delbrück Westf", :area_code => "5250") + AreaCode.create(:country => germany, :name => "Paderborn", :area_code => "5251") + AreaCode.create(:country => germany, :name => "Bad Lippspringe", :area_code => "5252") + AreaCode.create(:country => germany, :name => "Bad Driburg", :area_code => "5253") + AreaCode.create(:country => germany, :name => "Paderborn-Schloss Neuhaus", :area_code => "5254") + AreaCode.create(:country => germany, :name => "Altenbeken", :area_code => "5255") + AreaCode.create(:country => germany, :name => "Hövelhof", :area_code => "5257") + AreaCode.create(:country => germany, :name => "Salzkotten", :area_code => "5258") + AreaCode.create(:country => germany, :name => "Bad Driburg-Neuenheerse", :area_code => "5259") + AreaCode.create(:country => germany, :name => "Lemgo", :area_code => "5261") + AreaCode.create(:country => germany, :name => "Extertal", :area_code => "5262") + AreaCode.create(:country => germany, :name => "Barntrup", :area_code => "5263") + AreaCode.create(:country => germany, :name => "Kalletal", :area_code => "5264") + AreaCode.create(:country => germany, :name => "Dörentrup", :area_code => "5265") + AreaCode.create(:country => germany, :name => "Lemgo-Kirchheide", :area_code => "5266") + AreaCode.create(:country => germany, :name => "Höxter", :area_code => "5271") + AreaCode.create(:country => germany, :name => "Brakel Westf", :area_code => "5272") + AreaCode.create(:country => germany, :name => "Beverungen", :area_code => "5273") + AreaCode.create(:country => germany, :name => "Nieheim", :area_code => "5274") + AreaCode.create(:country => germany, :name => "Höxter-Ottbergen", :area_code => "5275") + AreaCode.create(:country => germany, :name => "Marienmünster", :area_code => "5276") + AreaCode.create(:country => germany, :name => "Höxter-Fürstenau", :area_code => "5277") + AreaCode.create(:country => germany, :name => "Höxter-Ovenhausen", :area_code => "5278") + AreaCode.create(:country => germany, :name => "Bad Pyrmont", :area_code => "5281") + AreaCode.create(:country => germany, :name => "Schieder-Schwalenberg", :area_code => "5282") + AreaCode.create(:country => germany, :name => "Lügde-Rischenau", :area_code => "5283") + AreaCode.create(:country => germany, :name => "Schwalenberg", :area_code => "5284") + AreaCode.create(:country => germany, :name => "Bad Pyrmont-Kleinenberg", :area_code => "5285") + AreaCode.create(:country => germany, :name => "Ottenstein Niedersachs", :area_code => "5286") + AreaCode.create(:country => germany, :name => "Lichtenau-Atteln", :area_code => "5292") + AreaCode.create(:country => germany, :name => "Paderborn-Dahl", :area_code => "5293") + AreaCode.create(:country => germany, :name => "Hövelhof-Espeln", :area_code => "5294") + AreaCode.create(:country => germany, :name => "Lichtenau Westf", :area_code => "5295") + AreaCode.create(:country => germany, :name => "Salzgitter-Üfingen", :area_code => "5300") + AreaCode.create(:country => germany, :name => "Lehre-Essenrode", :area_code => "5301") + AreaCode.create(:country => germany, :name => "Vechelde", :area_code => "5302") + AreaCode.create(:country => germany, :name => "Wendeburg", :area_code => "5303") + AreaCode.create(:country => germany, :name => "Meine", :area_code => "5304") + AreaCode.create(:country => germany, :name => "Sickte", :area_code => "5305") + AreaCode.create(:country => germany, :name => "Cremlingen", :area_code => "5306") + AreaCode.create(:country => germany, :name => "Braunschweig-Wenden", :area_code => "5307") + AreaCode.create(:country => germany, :name => "Lehre", :area_code => "5308") + AreaCode.create(:country => germany, :name => "Lehre-Wendhausen", :area_code => "5309") + AreaCode.create(:country => germany, :name => "Braunschweig", :area_code => "531") + AreaCode.create(:country => germany, :name => "Torfhaus", :area_code => "5320") + AreaCode.create(:country => germany, :name => "Goslar", :area_code => "5321") + AreaCode.create(:country => germany, :name => "Bad Harzburg", :area_code => "5322") + AreaCode.create(:country => germany, :name => "Clausthal-Zellerfeld", :area_code => "5323") + AreaCode.create(:country => germany, :name => "Vienenburg", :area_code => "5324") + AreaCode.create(:country => germany, :name => "Goslar-Hahnenklee", :area_code => "5325") + AreaCode.create(:country => germany, :name => "Langelsheim", :area_code => "5326") + AreaCode.create(:country => germany, :name => "Bad Grund Harz", :area_code => "5327") + AreaCode.create(:country => germany, :name => "Altenau Harz", :area_code => "5328") + AreaCode.create(:country => germany, :name => "Schulenberg im Oberharz", :area_code => "5329") + AreaCode.create(:country => germany, :name => "Wolfenbüttel", :area_code => "5331") + AreaCode.create(:country => germany, :name => "Schöppenstedt", :area_code => "5332") + AreaCode.create(:country => germany, :name => "Dettum", :area_code => "5333") + AreaCode.create(:country => germany, :name => "Hornburg Kr Wolfenbüttel", :area_code => "5334") + AreaCode.create(:country => germany, :name => "Schladen", :area_code => "5335") + AreaCode.create(:country => germany, :name => "Semmenstedt", :area_code => "5336") + AreaCode.create(:country => germany, :name => "Kissenbrück", :area_code => "5337") + AreaCode.create(:country => germany, :name => "Gielde", :area_code => "5339") + AreaCode.create(:country => germany, :name => "Salzgitter", :area_code => "5341") + AreaCode.create(:country => germany, :name => "Lengede", :area_code => "5344") + AreaCode.create(:country => germany, :name => "Baddeckenstedt", :area_code => "5345") + AreaCode.create(:country => germany, :name => "Liebenburg", :area_code => "5346") + AreaCode.create(:country => germany, :name => "Burgdorf b Salzgitter", :area_code => "5347") + AreaCode.create(:country => germany, :name => "Helmstedt", :area_code => "5351") + AreaCode.create(:country => germany, :name => "Schöningen", :area_code => "5352") + AreaCode.create(:country => germany, :name => "Königslutter am Elm", :area_code => "5353") + AreaCode.create(:country => germany, :name => "Jerxheim", :area_code => "5354") + AreaCode.create(:country => germany, :name => "Frellstedt", :area_code => "5355") + AreaCode.create(:country => germany, :name => "Helmstedt-Barmke", :area_code => "5356") + AreaCode.create(:country => germany, :name => "Grasleben", :area_code => "5357") + AreaCode.create(:country => germany, :name => "Bahrdorf-Mackendorf", :area_code => "5358") + AreaCode.create(:country => germany, :name => "Wolfsburg", :area_code => "5361") + AreaCode.create(:country => germany, :name => "Wolfsburg-Fallersleben", :area_code => "5362") + AreaCode.create(:country => germany, :name => "Wolfsburg-Vorsfelde", :area_code => "5363") + AreaCode.create(:country => germany, :name => "Velpke", :area_code => "5364") + AreaCode.create(:country => germany, :name => "Wolfsburg-Neindorf", :area_code => "5365") + AreaCode.create(:country => germany, :name => "Jembke", :area_code => "5366") + AreaCode.create(:country => germany, :name => "Rühen", :area_code => "5367") + AreaCode.create(:country => germany, :name => "Parsau", :area_code => "5368") + AreaCode.create(:country => germany, :name => "Gifhorn", :area_code => "5371") + AreaCode.create(:country => germany, :name => "Meinersen", :area_code => "5372") + AreaCode.create(:country => germany, :name => "Hillerse Kr Gifhorn", :area_code => "5373") + AreaCode.create(:country => germany, :name => "Isenbüttel", :area_code => "5374") + AreaCode.create(:country => germany, :name => "Müden Aller", :area_code => "5375") + AreaCode.create(:country => germany, :name => "Wesendorf Kr Gifhorn", :area_code => "5376") + AreaCode.create(:country => germany, :name => "Ehra-Lessien", :area_code => "5377") + AreaCode.create(:country => germany, :name => "Sassenburg-Platendorf", :area_code => "5378") + AreaCode.create(:country => germany, :name => "Sassenburg-Grussendorf", :area_code => "5379") + AreaCode.create(:country => germany, :name => "Seesen", :area_code => "5381") + AreaCode.create(:country => germany, :name => "Bad Gandersheim", :area_code => "5382") + AreaCode.create(:country => germany, :name => "Lutter am Barenberge", :area_code => "5383") + AreaCode.create(:country => germany, :name => "Seesen-Groß Rhüden", :area_code => "5384") + AreaCode.create(:country => germany, :name => "Georgsmarienhütte", :area_code => "5401") + AreaCode.create(:country => germany, :name => "Bissendorf Kr Osnabrück", :area_code => "5402") + AreaCode.create(:country => germany, :name => "Bad Iburg", :area_code => "5403") + AreaCode.create(:country => germany, :name => "Westerkappeln", :area_code => "5404") + AreaCode.create(:country => germany, :name => "Hasbergen Kr Osnabrück", :area_code => "5405") + AreaCode.create(:country => germany, :name => "Belm", :area_code => "5406") + AreaCode.create(:country => germany, :name => "Wallenhorst", :area_code => "5407") + AreaCode.create(:country => germany, :name => "Hilter am Teutoburger Wald", :area_code => "5409") + AreaCode.create(:country => germany, :name => "Osnabrück", :area_code => "541") + AreaCode.create(:country => germany, :name => "Dissen am Teutoburger Wald", :area_code => "5421") + AreaCode.create(:country => germany, :name => "Melle", :area_code => "5422") + AreaCode.create(:country => germany, :name => "Versmold", :area_code => "5423") + AreaCode.create(:country => germany, :name => "Bad Rothenfelde", :area_code => "5424") + AreaCode.create(:country => germany, :name => "Borgholzhausen", :area_code => "5425") + AreaCode.create(:country => germany, :name => "Glandorf", :area_code => "5426") + AreaCode.create(:country => germany, :name => "Melle-Buer", :area_code => "5427") + AreaCode.create(:country => germany, :name => "Melle-Neuenkirchen", :area_code => "5428") + AreaCode.create(:country => germany, :name => "Melle-Wellingholzhausen", :area_code => "5429") + AreaCode.create(:country => germany, :name => "Quakenbrück", :area_code => "5431") + AreaCode.create(:country => germany, :name => "Löningen", :area_code => "5432") + AreaCode.create(:country => germany, :name => "Badbergen", :area_code => "5433") + AreaCode.create(:country => germany, :name => "Essen Oldenburg", :area_code => "5434") + AreaCode.create(:country => germany, :name => "Berge b Quakenbrück", :area_code => "5435") + AreaCode.create(:country => germany, :name => "Nortrup", :area_code => "5436") + AreaCode.create(:country => germany, :name => "Menslage", :area_code => "5437") + AreaCode.create(:country => germany, :name => "Bakum-Lüsche", :area_code => "5438") + AreaCode.create(:country => germany, :name => "Bersenbrück", :area_code => "5439") + AreaCode.create(:country => germany, :name => "Diepholz", :area_code => "5441") + AreaCode.create(:country => germany, :name => "Barnstorf Kr Diepholz", :area_code => "5442") + AreaCode.create(:country => germany, :name => "Lemförde", :area_code => "5443") + AreaCode.create(:country => germany, :name => "Wagenfeld", :area_code => "5444") + AreaCode.create(:country => germany, :name => "Drebber", :area_code => "5445") + AreaCode.create(:country => germany, :name => "Rehden", :area_code => "5446") + AreaCode.create(:country => germany, :name => "Lembruch", :area_code => "5447") + AreaCode.create(:country => germany, :name => "Barver", :area_code => "5448") + AreaCode.create(:country => germany, :name => "Ibbenbüren", :area_code => "5451") + AreaCode.create(:country => germany, :name => "Mettingen Westf", :area_code => "5452") + AreaCode.create(:country => germany, :name => "Recke", :area_code => "5453") + AreaCode.create(:country => germany, :name => "Hörstel-Riesenbeck", :area_code => "5454") + AreaCode.create(:country => germany, :name => "Tecklenburg-Brochterbeck", :area_code => "5455") + AreaCode.create(:country => germany, :name => "Westerkappeln-Velpe", :area_code => "5456") + AreaCode.create(:country => germany, :name => "Hopsten-Schale", :area_code => "5457") + AreaCode.create(:country => germany, :name => "Hopsten", :area_code => "5458") + AreaCode.create(:country => germany, :name => "Hörstel", :area_code => "5459") + AreaCode.create(:country => germany, :name => "Bramsche Hase", :area_code => "5461") + AreaCode.create(:country => germany, :name => "Ankum", :area_code => "5462") + AreaCode.create(:country => germany, :name => "Alfhausen", :area_code => "5464") + AreaCode.create(:country => germany, :name => "Neuenkirchen b Bramsche", :area_code => "5465") + AreaCode.create(:country => germany, :name => "Merzen", :area_code => "5466") + AreaCode.create(:country => germany, :name => "Voltlage", :area_code => "5467") + AreaCode.create(:country => germany, :name => "Bramsche-Engter", :area_code => "5468") + AreaCode.create(:country => germany, :name => "Bohmte", :area_code => "5471") + AreaCode.create(:country => germany, :name => "Bad Essen", :area_code => "5472") + AreaCode.create(:country => germany, :name => "Ostercappeln", :area_code => "5473") + AreaCode.create(:country => germany, :name => "Stemwede-Dielingen", :area_code => "5474") + AreaCode.create(:country => germany, :name => "Bohmte-Hunteburg", :area_code => "5475") + AreaCode.create(:country => germany, :name => "Ostercappeln-Venne", :area_code => "5476") + AreaCode.create(:country => germany, :name => "Lengerich Westf", :area_code => "5481") + AreaCode.create(:country => germany, :name => "Tecklenburg", :area_code => "5482") + AreaCode.create(:country => germany, :name => "Lienen", :area_code => "5483") + AreaCode.create(:country => germany, :name => "Lienen-Kattenvenne", :area_code => "5484") + AreaCode.create(:country => germany, :name => "Ladbergen", :area_code => "5485") + AreaCode.create(:country => germany, :name => "Damme Dümmer", :area_code => "5491") + AreaCode.create(:country => germany, :name => "Steinfeld Oldenburg", :area_code => "5492") + AreaCode.create(:country => germany, :name => "Neuenkirchen Kr Vechta", :area_code => "5493") + AreaCode.create(:country => germany, :name => "Holdorf Niedersachs", :area_code => "5494") + AreaCode.create(:country => germany, :name => "Vörden Kr Vechta", :area_code => "5495") + AreaCode.create(:country => germany, :name => "Dransfeld", :area_code => "5502") + AreaCode.create(:country => germany, :name => "Nörten-Hardenberg", :area_code => "5503") + AreaCode.create(:country => germany, :name => "Friedland Kr Göttingen", :area_code => "5504") + AreaCode.create(:country => germany, :name => "Hardegsen", :area_code => "5505") + AreaCode.create(:country => germany, :name => "Adelebsen", :area_code => "5506") + AreaCode.create(:country => germany, :name => "Ebergötzen", :area_code => "5507") + AreaCode.create(:country => germany, :name => "Gleichen-Rittmarshausen", :area_code => "5508") + AreaCode.create(:country => germany, :name => "Rosdorf Kr Göttingen", :area_code => "5509") + AreaCode.create(:country => germany, :name => "Göttingen", :area_code => "551") + AreaCode.create(:country => germany, :name => "Braunlage", :area_code => "5520") + AreaCode.create(:country => germany, :name => "Herzberg am Harz", :area_code => "5521") + AreaCode.create(:country => germany, :name => "Osterode am Harz", :area_code => "5522") + AreaCode.create(:country => germany, :name => "Bad Sachsa", :area_code => "5523") + AreaCode.create(:country => germany, :name => "Bad Lauterberg im Harz", :area_code => "5524") + AreaCode.create(:country => germany, :name => "Walkenried", :area_code => "5525") + AreaCode.create(:country => germany, :name => "Duderstadt", :area_code => "5527") + AreaCode.create(:country => germany, :name => "Gieboldehausen", :area_code => "5528") + AreaCode.create(:country => germany, :name => "Rhumspringe", :area_code => "5529") + AreaCode.create(:country => germany, :name => "Holzminden", :area_code => "5531") + AreaCode.create(:country => germany, :name => "Stadtoldendorf", :area_code => "5532") + AreaCode.create(:country => germany, :name => "Bodenwerder", :area_code => "5533") + AreaCode.create(:country => germany, :name => "Eschershausen a d Lenne", :area_code => "5534") + AreaCode.create(:country => germany, :name => "Polle", :area_code => "5535") + AreaCode.create(:country => germany, :name => "Holzminden-Neuhaus", :area_code => "5536") + AreaCode.create(:country => germany, :name => "Hann. Münden", :area_code => "5541") + AreaCode.create(:country => germany, :name => "Witzenhausen", :area_code => "5542") + AreaCode.create(:country => germany, :name => "Staufenberg Niedersachs", :area_code => "5543") + AreaCode.create(:country => germany, :name => "Reinhardshagen", :area_code => "5544") + AreaCode.create(:country => germany, :name => "Hedemünden", :area_code => "5545") + AreaCode.create(:country => germany, :name => "Scheden", :area_code => "5546") + AreaCode.create(:country => germany, :name => "Northeim", :area_code => "5551") + AreaCode.create(:country => germany, :name => "Katlenburg", :area_code => "5552") + AreaCode.create(:country => germany, :name => "Kalefeld", :area_code => "5553") + AreaCode.create(:country => germany, :name => "Moringen", :area_code => "5554") + AreaCode.create(:country => germany, :name => "Moringen-Fredelsloh", :area_code => "5555") + AreaCode.create(:country => germany, :name => "Lindau Harz", :area_code => "5556") + AreaCode.create(:country => germany, :name => "Einbeck", :area_code => "5561") + AreaCode.create(:country => germany, :name => "Dassel-Markoldendorf", :area_code => "5562") + AreaCode.create(:country => germany, :name => "Kreiensen", :area_code => "5563") + AreaCode.create(:country => germany, :name => "Dassel", :area_code => "5564") + AreaCode.create(:country => germany, :name => "Einbeck-Wenzen", :area_code => "5565") + AreaCode.create(:country => germany, :name => "Uslar", :area_code => "5571") + AreaCode.create(:country => germany, :name => "Bodenfelde", :area_code => "5572") + AreaCode.create(:country => germany, :name => "Uslar-Volpriehausen", :area_code => "5573") + AreaCode.create(:country => germany, :name => "Oberweser", :area_code => "5574") + AreaCode.create(:country => germany, :name => "Sankt Andreasberg", :area_code => "5582") + AreaCode.create(:country => germany, :name => "Braunlage-Hohegeiss", :area_code => "5583") + AreaCode.create(:country => germany, :name => "Hattorf am Harz", :area_code => "5584") + AreaCode.create(:country => germany, :name => "Herzberg-Sieber", :area_code => "5585") + AreaCode.create(:country => germany, :name => "Wieda", :area_code => "5586") + AreaCode.create(:country => germany, :name => "Gleichen-Bremke", :area_code => "5592") + AreaCode.create(:country => germany, :name => "Bovenden-Lenglern", :area_code => "5593") + AreaCode.create(:country => germany, :name => "Bovenden-Reyershausen", :area_code => "5594") + AreaCode.create(:country => germany, :name => "Schauenburg", :area_code => "5601") + AreaCode.create(:country => germany, :name => "Hessisch Lichtenau", :area_code => "5602") + AreaCode.create(:country => germany, :name => "Gudensberg", :area_code => "5603") + AreaCode.create(:country => germany, :name => "Grossalmerode", :area_code => "5604") + AreaCode.create(:country => germany, :name => "Kaufungen Hess", :area_code => "5605") + AreaCode.create(:country => germany, :name => "Zierenberg", :area_code => "5606") + AreaCode.create(:country => germany, :name => "Fuldatal", :area_code => "5607") + AreaCode.create(:country => germany, :name => "Söhrewald", :area_code => "5608") + AreaCode.create(:country => germany, :name => "Ahnatal", :area_code => "5609") + AreaCode.create(:country => germany, :name => "Kassel", :area_code => "561") + AreaCode.create(:country => germany, :name => "Bad Wildungen", :area_code => "5621") + AreaCode.create(:country => germany, :name => "Fritzlar", :area_code => "5622") + AreaCode.create(:country => germany, :name => "Edertal", :area_code => "5623") + AreaCode.create(:country => germany, :name => "Bad Emstal", :area_code => "5624") + AreaCode.create(:country => germany, :name => "Naumburg Hess", :area_code => "5625") + AreaCode.create(:country => germany, :name => "Bad Zwesten", :area_code => "5626") + AreaCode.create(:country => germany, :name => "Korbach", :area_code => "5631") + AreaCode.create(:country => germany, :name => "Willingen Upland", :area_code => "5632") + AreaCode.create(:country => germany, :name => "Diemelsee", :area_code => "5633") + AreaCode.create(:country => germany, :name => "Waldeck-Sachsenhausen", :area_code => "5634") + AreaCode.create(:country => germany, :name => "Vöhl", :area_code => "5635") + AreaCode.create(:country => germany, :name => "Lichtenfels-Goddelsheim", :area_code => "5636") + AreaCode.create(:country => germany, :name => "Warburg", :area_code => "5641") + AreaCode.create(:country => germany, :name => "Warburg-Scherfede", :area_code => "5642") + AreaCode.create(:country => germany, :name => "Borgentreich", :area_code => "5643") + AreaCode.create(:country => germany, :name => "Willebadessen-Peckelsheim", :area_code => "5644") + AreaCode.create(:country => germany, :name => "Borgentreich-Borgholz", :area_code => "5645") + AreaCode.create(:country => germany, :name => "Willebadessen", :area_code => "5646") + AreaCode.create(:country => germany, :name => "Lichtenau-Kleinenberg", :area_code => "5647") + AreaCode.create(:country => germany, :name => "Brakel-Gehrden", :area_code => "5648") + AreaCode.create(:country => germany, :name => "Cornberg", :area_code => "5650") + AreaCode.create(:country => germany, :name => "Eschwege", :area_code => "5651") + AreaCode.create(:country => germany, :name => "Bad Sooden-Allendorf", :area_code => "5652") + AreaCode.create(:country => germany, :name => "Sontra", :area_code => "5653") + AreaCode.create(:country => germany, :name => "Herleshausen", :area_code => "5654") + AreaCode.create(:country => germany, :name => "Wanfried", :area_code => "5655") + AreaCode.create(:country => germany, :name => "Waldkappel", :area_code => "5656") + AreaCode.create(:country => germany, :name => "Meissner", :area_code => "5657") + AreaCode.create(:country => germany, :name => "Wehretal", :area_code => "5658") + AreaCode.create(:country => germany, :name => "Ringgau", :area_code => "5659") + AreaCode.create(:country => germany, :name => "Melsungen", :area_code => "5661") + AreaCode.create(:country => germany, :name => "Felsberg Hess", :area_code => "5662") + AreaCode.create(:country => germany, :name => "Spangenberg", :area_code => "5663") + AreaCode.create(:country => germany, :name => "Morschen", :area_code => "5664") + AreaCode.create(:country => germany, :name => "Guxhagen", :area_code => "5665") + AreaCode.create(:country => germany, :name => "Hofgeismar", :area_code => "5671") + AreaCode.create(:country => germany, :name => "Bad Karlshafen", :area_code => "5672") + AreaCode.create(:country => germany, :name => "Immenhausen Hess", :area_code => "5673") + AreaCode.create(:country => germany, :name => "Grebenstein", :area_code => "5674") + AreaCode.create(:country => germany, :name => "Trendelburg", :area_code => "5675") + AreaCode.create(:country => germany, :name => "Liebenau Hess", :area_code => "5676") + AreaCode.create(:country => germany, :name => "Calden-Westuffeln", :area_code => "5677") + AreaCode.create(:country => germany, :name => "Homberg Efze", :area_code => "5681") + AreaCode.create(:country => germany, :name => "Borken Hessen", :area_code => "5682") + AreaCode.create(:country => germany, :name => "Wabern Hess", :area_code => "5683") + AreaCode.create(:country => germany, :name => "Frielendorf", :area_code => "5684") + AreaCode.create(:country => germany, :name => "Knüllwald", :area_code => "5685") + AreaCode.create(:country => germany, :name => "Schwarzenborn Knüll", :area_code => "5686") + AreaCode.create(:country => germany, :name => "Bad Arolsen", :area_code => "5691") + AreaCode.create(:country => germany, :name => "Wolfhagen", :area_code => "5692") + AreaCode.create(:country => germany, :name => "Volkmarsen", :area_code => "5693") + AreaCode.create(:country => germany, :name => "Diemelstadt", :area_code => "5694") + AreaCode.create(:country => germany, :name => "Twistetal", :area_code => "5695") + AreaCode.create(:country => germany, :name => "Bad Arolsen-Landau", :area_code => "5696") + AreaCode.create(:country => germany, :name => "Petershagen-Lahde", :area_code => "5702") + AreaCode.create(:country => germany, :name => "Hille", :area_code => "5703") + AreaCode.create(:country => germany, :name => "Petershagen-Friedewalde", :area_code => "5704") + AreaCode.create(:country => germany, :name => "Petershagen-Windheim", :area_code => "5705") + AreaCode.create(:country => germany, :name => "Porta Westfalica", :area_code => "5706") + AreaCode.create(:country => germany, :name => "Petershagen Weser", :area_code => "5707") + AreaCode.create(:country => germany, :name => "Minden Westf", :area_code => "571") + AreaCode.create(:country => germany, :name => "Stadthagen", :area_code => "5721") + AreaCode.create(:country => germany, :name => "Bückeburg", :area_code => "5722") + AreaCode.create(:country => germany, :name => "Bad Nenndorf", :area_code => "5723") + AreaCode.create(:country => germany, :name => "Obernkirchen", :area_code => "5724") + AreaCode.create(:country => germany, :name => "Lindhorst b Stadthagen", :area_code => "5725") + AreaCode.create(:country => germany, :name => "Wiedensahl", :area_code => "5726") + AreaCode.create(:country => germany, :name => "Bad Oeynhausen", :area_code => "5731") + AreaCode.create(:country => germany, :name => "Löhne", :area_code => "5732") + AreaCode.create(:country => germany, :name => "Vlotho", :area_code => "5733") + AreaCode.create(:country => germany, :name => "Bergkirchen Westf", :area_code => "5734") + AreaCode.create(:country => germany, :name => "Lübbecke", :area_code => "5741") + AreaCode.create(:country => germany, :name => "Preussisch Oldendorf", :area_code => "5742") + AreaCode.create(:country => germany, :name => "Espelkamp-Gestringen", :area_code => "5743") + AreaCode.create(:country => germany, :name => "Hüllhorst", :area_code => "5744") + AreaCode.create(:country => germany, :name => "Stemwede-Levern", :area_code => "5745") + AreaCode.create(:country => germany, :name => "Rödinghausen", :area_code => "5746") + AreaCode.create(:country => germany, :name => "Rinteln", :area_code => "5751") + AreaCode.create(:country => germany, :name => "Auetal-Hattendorf", :area_code => "5752") + AreaCode.create(:country => germany, :name => "Auetal-Bernsen", :area_code => "5753") + AreaCode.create(:country => germany, :name => "Extertal-Bremke", :area_code => "5754") + AreaCode.create(:country => germany, :name => "Kalletal-Varenholz", :area_code => "5755") + AreaCode.create(:country => germany, :name => "Stolzenau", :area_code => "5761") + AreaCode.create(:country => germany, :name => "Uchte", :area_code => "5763") + AreaCode.create(:country => germany, :name => "Steyerberg", :area_code => "5764") + AreaCode.create(:country => germany, :name => "Raddestorf", :area_code => "5765") + AreaCode.create(:country => germany, :name => "Rehburg-Loccum", :area_code => "5766") + AreaCode.create(:country => germany, :name => "Warmsen", :area_code => "5767") + AreaCode.create(:country => germany, :name => "Petershagen-Heimsen", :area_code => "5768") + AreaCode.create(:country => germany, :name => "Steyerberg-Voigtei", :area_code => "5769") + AreaCode.create(:country => germany, :name => "Rahden Westf", :area_code => "5771") + AreaCode.create(:country => germany, :name => "Espelkamp", :area_code => "5772") + AreaCode.create(:country => germany, :name => "Stemwede-Wehdem", :area_code => "5773") + AreaCode.create(:country => germany, :name => "Wagenfeld-Ströhen", :area_code => "5774") + AreaCode.create(:country => germany, :name => "Diepenau", :area_code => "5775") + AreaCode.create(:country => germany, :name => "Preussisch Ströhen", :area_code => "5776") + AreaCode.create(:country => germany, :name => "Diepenau-Essern", :area_code => "5777") + AreaCode.create(:country => germany, :name => "Wrestedt", :area_code => "5802") + AreaCode.create(:country => germany, :name => "Rosche", :area_code => "5803") + AreaCode.create(:country => germany, :name => "Rätzlingen Kr Uelzen", :area_code => "5804") + AreaCode.create(:country => germany, :name => "Oetzen", :area_code => "5805") + AreaCode.create(:country => germany, :name => "Barum b Bad Bevensen", :area_code => "5806") + AreaCode.create(:country => germany, :name => "Altenmedingen", :area_code => "5807") + AreaCode.create(:country => germany, :name => "Gerdau", :area_code => "5808") + AreaCode.create(:country => germany, :name => "Uelzen", :area_code => "581") + AreaCode.create(:country => germany, :name => "Suhlendorf", :area_code => "5820") + AreaCode.create(:country => germany, :name => "Bad Bevensen", :area_code => "5821") + AreaCode.create(:country => germany, :name => "Ebstorf", :area_code => "5822") + AreaCode.create(:country => germany, :name => "Bienenbüttel", :area_code => "5823") + AreaCode.create(:country => germany, :name => "Bad Bodenteich", :area_code => "5824") + AreaCode.create(:country => germany, :name => "Wieren", :area_code => "5825") + AreaCode.create(:country => germany, :name => "Suderburg", :area_code => "5826") + AreaCode.create(:country => germany, :name => "Unterlüß", :area_code => "5827") + AreaCode.create(:country => germany, :name => "Himbergen", :area_code => "5828") + AreaCode.create(:country => germany, :name => "Wriedel", :area_code => "5829") + AreaCode.create(:country => germany, :name => "Wittingen", :area_code => "5831") + AreaCode.create(:country => germany, :name => "Hankensbüttel", :area_code => "5832") + AreaCode.create(:country => germany, :name => "Brome", :area_code => "5833") + AreaCode.create(:country => germany, :name => "Wittingen-Knesebeck", :area_code => "5834") + AreaCode.create(:country => germany, :name => "Wahrenholz", :area_code => "5835") + AreaCode.create(:country => germany, :name => "Wittingen-Radenbeck", :area_code => "5836") + AreaCode.create(:country => germany, :name => "Sprakensehl", :area_code => "5837") + AreaCode.create(:country => germany, :name => "Gross Oesingen", :area_code => "5838") + AreaCode.create(:country => germany, :name => "Wittingen-Ohrdorf", :area_code => "5839") + AreaCode.create(:country => germany, :name => "Schnackenburg", :area_code => "5840") + AreaCode.create(:country => germany, :name => "Lüchow Wendland", :area_code => "5841") + AreaCode.create(:country => germany, :name => "Schnega", :area_code => "5842") + AreaCode.create(:country => germany, :name => "Wustrow", :area_code => "5843") + AreaCode.create(:country => germany, :name => "Clenze", :area_code => "5844") + AreaCode.create(:country => germany, :name => "Bergen Dumme", :area_code => "5845") + AreaCode.create(:country => germany, :name => "Gartow Niedersachs", :area_code => "5846") + AreaCode.create(:country => germany, :name => "Trebel", :area_code => "5848") + AreaCode.create(:country => germany, :name => "Waddeweitz", :area_code => "5849") + AreaCode.create(:country => germany, :name => "Neetze", :area_code => "5850") + AreaCode.create(:country => germany, :name => "Dahlenburg", :area_code => "5851") + AreaCode.create(:country => germany, :name => "Bleckede", :area_code => "5852") + AreaCode.create(:country => germany, :name => "Neu Darchau", :area_code => "5853") + AreaCode.create(:country => germany, :name => "Bleckede-Barskamp", :area_code => "5854") + AreaCode.create(:country => germany, :name => "Nahrendorf", :area_code => "5855") + AreaCode.create(:country => germany, :name => "Bleckede-Brackede", :area_code => "5857") + AreaCode.create(:country => germany, :name => "Hitzacker-Wietzetze", :area_code => "5858") + AreaCode.create(:country => germany, :name => "Thomasburg", :area_code => "5859") + AreaCode.create(:country => germany, :name => "Dannenberg Elbe", :area_code => "5861") + AreaCode.create(:country => germany, :name => "Hitzacker Elbe", :area_code => "5862") + AreaCode.create(:country => germany, :name => "Zernien", :area_code => "5863") + AreaCode.create(:country => germany, :name => "Jameln", :area_code => "5864") + AreaCode.create(:country => germany, :name => "Gusborn", :area_code => "5865") + AreaCode.create(:country => germany, :name => "Stoetze", :area_code => "5872") + AreaCode.create(:country => germany, :name => "Eimke", :area_code => "5873") + AreaCode.create(:country => germany, :name => "Soltendieck", :area_code => "5874") + AreaCode.create(:country => germany, :name => "Emmendorf", :area_code => "5875") + AreaCode.create(:country => germany, :name => "Gorleben", :area_code => "5882") + AreaCode.create(:country => germany, :name => "Lemgow", :area_code => "5883") + AreaCode.create(:country => germany, :name => "Fürstenau b Bramsche", :area_code => "5901") + AreaCode.create(:country => germany, :name => "Freren", :area_code => "5902") + AreaCode.create(:country => germany, :name => "Emsbüren", :area_code => "5903") + AreaCode.create(:country => germany, :name => "Lengerich Emsl", :area_code => "5904") + AreaCode.create(:country => germany, :name => "Beesten", :area_code => "5905") + AreaCode.create(:country => germany, :name => "Lünne", :area_code => "5906") + AreaCode.create(:country => germany, :name => "Geeste", :area_code => "5907") + AreaCode.create(:country => germany, :name => "Wietmarschen-Lohne", :area_code => "5908") + AreaCode.create(:country => germany, :name => "Wettrup", :area_code => "5909") + AreaCode.create(:country => germany, :name => "Lingen (Ems)", :area_code => "591") + AreaCode.create(:country => germany, :name => "Nordhorn", :area_code => "5921") + AreaCode.create(:country => germany, :name => "Bad Bentheim", :area_code => "5922") + AreaCode.create(:country => germany, :name => "Schüttorf", :area_code => "5923") + AreaCode.create(:country => germany, :name => "Bad Bentheim-Gildehaus", :area_code => "5924") + AreaCode.create(:country => germany, :name => "Wietmarschen", :area_code => "5925") + AreaCode.create(:country => germany, :name => "Engden", :area_code => "5926") + AreaCode.create(:country => germany, :name => "Meppen", :area_code => "5931") + AreaCode.create(:country => germany, :name => "Haren Ems", :area_code => "5932") + AreaCode.create(:country => germany, :name => "Lathen", :area_code => "5933") + AreaCode.create(:country => germany, :name => "Haren-Rütenbrock", :area_code => "5934") + AreaCode.create(:country => germany, :name => "Twist-Schöninghsdorf", :area_code => "5935") + AreaCode.create(:country => germany, :name => "Twist", :area_code => "5936") + AreaCode.create(:country => germany, :name => "Geeste-Gross Hesepe", :area_code => "5937") + AreaCode.create(:country => germany, :name => "Sustrum", :area_code => "5939") + AreaCode.create(:country => germany, :name => "Neuenhaus Dinkel", :area_code => "5941") + AreaCode.create(:country => germany, :name => "Uelsen", :area_code => "5942") + AreaCode.create(:country => germany, :name => "Emlichheim", :area_code => "5943") + AreaCode.create(:country => germany, :name => "Hoogstede", :area_code => "5944") + AreaCode.create(:country => germany, :name => "Wilsum", :area_code => "5945") + AreaCode.create(:country => germany, :name => "Georgsdorf", :area_code => "5946") + AreaCode.create(:country => germany, :name => "Laar Vechte", :area_code => "5947") + AreaCode.create(:country => germany, :name => "Itterbeck", :area_code => "5948") + AreaCode.create(:country => germany, :name => "Werlte", :area_code => "5951") + AreaCode.create(:country => germany, :name => "Sögel", :area_code => "5952") + AreaCode.create(:country => germany, :name => "Börger", :area_code => "5953") + AreaCode.create(:country => germany, :name => "Lorup", :area_code => "5954") + AreaCode.create(:country => germany, :name => "Esterwegen", :area_code => "5955") + AreaCode.create(:country => germany, :name => "Rastdorf", :area_code => "5956") + AreaCode.create(:country => germany, :name => "Lindern Oldenburg", :area_code => "5957") + AreaCode.create(:country => germany, :name => "Haselünne", :area_code => "5961") + AreaCode.create(:country => germany, :name => "Herzlake", :area_code => "5962") + AreaCode.create(:country => germany, :name => "Bawinkel", :area_code => "5963") + AreaCode.create(:country => germany, :name => "Lähden", :area_code => "5964") + AreaCode.create(:country => germany, :name => "Klein Berssen", :area_code => "5965") + AreaCode.create(:country => germany, :name => "Meppen-Apeldorn", :area_code => "5966") + AreaCode.create(:country => germany, :name => "Rheine", :area_code => "5971") + AreaCode.create(:country => germany, :name => "Neuenkirchen Kr Steinfurt", :area_code => "5973") + AreaCode.create(:country => germany, :name => "Rheine-Mesum", :area_code => "5975") + AreaCode.create(:country => germany, :name => "Salzbergen", :area_code => "5976") + AreaCode.create(:country => germany, :name => "Spelle", :area_code => "5977") + AreaCode.create(:country => germany, :name => "Hörstel-Dreierwalde", :area_code => "5978") + AreaCode.create(:country => germany, :name => "Ober-Mörlen", :area_code => "6002") + AreaCode.create(:country => germany, :name => "Rosbach v d Höhe", :area_code => "6003") + AreaCode.create(:country => germany, :name => "Lich-Eberstadt", :area_code => "6004") + AreaCode.create(:country => germany, :name => "Rosbach-Rodheim", :area_code => "6007") + AreaCode.create(:country => germany, :name => "Echzell", :area_code => "6008") + AreaCode.create(:country => germany, :name => "Heigenbrücken", :area_code => "6020") + AreaCode.create(:country => germany, :name => "Aschaffenburg", :area_code => "6021") + AreaCode.create(:country => germany, :name => "Obernburg a Main", :area_code => "6022") + AreaCode.create(:country => germany, :name => "Alzenau i Ufr", :area_code => "6023") + AreaCode.create(:country => germany, :name => "Schöllkrippen", :area_code => "6024") + AreaCode.create(:country => germany, :name => "Grossostheim", :area_code => "6026") + AreaCode.create(:country => germany, :name => "Stockstadt a Main", :area_code => "6027") + AreaCode.create(:country => germany, :name => "Sulzbach a Main", :area_code => "6028") + AreaCode.create(:country => germany, :name => "Mömbris", :area_code => "6029") + AreaCode.create(:country => germany, :name => "Friedberg Hess", :area_code => "6031") + AreaCode.create(:country => germany, :name => "Bad Nauheim", :area_code => "6032") + AreaCode.create(:country => germany, :name => "Butzbach", :area_code => "6033") + AreaCode.create(:country => germany, :name => "Wöllstadt", :area_code => "6034") + AreaCode.create(:country => germany, :name => "Reichelsheim Wetterau", :area_code => "6035") + AreaCode.create(:country => germany, :name => "Wölfersheim", :area_code => "6036") + AreaCode.create(:country => germany, :name => "Karben", :area_code => "6039") + AreaCode.create(:country => germany, :name => "Glauburg", :area_code => "6041") + AreaCode.create(:country => germany, :name => "Büdingen Hess", :area_code => "6042") + AreaCode.create(:country => germany, :name => "Nidda", :area_code => "6043") + AreaCode.create(:country => germany, :name => "Schotten Hess", :area_code => "6044") + AreaCode.create(:country => germany, :name => "Gedern", :area_code => "6045") + AreaCode.create(:country => germany, :name => "Ortenberg Hess", :area_code => "6046") + AreaCode.create(:country => germany, :name => "Altenstadt Hess", :area_code => "6047") + AreaCode.create(:country => germany, :name => "Büdingen-Eckartshausen", :area_code => "6048") + AreaCode.create(:country => germany, :name => "Kefenrod", :area_code => "6049") + AreaCode.create(:country => germany, :name => "Biebergemünd", :area_code => "6050") + AreaCode.create(:country => germany, :name => "Gelnhausen", :area_code => "6051") + AreaCode.create(:country => germany, :name => "Bad Orb", :area_code => "6052") + AreaCode.create(:country => germany, :name => "Wächtersbach", :area_code => "6053") + AreaCode.create(:country => germany, :name => "Birstein", :area_code => "6054") + AreaCode.create(:country => germany, :name => "Freigericht", :area_code => "6055") + AreaCode.create(:country => germany, :name => "Bad Soden-Salmünster", :area_code => "6056") + AreaCode.create(:country => germany, :name => "Flörsbachtal", :area_code => "6057") + AreaCode.create(:country => germany, :name => "Gründau", :area_code => "6058") + AreaCode.create(:country => germany, :name => "Jossgrund", :area_code => "6059") + AreaCode.create(:country => germany, :name => "Michelstadt", :area_code => "6061") + AreaCode.create(:country => germany, :name => "Erbach Odenw", :area_code => "6062") + AreaCode.create(:country => germany, :name => "Bad König", :area_code => "6063") + AreaCode.create(:country => germany, :name => "Michelstadt-Vielbrunn", :area_code => "6066") + AreaCode.create(:country => germany, :name => "Beerfelden", :area_code => "6068") + AreaCode.create(:country => germany, :name => "Dieburg", :area_code => "6071") + AreaCode.create(:country => germany, :name => "Babenhausen Hess", :area_code => "6073") + AreaCode.create(:country => germany, :name => "Rödermark", :area_code => "6074") + AreaCode.create(:country => germany, :name => "Gross-Umstadt", :area_code => "6078") + AreaCode.create(:country => germany, :name => "Usingen", :area_code => "6081") + AreaCode.create(:country => germany, :name => "Niederreifenberg", :area_code => "6082") + AreaCode.create(:country => germany, :name => "Weilrod", :area_code => "6083") + AreaCode.create(:country => germany, :name => "Schmitten Taunus", :area_code => "6084") + AreaCode.create(:country => germany, :name => "Waldsolms", :area_code => "6085") + AreaCode.create(:country => germany, :name => "Grävenwiesbach", :area_code => "6086") + AreaCode.create(:country => germany, :name => "Waldems", :area_code => "6087") + AreaCode.create(:country => germany, :name => "Heimbuchenthal", :area_code => "6092") + AreaCode.create(:country => germany, :name => "Laufach", :area_code => "6093") + AreaCode.create(:country => germany, :name => "Weibersbrunn", :area_code => "6094") + AreaCode.create(:country => germany, :name => "Bessenbach", :area_code => "6095") + AreaCode.create(:country => germany, :name => "Wiesen Unterfr", :area_code => "6096") + AreaCode.create(:country => germany, :name => "Bad Vilbel", :area_code => "6101") + AreaCode.create(:country => germany, :name => "Neu-Isenburg", :area_code => "6102") + AreaCode.create(:country => germany, :name => "Langen Hess", :area_code => "6103") + AreaCode.create(:country => germany, :name => "Heusenstamm", :area_code => "6104") + AreaCode.create(:country => germany, :name => "Mörfelden-Walldorf", :area_code => "6105") + AreaCode.create(:country => germany, :name => "Rodgau", :area_code => "6106") + AreaCode.create(:country => germany, :name => "Kelsterbach", :area_code => "6107") + AreaCode.create(:country => germany, :name => "Mühlheim am Main", :area_code => "6108") + AreaCode.create(:country => germany, :name => "Frankfurt-Bergen-Enkheim", :area_code => "6109") + AreaCode.create(:country => germany, :name => "Wiesbaden", :area_code => "611") + AreaCode.create(:country => germany, :name => "Aarbergen", :area_code => "6120") + AreaCode.create(:country => germany, :name => "Hofheim-Wallau", :area_code => "6122") + AreaCode.create(:country => germany, :name => "Eltville am Rhein", :area_code => "6123") + AreaCode.create(:country => germany, :name => "Bad Schwalbach", :area_code => "6124") + AreaCode.create(:country => germany, :name => "Idstein", :area_code => "6126") + AreaCode.create(:country => germany, :name => "Niedernhausen Taunus", :area_code => "6127") + AreaCode.create(:country => germany, :name => "Taunusstein", :area_code => "6128") + AreaCode.create(:country => germany, :name => "Schlangenbad", :area_code => "6129") + AreaCode.create(:country => germany, :name => "Schwabenheim an der Selz", :area_code => "6130") + AreaCode.create(:country => germany, :name => "Mainz", :area_code => "6131") + AreaCode.create(:country => germany, :name => "Ingelheim am Rhein", :area_code => "6132") + AreaCode.create(:country => germany, :name => "Oppenheim", :area_code => "6133") + AreaCode.create(:country => germany, :name => "Mainz-Kastel", :area_code => "6134") + AreaCode.create(:country => germany, :name => "Bodenheim Rhein", :area_code => "6135") + AreaCode.create(:country => germany, :name => "Nieder-Olm", :area_code => "6136") + AreaCode.create(:country => germany, :name => "Mommenheim", :area_code => "6138") + AreaCode.create(:country => germany, :name => "Budenheim", :area_code => "6139") + AreaCode.create(:country => germany, :name => "Rüsselsheim", :area_code => "6142") + AreaCode.create(:country => germany, :name => "Bischofsheim b Rüsselsheim", :area_code => "6144") + AreaCode.create(:country => germany, :name => "Flörsheim am Main", :area_code => "6145") + AreaCode.create(:country => germany, :name => "Hochheim am Main", :area_code => "6146") + AreaCode.create(:country => germany, :name => "Trebur", :area_code => "6147") + AreaCode.create(:country => germany, :name => "Weiterstadt", :area_code => "6150") + AreaCode.create(:country => germany, :name => "Darmstadt", :area_code => "6151") + AreaCode.create(:country => germany, :name => "Gross-Gerau", :area_code => "6152") + AreaCode.create(:country => germany, :name => "Ober-Ramstadt", :area_code => "6154") + AreaCode.create(:country => germany, :name => "Griesheim Hess", :area_code => "6155") + AreaCode.create(:country => germany, :name => "Pfungstadt", :area_code => "6157") + AreaCode.create(:country => germany, :name => "Riedstadt", :area_code => "6158") + AreaCode.create(:country => germany, :name => "Messel", :area_code => "6159") + AreaCode.create(:country => germany, :name => "Brensbach", :area_code => "6161") + AreaCode.create(:country => germany, :name => "Reinheim Odenw", :area_code => "6162") + AreaCode.create(:country => germany, :name => "Höchst i Odw", :area_code => "6163") + AreaCode.create(:country => germany, :name => "Reichelsheim Odenwald", :area_code => "6164") + AreaCode.create(:country => germany, :name => "Breuberg", :area_code => "6165") + AreaCode.create(:country => germany, :name => "Fischbachtal", :area_code => "6166") + AreaCode.create(:country => germany, :name => "Modautal", :area_code => "6167") + AreaCode.create(:country => germany, :name => "Oberursel Taunus", :area_code => "6171") + AreaCode.create(:country => germany, :name => "Bad Homburg v d Höhe", :area_code => "6172") + AreaCode.create(:country => germany, :name => "Kronberg im Taunus", :area_code => "6173") + AreaCode.create(:country => germany, :name => "Königstein im Taunus", :area_code => "6174") + AreaCode.create(:country => germany, :name => "Friedrichsdorf Taunus", :area_code => "6175") + AreaCode.create(:country => germany, :name => "Hanau", :area_code => "6181") + AreaCode.create(:country => germany, :name => "Seligenstadt", :area_code => "6182") + AreaCode.create(:country => germany, :name => "Erlensee", :area_code => "6183") + AreaCode.create(:country => germany, :name => "Langenselbold", :area_code => "6184") + AreaCode.create(:country => germany, :name => "Hammersbach Hess", :area_code => "6185") + AreaCode.create(:country => germany, :name => "Grosskrotzenburg", :area_code => "6186") + AreaCode.create(:country => germany, :name => "Schöneck", :area_code => "6187") + AreaCode.create(:country => germany, :name => "Kahl a Main", :area_code => "6188") + AreaCode.create(:country => germany, :name => "Hattersheim a Main", :area_code => "6190") + AreaCode.create(:country => germany, :name => "Hofheim am Taunus", :area_code => "6192") + AreaCode.create(:country => germany, :name => "Kelkheim Taunus", :area_code => "6195") + AreaCode.create(:country => germany, :name => "Bad Soden am Taunus", :area_code => "6196") + AreaCode.create(:country => germany, :name => "Eppstein", :area_code => "6198") + AreaCode.create(:country => germany, :name => "Weinheim Bergstr", :area_code => "6201") + AreaCode.create(:country => germany, :name => "Schwetzingen", :area_code => "6202") + AreaCode.create(:country => germany, :name => "Ladenburg", :area_code => "6203") + AreaCode.create(:country => germany, :name => "Viernheim", :area_code => "6204") + AreaCode.create(:country => germany, :name => "Hockenheim", :area_code => "6205") + AreaCode.create(:country => germany, :name => "Lampertheim", :area_code => "6206") + AreaCode.create(:country => germany, :name => "Wald-Michelbach", :area_code => "6207") + AreaCode.create(:country => germany, :name => "Mörlenbach", :area_code => "6209") + AreaCode.create(:country => germany, :name => "Mannheim", :area_code => "621") + AreaCode.create(:country => germany, :name => "Wilhelmsfeld", :area_code => "6220") + AreaCode.create(:country => germany, :name => "Heidelberg", :area_code => "6221") + AreaCode.create(:country => germany, :name => "Wiesloch", :area_code => "6222") + AreaCode.create(:country => germany, :name => "Neckargemünd", :area_code => "6223") + AreaCode.create(:country => germany, :name => "Sandhausen Baden", :area_code => "6224") + AreaCode.create(:country => germany, :name => "Meckesheim", :area_code => "6226") + AreaCode.create(:country => germany, :name => "Walldorf Baden", :area_code => "6227") + AreaCode.create(:country => germany, :name => "Schönau Odenw", :area_code => "6228") + AreaCode.create(:country => germany, :name => "Neckarsteinach", :area_code => "6229") + AreaCode.create(:country => germany, :name => "Hochdorf-Assenheim", :area_code => "6231") + AreaCode.create(:country => germany, :name => "Speyer", :area_code => "6232") + AreaCode.create(:country => germany, :name => "Frankenthal Pfalz", :area_code => "6233") + AreaCode.create(:country => germany, :name => "Mutterstadt", :area_code => "6234") + AreaCode.create(:country => germany, :name => "Schifferstadt", :area_code => "6235") + AreaCode.create(:country => germany, :name => "Neuhofen Pfalz", :area_code => "6236") + AreaCode.create(:country => germany, :name => "Maxdorf", :area_code => "6237") + AreaCode.create(:country => germany, :name => "Dirmstein", :area_code => "6238") + AreaCode.create(:country => germany, :name => "Bobenheim-Roxheim", :area_code => "6239") + AreaCode.create(:country => germany, :name => "Worms", :area_code => "6241") + AreaCode.create(:country => germany, :name => "Osthofen", :area_code => "6242") + AreaCode.create(:country => germany, :name => "Monsheim", :area_code => "6243") + AreaCode.create(:country => germany, :name => "Westhofen Rheinhess", :area_code => "6244") + AreaCode.create(:country => germany, :name => "Biblis", :area_code => "6245") + AreaCode.create(:country => germany, :name => "Eich Rheinhess", :area_code => "6246") + AreaCode.create(:country => germany, :name => "Worms-Pfeddersheim", :area_code => "6247") + AreaCode.create(:country => germany, :name => "Guntersblum", :area_code => "6249") + AreaCode.create(:country => germany, :name => "Bensheim", :area_code => "6251") + AreaCode.create(:country => germany, :name => "Heppenheim Bergstraße", :area_code => "6252") + AreaCode.create(:country => germany, :name => "Fürth Odenw", :area_code => "6253") + AreaCode.create(:country => germany, :name => "Lautertal Odenwald", :area_code => "6254") + AreaCode.create(:country => germany, :name => "Lindenfels", :area_code => "6255") + AreaCode.create(:country => germany, :name => "Lampertheim-Hüttenfeld", :area_code => "6256") + AreaCode.create(:country => germany, :name => "Seeheim-Jugenheim", :area_code => "6257") + AreaCode.create(:country => germany, :name => "Gernsheim", :area_code => "6258") + AreaCode.create(:country => germany, :name => "Mosbach Baden", :area_code => "6261") + AreaCode.create(:country => germany, :name => "Aglasterhausen", :area_code => "6262") + AreaCode.create(:country => germany, :name => "Neckargerach", :area_code => "6263") + AreaCode.create(:country => germany, :name => "Neudenau", :area_code => "6264") + AreaCode.create(:country => germany, :name => "Billigheim Baden", :area_code => "6265") + AreaCode.create(:country => germany, :name => "Hassmersheim", :area_code => "6266") + AreaCode.create(:country => germany, :name => "Fahrenbach Baden", :area_code => "6267") + AreaCode.create(:country => germany, :name => "Hüffenhardt", :area_code => "6268") + AreaCode.create(:country => germany, :name => "Gundelsheim Württ", :area_code => "6269") + AreaCode.create(:country => germany, :name => "Eberbach Baden", :area_code => "6271") + AreaCode.create(:country => germany, :name => "Hirschhorn Neckar", :area_code => "6272") + AreaCode.create(:country => germany, :name => "Waldbrunn Odenw", :area_code => "6274") + AreaCode.create(:country => germany, :name => "Rothenberg Odenw", :area_code => "6275") + AreaCode.create(:country => germany, :name => "Hesseneck", :area_code => "6276") + AreaCode.create(:country => germany, :name => "Buchen Odenwald", :area_code => "6281") + AreaCode.create(:country => germany, :name => "Walldürn", :area_code => "6282") + AreaCode.create(:country => germany, :name => "Hardheim Odenw", :area_code => "6283") + AreaCode.create(:country => germany, :name => "Mudau", :area_code => "6284") + AreaCode.create(:country => germany, :name => "Walldürn-Altheim", :area_code => "6285") + AreaCode.create(:country => germany, :name => "Walldürn-Rippberg", :area_code => "6286") + AreaCode.create(:country => germany, :name => "Limbach Baden", :area_code => "6287") + AreaCode.create(:country => germany, :name => "Adelsheim", :area_code => "6291") + AreaCode.create(:country => germany, :name => "Seckach", :area_code => "6292") + AreaCode.create(:country => germany, :name => "Schefflenz", :area_code => "6293") + AreaCode.create(:country => germany, :name => "Krautheim Jagst", :area_code => "6294") + AreaCode.create(:country => germany, :name => "RosenbergBaden", :area_code => "6295") + AreaCode.create(:country => germany, :name => "Ahorn Baden", :area_code => "6296") + AreaCode.create(:country => germany, :name => "Ravenstein Baden", :area_code => "6297") + AreaCode.create(:country => germany, :name => "Möckmühl", :area_code => "6298") + AreaCode.create(:country => germany, :name => "Otterbach Pfalz", :area_code => "6301") + AreaCode.create(:country => germany, :name => "Winnweiler", :area_code => "6302") + AreaCode.create(:country => germany, :name => "Enkenbach-Alsenborn", :area_code => "6303") + AreaCode.create(:country => germany, :name => "Wolfstein Pfalz", :area_code => "6304") + AreaCode.create(:country => germany, :name => "Hochspeyer", :area_code => "6305") + AreaCode.create(:country => germany, :name => "Trippstadt", :area_code => "6306") + AreaCode.create(:country => germany, :name => "Schopp", :area_code => "6307") + AreaCode.create(:country => germany, :name => "Olsbrücken", :area_code => "6308") + AreaCode.create(:country => germany, :name => "Kaiserslautern", :area_code => "631") + AreaCode.create(:country => germany, :name => "Neustadt an der Weinstraße", :area_code => "6321") + AreaCode.create(:country => germany, :name => "Bad Dürkheim", :area_code => "6322") + AreaCode.create(:country => germany, :name => "Edenkoben", :area_code => "6323") + AreaCode.create(:country => germany, :name => "Hassloch", :area_code => "6324") + AreaCode.create(:country => germany, :name => "Lambrecht Pfalz", :area_code => "6325") + AreaCode.create(:country => germany, :name => "Deidesheim", :area_code => "6326") + AreaCode.create(:country => germany, :name => "Neustadt-Lachen", :area_code => "6327") + AreaCode.create(:country => germany, :name => "Elmstein", :area_code => "6328") + AreaCode.create(:country => germany, :name => "Weidenthal Pfalz", :area_code => "6329") + AreaCode.create(:country => germany, :name => "Pirmasens", :area_code => "6331") + AreaCode.create(:country => germany, :name => "Zweibrücken", :area_code => "6332") + AreaCode.create(:country => germany, :name => "Waldfischbach-Burgalben", :area_code => "6333") + AreaCode.create(:country => germany, :name => "Thaleischweiler-Fröschen", :area_code => "6334") + AreaCode.create(:country => germany, :name => "Trulben", :area_code => "6335") + AreaCode.create(:country => germany, :name => "Dellfeld", :area_code => "6336") + AreaCode.create(:country => germany, :name => "Grossbundenbach", :area_code => "6337") + AreaCode.create(:country => germany, :name => "Hornbach Pfalz", :area_code => "6338") + AreaCode.create(:country => germany, :name => "Grosssteinhausen", :area_code => "6339") + AreaCode.create(:country => germany, :name => "Wörth-Schaidt", :area_code => "6340") + AreaCode.create(:country => germany, :name => "Landau in der Pfalz", :area_code => "6341") + AreaCode.create(:country => germany, :name => "Schweigen-Rechtenbach", :area_code => "6342") + AreaCode.create(:country => germany, :name => "Bad Bergzabern", :area_code => "6343") + AreaCode.create(:country => germany, :name => "Schwegenheim", :area_code => "6344") + AreaCode.create(:country => germany, :name => "Albersweiler", :area_code => "6345") + AreaCode.create(:country => germany, :name => "Annweiler am Trifels", :area_code => "6346") + AreaCode.create(:country => germany, :name => "Hochstadt Pfalz", :area_code => "6347") + AreaCode.create(:country => germany, :name => "Offenbach an der Queich", :area_code => "6348") + AreaCode.create(:country => germany, :name => "Billigheim-Ingenheim", :area_code => "6349") + AreaCode.create(:country => germany, :name => "Eisenberg Pfalz", :area_code => "6351") + AreaCode.create(:country => germany, :name => "Kirchheimbolanden", :area_code => "6352") + AreaCode.create(:country => germany, :name => "Freinsheim", :area_code => "6353") + AreaCode.create(:country => germany, :name => "Albisheim Pfrimm", :area_code => "6355") + AreaCode.create(:country => germany, :name => "Carlsberg Pfalz", :area_code => "6356") + AreaCode.create(:country => germany, :name => "Standenbühl", :area_code => "6357") + AreaCode.create(:country => germany, :name => "Kriegsfeld", :area_code => "6358") + AreaCode.create(:country => germany, :name => "Grünstadt", :area_code => "6359") + AreaCode.create(:country => germany, :name => "Rockenhausen", :area_code => "6361") + AreaCode.create(:country => germany, :name => "Alsenz", :area_code => "6362") + AreaCode.create(:country => germany, :name => "Niederkirchen", :area_code => "6363") + AreaCode.create(:country => germany, :name => "Nußbach Pfalz", :area_code => "6364") + AreaCode.create(:country => germany, :name => "Landstuhl", :area_code => "6371") + AreaCode.create(:country => germany, :name => "Bruchmühlbach-Miesau", :area_code => "6372") + AreaCode.create(:country => germany, :name => "Schönenberg-Kübelberg", :area_code => "6373") + AreaCode.create(:country => germany, :name => "Weilerbach", :area_code => "6374") + AreaCode.create(:country => germany, :name => "Wallhalben", :area_code => "6375") + AreaCode.create(:country => germany, :name => "Kusel", :area_code => "6381") + AreaCode.create(:country => germany, :name => "Lauterecken", :area_code => "6382") + AreaCode.create(:country => germany, :name => "Glan-Münchweiler", :area_code => "6383") + AreaCode.create(:country => germany, :name => "Konken", :area_code => "6384") + AreaCode.create(:country => germany, :name => "Reichenbach-Steegen", :area_code => "6385") + AreaCode.create(:country => germany, :name => "Altenkirchen Pfalz", :area_code => "6386") + AreaCode.create(:country => germany, :name => "Sankt Julian", :area_code => "6387") + AreaCode.create(:country => germany, :name => "Dahn", :area_code => "6391") + AreaCode.create(:country => germany, :name => "Hauenstein Pfalz", :area_code => "6392") + AreaCode.create(:country => germany, :name => "Fischbach bei Dahn", :area_code => "6393") + AreaCode.create(:country => germany, :name => "Bundenthal", :area_code => "6394") + AreaCode.create(:country => germany, :name => "Münchweiler an der Rodalb", :area_code => "6395") + AreaCode.create(:country => germany, :name => "Hinterweidenthal", :area_code => "6396") + AreaCode.create(:country => germany, :name => "Leimen Pfalz", :area_code => "6397") + AreaCode.create(:country => germany, :name => "Vorderweidenthal", :area_code => "6398") + AreaCode.create(:country => germany, :name => "Mücke", :area_code => "6400") + AreaCode.create(:country => germany, :name => "Grünberg Hess", :area_code => "6401") + AreaCode.create(:country => germany, :name => "Hungen", :area_code => "6402") + AreaCode.create(:country => germany, :name => "Linden Hess", :area_code => "6403") + AreaCode.create(:country => germany, :name => "Lich Hess", :area_code => "6404") + AreaCode.create(:country => germany, :name => "Laubach Hess", :area_code => "6405") + AreaCode.create(:country => germany, :name => "Lollar", :area_code => "6406") + AreaCode.create(:country => germany, :name => "Rabenau Hess", :area_code => "6407") + AreaCode.create(:country => germany, :name => "Buseck", :area_code => "6408") + AreaCode.create(:country => germany, :name => "Biebertal", :area_code => "6409") + AreaCode.create(:country => germany, :name => "Giessen", :area_code => "641") + AreaCode.create(:country => germany, :name => "Lahntal", :area_code => "6420") + AreaCode.create(:country => germany, :name => "Marburg", :area_code => "6421") + AreaCode.create(:country => germany, :name => "Kirchhain", :area_code => "6422") + AreaCode.create(:country => germany, :name => "Wetter Hessen", :area_code => "6423") + AreaCode.create(:country => germany, :name => "Ebsdorfergrund", :area_code => "6424") + AreaCode.create(:country => germany, :name => "Rauschenberg Hess", :area_code => "6425") + AreaCode.create(:country => germany, :name => "Fronhausen", :area_code => "6426") + AreaCode.create(:country => germany, :name => "Cölbe-Schönstadt", :area_code => "6427") + AreaCode.create(:country => germany, :name => "Stadtallendorf", :area_code => "6428") + AreaCode.create(:country => germany, :name => "Schweinsberg Hess", :area_code => "6429") + AreaCode.create(:country => germany, :name => "Hahnstätten", :area_code => "6430") + AreaCode.create(:country => germany, :name => "Limburg a d Lahn", :area_code => "6431") + AreaCode.create(:country => germany, :name => "Diez", :area_code => "6432") + AreaCode.create(:country => germany, :name => "Hadamar", :area_code => "6433") + AreaCode.create(:country => germany, :name => "Bad Camberg", :area_code => "6434") + AreaCode.create(:country => germany, :name => "Wallmerod", :area_code => "6435") + AreaCode.create(:country => germany, :name => "Dornburg Hess", :area_code => "6436") + AreaCode.create(:country => germany, :name => "Hünfelden", :area_code => "6438") + AreaCode.create(:country => germany, :name => "Holzappel", :area_code => "6439") + AreaCode.create(:country => germany, :name => "Kölschhausen", :area_code => "6440") + AreaCode.create(:country => germany, :name => "Wetzlar", :area_code => "6441") + AreaCode.create(:country => germany, :name => "Braunfels", :area_code => "6442") + AreaCode.create(:country => germany, :name => "Ehringshausen Dill", :area_code => "6443") + AreaCode.create(:country => germany, :name => "Bischoffen", :area_code => "6444") + AreaCode.create(:country => germany, :name => "Schöffengrund", :area_code => "6445") + AreaCode.create(:country => germany, :name => "Hohenahr", :area_code => "6446") + AreaCode.create(:country => germany, :name => "Langgöns-Niederkleen", :area_code => "6447") + AreaCode.create(:country => germany, :name => "Ehringshausen-Katzenfurt", :area_code => "6449") + AreaCode.create(:country => germany, :name => "Frankenberg Eder", :area_code => "6451") + AreaCode.create(:country => germany, :name => "Battenberg Eder", :area_code => "6452") + AreaCode.create(:country => germany, :name => "Gemünden Wohra", :area_code => "6453") + AreaCode.create(:country => germany, :name => "Lichtenfels-Sachsenberg", :area_code => "6454") + AreaCode.create(:country => germany, :name => "Frankenau Hess", :area_code => "6455") + AreaCode.create(:country => germany, :name => "Haina Kloster", :area_code => "6456") + AreaCode.create(:country => germany, :name => "Burgwald Eder", :area_code => "6457") + AreaCode.create(:country => germany, :name => "Rosenthal Hess", :area_code => "6458") + AreaCode.create(:country => germany, :name => "Biedenkopf", :area_code => "6461") + AreaCode.create(:country => germany, :name => "Gladenbach", :area_code => "6462") + AreaCode.create(:country => germany, :name => "Angelburg", :area_code => "6464") + AreaCode.create(:country => germany, :name => "Breidenbach b Biedenkopf", :area_code => "6465") + AreaCode.create(:country => germany, :name => "Dautphetal-Friedensdorf", :area_code => "6466") + AreaCode.create(:country => germany, :name => "Hatzfeld Eder", :area_code => "6467") + AreaCode.create(:country => germany, :name => "Dautphetal-Mornshausen", :area_code => "6468") + AreaCode.create(:country => germany, :name => "Weilburg", :area_code => "6471") + AreaCode.create(:country => germany, :name => "Weilmünster", :area_code => "6472") + AreaCode.create(:country => germany, :name => "Leun", :area_code => "6473") + AreaCode.create(:country => germany, :name => "Villmar-Aumenau", :area_code => "6474") + AreaCode.create(:country => germany, :name => "Weilmünster-Wolfenhausen", :area_code => "6475") + AreaCode.create(:country => germany, :name => "Mengerskirchen", :area_code => "6476") + AreaCode.create(:country => germany, :name => "Greifenstein-Nenderoth", :area_code => "6477") + AreaCode.create(:country => germany, :name => "Greifenstein-Ulm", :area_code => "6478") + AreaCode.create(:country => germany, :name => "Waldbrunn Westerwald", :area_code => "6479") + AreaCode.create(:country => germany, :name => "Runkel", :area_code => "6482") + AreaCode.create(:country => germany, :name => "Selters Taunus", :area_code => "6483") + AreaCode.create(:country => germany, :name => "Beselich", :area_code => "6484") + AreaCode.create(:country => germany, :name => "Nentershausen Westerw", :area_code => "6485") + AreaCode.create(:country => germany, :name => "Katzenelnbogen", :area_code => "6486") + AreaCode.create(:country => germany, :name => "Waldrach", :area_code => "6500") + AreaCode.create(:country => germany, :name => "Konz", :area_code => "6501") + AreaCode.create(:country => germany, :name => "Schweich", :area_code => "6502") + AreaCode.create(:country => germany, :name => "Hermeskeil", :area_code => "6503") + AreaCode.create(:country => germany, :name => "Thalfang", :area_code => "6504") + AreaCode.create(:country => germany, :name => "Kordel", :area_code => "6505") + AreaCode.create(:country => germany, :name => "Welschbillig", :area_code => "6506") + AreaCode.create(:country => germany, :name => "Neumagen-Dhron", :area_code => "6507") + AreaCode.create(:country => germany, :name => "Hetzerath Mosel", :area_code => "6508") + AreaCode.create(:country => germany, :name => "Büdlich", :area_code => "6509") + AreaCode.create(:country => germany, :name => "Trier", :area_code => "651") + AreaCode.create(:country => germany, :name => "Mettendorf", :area_code => "6522") + AreaCode.create(:country => germany, :name => "Holsthum", :area_code => "6523") + AreaCode.create(:country => germany, :name => "Rodershausen", :area_code => "6524") + AreaCode.create(:country => germany, :name => "Irrel", :area_code => "6525") + AreaCode.create(:country => germany, :name => "Bollendorf", :area_code => "6526") + AreaCode.create(:country => germany, :name => "Oberweis", :area_code => "6527") + AreaCode.create(:country => germany, :name => "Bernkastel-Kues", :area_code => "6531") + AreaCode.create(:country => germany, :name => "Zeltingen-Rachtig", :area_code => "6532") + AreaCode.create(:country => germany, :name => "Morbach Hunsrück", :area_code => "6533") + AreaCode.create(:country => germany, :name => "Mülheim Mosel", :area_code => "6534") + AreaCode.create(:country => germany, :name => "Osann-Monzel", :area_code => "6535") + AreaCode.create(:country => germany, :name => "Kleinich", :area_code => "6536") + AreaCode.create(:country => germany, :name => "Traben-Trarbach", :area_code => "6541") + AreaCode.create(:country => germany, :name => "Bullay", :area_code => "6542") + AreaCode.create(:country => germany, :name => "Büchenbeuren", :area_code => "6543") + AreaCode.create(:country => germany, :name => "Rhaunen", :area_code => "6544") + AreaCode.create(:country => germany, :name => "Blankenrath", :area_code => "6545") + AreaCode.create(:country => germany, :name => "Irrhausen", :area_code => "6550") + AreaCode.create(:country => germany, :name => "Prüm", :area_code => "6551") + AreaCode.create(:country => germany, :name => "Olzheim", :area_code => "6552") + AreaCode.create(:country => germany, :name => "Schönecken", :area_code => "6553") + AreaCode.create(:country => germany, :name => "Waxweiler", :area_code => "6554") + AreaCode.create(:country => germany, :name => "Bleialf", :area_code => "6555") + AreaCode.create(:country => germany, :name => "Pronsfeld", :area_code => "6556") + AreaCode.create(:country => germany, :name => "Hallschlag", :area_code => "6557") + AreaCode.create(:country => germany, :name => "Büdesheim Eifel", :area_code => "6558") + AreaCode.create(:country => germany, :name => "Leidenborn", :area_code => "6559") + AreaCode.create(:country => germany, :name => "Bitburg", :area_code => "6561") + AreaCode.create(:country => germany, :name => "Speicher", :area_code => "6562") + AreaCode.create(:country => germany, :name => "Kyllburg", :area_code => "6563") + AreaCode.create(:country => germany, :name => "Neuerburg Eifel", :area_code => "6564") + AreaCode.create(:country => germany, :name => "Dudeldorf", :area_code => "6565") + AreaCode.create(:country => germany, :name => "Körperich", :area_code => "6566") + AreaCode.create(:country => germany, :name => "Oberkail", :area_code => "6567") + AreaCode.create(:country => germany, :name => "Wolsfeld", :area_code => "6568") + AreaCode.create(:country => germany, :name => "Bickendorf", :area_code => "6569") + AreaCode.create(:country => germany, :name => "Wittlich", :area_code => "6571") + AreaCode.create(:country => germany, :name => "Manderscheid Eifel", :area_code => "6572") + AreaCode.create(:country => germany, :name => "Gillenfeld", :area_code => "6573") + AreaCode.create(:country => germany, :name => "Hasborn", :area_code => "6574") + AreaCode.create(:country => germany, :name => "Landscheid", :area_code => "6575") + AreaCode.create(:country => germany, :name => "Salmtal", :area_code => "6578") + AreaCode.create(:country => germany, :name => "Zemmer", :area_code => "6580") + AreaCode.create(:country => germany, :name => "Saarburg", :area_code => "6581") + AreaCode.create(:country => germany, :name => "Freudenburg", :area_code => "6582") + AreaCode.create(:country => germany, :name => "Palzem", :area_code => "6583") + AreaCode.create(:country => germany, :name => "Wellen Mosel", :area_code => "6584") + AreaCode.create(:country => germany, :name => "Ralingen", :area_code => "6585") + AreaCode.create(:country => germany, :name => "Beuren Hochwald", :area_code => "6586") + AreaCode.create(:country => germany, :name => "Zerf", :area_code => "6587") + AreaCode.create(:country => germany, :name => "Pluwig", :area_code => "6588") + AreaCode.create(:country => germany, :name => "Kell am See", :area_code => "6589") + AreaCode.create(:country => germany, :name => "Gerolstein", :area_code => "6591") + AreaCode.create(:country => germany, :name => "Daun", :area_code => "6592") + AreaCode.create(:country => germany, :name => "Hillesheim Eifel", :area_code => "6593") + AreaCode.create(:country => germany, :name => "Birresborn", :area_code => "6594") + AreaCode.create(:country => germany, :name => "Dockweiler", :area_code => "6595") + AreaCode.create(:country => germany, :name => "Üdersdorf", :area_code => "6596") + AreaCode.create(:country => germany, :name => "Jünkerath", :area_code => "6597") + AreaCode.create(:country => germany, :name => "Weidenbach b Gerolstein", :area_code => "6599") + AreaCode.create(:country => germany, :name => "Fulda", :area_code => "661") + AreaCode.create(:country => germany, :name => "Philippsthal Werra", :area_code => "6620") + AreaCode.create(:country => germany, :name => "Bad Hersfeld", :area_code => "6621") + AreaCode.create(:country => germany, :name => "Bebra", :area_code => "6622") + AreaCode.create(:country => germany, :name => "Rotenburg a d Fulda", :area_code => "6623") + AreaCode.create(:country => germany, :name => "Heringen Werra", :area_code => "6624") + AreaCode.create(:country => germany, :name => "Niederaula", :area_code => "6625") + AreaCode.create(:country => germany, :name => "Wildeck-Obersuhl", :area_code => "6626") + AreaCode.create(:country => germany, :name => "Nentershausen Hess", :area_code => "6627") + AreaCode.create(:country => germany, :name => "Oberaula", :area_code => "6628") + AreaCode.create(:country => germany, :name => "Schenklengsfeld", :area_code => "6629") + AreaCode.create(:country => germany, :name => "Schwalmtal-Storndorf", :area_code => "6630") + AreaCode.create(:country => germany, :name => "Alsfeld", :area_code => "6631") + AreaCode.create(:country => germany, :name => "Homberg Ohm", :area_code => "6633") + AreaCode.create(:country => germany, :name => "Gemünden Felda", :area_code => "6634") + AreaCode.create(:country => germany, :name => "Kirtorf", :area_code => "6635") + AreaCode.create(:country => germany, :name => "Romrod", :area_code => "6636") + AreaCode.create(:country => germany, :name => "Feldatal", :area_code => "6637") + AreaCode.create(:country => germany, :name => "Schwalmtal-Renzendorf", :area_code => "6638") + AreaCode.create(:country => germany, :name => "Ottrau", :area_code => "6639") + AreaCode.create(:country => germany, :name => "Lauterbach Hessen", :area_code => "6641") + AreaCode.create(:country => germany, :name => "Schlitz", :area_code => "6642") + AreaCode.create(:country => germany, :name => "Herbstein", :area_code => "6643") + AreaCode.create(:country => germany, :name => "Grebenhain", :area_code => "6644") + AreaCode.create(:country => germany, :name => "Ulrichstein", :area_code => "6645") + AreaCode.create(:country => germany, :name => "Grebenau", :area_code => "6646") + AreaCode.create(:country => germany, :name => "Herbstein-Stockhausen", :area_code => "6647") + AreaCode.create(:country => germany, :name => "Bad Salzschlirf", :area_code => "6648") + AreaCode.create(:country => germany, :name => "Hosenfeld", :area_code => "6650") + AreaCode.create(:country => germany, :name => "Rasdorf", :area_code => "6651") + AreaCode.create(:country => germany, :name => "Hünfeld", :area_code => "6652") + AreaCode.create(:country => germany, :name => "Burghaun", :area_code => "6653") + AreaCode.create(:country => germany, :name => "Gersfeld Rhön", :area_code => "6654") + AreaCode.create(:country => germany, :name => "Neuhof Kr Fulda", :area_code => "6655") + AreaCode.create(:country => germany, :name => "Ebersburg", :area_code => "6656") + AreaCode.create(:country => germany, :name => "Hofbieber", :area_code => "6657") + AreaCode.create(:country => germany, :name => "Poppenhausen Wasserkuppe", :area_code => "6658") + AreaCode.create(:country => germany, :name => "Eichenzell", :area_code => "6659") + AreaCode.create(:country => germany, :name => "Steinau-Marjoss", :area_code => "6660") + AreaCode.create(:country => germany, :name => "Schlüchtern", :area_code => "6661") + AreaCode.create(:country => germany, :name => "Steinau an der Straße", :area_code => "6663") + AreaCode.create(:country => germany, :name => "Sinntal-Sterbfritz", :area_code => "6664") + AreaCode.create(:country => germany, :name => "Sinntal-Altengronau", :area_code => "6665") + AreaCode.create(:country => germany, :name => "Freiensteinau", :area_code => "6666") + AreaCode.create(:country => germany, :name => "Steinau-Ulmbach", :area_code => "6667") + AreaCode.create(:country => germany, :name => "Birstein-Lichenroth", :area_code => "6668") + AreaCode.create(:country => germany, :name => "Neuhof-Hauswurz", :area_code => "6669") + AreaCode.create(:country => germany, :name => "Ludwigsau Hess", :area_code => "6670") + AreaCode.create(:country => germany, :name => "Eiterfeld", :area_code => "6672") + AreaCode.create(:country => germany, :name => "Haunetal", :area_code => "6673") + AreaCode.create(:country => germany, :name => "Friedewald Hess", :area_code => "6674") + AreaCode.create(:country => germany, :name => "Breitenbach a Herzberg", :area_code => "6675") + AreaCode.create(:country => germany, :name => "Hohenroda Hess", :area_code => "6676") + AreaCode.create(:country => germany, :name => "Neuenstein Hess", :area_code => "6677") + AreaCode.create(:country => germany, :name => "Wildeck-Hönebach", :area_code => "6678") + AreaCode.create(:country => germany, :name => "Hilders", :area_code => "6681") + AreaCode.create(:country => germany, :name => "Tann Rhön", :area_code => "6682") + AreaCode.create(:country => germany, :name => "Ehrenberg Rhön", :area_code => "6683") + AreaCode.create(:country => germany, :name => "Hofbieber-Schwarzbach", :area_code => "6684") + AreaCode.create(:country => germany, :name => "Schwalmstadt", :area_code => "6691") + AreaCode.create(:country => germany, :name => "Neustadt Hessen", :area_code => "6692") + AreaCode.create(:country => germany, :name => "Neuental", :area_code => "6693") + AreaCode.create(:country => germany, :name => "Neukirchen Knüll", :area_code => "6694") + AreaCode.create(:country => germany, :name => "Jesberg", :area_code => "6695") + AreaCode.create(:country => germany, :name => "Gilserberg", :area_code => "6696") + AreaCode.create(:country => germany, :name => "Willingshausen", :area_code => "6697") + AreaCode.create(:country => germany, :name => "Schrecksbach", :area_code => "6698") + AreaCode.create(:country => germany, :name => "Sprendlingen Rheinhess", :area_code => "6701") + AreaCode.create(:country => germany, :name => "Wöllstein Rheinhess", :area_code => "6703") + AreaCode.create(:country => germany, :name => "Langenlonsheim", :area_code => "6704") + AreaCode.create(:country => germany, :name => "Wallhausen Nahe", :area_code => "6706") + AreaCode.create(:country => germany, :name => "Windesheim", :area_code => "6707") + AreaCode.create(:country => germany, :name => "Bad Münster am Stein-Ebernburg", :area_code => "6708") + AreaCode.create(:country => germany, :name => "Fürfeld Kr Bad Kreuznach", :area_code => "6709") + AreaCode.create(:country => germany, :name => "Bad Kreuznach", :area_code => "671") + AreaCode.create(:country => germany, :name => "Bingen am Rhein", :area_code => "6721") + AreaCode.create(:country => germany, :name => "Rüdesheim am Rhein", :area_code => "6722") + AreaCode.create(:country => germany, :name => "Oestrich-Winkel", :area_code => "6723") + AreaCode.create(:country => germany, :name => "Stromberg Hunsrück", :area_code => "6724") + AreaCode.create(:country => germany, :name => "Gau-Algesheim", :area_code => "6725") + AreaCode.create(:country => germany, :name => "Lorch Rheingau", :area_code => "6726") + AreaCode.create(:country => germany, :name => "Gensingen", :area_code => "6727") + AreaCode.create(:country => germany, :name => "Ober-Hilbersheim", :area_code => "6728") + AreaCode.create(:country => germany, :name => "Alzey", :area_code => "6731") + AreaCode.create(:country => germany, :name => "Wörrstadt", :area_code => "6732") + AreaCode.create(:country => germany, :name => "Gau-Odernheim", :area_code => "6733") + AreaCode.create(:country => germany, :name => "Flonheim", :area_code => "6734") + AreaCode.create(:country => germany, :name => "Eppelsheim", :area_code => "6735") + AreaCode.create(:country => germany, :name => "Bechenheim", :area_code => "6736") + AreaCode.create(:country => germany, :name => "Köngernheim", :area_code => "6737") + AreaCode.create(:country => germany, :name => "St Goar", :area_code => "6741") + AreaCode.create(:country => germany, :name => "Boppard", :area_code => "6742") + AreaCode.create(:country => germany, :name => "Bacharach", :area_code => "6743") + AreaCode.create(:country => germany, :name => "Oberwesel", :area_code => "6744") + AreaCode.create(:country => germany, :name => "Gondershausen", :area_code => "6745") + AreaCode.create(:country => germany, :name => "Pfalzfeld", :area_code => "6746") + AreaCode.create(:country => germany, :name => "Emmelshausen", :area_code => "6747") + AreaCode.create(:country => germany, :name => "Bad Sobernheim", :area_code => "6751") + AreaCode.create(:country => germany, :name => "Kirn Nahe", :area_code => "6752") + AreaCode.create(:country => germany, :name => "Meisenheim", :area_code => "6753") + AreaCode.create(:country => germany, :name => "Martinstein", :area_code => "6754") + AreaCode.create(:country => germany, :name => "Odernheim am Glan", :area_code => "6755") + AreaCode.create(:country => germany, :name => "Winterbach Soonwald", :area_code => "6756") + AreaCode.create(:country => germany, :name => "Becherbach bei Kirn", :area_code => "6757") + AreaCode.create(:country => germany, :name => "Waldböckelheim", :area_code => "6758") + AreaCode.create(:country => germany, :name => "Simmern Hunsrück", :area_code => "6761") + AreaCode.create(:country => germany, :name => "Kastellaun", :area_code => "6762") + AreaCode.create(:country => germany, :name => "Kirchberg Hunsrück", :area_code => "6763") + AreaCode.create(:country => germany, :name => "Rheinböllen", :area_code => "6764") + AreaCode.create(:country => germany, :name => "Gemünden Hunsrück", :area_code => "6765") + AreaCode.create(:country => germany, :name => "Kisselbach", :area_code => "6766") + AreaCode.create(:country => germany, :name => "St Goarshausen", :area_code => "6771") + AreaCode.create(:country => germany, :name => "Nastätten", :area_code => "6772") + AreaCode.create(:country => germany, :name => "Kamp-Bornhofen", :area_code => "6773") + AreaCode.create(:country => germany, :name => "Kaub", :area_code => "6774") + AreaCode.create(:country => germany, :name => "Strüth Taunus", :area_code => "6775") + AreaCode.create(:country => germany, :name => "Dachsenhausen", :area_code => "6776") + AreaCode.create(:country => germany, :name => "Idar-Oberstein", :area_code => "6781") + AreaCode.create(:country => germany, :name => "Birkenfeld Nahe", :area_code => "6782") + AreaCode.create(:country => germany, :name => "Baumholder", :area_code => "6783") + AreaCode.create(:country => germany, :name => "Weierbach", :area_code => "6784") + AreaCode.create(:country => germany, :name => "Herrstein", :area_code => "6785") + AreaCode.create(:country => germany, :name => "Kempfeld", :area_code => "6786") + AreaCode.create(:country => germany, :name => "Niederbrombach", :area_code => "6787") + AreaCode.create(:country => germany, :name => "Sien", :area_code => "6788") + AreaCode.create(:country => germany, :name => "Heimbach Nahe", :area_code => "6789") + AreaCode.create(:country => germany, :name => "Völklingen-Lauterbach", :area_code => "6802") + AreaCode.create(:country => germany, :name => "Mandelbachtal-Ommersheim", :area_code => "6803") + AreaCode.create(:country => germany, :name => "Mandelbachtal", :area_code => "6804") + AreaCode.create(:country => germany, :name => "Kleinblittersdorf", :area_code => "6805") + AreaCode.create(:country => germany, :name => "Heusweiler", :area_code => "6806") + AreaCode.create(:country => germany, :name => "Grossrosseln", :area_code => "6809") + AreaCode.create(:country => germany, :name => "Saarbrücken", :area_code => "681") + AreaCode.create(:country => germany, :name => "Neunkirchen Saar", :area_code => "6821") + AreaCode.create(:country => germany, :name => "Ottweiler", :area_code => "6824") + AreaCode.create(:country => germany, :name => "Illingen Saar", :area_code => "6825") + AreaCode.create(:country => germany, :name => "Bexbach", :area_code => "6826") + AreaCode.create(:country => germany, :name => "Eppelborn", :area_code => "6827") + AreaCode.create(:country => germany, :name => "Saarlouis", :area_code => "6831") + AreaCode.create(:country => germany, :name => "Beckingen-Reimsbach", :area_code => "6832") + AreaCode.create(:country => germany, :name => "Rehlingen-Siersburg", :area_code => "6833") + AreaCode.create(:country => germany, :name => "Bous", :area_code => "6834") + AreaCode.create(:country => germany, :name => "Beckingen", :area_code => "6835") + AreaCode.create(:country => germany, :name => "Überherrn", :area_code => "6836") + AreaCode.create(:country => germany, :name => "Wallerfangen", :area_code => "6837") + AreaCode.create(:country => germany, :name => "Saarwellingen", :area_code => "6838") + AreaCode.create(:country => germany, :name => "Homburg Saar", :area_code => "6841") + AreaCode.create(:country => germany, :name => "Blieskastel", :area_code => "6842") + AreaCode.create(:country => germany, :name => "Gersheim", :area_code => "6843") + AreaCode.create(:country => germany, :name => "Blieskastel-Altheim", :area_code => "6844") + AreaCode.create(:country => germany, :name => "Homburg-Einöd", :area_code => "6848") + AreaCode.create(:country => germany, :name => "Kirkel", :area_code => "6849") + AreaCode.create(:country => germany, :name => "St Wendel", :area_code => "6851") + AreaCode.create(:country => germany, :name => "Nohfelden", :area_code => "6852") + AreaCode.create(:country => germany, :name => "Marpingen", :area_code => "6853") + AreaCode.create(:country => germany, :name => "Oberthal Saar", :area_code => "6854") + AreaCode.create(:country => germany, :name => "Freisen", :area_code => "6855") + AreaCode.create(:country => germany, :name => "St Wendel-Niederkirchen", :area_code => "6856") + AreaCode.create(:country => germany, :name => "Namborn", :area_code => "6857") + AreaCode.create(:country => germany, :name => "Ottweiler-Fürth", :area_code => "6858") + AreaCode.create(:country => germany, :name => "Merzig", :area_code => "6861") + AreaCode.create(:country => germany, :name => "Mettlach", :area_code => "6864") + AreaCode.create(:country => germany, :name => "Mettlach-Orscholz", :area_code => "6865") + AreaCode.create(:country => germany, :name => "Perl-Nennig", :area_code => "6866") + AreaCode.create(:country => germany, :name => "Perl", :area_code => "6867") + AreaCode.create(:country => germany, :name => "Mettlach-Tünsdorf", :area_code => "6868") + AreaCode.create(:country => germany, :name => "Merzig-Silwingen", :area_code => "6869") + AreaCode.create(:country => germany, :name => "Wadern", :area_code => "6871") + AreaCode.create(:country => germany, :name => "Losheim am See", :area_code => "6872") + AreaCode.create(:country => germany, :name => "Nonnweiler", :area_code => "6873") + AreaCode.create(:country => germany, :name => "Wadern-Nunkirchen", :area_code => "6874") + AreaCode.create(:country => germany, :name => "Nonnweiler-Primstal", :area_code => "6875") + AreaCode.create(:country => germany, :name => "Weiskirchen Saar", :area_code => "6876") + AreaCode.create(:country => germany, :name => "Lebach", :area_code => "6881") + AreaCode.create(:country => germany, :name => "Schmelz Saar", :area_code => "6887") + AreaCode.create(:country => germany, :name => "Lebach-Steinbach", :area_code => "6888") + AreaCode.create(:country => germany, :name => "Saarbrücken-Ensheim", :area_code => "6893") + AreaCode.create(:country => germany, :name => "St Ingbert", :area_code => "6894") + AreaCode.create(:country => germany, :name => "Sulzbach Saar", :area_code => "6897") + AreaCode.create(:country => germany, :name => "Völklingen", :area_code => "6898") + AreaCode.create(:country => germany, :name => "Frankfurt am Main", :area_code => "69") + AreaCode.create(:country => germany, :name => "Kirchheim unter Teck", :area_code => "7021") + AreaCode.create(:country => germany, :name => "Nürtingen", :area_code => "7022") + AreaCode.create(:country => germany, :name => "Weilheim an der Teck", :area_code => "7023") + AreaCode.create(:country => germany, :name => "Wendlingen am Neckar", :area_code => "7024") + AreaCode.create(:country => germany, :name => "Neuffen", :area_code => "7025") + AreaCode.create(:country => germany, :name => "Lenningen", :area_code => "7026") + AreaCode.create(:country => germany, :name => "Böblingen", :area_code => "7031") + AreaCode.create(:country => germany, :name => "Herrenberg", :area_code => "7032") + AreaCode.create(:country => germany, :name => "Weil Der Stadt", :area_code => "7033") + AreaCode.create(:country => germany, :name => "Ehningen", :area_code => "7034") + AreaCode.create(:country => germany, :name => "Mühlacker", :area_code => "7041") + AreaCode.create(:country => germany, :name => "Vaihingen an der Enz", :area_code => "7042") + AreaCode.create(:country => germany, :name => "Maulbronn", :area_code => "7043") + AreaCode.create(:country => germany, :name => "Mönsheim", :area_code => "7044") + AreaCode.create(:country => germany, :name => "Oberderdingen", :area_code => "7045") + AreaCode.create(:country => germany, :name => "Zaberfeld", :area_code => "7046") + AreaCode.create(:country => germany, :name => "Calw", :area_code => "7051") + AreaCode.create(:country => germany, :name => "Bad Liebenzell", :area_code => "7052") + AreaCode.create(:country => germany, :name => "Bad Teinach-Zavelstein", :area_code => "7053") + AreaCode.create(:country => germany, :name => "Wildberg Württ", :area_code => "7054") + AreaCode.create(:country => germany, :name => "Neuweiler Kr Calw", :area_code => "7055") + AreaCode.create(:country => germany, :name => "Gechingen", :area_code => "7056") + AreaCode.create(:country => germany, :name => "Beilstein Württ", :area_code => "7062") + AreaCode.create(:country => germany, :name => "Bad Wimpfen", :area_code => "7063") + AreaCode.create(:country => germany, :name => "Bad Rappenau-Bonfeld", :area_code => "7066") + AreaCode.create(:country => germany, :name => "Tübingen", :area_code => "7071") + AreaCode.create(:country => germany, :name => "Gomaringen", :area_code => "7072") + AreaCode.create(:country => germany, :name => "Ammerbuch", :area_code => "7073") + AreaCode.create(:country => germany, :name => "Bad Wildbad", :area_code => "7081") + AreaCode.create(:country => germany, :name => "Neuenbürg Württ", :area_code => "7082") + AreaCode.create(:country => germany, :name => "Bad Herrenalb", :area_code => "7083") + AreaCode.create(:country => germany, :name => "Schömberg b Neuenbürg", :area_code => "7084") + AreaCode.create(:country => germany, :name => "Enzklösterle", :area_code => "7085") + AreaCode.create(:country => germany, :name => "Stuttgart", :area_code => "711") + AreaCode.create(:country => germany, :name => "Reutlingen", :area_code => "7121") + AreaCode.create(:country => germany, :name => "St Johann Württ", :area_code => "7122") + AreaCode.create(:country => germany, :name => "Metzingen Württ", :area_code => "7123") + AreaCode.create(:country => germany, :name => "Trochtelfingen Hohenz", :area_code => "7124") + AreaCode.create(:country => germany, :name => "Bad Urach", :area_code => "7125") + AreaCode.create(:country => germany, :name => "Burladingen-Melchingen", :area_code => "7126") + AreaCode.create(:country => germany, :name => "Neckartenzlingen", :area_code => "7127") + AreaCode.create(:country => germany, :name => "Sonnenbühl", :area_code => "7128") + AreaCode.create(:country => germany, :name => "Lichtenstein Württ", :area_code => "7129") + AreaCode.create(:country => germany, :name => "Löwenstein Württ", :area_code => "7130") + AreaCode.create(:country => germany, :name => "Heilbronn Neckar", :area_code => "7131") + AreaCode.create(:country => germany, :name => "Neckarsulm", :area_code => "7132") + AreaCode.create(:country => germany, :name => "Lauffen am Neckar", :area_code => "7133") + AreaCode.create(:country => germany, :name => "Weinsberg", :area_code => "7134") + AreaCode.create(:country => germany, :name => "Brackenheim", :area_code => "7135") + AreaCode.create(:country => germany, :name => "Bad Friedrichshall", :area_code => "7136") + AreaCode.create(:country => germany, :name => "Schwaigern", :area_code => "7138") + AreaCode.create(:country => germany, :name => "Neuenstadt am Kocher", :area_code => "7139") + AreaCode.create(:country => germany, :name => "Ludwigsburg Württ", :area_code => "7141") + AreaCode.create(:country => germany, :name => "Bietigheim-Bissingen", :area_code => "7142") + AreaCode.create(:country => germany, :name => "Besigheim", :area_code => "7143") + AreaCode.create(:country => germany, :name => "Marbach am Neckar", :area_code => "7144") + AreaCode.create(:country => germany, :name => "Markgröningen", :area_code => "7145") + AreaCode.create(:country => germany, :name => "Remseck am Neckar", :area_code => "7146") + AreaCode.create(:country => germany, :name => "Sachsenheim Württ", :area_code => "7147") + AreaCode.create(:country => germany, :name => "Grossbottwar", :area_code => "7148") + AreaCode.create(:country => germany, :name => "Korntal-Münchingen", :area_code => "7150") + AreaCode.create(:country => germany, :name => "Waiblingen", :area_code => "7151") + AreaCode.create(:country => germany, :name => "Leonberg Württ", :area_code => "7152") + AreaCode.create(:country => germany, :name => "Plochingen", :area_code => "7153") + AreaCode.create(:country => germany, :name => "Kornwestheim", :area_code => "7154") + AreaCode.create(:country => germany, :name => "Ditzingen", :area_code => "7156") + AreaCode.create(:country => germany, :name => "Waldenbuch", :area_code => "7157") + AreaCode.create(:country => germany, :name => "Neuhausen auf den Fildern", :area_code => "7158") + AreaCode.create(:country => germany, :name => "Renningen", :area_code => "7159") + AreaCode.create(:country => germany, :name => "Göppingen", :area_code => "7161") + AreaCode.create(:country => germany, :name => "Süßen", :area_code => "7162") + AreaCode.create(:country => germany, :name => "Ebersbach an der Fils", :area_code => "7163") + AreaCode.create(:country => germany, :name => "Boll Kr Göppingen", :area_code => "7164") + AreaCode.create(:country => germany, :name => "Göppingen-Hohenstaufen", :area_code => "7165") + AreaCode.create(:country => germany, :name => "Adelberg", :area_code => "7166") + AreaCode.create(:country => germany, :name => "Schwäbisch Gmünd", :area_code => "7171") + AreaCode.create(:country => germany, :name => "Lorch Württ", :area_code => "7172") + AreaCode.create(:country => germany, :name => "Heubach", :area_code => "7173") + AreaCode.create(:country => germany, :name => "Mögglingen", :area_code => "7174") + AreaCode.create(:country => germany, :name => "Leinzell", :area_code => "7175") + AreaCode.create(:country => germany, :name => "Spraitbach", :area_code => "7176") + AreaCode.create(:country => germany, :name => "Schorndorf Württ", :area_code => "7181") + AreaCode.create(:country => germany, :name => "Welzheim", :area_code => "7182") + AreaCode.create(:country => germany, :name => "Rudersberg Württ", :area_code => "7183") + AreaCode.create(:country => germany, :name => "Kaisersbach", :area_code => "7184") + AreaCode.create(:country => germany, :name => "Backnang", :area_code => "7191") + AreaCode.create(:country => germany, :name => "Murrhardt", :area_code => "7192") + AreaCode.create(:country => germany, :name => "Sulzbach an der Murr", :area_code => "7193") + AreaCode.create(:country => germany, :name => "Spiegelberg", :area_code => "7194") + AreaCode.create(:country => germany, :name => "Winnenden", :area_code => "7195") + AreaCode.create(:country => germany, :name => "Karlsbad", :area_code => "7202") + AreaCode.create(:country => germany, :name => "Walzbachtal", :area_code => "7203") + AreaCode.create(:country => germany, :name => "Malsch-Völkersbach", :area_code => "7204") + AreaCode.create(:country => germany, :name => "Karlsruhe", :area_code => "721") + AreaCode.create(:country => germany, :name => "Forbach-Hundsbach", :area_code => "7220") + AreaCode.create(:country => germany, :name => "Baden-Baden", :area_code => "7221") + AreaCode.create(:country => germany, :name => "Rastatt", :area_code => "7222") + AreaCode.create(:country => germany, :name => "Bühl Baden", :area_code => "7223") + AreaCode.create(:country => germany, :name => "Gernsbach", :area_code => "7224") + AreaCode.create(:country => germany, :name => "Gaggenau", :area_code => "7225") + AreaCode.create(:country => germany, :name => "Bühl-Sand", :area_code => "7226") + AreaCode.create(:country => germany, :name => "Lichtenau Baden", :area_code => "7227") + AreaCode.create(:country => germany, :name => "Forbach", :area_code => "7228") + AreaCode.create(:country => germany, :name => "Iffezheim", :area_code => "7229") + AreaCode.create(:country => germany, :name => "Pforzheim", :area_code => "7231") + AreaCode.create(:country => germany, :name => "Königsbach-Stein", :area_code => "7232") + AreaCode.create(:country => germany, :name => "Niefern-Öschelbronn", :area_code => "7233") + AreaCode.create(:country => germany, :name => "Tiefenbronn", :area_code => "7234") + AreaCode.create(:country => germany, :name => "Unterreichenbach Kr Calw", :area_code => "7235") + AreaCode.create(:country => germany, :name => "Keltern", :area_code => "7236") + AreaCode.create(:country => germany, :name => "Neulingen Enzkreis", :area_code => "7237") + AreaCode.create(:country => germany, :name => "Pfinztal", :area_code => "7240") + AreaCode.create(:country => germany, :name => "Rheinstetten", :area_code => "7242") + AreaCode.create(:country => germany, :name => "Ettlingen", :area_code => "7243") + AreaCode.create(:country => germany, :name => "Weingarten Baden", :area_code => "7244") + AreaCode.create(:country => germany, :name => "Durmersheim", :area_code => "7245") + AreaCode.create(:country => germany, :name => "Malsch Kr Karlsruhe", :area_code => "7246") + AreaCode.create(:country => germany, :name => "Linkenheim-Hochstetten", :area_code => "7247") + AreaCode.create(:country => germany, :name => "Marxzell", :area_code => "7248") + AreaCode.create(:country => germany, :name => "Stutensee", :area_code => "7249") + AreaCode.create(:country => germany, :name => "Kraichtal", :area_code => "7250") + AreaCode.create(:country => germany, :name => "Bruchsal", :area_code => "7251") + AreaCode.create(:country => germany, :name => "Bretten", :area_code => "7252") + AreaCode.create(:country => germany, :name => "Bad Schönborn", :area_code => "7253") + AreaCode.create(:country => germany, :name => "Waghäusel", :area_code => "7254") + AreaCode.create(:country => germany, :name => "Graben-Neudorf", :area_code => "7255") + AreaCode.create(:country => germany, :name => "Philippsburg", :area_code => "7256") + AreaCode.create(:country => germany, :name => "Bruchsal-Untergrombach", :area_code => "7257") + AreaCode.create(:country => germany, :name => "Oberderdingen-Flehingen", :area_code => "7258") + AreaCode.create(:country => germany, :name => "Östringen-Odenheim", :area_code => "7259") + AreaCode.create(:country => germany, :name => "Sinsheim-Hilsbach", :area_code => "7260") + AreaCode.create(:country => germany, :name => "Sinsheim", :area_code => "7261") + AreaCode.create(:country => germany, :name => "Eppingen", :area_code => "7262") + AreaCode.create(:country => germany, :name => "Waibstadt", :area_code => "7263") + AreaCode.create(:country => germany, :name => "Bad Rappenau", :area_code => "7264") + AreaCode.create(:country => germany, :name => "Angelbachtal", :area_code => "7265") + AreaCode.create(:country => germany, :name => "Kirchardt", :area_code => "7266") + AreaCode.create(:country => germany, :name => "Gemmingen", :area_code => "7267") + AreaCode.create(:country => germany, :name => "Bad Rappenau-Obergimpern", :area_code => "7268") + AreaCode.create(:country => germany, :name => "Sulzfeld Baden", :area_code => "7269") + AreaCode.create(:country => germany, :name => "Wörth am Rhein", :area_code => "7271") + AreaCode.create(:country => germany, :name => "Rülzheim", :area_code => "7272") + AreaCode.create(:country => germany, :name => "Hagenbach Pfalz", :area_code => "7273") + AreaCode.create(:country => germany, :name => "Germersheim", :area_code => "7274") + AreaCode.create(:country => germany, :name => "Kandel", :area_code => "7275") + AreaCode.create(:country => germany, :name => "Herxheim bei Landau Pfalz", :area_code => "7276") + AreaCode.create(:country => germany, :name => "Wörth-Büchelberg", :area_code => "7277") + AreaCode.create(:country => germany, :name => "Roggenburg", :area_code => "7300") + AreaCode.create(:country => germany, :name => "Pfaffenhofen a d Roth", :area_code => "7302") + AreaCode.create(:country => germany, :name => "Illertissen", :area_code => "7303") + AreaCode.create(:country => germany, :name => "Blaustein Württ", :area_code => "7304") + AreaCode.create(:country => germany, :name => "Erbach Donau", :area_code => "7305") + AreaCode.create(:country => germany, :name => "Vöhringen Iller", :area_code => "7306") + AreaCode.create(:country => germany, :name => "Senden Iller", :area_code => "7307") + AreaCode.create(:country => germany, :name => "Nersingen", :area_code => "7308") + AreaCode.create(:country => germany, :name => "Weissenhorn", :area_code => "7309") + AreaCode.create(:country => germany, :name => "Ulm Donau", :area_code => "731") + AreaCode.create(:country => germany, :name => "Heidenheim a d Brenz", :area_code => "7321") + AreaCode.create(:country => germany, :name => "Giengen a d Brenz", :area_code => "7322") + AreaCode.create(:country => germany, :name => "Gerstetten", :area_code => "7323") + AreaCode.create(:country => germany, :name => "Herbrechtingen", :area_code => "7324") + AreaCode.create(:country => germany, :name => "Sontheim a d Brenz", :area_code => "7325") + AreaCode.create(:country => germany, :name => "Neresheim", :area_code => "7326") + AreaCode.create(:country => germany, :name => "Dischingen", :area_code => "7327") + AreaCode.create(:country => germany, :name => "Königsbronn", :area_code => "7328") + AreaCode.create(:country => germany, :name => "Steinheim am Albuch", :area_code => "7329") + AreaCode.create(:country => germany, :name => "Geislingen an der Steige", :area_code => "7331") + AreaCode.create(:country => germany, :name => "Lauterstein", :area_code => "7332") + AreaCode.create(:country => germany, :name => "Laichingen", :area_code => "7333") + AreaCode.create(:country => germany, :name => "Deggingen", :area_code => "7334") + AreaCode.create(:country => germany, :name => "Wiesensteig", :area_code => "7335") + AreaCode.create(:country => germany, :name => "Lonsee", :area_code => "7336") + AreaCode.create(:country => germany, :name => "Nellingen Alb", :area_code => "7337") + AreaCode.create(:country => germany, :name => "Neenstetten", :area_code => "7340") + AreaCode.create(:country => germany, :name => "Buch b Illertissen", :area_code => "7343") + AreaCode.create(:country => germany, :name => "Blaubeuren", :area_code => "7344") + AreaCode.create(:country => germany, :name => "Langenau Württ", :area_code => "7345") + AreaCode.create(:country => germany, :name => "Illerkirchberg", :area_code => "7346") + AreaCode.create(:country => germany, :name => "Dietenheim", :area_code => "7347") + AreaCode.create(:country => germany, :name => "Beimerstetten", :area_code => "7348") + AreaCode.create(:country => germany, :name => "Biberach an der Riß", :area_code => "7351") + AreaCode.create(:country => germany, :name => "Ochsenhausen", :area_code => "7352") + AreaCode.create(:country => germany, :name => "Schwendi", :area_code => "7353") + AreaCode.create(:country => germany, :name => "Erolzheim", :area_code => "7354") + AreaCode.create(:country => germany, :name => "Hochdorf Riß", :area_code => "7355") + AreaCode.create(:country => germany, :name => "Schemmerhofen", :area_code => "7356") + AreaCode.create(:country => germany, :name => "Attenweiler", :area_code => "7357") + AreaCode.create(:country => germany, :name => "Eberhardzell-Füramoos", :area_code => "7358") + AreaCode.create(:country => germany, :name => "Aalen", :area_code => "7361") + AreaCode.create(:country => germany, :name => "Bopfingen", :area_code => "7362") + AreaCode.create(:country => germany, :name => "Lauchheim", :area_code => "7363") + AreaCode.create(:country => germany, :name => "Oberkochen", :area_code => "7364") + AreaCode.create(:country => germany, :name => "Essingen Württ", :area_code => "7365") + AreaCode.create(:country => germany, :name => "Abtsgmünd", :area_code => "7366") + AreaCode.create(:country => germany, :name => "Aalen-Ebnat", :area_code => "7367") + AreaCode.create(:country => germany, :name => "Riedlingen Württ", :area_code => "7371") + AreaCode.create(:country => germany, :name => "Zwiefalten", :area_code => "7373") + AreaCode.create(:country => germany, :name => "Uttenweiler", :area_code => "7374") + AreaCode.create(:country => germany, :name => "Obermarchtal", :area_code => "7375") + AreaCode.create(:country => germany, :name => "Langenenslingen", :area_code => "7376") + AreaCode.create(:country => germany, :name => "Münsingen", :area_code => "7381") + AreaCode.create(:country => germany, :name => "Römerstein", :area_code => "7382") + AreaCode.create(:country => germany, :name => "Münsingen-Buttenhausen", :area_code => "7383") + AreaCode.create(:country => germany, :name => "Schelklingen-Hütten", :area_code => "7384") + AreaCode.create(:country => germany, :name => "Gomadingen", :area_code => "7385") + AreaCode.create(:country => germany, :name => "Hayingen", :area_code => "7386") + AreaCode.create(:country => germany, :name => "Hohenstein Württ", :area_code => "7387") + AreaCode.create(:country => germany, :name => "Pfronstetten", :area_code => "7388") + AreaCode.create(:country => germany, :name => "Heroldstatt", :area_code => "7389") + AreaCode.create(:country => germany, :name => "Ehingen Donau", :area_code => "7391") + AreaCode.create(:country => germany, :name => "Laupheim", :area_code => "7392") + AreaCode.create(:country => germany, :name => "Munderkingen", :area_code => "7393") + AreaCode.create(:country => germany, :name => "Schelklingen", :area_code => "7394") + AreaCode.create(:country => germany, :name => "Ehingen-Dächingen", :area_code => "7395") + AreaCode.create(:country => germany, :name => "Fluorn-Winzeln", :area_code => "7402") + AreaCode.create(:country => germany, :name => "Dunningen", :area_code => "7403") + AreaCode.create(:country => germany, :name => "Epfendorf", :area_code => "7404") + AreaCode.create(:country => germany, :name => "Rottweil", :area_code => "741") + AreaCode.create(:country => germany, :name => "Deisslingen", :area_code => "7420") + AreaCode.create(:country => germany, :name => "Schramberg", :area_code => "7422") + AreaCode.create(:country => germany, :name => "Oberndorf am Neckar", :area_code => "7423") + AreaCode.create(:country => germany, :name => "Spaichingen", :area_code => "7424") + AreaCode.create(:country => germany, :name => "Trossingen", :area_code => "7425") + AreaCode.create(:country => germany, :name => "Gosheim", :area_code => "7426") + AreaCode.create(:country => germany, :name => "Schömberg b Balingen", :area_code => "7427") + AreaCode.create(:country => germany, :name => "Rosenfeld", :area_code => "7428") + AreaCode.create(:country => germany, :name => "Egesheim", :area_code => "7429") + AreaCode.create(:country => germany, :name => "Albstadt-Ebingen", :area_code => "7431") + AreaCode.create(:country => germany, :name => "Albstadt-Tailfingen", :area_code => "7432") + AreaCode.create(:country => germany, :name => "Balingen", :area_code => "7433") + AreaCode.create(:country => germany, :name => "Winterlingen", :area_code => "7434") + AreaCode.create(:country => germany, :name => "Albstadt-Laufen", :area_code => "7435") + AreaCode.create(:country => germany, :name => "Messstetten-Oberdigisheim", :area_code => "7436") + AreaCode.create(:country => germany, :name => "Bad Rippoldsau", :area_code => "7440") + AreaCode.create(:country => germany, :name => "Freudenstadt", :area_code => "7441") + AreaCode.create(:country => germany, :name => "Baiersbronn", :area_code => "7442") + AreaCode.create(:country => germany, :name => "Dornstetten", :area_code => "7443") + AreaCode.create(:country => germany, :name => "Alpirsbach", :area_code => "7444") + AreaCode.create(:country => germany, :name => "Pfalzgrafenweiler", :area_code => "7445") + AreaCode.create(:country => germany, :name => "Lossburg", :area_code => "7446") + AreaCode.create(:country => germany, :name => "Baiersbronn-Schwarzenberg", :area_code => "7447") + AreaCode.create(:country => germany, :name => "Seewald", :area_code => "7448") + AreaCode.create(:country => germany, :name => "Baiersbronn-Obertal", :area_code => "7449") + AreaCode.create(:country => germany, :name => "Horb am Neckar", :area_code => "7451") + AreaCode.create(:country => germany, :name => "Nagold", :area_code => "7452") + AreaCode.create(:country => germany, :name => "Altensteig Württ", :area_code => "7453") + AreaCode.create(:country => germany, :name => "Sulz am Neckar", :area_code => "7454") + AreaCode.create(:country => germany, :name => "Dornhan", :area_code => "7455") + AreaCode.create(:country => germany, :name => "Haiterbach", :area_code => "7456") + AreaCode.create(:country => germany, :name => "Rottenburg-Ergenzingen", :area_code => "7457") + AreaCode.create(:country => germany, :name => "Ebhausen", :area_code => "7458") + AreaCode.create(:country => germany, :name => "Nagold-Hochdorf", :area_code => "7459") + AreaCode.create(:country => germany, :name => "Tuttlingen", :area_code => "7461") + AreaCode.create(:country => germany, :name => "Immendingen", :area_code => "7462") + AreaCode.create(:country => germany, :name => "Mühlheim an der Donau", :area_code => "7463") + AreaCode.create(:country => germany, :name => "Talheim Kr Tuttlingen", :area_code => "7464") + AreaCode.create(:country => germany, :name => "Emmingen-Liptingen", :area_code => "7465") + AreaCode.create(:country => germany, :name => "Beuron", :area_code => "7466") + AreaCode.create(:country => germany, :name => "Neuhausen ob Eck", :area_code => "7467") + AreaCode.create(:country => germany, :name => "Hechingen", :area_code => "7471") + AreaCode.create(:country => germany, :name => "Rottenburg am Neckar", :area_code => "7472") + AreaCode.create(:country => germany, :name => "Mössingen", :area_code => "7473") + AreaCode.create(:country => germany, :name => "Haigerloch", :area_code => "7474") + AreaCode.create(:country => germany, :name => "Burladingen", :area_code => "7475") + AreaCode.create(:country => germany, :name => "Bisingen", :area_code => "7476") + AreaCode.create(:country => germany, :name => "Jungingen b Hechingen", :area_code => "7477") + AreaCode.create(:country => germany, :name => "Hirrlingen", :area_code => "7478") + AreaCode.create(:country => germany, :name => "Horb-Dettingen", :area_code => "7482") + AreaCode.create(:country => germany, :name => "Horb-Mühringen", :area_code => "7483") + AreaCode.create(:country => germany, :name => "Simmersfeld", :area_code => "7484") + AreaCode.create(:country => germany, :name => "Empfingen", :area_code => "7485") + AreaCode.create(:country => germany, :name => "Horb-Altheim", :area_code => "7486") + AreaCode.create(:country => germany, :name => "Wolpertswende", :area_code => "7502") + AreaCode.create(:country => germany, :name => "Wilhelmsdorf Württ", :area_code => "7503") + AreaCode.create(:country => germany, :name => "Horgenzell", :area_code => "7504") + AreaCode.create(:country => germany, :name => "Fronreute", :area_code => "7505") + AreaCode.create(:country => germany, :name => "Wangen-Leupolz", :area_code => "7506") + AreaCode.create(:country => germany, :name => "Ravensburg", :area_code => "751") + AreaCode.create(:country => germany, :name => "Bodnegg", :area_code => "7520") + AreaCode.create(:country => germany, :name => "Wangen im Allgäu", :area_code => "7522") + AreaCode.create(:country => germany, :name => "Bad Waldsee", :area_code => "7524") + AreaCode.create(:country => germany, :name => "Aulendorf", :area_code => "7525") + AreaCode.create(:country => germany, :name => "Wolfegg", :area_code => "7527") + AreaCode.create(:country => germany, :name => "Neukirch b Tettnang", :area_code => "7528") + AreaCode.create(:country => germany, :name => "Waldburg Württ", :area_code => "7529") + AreaCode.create(:country => germany, :name => "Konstanz", :area_code => "7531") + AreaCode.create(:country => germany, :name => "Meersburg", :area_code => "7532") + AreaCode.create(:country => germany, :name => "Allensbach", :area_code => "7533") + AreaCode.create(:country => germany, :name => "Reichenau Baden", :area_code => "7534") + AreaCode.create(:country => germany, :name => "Friedrichshafen", :area_code => "7541") + AreaCode.create(:country => germany, :name => "Tettnang", :area_code => "7542") + AreaCode.create(:country => germany, :name => "Kressbronn am Bodensee", :area_code => "7543") + AreaCode.create(:country => germany, :name => "Markdorf", :area_code => "7544") + AreaCode.create(:country => germany, :name => "Immenstaad am Bodensee", :area_code => "7545") + AreaCode.create(:country => germany, :name => "Oberteuringen", :area_code => "7546") + AreaCode.create(:country => germany, :name => "Überlingen Bodensee", :area_code => "7551") + AreaCode.create(:country => germany, :name => "Pfullendorf", :area_code => "7552") + AreaCode.create(:country => germany, :name => "Salem Baden", :area_code => "7553") + AreaCode.create(:country => germany, :name => "Heiligenberg Baden", :area_code => "7554") + AreaCode.create(:country => germany, :name => "Deggenhausertal", :area_code => "7555") + AreaCode.create(:country => germany, :name => "Uhldingen-Mühlhofen", :area_code => "7556") + AreaCode.create(:country => germany, :name => "Herdwangen-Schönach", :area_code => "7557") + AreaCode.create(:country => germany, :name => "Illmensee", :area_code => "7558") + AreaCode.create(:country => germany, :name => "Leutkirch im Allgäu", :area_code => "7561") + AreaCode.create(:country => germany, :name => "Isny im Allgäu", :area_code => "7562") + AreaCode.create(:country => germany, :name => "Kisslegg", :area_code => "7563") + AreaCode.create(:country => germany, :name => "Bad Wurzach", :area_code => "7564") + AreaCode.create(:country => germany, :name => "Aichstetten Kr Ravensburg", :area_code => "7565") + AreaCode.create(:country => germany, :name => "Argenbühl", :area_code => "7566") + AreaCode.create(:country => germany, :name => "Leutkirch-Friesenhofen", :area_code => "7567") + AreaCode.create(:country => germany, :name => "Bad Wurzach-Hauerz", :area_code => "7568") + AreaCode.create(:country => germany, :name => "Isny-Eisenbach", :area_code => "7569") + AreaCode.create(:country => germany, :name => "Sigmaringen-Gutenstein", :area_code => "7570") + AreaCode.create(:country => germany, :name => "Sigmaringen", :area_code => "7571") + AreaCode.create(:country => germany, :name => "Mengen Württ", :area_code => "7572") + AreaCode.create(:country => germany, :name => "Stetten am kalten Markt", :area_code => "7573") + AreaCode.create(:country => germany, :name => "Gammertingen", :area_code => "7574") + AreaCode.create(:country => germany, :name => "Messkirch", :area_code => "7575") + AreaCode.create(:country => germany, :name => "Krauchenwies", :area_code => "7576") + AreaCode.create(:country => germany, :name => "Veringenstadt", :area_code => "7577") + AreaCode.create(:country => germany, :name => "Wald Hohenz", :area_code => "7578") + AreaCode.create(:country => germany, :name => "Schwenningen Baden", :area_code => "7579") + AreaCode.create(:country => germany, :name => "Saulgau", :area_code => "7581") + AreaCode.create(:country => germany, :name => "Bad Buchau", :area_code => "7582") + AreaCode.create(:country => germany, :name => "Bad Schussenried", :area_code => "7583") + AreaCode.create(:country => germany, :name => "Altshausen", :area_code => "7584") + AreaCode.create(:country => germany, :name => "Ostrach", :area_code => "7585") + AreaCode.create(:country => germany, :name => "Herbertingen", :area_code => "7586") + AreaCode.create(:country => germany, :name => "Hosskirch", :area_code => "7587") + AreaCode.create(:country => germany, :name => "Oberried Breisgau", :area_code => "7602") + AreaCode.create(:country => germany, :name => "Freiburg im Breisgau", :area_code => "761") + AreaCode.create(:country => germany, :name => "Schopfheim-Gersbach", :area_code => "7620") + AreaCode.create(:country => germany, :name => "Lörrach", :area_code => "7621") + AreaCode.create(:country => germany, :name => "Schopfheim", :area_code => "7622") + AreaCode.create(:country => germany, :name => "Rheinfelden Baden", :area_code => "7623") + AreaCode.create(:country => germany, :name => "Grenzach-Wyhlen", :area_code => "7624") + AreaCode.create(:country => germany, :name => "Zell im Wiesental", :area_code => "7625") + AreaCode.create(:country => germany, :name => "Kandern", :area_code => "7626") + AreaCode.create(:country => germany, :name => "Steinen Kr Lörrach", :area_code => "7627") + AreaCode.create(:country => germany, :name => "Efringen-Kirchen", :area_code => "7628") + AreaCode.create(:country => germany, :name => "Tegernau Baden", :area_code => "7629") + AreaCode.create(:country => germany, :name => "Müllheim Baden", :area_code => "7631") + AreaCode.create(:country => germany, :name => "Badenweiler", :area_code => "7632") + AreaCode.create(:country => germany, :name => "Staufen im Breisgau", :area_code => "7633") + AreaCode.create(:country => germany, :name => "Sulzburg", :area_code => "7634") + AreaCode.create(:country => germany, :name => "Schliengen", :area_code => "7635") + AreaCode.create(:country => germany, :name => "Münstertal Schwarzwald", :area_code => "7636") + AreaCode.create(:country => germany, :name => "Emmendingen", :area_code => "7641") + AreaCode.create(:country => germany, :name => "Endingen Kaiserstuh", :area_code => "7642") + AreaCode.create(:country => germany, :name => "Herbolzheim Breisgau", :area_code => "7643") + AreaCode.create(:country => germany, :name => "Kenzingen", :area_code => "7644") + AreaCode.create(:country => germany, :name => "Freiamt", :area_code => "7645") + AreaCode.create(:country => germany, :name => "Weisweil Breisgau", :area_code => "7646") + AreaCode.create(:country => germany, :name => "Titisee-Neustadt", :area_code => "7651") + AreaCode.create(:country => germany, :name => "Hinterzarten", :area_code => "7652") + AreaCode.create(:country => germany, :name => "Lenzkirch", :area_code => "7653") + AreaCode.create(:country => germany, :name => "Löffingen", :area_code => "7654") + AreaCode.create(:country => germany, :name => "Feldberg-Altglashütten", :area_code => "7655") + AreaCode.create(:country => germany, :name => "Schluchsee", :area_code => "7656") + AreaCode.create(:country => germany, :name => "Eisenbach Hochschwarzwald", :area_code => "7657") + AreaCode.create(:country => germany, :name => "St Peter Schwarzw", :area_code => "7660") + AreaCode.create(:country => germany, :name => "Kirchzarten", :area_code => "7661") + AreaCode.create(:country => germany, :name => "Vogtsburg im Kaiserstuh", :area_code => "7662") + AreaCode.create(:country => germany, :name => "Eichstetten", :area_code => "7663") + AreaCode.create(:country => germany, :name => "Freiburg-Tiengen", :area_code => "7664") + AreaCode.create(:country => germany, :name => "March Breisgau", :area_code => "7665") + AreaCode.create(:country => germany, :name => "Denzlingen", :area_code => "7666") + AreaCode.create(:country => germany, :name => "Breisach am Rhein", :area_code => "7667") + AreaCode.create(:country => germany, :name => "Ihringen", :area_code => "7668") + AreaCode.create(:country => germany, :name => "St Märgen", :area_code => "7669") + AreaCode.create(:country => germany, :name => "Todtnau", :area_code => "7671") + AreaCode.create(:country => germany, :name => "St Blasien", :area_code => "7672") + AreaCode.create(:country => germany, :name => "Schönau im Schwarzwald", :area_code => "7673") + AreaCode.create(:country => germany, :name => "Todtmoos", :area_code => "7674") + AreaCode.create(:country => germany, :name => "Bernau Baden", :area_code => "7675") + AreaCode.create(:country => germany, :name => "Feldberg Schwarzwald", :area_code => "7676") + AreaCode.create(:country => germany, :name => "Waldkirch Breisgau", :area_code => "7681") + AreaCode.create(:country => germany, :name => "Elzach", :area_code => "7682") + AreaCode.create(:country => germany, :name => "Simonswald", :area_code => "7683") + AreaCode.create(:country => germany, :name => "Glottertal", :area_code => "7684") + AreaCode.create(:country => germany, :name => "Gutach-Bleibach", :area_code => "7685") + AreaCode.create(:country => germany, :name => "Blumberg Baden", :area_code => "7702") + AreaCode.create(:country => germany, :name => "Bonndorf im Schwarzwald", :area_code => "7703") + AreaCode.create(:country => germany, :name => "Geisingen Baden", :area_code => "7704") + AreaCode.create(:country => germany, :name => "Wolterdingen Schwarzw", :area_code => "7705") + AreaCode.create(:country => germany, :name => "Oberbaldingen", :area_code => "7706") + AreaCode.create(:country => germany, :name => "Bräunlingen", :area_code => "7707") + AreaCode.create(:country => germany, :name => "Geisingen-Leipferdingen", :area_code => "7708") + AreaCode.create(:country => germany, :name => "Wutach", :area_code => "7709") + AreaCode.create(:country => germany, :name => "Donaueschingen", :area_code => "771") + AreaCode.create(:country => germany, :name => "Schwenningen a Neckar", :area_code => "7720") + AreaCode.create(:country => germany, :name => "Villingen i Schwarzw", :area_code => "7721") + AreaCode.create(:country => germany, :name => "Triberg im Schwarzwald", :area_code => "7722") + AreaCode.create(:country => germany, :name => "Furtwangen im Schwarzwald", :area_code => "7723") + AreaCode.create(:country => germany, :name => "St Georgen im Schwarzwald", :area_code => "7724") + AreaCode.create(:country => germany, :name => "Königsfeld im Schwarzwald", :area_code => "7725") + AreaCode.create(:country => germany, :name => "Bad Dürrheim", :area_code => "7726") + AreaCode.create(:country => germany, :name => "Vöhrenbach", :area_code => "7727") + AreaCode.create(:country => germany, :name => "Niedereschach", :area_code => "7728") + AreaCode.create(:country => germany, :name => "Tennenbronn", :area_code => "7729") + AreaCode.create(:country => germany, :name => "Singen Hohentwiel", :area_code => "7731") + AreaCode.create(:country => germany, :name => "Radolfzell am Bodensee", :area_code => "7732") + AreaCode.create(:country => germany, :name => "Engen Hegau", :area_code => "7733") + AreaCode.create(:country => germany, :name => "Gailingen", :area_code => "7734") + AreaCode.create(:country => germany, :name => "Öhningen", :area_code => "7735") + AreaCode.create(:country => germany, :name => "Tengen", :area_code => "7736") + AreaCode.create(:country => germany, :name => "Steisslingen", :area_code => "7738") + AreaCode.create(:country => germany, :name => "Hilzingen", :area_code => "7739") + AreaCode.create(:country => germany, :name => "Tiengen Hochrhein", :area_code => "7741") + AreaCode.create(:country => germany, :name => "Klettgau", :area_code => "7742") + AreaCode.create(:country => germany, :name => "Ühlingen-Birkendorf", :area_code => "7743") + AreaCode.create(:country => germany, :name => "Stühlingen", :area_code => "7744") + AreaCode.create(:country => germany, :name => "Jestetten", :area_code => "7745") + AreaCode.create(:country => germany, :name => "Wutöschingen", :area_code => "7746") + AreaCode.create(:country => germany, :name => "Berau", :area_code => "7747") + AreaCode.create(:country => germany, :name => "Grafenhausen Hochschwarzw", :area_code => "7748") + AreaCode.create(:country => germany, :name => "Waldshut", :area_code => "7751") + AreaCode.create(:country => germany, :name => "Albbruck", :area_code => "7753") + AreaCode.create(:country => germany, :name => "Görwihl", :area_code => "7754") + AreaCode.create(:country => germany, :name => "Weilheim Kr Waldshut", :area_code => "7755") + AreaCode.create(:country => germany, :name => "Bad Säckingen", :area_code => "7761") + AreaCode.create(:country => germany, :name => "Wehr Baden", :area_code => "7762") + AreaCode.create(:country => germany, :name => "Murg", :area_code => "7763") + AreaCode.create(:country => germany, :name => "Herrischried", :area_code => "7764") + AreaCode.create(:country => germany, :name => "Rickenbach Hotzenw", :area_code => "7765") + AreaCode.create(:country => germany, :name => "Stockach", :area_code => "7771") + AreaCode.create(:country => germany, :name => "Bodman-Ludwigshafen", :area_code => "7773") + AreaCode.create(:country => germany, :name => "Eigeltingen", :area_code => "7774") + AreaCode.create(:country => germany, :name => "Mühlingen", :area_code => "7775") + AreaCode.create(:country => germany, :name => "Sauldorf", :area_code => "7777") + AreaCode.create(:country => germany, :name => "Oberkirch Baden", :area_code => "7802") + AreaCode.create(:country => germany, :name => "Gengenbach", :area_code => "7803") + AreaCode.create(:country => germany, :name => "Oppenau", :area_code => "7804") + AreaCode.create(:country => germany, :name => "Appenweier", :area_code => "7805") + AreaCode.create(:country => germany, :name => "Bad Peterstal-Griesbach", :area_code => "7806") + AreaCode.create(:country => germany, :name => "Neuried Ortenaukreis", :area_code => "7807") + AreaCode.create(:country => germany, :name => "Hohberg b Offenburg", :area_code => "7808") + AreaCode.create(:country => germany, :name => "Offenburg", :area_code => "781") + AreaCode.create(:country => germany, :name => "Lahr Schwarzwald", :area_code => "7821") + AreaCode.create(:country => germany, :name => "Ettenheim", :area_code => "7822") + AreaCode.create(:country => germany, :name => "Seelbach Schutter", :area_code => "7823") + AreaCode.create(:country => germany, :name => "Schwanau", :area_code => "7824") + AreaCode.create(:country => germany, :name => "Kippenheim", :area_code => "7825") + AreaCode.create(:country => germany, :name => "Schuttertal", :area_code => "7826") + AreaCode.create(:country => germany, :name => "Hausach", :area_code => "7831") + AreaCode.create(:country => germany, :name => "Haslach im Kinzigtal", :area_code => "7832") + AreaCode.create(:country => germany, :name => "Hornberg Schwarzwaldbahn", :area_code => "7833") + AreaCode.create(:country => germany, :name => "Wolfach", :area_code => "7834") + AreaCode.create(:country => germany, :name => "Zell am Harmersbach", :area_code => "7835") + AreaCode.create(:country => germany, :name => "Schiltach", :area_code => "7836") + AreaCode.create(:country => germany, :name => "Oberharmersbach", :area_code => "7837") + AreaCode.create(:country => germany, :name => "Nordrach", :area_code => "7838") + AreaCode.create(:country => germany, :name => "Schapbach", :area_code => "7839") + AreaCode.create(:country => germany, :name => "Achern", :area_code => "7841") + AreaCode.create(:country => germany, :name => "Kappelrodeck", :area_code => "7842") + AreaCode.create(:country => germany, :name => "Renchen", :area_code => "7843") + AreaCode.create(:country => germany, :name => "Rheinau", :area_code => "7844") + AreaCode.create(:country => germany, :name => "Kehl", :area_code => "7851") + AreaCode.create(:country => germany, :name => "Willstätt", :area_code => "7852") + AreaCode.create(:country => germany, :name => "Kehl-Bodersweier", :area_code => "7853") + AreaCode.create(:country => germany, :name => "Kehl-Goldscheuer", :area_code => "7854") + AreaCode.create(:country => germany, :name => "Mainhardt", :area_code => "7903") + AreaCode.create(:country => germany, :name => "Ilshofen", :area_code => "7904") + AreaCode.create(:country => germany, :name => "Langenburg", :area_code => "7905") + AreaCode.create(:country => germany, :name => "Braunsbach", :area_code => "7906") + AreaCode.create(:country => germany, :name => "Schwäbisch Hall-Sulzdorf", :area_code => "7907") + AreaCode.create(:country => germany, :name => "Schwäbisch Hall", :area_code => "791") + AreaCode.create(:country => germany, :name => "Boxberg Baden", :area_code => "7930") + AreaCode.create(:country => germany, :name => "Bad Mergentheim", :area_code => "7931") + AreaCode.create(:country => germany, :name => "Niederstetten Württ", :area_code => "7932") + AreaCode.create(:country => germany, :name => "Creglingen", :area_code => "7933") + AreaCode.create(:country => germany, :name => "Weikersheim", :area_code => "7934") + AreaCode.create(:country => germany, :name => "Schrozberg", :area_code => "7935") + AreaCode.create(:country => germany, :name => "Schrozberg-Bartenstein", :area_code => "7936") + AreaCode.create(:country => germany, :name => "Dörzbach", :area_code => "7937") + AreaCode.create(:country => germany, :name => "Mulfingen Jagst", :area_code => "7938") + AreaCode.create(:country => germany, :name => "Schrozberg-Spielbach", :area_code => "7939") + AreaCode.create(:country => germany, :name => "Künzelsau", :area_code => "7940") + AreaCode.create(:country => germany, :name => "Öhringen", :area_code => "7941") + AreaCode.create(:country => germany, :name => "Neuenstein Württ", :area_code => "7942") + AreaCode.create(:country => germany, :name => "Schöntal Jagst", :area_code => "7943") + AreaCode.create(:country => germany, :name => "Kupferzell", :area_code => "7944") + AreaCode.create(:country => germany, :name => "Wüstenrot", :area_code => "7945") + AreaCode.create(:country => germany, :name => "Bretzfeld", :area_code => "7946") + AreaCode.create(:country => germany, :name => "Forchtenberg", :area_code => "7947") + AreaCode.create(:country => germany, :name => "Öhringen-Ohrnberg", :area_code => "7948") + AreaCode.create(:country => germany, :name => "Pfedelbach-Untersteinbach", :area_code => "7949") + AreaCode.create(:country => germany, :name => "Schnelldorf", :area_code => "7950") + AreaCode.create(:country => germany, :name => "Crailsheim", :area_code => "7951") + AreaCode.create(:country => germany, :name => "Gerabronn", :area_code => "7952") + AreaCode.create(:country => germany, :name => "Blaufelden", :area_code => "7953") + AreaCode.create(:country => germany, :name => "Kirchberg an der Jagst", :area_code => "7954") + AreaCode.create(:country => germany, :name => "Wallhausen Württ", :area_code => "7955") + AreaCode.create(:country => germany, :name => "Kressberg", :area_code => "7957") + AreaCode.create(:country => germany, :name => "Rot Am See-Brettheim", :area_code => "7958") + AreaCode.create(:country => germany, :name => "Frankenhardt", :area_code => "7959") + AreaCode.create(:country => germany, :name => "Ellwangen Jagst", :area_code => "7961") + AreaCode.create(:country => germany, :name => "Fichtenau", :area_code => "7962") + AreaCode.create(:country => germany, :name => "Adelmannsfelden", :area_code => "7963") + AreaCode.create(:country => germany, :name => "Stödtlen", :area_code => "7964") + AreaCode.create(:country => germany, :name => "Ellwangen-Röhlingen", :area_code => "7965") + AreaCode.create(:country => germany, :name => "Unterschneidheim", :area_code => "7966") + AreaCode.create(:country => germany, :name => "Jagstzell", :area_code => "7967") + AreaCode.create(:country => germany, :name => "Gaildorf", :area_code => "7971") + AreaCode.create(:country => germany, :name => "Gschwend b Gaildorf", :area_code => "7972") + AreaCode.create(:country => germany, :name => "Obersontheim", :area_code => "7973") + AreaCode.create(:country => germany, :name => "Bühlerzell", :area_code => "7974") + AreaCode.create(:country => germany, :name => "Untergröningen", :area_code => "7975") + AreaCode.create(:country => germany, :name => "Sulzbach-Laufen", :area_code => "7976") + AreaCode.create(:country => germany, :name => "Oberrot b Gaildorf", :area_code => "7977") + AreaCode.create(:country => germany, :name => "Weyarn", :area_code => "8020") + AreaCode.create(:country => germany, :name => "Waakirchen", :area_code => "8021") + AreaCode.create(:country => germany, :name => "Tegernsee", :area_code => "8022") + AreaCode.create(:country => germany, :name => "Bayrischzell", :area_code => "8023") + AreaCode.create(:country => germany, :name => "Holzkirchen", :area_code => "8024") + AreaCode.create(:country => germany, :name => "Miesbach", :area_code => "8025") + AreaCode.create(:country => germany, :name => "Hausham", :area_code => "8026") + AreaCode.create(:country => germany, :name => "Dietramszell", :area_code => "8027") + AreaCode.create(:country => germany, :name => "Fischbachau", :area_code => "8028") + AreaCode.create(:country => germany, :name => "Kreuth b Tegernsee", :area_code => "8029") + AreaCode.create(:country => germany, :name => "Rosenheim Oberbay", :area_code => "8031") + AreaCode.create(:country => germany, :name => "Rohrdorf Kr Rosenheim", :area_code => "8032") + AreaCode.create(:country => germany, :name => "Oberaudorf", :area_code => "8033") + AreaCode.create(:country => germany, :name => "Brannenburg", :area_code => "8034") + AreaCode.create(:country => germany, :name => "Raubling", :area_code => "8035") + AreaCode.create(:country => germany, :name => "Stephanskirchen Simssee", :area_code => "8036") + AreaCode.create(:country => germany, :name => "Vogtareuth", :area_code => "8038") + AreaCode.create(:country => germany, :name => "Rott a Inn", :area_code => "8039") + AreaCode.create(:country => germany, :name => "Bad Tölz", :area_code => "8041") + AreaCode.create(:country => germany, :name => "Lenggries", :area_code => "8042") + AreaCode.create(:country => germany, :name => "Jachenau", :area_code => "8043") + AreaCode.create(:country => germany, :name => "Lenggries-Fall", :area_code => "8045") + AreaCode.create(:country => germany, :name => "Bad Heilbrunn", :area_code => "8046") + AreaCode.create(:country => germany, :name => "Prien a Chiemsee", :area_code => "8051") + AreaCode.create(:country => germany, :name => "Aschau i Chiemgau", :area_code => "8052") + AreaCode.create(:country => germany, :name => "Bad Endorf", :area_code => "8053") + AreaCode.create(:country => germany, :name => "Breitbrunn a Chiemsee", :area_code => "8054") + AreaCode.create(:country => germany, :name => "Halfing", :area_code => "8055") + AreaCode.create(:country => germany, :name => "Eggstätt", :area_code => "8056") + AreaCode.create(:country => germany, :name => "Aschau-Sachrang", :area_code => "8057") + AreaCode.create(:country => germany, :name => "Bad Aibling", :area_code => "8061") + AreaCode.create(:country => germany, :name => "Bruckmühl Mangfall", :area_code => "8062") + AreaCode.create(:country => germany, :name => "Feldkirchen-Westerham", :area_code => "8063") + AreaCode.create(:country => germany, :name => "Au b Bad Aibling", :area_code => "8064") + AreaCode.create(:country => germany, :name => "Tuntenhausen-Schönau", :area_code => "8065") + AreaCode.create(:country => germany, :name => "Bad Feilnbach", :area_code => "8066") + AreaCode.create(:country => germany, :name => "Tuntenhausen", :area_code => "8067") + AreaCode.create(:country => germany, :name => "Wasserburg a Inn", :area_code => "8071") + AreaCode.create(:country => germany, :name => "Haag i OB", :area_code => "8072") + AreaCode.create(:country => germany, :name => "Gars a Inn", :area_code => "8073") + AreaCode.create(:country => germany, :name => "Schnaitsee", :area_code => "8074") + AreaCode.create(:country => germany, :name => "Amerang", :area_code => "8075") + AreaCode.create(:country => germany, :name => "Pfaffing", :area_code => "8076") + AreaCode.create(:country => germany, :name => "Dorfen Stadt", :area_code => "8081") + AreaCode.create(:country => germany, :name => "Schwindegg", :area_code => "8082") + AreaCode.create(:country => germany, :name => "Isen", :area_code => "8083") + AreaCode.create(:country => germany, :name => "Taufkirchen Vils", :area_code => "8084") + AreaCode.create(:country => germany, :name => "Sankt Wolfgang", :area_code => "8085") + AreaCode.create(:country => germany, :name => "Buchbach Oberbay", :area_code => "8086") + AreaCode.create(:country => germany, :name => "Kirchseeon", :area_code => "8091") + AreaCode.create(:country => germany, :name => "Grafing b München", :area_code => "8092") + AreaCode.create(:country => germany, :name => "Glonn Kr Ebersberg", :area_code => "8093") + AreaCode.create(:country => germany, :name => "Steinhöring", :area_code => "8094") + AreaCode.create(:country => germany, :name => "Aying", :area_code => "8095") + AreaCode.create(:country => germany, :name => "Höhenkirchen-Siegertsbrunn", :area_code => "8102") + AreaCode.create(:country => germany, :name => "Sauerlach", :area_code => "8104") + AreaCode.create(:country => germany, :name => "Gilching", :area_code => "8105") + AreaCode.create(:country => germany, :name => "Vaterstetten", :area_code => "8106") + AreaCode.create(:country => germany, :name => "Hallbergmoos", :area_code => "811") + AreaCode.create(:country => germany, :name => "Markt Schwaben", :area_code => "8121") + AreaCode.create(:country => germany, :name => "Erding", :area_code => "8122") + AreaCode.create(:country => germany, :name => "Moosinning", :area_code => "8123") + AreaCode.create(:country => germany, :name => "Forstern Oberbay", :area_code => "8124") + AreaCode.create(:country => germany, :name => "Dachau", :area_code => "8131") + AreaCode.create(:country => germany, :name => "Haimhausen Oberbay", :area_code => "8133") + AreaCode.create(:country => germany, :name => "Odelzhausen", :area_code => "8134") + AreaCode.create(:country => germany, :name => "Sulzemoos", :area_code => "8135") + AreaCode.create(:country => germany, :name => "Markt Indersdorf", :area_code => "8136") + AreaCode.create(:country => germany, :name => "Petershausen", :area_code => "8137") + AreaCode.create(:country => germany, :name => "Schwabhausen b Dachau", :area_code => "8138") + AreaCode.create(:country => germany, :name => "Röhrmoos", :area_code => "8139") + AreaCode.create(:country => germany, :name => "Fürstenfeldbruck", :area_code => "8141") + AreaCode.create(:country => germany, :name => "Olching", :area_code => "8142") + AreaCode.create(:country => germany, :name => "Inning a Ammersee", :area_code => "8143") + AreaCode.create(:country => germany, :name => "Grafrath", :area_code => "8144") + AreaCode.create(:country => germany, :name => "Mammendorf", :area_code => "8145") + AreaCode.create(:country => germany, :name => "Moorenweis", :area_code => "8146") + AreaCode.create(:country => germany, :name => "Starnberg", :area_code => "8151") + AreaCode.create(:country => germany, :name => "Herrsching a Ammersee", :area_code => "8152") + AreaCode.create(:country => germany, :name => "Wessling", :area_code => "8153") + AreaCode.create(:country => germany, :name => "Feldafing", :area_code => "8157") + AreaCode.create(:country => germany, :name => "Tutzing", :area_code => "8158") + AreaCode.create(:country => germany, :name => "Freising", :area_code => "8161") + AreaCode.create(:country => germany, :name => "Neufahrn b Freising", :area_code => "8165") + AreaCode.create(:country => germany, :name => "Allershausen Oberbay", :area_code => "8166") + AreaCode.create(:country => germany, :name => "Zolling", :area_code => "8167") + AreaCode.create(:country => germany, :name => "Attenkirchen", :area_code => "8168") + AreaCode.create(:country => germany, :name => "Straßlach-Dingharting", :area_code => "8170") + AreaCode.create(:country => germany, :name => "Wolfratshausen", :area_code => "8171") + AreaCode.create(:country => germany, :name => "Egling b Wolfratshausen", :area_code => "8176") + AreaCode.create(:country => germany, :name => "Münsing Starnberger See", :area_code => "8177") + AreaCode.create(:country => germany, :name => "Icking", :area_code => "8178") + AreaCode.create(:country => germany, :name => "Eurasburg a d Loisach", :area_code => "8179") + AreaCode.create(:country => germany, :name => "Landsberg a Lech", :area_code => "8191") + AreaCode.create(:country => germany, :name => "Schondorf a Ammersee", :area_code => "8192") + AreaCode.create(:country => germany, :name => "Geltendorf", :area_code => "8193") + AreaCode.create(:country => germany, :name => "Vilgertshofen", :area_code => "8194") + AreaCode.create(:country => germany, :name => "Weil Kr Landsberg a Lech", :area_code => "8195") + AreaCode.create(:country => germany, :name => "Pürgen", :area_code => "8196") + AreaCode.create(:country => germany, :name => "Althegnenberg", :area_code => "8202") + AreaCode.create(:country => germany, :name => "Grossaitingen", :area_code => "8203") + AreaCode.create(:country => germany, :name => "Mickhausen", :area_code => "8204") + AreaCode.create(:country => germany, :name => "Dasing", :area_code => "8205") + AreaCode.create(:country => germany, :name => "Egling a d Paar", :area_code => "8206") + AreaCode.create(:country => germany, :name => "Affing", :area_code => "8207") + AreaCode.create(:country => germany, :name => "Eurasburg b Augsburg", :area_code => "8208") + AreaCode.create(:country => germany, :name => "Augsburg", :area_code => "821") + AreaCode.create(:country => germany, :name => "Günzburg", :area_code => "8221") + AreaCode.create(:country => germany, :name => "Burgau Schwab", :area_code => "8222") + AreaCode.create(:country => germany, :name => "Ichenhausen", :area_code => "8223") + AreaCode.create(:country => germany, :name => "Offingen Donau", :area_code => "8224") + AreaCode.create(:country => germany, :name => "Jettingen-Scheppach", :area_code => "8225") + AreaCode.create(:country => germany, :name => "Bibertal", :area_code => "8226") + AreaCode.create(:country => germany, :name => "Gablingen", :area_code => "8230") + AreaCode.create(:country => germany, :name => "Königsbrunn b Augsburg", :area_code => "8231") + AreaCode.create(:country => germany, :name => "Schwabmünchen", :area_code => "8232") + AreaCode.create(:country => germany, :name => "Kissing", :area_code => "8233") + AreaCode.create(:country => germany, :name => "Bobingen", :area_code => "8234") + AreaCode.create(:country => germany, :name => "Fischach", :area_code => "8236") + AreaCode.create(:country => germany, :name => "Aindling", :area_code => "8237") + AreaCode.create(:country => germany, :name => "Gessertshausen", :area_code => "8238") + AreaCode.create(:country => germany, :name => "Langenneufnach", :area_code => "8239") + AreaCode.create(:country => germany, :name => "Buchloe", :area_code => "8241") + AreaCode.create(:country => germany, :name => "Fuchstal", :area_code => "8243") + AreaCode.create(:country => germany, :name => "Türkheim Wertach", :area_code => "8245") + AreaCode.create(:country => germany, :name => "Waal", :area_code => "8246") + AreaCode.create(:country => germany, :name => "Bad Wörishofen", :area_code => "8247") + AreaCode.create(:country => germany, :name => "Lamerdingen", :area_code => "8248") + AreaCode.create(:country => germany, :name => "Ettringen Wertach", :area_code => "8249") + AreaCode.create(:country => germany, :name => "Hilgertshausen-Tandern", :area_code => "8250") + AreaCode.create(:country => germany, :name => "Aichach", :area_code => "8251") + AreaCode.create(:country => germany, :name => "Schrobenhausen", :area_code => "8252") + AreaCode.create(:country => germany, :name => "Pöttmes", :area_code => "8253") + AreaCode.create(:country => germany, :name => "Altomünster", :area_code => "8254") + AreaCode.create(:country => germany, :name => "Inchenhofen", :area_code => "8257") + AreaCode.create(:country => germany, :name => "Sielenbach", :area_code => "8258") + AreaCode.create(:country => germany, :name => "Schiltberg", :area_code => "8259") + AreaCode.create(:country => germany, :name => "Mindelheim", :area_code => "8261") + AreaCode.create(:country => germany, :name => "Mittelneufnach", :area_code => "8262") + AreaCode.create(:country => germany, :name => "Breitenbrunn Schwab", :area_code => "8263") + AreaCode.create(:country => germany, :name => "Pfaffenhausen Schwab", :area_code => "8265") + AreaCode.create(:country => germany, :name => "Kirchheim i Schw", :area_code => "8266") + AreaCode.create(:country => germany, :name => "Dirlewang", :area_code => "8267") + AreaCode.create(:country => germany, :name => "Tussenhausen", :area_code => "8268") + AreaCode.create(:country => germany, :name => "Unteregg b Mindelheim", :area_code => "8269") + AreaCode.create(:country => germany, :name => "Meitingen", :area_code => "8271") + AreaCode.create(:country => germany, :name => "Wertingen", :area_code => "8272") + AreaCode.create(:country => germany, :name => "Nordendorf", :area_code => "8273") + AreaCode.create(:country => germany, :name => "Buttenwiesen", :area_code => "8274") + AreaCode.create(:country => germany, :name => "Baar Schwaben", :area_code => "8276") + AreaCode.create(:country => germany, :name => "Thannhausen Schwab", :area_code => "8281") + AreaCode.create(:country => germany, :name => "Krumbach Schwaben", :area_code => "8282") + AreaCode.create(:country => germany, :name => "Neuburg a d Kammel", :area_code => "8283") + AreaCode.create(:country => germany, :name => "Ziemetshausen", :area_code => "8284") + AreaCode.create(:country => germany, :name => "Burtenbach", :area_code => "8285") + AreaCode.create(:country => germany, :name => "Zusmarshausen", :area_code => "8291") + AreaCode.create(:country => germany, :name => "Dinkelscherben", :area_code => "8292") + AreaCode.create(:country => germany, :name => "Welden b Augsburg", :area_code => "8293") + AreaCode.create(:country => germany, :name => "Horgau", :area_code => "8294") + AreaCode.create(:country => germany, :name => "Altenmünster Schwab", :area_code => "8295") + AreaCode.create(:country => germany, :name => "Villenbach", :area_code => "8296") + AreaCode.create(:country => germany, :name => "Görisried", :area_code => "8302") + AreaCode.create(:country => germany, :name => "Waltenhofen", :area_code => "8303") + AreaCode.create(:country => germany, :name => "Wildpoldsried", :area_code => "8304") + AreaCode.create(:country => germany, :name => "Ronsberg", :area_code => "8306") + AreaCode.create(:country => germany, :name => "Kempten Allgäu", :area_code => "831") + AreaCode.create(:country => germany, :name => "Missen-Wilhams", :area_code => "8320") + AreaCode.create(:country => germany, :name => "Sonthofen", :area_code => "8321") + AreaCode.create(:country => germany, :name => "Oberstdorf", :area_code => "8322") + AreaCode.create(:country => germany, :name => "Immenstadt i Allgäu", :area_code => "8323") + AreaCode.create(:country => germany, :name => "Hindelang", :area_code => "8324") + AreaCode.create(:country => germany, :name => "Oberstaufen-Thalkirchdorf", :area_code => "8325") + AreaCode.create(:country => germany, :name => "Fischen i Allgäu", :area_code => "8326") + AreaCode.create(:country => germany, :name => "Rettenberg", :area_code => "8327") + AreaCode.create(:country => germany, :name => "Balderschwang", :area_code => "8328") + AreaCode.create(:country => germany, :name => "Riezlern (Österreich)", :area_code => "8329") + AreaCode.create(:country => germany, :name => "Legau", :area_code => "8330") + AreaCode.create(:country => germany, :name => "Memmingen", :area_code => "8331") + AreaCode.create(:country => germany, :name => "Ottobeuren", :area_code => "8332") + AreaCode.create(:country => germany, :name => "Babenhausen Schwab", :area_code => "8333") + AreaCode.create(:country => germany, :name => "Bad Grönenbach", :area_code => "8334") + AreaCode.create(:country => germany, :name => "Fellheim", :area_code => "8335") + AreaCode.create(:country => germany, :name => "Erkheim", :area_code => "8336") + AreaCode.create(:country => germany, :name => "Altenstadt Iller", :area_code => "8337") + AreaCode.create(:country => germany, :name => "Böhen", :area_code => "8338") + AreaCode.create(:country => germany, :name => "Baisweil", :area_code => "8340") + AreaCode.create(:country => germany, :name => "Kaufbeuren", :area_code => "8341") + AreaCode.create(:country => germany, :name => "Marktoberdorf", :area_code => "8342") + AreaCode.create(:country => germany, :name => "Aitrang", :area_code => "8343") + AreaCode.create(:country => germany, :name => "Westendorf b Kaufbeuren", :area_code => "8344") + AreaCode.create(:country => germany, :name => "Stöttwang", :area_code => "8345") + AreaCode.create(:country => germany, :name => "Pforzen", :area_code => "8346") + AreaCode.create(:country => germany, :name => "Friesenried", :area_code => "8347") + AreaCode.create(:country => germany, :name => "Bidingen", :area_code => "8348") + AreaCode.create(:country => germany, :name => "Stötten a Auerberg", :area_code => "8349") + AreaCode.create(:country => germany, :name => "Nesselwang", :area_code => "8361") + AreaCode.create(:country => germany, :name => "Füssen", :area_code => "8362") + AreaCode.create(:country => germany, :name => "Pfronten", :area_code => "8363") + AreaCode.create(:country => germany, :name => "Seeg", :area_code => "8364") + AreaCode.create(:country => germany, :name => "Wertach", :area_code => "8365") + AreaCode.create(:country => germany, :name => "Oy-Mittelberg", :area_code => "8366") + AreaCode.create(:country => germany, :name => "Roßhaupten Forggensee", :area_code => "8367") + AreaCode.create(:country => germany, :name => "Halblech", :area_code => "8368") + AreaCode.create(:country => germany, :name => "Rückholz", :area_code => "8369") + AreaCode.create(:country => germany, :name => "Wiggensbach", :area_code => "8370") + AreaCode.create(:country => germany, :name => "Obergünzburg", :area_code => "8372") + AreaCode.create(:country => germany, :name => "Altusried", :area_code => "8373") + AreaCode.create(:country => germany, :name => "Dietmannsried", :area_code => "8374") + AreaCode.create(:country => germany, :name => "Weitnau", :area_code => "8375") + AreaCode.create(:country => germany, :name => "Sulzberg Allgäu", :area_code => "8376") + AreaCode.create(:country => germany, :name => "Unterthingau", :area_code => "8377") + AreaCode.create(:country => germany, :name => "Buchenberg b Kempten", :area_code => "8378") + AreaCode.create(:country => germany, :name => "Waltenhofen-Oberdorf", :area_code => "8379") + AreaCode.create(:country => germany, :name => "Achberg", :area_code => "8380") + AreaCode.create(:country => germany, :name => "Lindenberg i Allgäu", :area_code => "8381") + AreaCode.create(:country => germany, :name => "Lindau Bodensee", :area_code => "8382") + AreaCode.create(:country => germany, :name => "Grünenbach Allgäu", :area_code => "8383") + AreaCode.create(:country => germany, :name => "Röthenbach Allgäu", :area_code => "8384") + AreaCode.create(:country => germany, :name => "Hergatz", :area_code => "8385") + AreaCode.create(:country => germany, :name => "Oberstaufen", :area_code => "8386") + AreaCode.create(:country => germany, :name => "Weiler-Simmerberg", :area_code => "8387") + AreaCode.create(:country => germany, :name => "Hergensweiler", :area_code => "8388") + AreaCode.create(:country => germany, :name => "Weissensberg", :area_code => "8389") + AreaCode.create(:country => germany, :name => "Markt Rettenbach", :area_code => "8392") + AreaCode.create(:country => germany, :name => "Holzgünz", :area_code => "8393") + AreaCode.create(:country => germany, :name => "Lautrach", :area_code => "8394") + AreaCode.create(:country => germany, :name => "Tannheim Württ", :area_code => "8395") + AreaCode.create(:country => germany, :name => "Münchsmünster", :area_code => "8402") + AreaCode.create(:country => germany, :name => "Pförring", :area_code => "8403") + AreaCode.create(:country => germany, :name => "Oberdolling", :area_code => "8404") + AreaCode.create(:country => germany, :name => "Stammham b Ingolstadt", :area_code => "8405") + AreaCode.create(:country => germany, :name => "Böhmfeld", :area_code => "8406") + AreaCode.create(:country => germany, :name => "Grossmehring", :area_code => "8407") + AreaCode.create(:country => germany, :name => "Ingolstadt Donau", :area_code => "841") + AreaCode.create(:country => germany, :name => "Eichstätt Bay", :area_code => "8421") + AreaCode.create(:country => germany, :name => "Dollnstein", :area_code => "8422") + AreaCode.create(:country => germany, :name => "Titting", :area_code => "8423") + AreaCode.create(:country => germany, :name => "Nassenfels", :area_code => "8424") + AreaCode.create(:country => germany, :name => "Walting Kr Eichstätt", :area_code => "8426") + AreaCode.create(:country => germany, :name => "Wellheim", :area_code => "8427") + AreaCode.create(:country => germany, :name => "Neuburg a d Donau", :area_code => "8431") + AreaCode.create(:country => germany, :name => "Burgheim", :area_code => "8432") + AreaCode.create(:country => germany, :name => "Königsmoos", :area_code => "8433") + AreaCode.create(:country => germany, :name => "Rennertshofen", :area_code => "8434") + AreaCode.create(:country => germany, :name => "Ehekirchen", :area_code => "8435") + AreaCode.create(:country => germany, :name => "Pfaffenhofen a d Ilm", :area_code => "8441") + AreaCode.create(:country => germany, :name => "Wolnzach", :area_code => "8442") + AreaCode.create(:country => germany, :name => "Hohenwart Paar", :area_code => "8443") + AreaCode.create(:country => germany, :name => "Schweitenkirchen", :area_code => "8444") + AreaCode.create(:country => germany, :name => "Gerolsbach", :area_code => "8445") + AreaCode.create(:country => germany, :name => "Pörnbach", :area_code => "8446") + AreaCode.create(:country => germany, :name => "Ingolstadt-Zuchering", :area_code => "8450") + AreaCode.create(:country => germany, :name => "Geisenfeld", :area_code => "8452") + AreaCode.create(:country => germany, :name => "Reichertshofen Oberbay", :area_code => "8453") + AreaCode.create(:country => germany, :name => "Karlshuld", :area_code => "8454") + AreaCode.create(:country => germany, :name => "Lenting", :area_code => "8456") + AreaCode.create(:country => germany, :name => "Vohburg a d Donau", :area_code => "8457") + AreaCode.create(:country => germany, :name => "Gaimersheim", :area_code => "8458") + AreaCode.create(:country => germany, :name => "Manching", :area_code => "8459") + AreaCode.create(:country => germany, :name => "Berching-Holnstein", :area_code => "8460") + AreaCode.create(:country => germany, :name => "Beilngries", :area_code => "8461") + AreaCode.create(:country => germany, :name => "Berching", :area_code => "8462") + AreaCode.create(:country => germany, :name => "Greding", :area_code => "8463") + AreaCode.create(:country => germany, :name => "Dietfurt a d Altmühl", :area_code => "8464") + AreaCode.create(:country => germany, :name => "Kipfenberg", :area_code => "8465") + AreaCode.create(:country => germany, :name => "Denkendorf Oberbay", :area_code => "8466") + AreaCode.create(:country => germany, :name => "Kinding", :area_code => "8467") + AreaCode.create(:country => germany, :name => "Altmannstein-Pondorf", :area_code => "8468") + AreaCode.create(:country => germany, :name => "Freystadt-Burggriesbach", :area_code => "8469") + AreaCode.create(:country => germany, :name => "Thyrnau", :area_code => "8501") + AreaCode.create(:country => germany, :name => "Fürstenzell", :area_code => "8502") + AreaCode.create(:country => germany, :name => "Neuhaus a Inn", :area_code => "8503") + AreaCode.create(:country => germany, :name => "Tittling", :area_code => "8504") + AreaCode.create(:country => germany, :name => "Hutthurm", :area_code => "8505") + AreaCode.create(:country => germany, :name => "Bad Höhenstadt", :area_code => "8506") + AreaCode.create(:country => germany, :name => "Neuburg a Inn", :area_code => "8507") + AreaCode.create(:country => germany, :name => "Ruderting", :area_code => "8509") + AreaCode.create(:country => germany, :name => "Passau", :area_code => "851") + AreaCode.create(:country => germany, :name => "Pocking", :area_code => "8531") + AreaCode.create(:country => germany, :name => "Griesbach i Rottal", :area_code => "8532") + AreaCode.create(:country => germany, :name => "Rotthalmünster", :area_code => "8533") + AreaCode.create(:country => germany, :name => "Tettenweis", :area_code => "8534") + AreaCode.create(:country => germany, :name => "Haarbach", :area_code => "8535") + AreaCode.create(:country => germany, :name => "Kößlarn", :area_code => "8536") + AreaCode.create(:country => germany, :name => "Bad Füssing-Aigen", :area_code => "8537") + AreaCode.create(:country => germany, :name => "Pocking-Hartkirchen", :area_code => "8538") + AreaCode.create(:country => germany, :name => "Vilshofen Niederbay", :area_code => "8541") + AreaCode.create(:country => germany, :name => "Ortenburg", :area_code => "8542") + AreaCode.create(:country => germany, :name => "Aidenbach", :area_code => "8543") + AreaCode.create(:country => germany, :name => "Eging a See", :area_code => "8544") + AreaCode.create(:country => germany, :name => "Hofkirchen Bay", :area_code => "8545") + AreaCode.create(:country => germany, :name => "Windorf-Otterskirchen", :area_code => "8546") + AreaCode.create(:country => germany, :name => "Osterhofen-Gergweis", :area_code => "8547") + AreaCode.create(:country => germany, :name => "Vilshofen-Sandbach", :area_code => "8548") + AreaCode.create(:country => germany, :name => "Vilshofen-Pleinting", :area_code => "8549") + AreaCode.create(:country => germany, :name => "Philippsreut", :area_code => "8550") + AreaCode.create(:country => germany, :name => "Freyung", :area_code => "8551") + AreaCode.create(:country => germany, :name => "Grafenau Niederbay", :area_code => "8552") + AreaCode.create(:country => germany, :name => "Spiegelau", :area_code => "8553") + AreaCode.create(:country => germany, :name => "Schönberg Niederbay", :area_code => "8554") + AreaCode.create(:country => germany, :name => "Perlesreut", :area_code => "8555") + AreaCode.create(:country => germany, :name => "Haidmühle", :area_code => "8556") + AreaCode.create(:country => germany, :name => "Mauth", :area_code => "8557") + AreaCode.create(:country => germany, :name => "Hohenau Niederbay", :area_code => "8558") + AreaCode.create(:country => germany, :name => "Pfarrkirchen Niederbay", :area_code => "8561") + AreaCode.create(:country => germany, :name => "Triftern", :area_code => "8562") + AreaCode.create(:country => germany, :name => "Bad Birnbach Rottal", :area_code => "8563") + AreaCode.create(:country => germany, :name => "Johanniskirchen", :area_code => "8564") + AreaCode.create(:country => germany, :name => "Dietersburg-Baumgarten", :area_code => "8565") + AreaCode.create(:country => germany, :name => "Simbach a Inn", :area_code => "8571") + AreaCode.create(:country => germany, :name => "Tann Niederbay", :area_code => "8572") + AreaCode.create(:country => germany, :name => "Ering", :area_code => "8573") + AreaCode.create(:country => germany, :name => "Wittibreut", :area_code => "8574") + AreaCode.create(:country => germany, :name => "Waldkirchen Niederbay", :area_code => "8581") + AreaCode.create(:country => germany, :name => "Röhrnbach", :area_code => "8582") + AreaCode.create(:country => germany, :name => "Neureichenau", :area_code => "8583") + AreaCode.create(:country => germany, :name => "Breitenberg Niederbay", :area_code => "8584") + AreaCode.create(:country => germany, :name => "Grainet", :area_code => "8585") + AreaCode.create(:country => germany, :name => "Hauzenberg", :area_code => "8586") + AreaCode.create(:country => germany, :name => "Obernzell", :area_code => "8591") + AreaCode.create(:country => germany, :name => "Wegscheid Niederbay", :area_code => "8592") + AreaCode.create(:country => germany, :name => "Untergriesbach", :area_code => "8593") + AreaCode.create(:country => germany, :name => "Traunstein", :area_code => "861") + AreaCode.create(:country => germany, :name => "Trostberg", :area_code => "8621") + AreaCode.create(:country => germany, :name => "Tacherting- Peterskirchen", :area_code => "8622") + AreaCode.create(:country => germany, :name => "Kirchweidach", :area_code => "8623") + AreaCode.create(:country => germany, :name => "Obing", :area_code => "8624") + AreaCode.create(:country => germany, :name => "Kienberg Oberbay", :area_code => "8628") + AreaCode.create(:country => germany, :name => "Palling", :area_code => "8629") + AreaCode.create(:country => germany, :name => "Oberneukirchen", :area_code => "8630") + AreaCode.create(:country => germany, :name => "Mühldorf a Inn", :area_code => "8631") + AreaCode.create(:country => germany, :name => "Tüßling", :area_code => "8633") + AreaCode.create(:country => germany, :name => "Garching a d Alz", :area_code => "8634") + AreaCode.create(:country => germany, :name => "Pleiskirchen", :area_code => "8635") + AreaCode.create(:country => germany, :name => "Ampfing", :area_code => "8636") + AreaCode.create(:country => germany, :name => "Lohkirchen", :area_code => "8637") + AreaCode.create(:country => germany, :name => "Waldkraiburg", :area_code => "8638") + AreaCode.create(:country => germany, :name => "Neumarkt-Sankt Veit", :area_code => "8639") + AreaCode.create(:country => germany, :name => "Reit Im Winkl", :area_code => "8640") + AreaCode.create(:country => germany, :name => "Grassau Kr Traunstein", :area_code => "8641") + AreaCode.create(:country => germany, :name => "Übersee", :area_code => "8642") + AreaCode.create(:country => germany, :name => "Schleching", :area_code => "8649") + AreaCode.create(:country => germany, :name => "Marktschellenberg", :area_code => "8650") + AreaCode.create(:country => germany, :name => "Bad Reichenhall", :area_code => "8651") + AreaCode.create(:country => germany, :name => "Berchtesgaden", :area_code => "8652") + AreaCode.create(:country => germany, :name => "Freilassing", :area_code => "8654") + AreaCode.create(:country => germany, :name => "Anger", :area_code => "8656") + AreaCode.create(:country => germany, :name => "Ramsau b Berchtesgaden", :area_code => "8657") + AreaCode.create(:country => germany, :name => "Grabenstätt Chiemsee", :area_code => "8661") + AreaCode.create(:country => germany, :name => "Siegsdorf Kr Traunstein", :area_code => "8662") + AreaCode.create(:country => germany, :name => "Ruhpolding", :area_code => "8663") + AreaCode.create(:country => germany, :name => "Chieming", :area_code => "8664") + AreaCode.create(:country => germany, :name => "Inzell", :area_code => "8665") + AreaCode.create(:country => germany, :name => "Teisendorf", :area_code => "8666") + AreaCode.create(:country => germany, :name => "Seeon-Seebruck", :area_code => "8667") + AreaCode.create(:country => germany, :name => "Traunreut", :area_code => "8669") + AreaCode.create(:country => germany, :name => "Reischach Kr Altötting", :area_code => "8670") + AreaCode.create(:country => germany, :name => "Altötting", :area_code => "8671") + AreaCode.create(:country => germany, :name => "Burghausen Salzach", :area_code => "8677") + AreaCode.create(:country => germany, :name => "Marktl", :area_code => "8678") + AreaCode.create(:country => germany, :name => "Burgkirchen a d Alz", :area_code => "8679") + AreaCode.create(:country => germany, :name => "Waging a See", :area_code => "8681") + AreaCode.create(:country => germany, :name => "Laufen Salzach", :area_code => "8682") + AreaCode.create(:country => germany, :name => "Tittmoning", :area_code => "8683") + AreaCode.create(:country => germany, :name => "Fridolfing", :area_code => "8684") + AreaCode.create(:country => germany, :name => "Kirchanschöring", :area_code => "8685") + AreaCode.create(:country => germany, :name => "Petting", :area_code => "8686") + AreaCode.create(:country => germany, :name => "Taching-Tengling", :area_code => "8687") + AreaCode.create(:country => germany, :name => "Wörth a d Isar", :area_code => "8702") + AreaCode.create(:country => germany, :name => "Essenbach", :area_code => "8703") + AreaCode.create(:country => germany, :name => "Altdorf-Pfettrach", :area_code => "8704") + AreaCode.create(:country => germany, :name => "Altfraunhofen", :area_code => "8705") + AreaCode.create(:country => germany, :name => "Vilsheim", :area_code => "8706") + AreaCode.create(:country => germany, :name => "Adlkofen", :area_code => "8707") + AreaCode.create(:country => germany, :name => "Weihmichl-Unterneuhausen", :area_code => "8708") + AreaCode.create(:country => germany, :name => "Eching Niederbay", :area_code => "8709") + AreaCode.create(:country => germany, :name => "Landshut", :area_code => "871") + AreaCode.create(:country => germany, :name => "Eggenfelden", :area_code => "8721") + AreaCode.create(:country => germany, :name => "Gangkofen", :area_code => "8722") + AreaCode.create(:country => germany, :name => "Arnstorf", :area_code => "8723") + AreaCode.create(:country => germany, :name => "Massing", :area_code => "8724") + AreaCode.create(:country => germany, :name => "Wurmannsquick", :area_code => "8725") + AreaCode.create(:country => germany, :name => "Schönau Niederbay", :area_code => "8726") + AreaCode.create(:country => germany, :name => "Falkenberg Niederbay", :area_code => "8727") + AreaCode.create(:country => germany, :name => "Geratskirchen", :area_code => "8728") + AreaCode.create(:country => germany, :name => "Dingolfing", :area_code => "8731") + AreaCode.create(:country => germany, :name => "Frontenhausen", :area_code => "8732") + AreaCode.create(:country => germany, :name => "Mengkofen", :area_code => "8733") + AreaCode.create(:country => germany, :name => "Reisbach Niederbay", :area_code => "8734") + AreaCode.create(:country => germany, :name => "Gangkofen-Kollbach", :area_code => "8735") + AreaCode.create(:country => germany, :name => "Vilsbiburg", :area_code => "8741") + AreaCode.create(:country => germany, :name => "Velden Vils", :area_code => "8742") + AreaCode.create(:country => germany, :name => "Geisenhausen", :area_code => "8743") + AreaCode.create(:country => germany, :name => "Gerzen", :area_code => "8744") + AreaCode.create(:country => germany, :name => "Bodenkirchen", :area_code => "8745") + AreaCode.create(:country => germany, :name => "Mainburg", :area_code => "8751") + AreaCode.create(:country => germany, :name => "Au i d Hallertau", :area_code => "8752") + AreaCode.create(:country => germany, :name => "Elsendorf Niederbay", :area_code => "8753") + AreaCode.create(:country => germany, :name => "Volkenschwand", :area_code => "8754") + AreaCode.create(:country => germany, :name => "Nandlstadt", :area_code => "8756") + AreaCode.create(:country => germany, :name => "Moosburg a d Isar", :area_code => "8761") + AreaCode.create(:country => germany, :name => "Wartenberg Oberbay", :area_code => "8762") + AreaCode.create(:country => germany, :name => "Mauern Kr Freising", :area_code => "8764") + AreaCode.create(:country => germany, :name => "Bruckberg Niederbay", :area_code => "8765") + AreaCode.create(:country => germany, :name => "Gammelsdorf", :area_code => "8766") + AreaCode.create(:country => germany, :name => "Ergoldsbach", :area_code => "8771") + AreaCode.create(:country => germany, :name => "Mallersdorf-Pfaffenberg", :area_code => "8772") + AreaCode.create(:country => germany, :name => "Neufahrn i NB", :area_code => "8773") + AreaCode.create(:country => germany, :name => "Bayerbach b Ergoldsbach", :area_code => "8774") + AreaCode.create(:country => germany, :name => "Rottenburg a d Laaber", :area_code => "8781") + AreaCode.create(:country => germany, :name => "Pfeffenhausen", :area_code => "8782") + AreaCode.create(:country => germany, :name => "Rohr i NB", :area_code => "8783") + AreaCode.create(:country => germany, :name => "Hohenthann", :area_code => "8784") + AreaCode.create(:country => germany, :name => "Rottenburg-Oberroning", :area_code => "8785") + AreaCode.create(:country => germany, :name => "Seeshaupt", :area_code => "8801") + AreaCode.create(:country => germany, :name => "Huglfing", :area_code => "8802") + AreaCode.create(:country => germany, :name => "Peissenberg", :area_code => "8803") + AreaCode.create(:country => germany, :name => "Hohenpeissenberg", :area_code => "8805") + AreaCode.create(:country => germany, :name => "Utting a Ammersee", :area_code => "8806") + AreaCode.create(:country => germany, :name => "Dießen a Ammersee", :area_code => "8807") + AreaCode.create(:country => germany, :name => "Pähl", :area_code => "8808") + AreaCode.create(:country => germany, :name => "Wessobrunn", :area_code => "8809") + AreaCode.create(:country => germany, :name => "Weilheim i OB", :area_code => "881") + AreaCode.create(:country => germany, :name => "Garmisch-Partenkirchen", :area_code => "8821") + AreaCode.create(:country => germany, :name => "Oberammergau", :area_code => "8822") + AreaCode.create(:country => germany, :name => "Mittenwald", :area_code => "8823") + AreaCode.create(:country => germany, :name => "Oberau Loisach", :area_code => "8824") + AreaCode.create(:country => germany, :name => "Krün", :area_code => "8825") + AreaCode.create(:country => germany, :name => "Murnau a Staffelsee", :area_code => "8841") + AreaCode.create(:country => germany, :name => "Bad Kohlgrub", :area_code => "8845") + AreaCode.create(:country => germany, :name => "Uffing a Staffelsee", :area_code => "8846") + AreaCode.create(:country => germany, :name => "Obersöchering", :area_code => "8847") + AreaCode.create(:country => germany, :name => "Kochel a See", :area_code => "8851") + AreaCode.create(:country => germany, :name => "Penzberg", :area_code => "8856") + AreaCode.create(:country => germany, :name => "Benediktbeuern", :area_code => "8857") + AreaCode.create(:country => germany, :name => "Kochel-Walchensee", :area_code => "8858") + AreaCode.create(:country => germany, :name => "Bernbeuren", :area_code => "8860") + AreaCode.create(:country => germany, :name => "Schongau", :area_code => "8861") + AreaCode.create(:country => germany, :name => "Steingaden Oberbay", :area_code => "8862") + AreaCode.create(:country => germany, :name => "Rottenbuch Oberbay", :area_code => "8867") + AreaCode.create(:country => germany, :name => "Schwabsoien", :area_code => "8868") + AreaCode.create(:country => germany, :name => "Kinsau", :area_code => "8869") + AreaCode.create(:country => germany, :name => "München", :area_code => "89") + AreaCode.create(:country => germany, :name => "Donauwörth", :area_code => "906") + AreaCode.create(:country => germany, :name => "Tapfheim", :area_code => "9070") + AreaCode.create(:country => germany, :name => "Dillingen a d Donau", :area_code => "9071") + AreaCode.create(:country => germany, :name => "Lauingen Donau", :area_code => "9072") + AreaCode.create(:country => germany, :name => "Gundelfingen a d Donau", :area_code => "9073") + AreaCode.create(:country => germany, :name => "Höchstädt a d Donau", :area_code => "9074") + AreaCode.create(:country => germany, :name => "Glött", :area_code => "9075") + AreaCode.create(:country => germany, :name => "Wittislingen", :area_code => "9076") + AreaCode.create(:country => germany, :name => "Bachhagel", :area_code => "9077") + AreaCode.create(:country => germany, :name => "Mertingen", :area_code => "9078") + AreaCode.create(:country => germany, :name => "Harburg Schwaben", :area_code => "9080") + AreaCode.create(:country => germany, :name => "Nördlingen", :area_code => "9081") + AreaCode.create(:country => germany, :name => "Oettingen i Bay", :area_code => "9082") + AreaCode.create(:country => germany, :name => "Möttingen", :area_code => "9083") + AreaCode.create(:country => germany, :name => "Bissingen Schwab", :area_code => "9084") + AreaCode.create(:country => germany, :name => "Alerheim", :area_code => "9085") + AreaCode.create(:country => germany, :name => "Fremdingen", :area_code => "9086") + AreaCode.create(:country => germany, :name => "Marktoffingen", :area_code => "9087") + AreaCode.create(:country => germany, :name => "Mönchsdeggingen", :area_code => "9088") + AreaCode.create(:country => germany, :name => "Bissingen-Unterringingen", :area_code => "9089") + AreaCode.create(:country => germany, :name => "Rain Lech", :area_code => "9090") + AreaCode.create(:country => germany, :name => "Monheim Schwab", :area_code => "9091") + AreaCode.create(:country => germany, :name => "Wemding", :area_code => "9092") + AreaCode.create(:country => germany, :name => "Polsingen", :area_code => "9093") + AreaCode.create(:country => germany, :name => "Tagmersheim", :area_code => "9094") + AreaCode.create(:country => germany, :name => "Marxheim", :area_code => "9097") + AreaCode.create(:country => germany, :name => "Kaisheim", :area_code => "9099") + AreaCode.create(:country => germany, :name => "Langenzenn", :area_code => "9101") + AreaCode.create(:country => germany, :name => "Wilhermsdorf", :area_code => "9102") + AreaCode.create(:country => germany, :name => "Cadolzburg", :area_code => "9103") + AreaCode.create(:country => germany, :name => "Emskirchen", :area_code => "9104") + AreaCode.create(:country => germany, :name => "Grosshabersdorf", :area_code => "9105") + AreaCode.create(:country => germany, :name => "Markt Erlbach", :area_code => "9106") + AreaCode.create(:country => germany, :name => "Trautskirchen", :area_code => "9107") + AreaCode.create(:country => germany, :name => "Nürnberg", :area_code => "911") + AreaCode.create(:country => germany, :name => "Leinburg", :area_code => "9120") + AreaCode.create(:country => germany, :name => "Schwabach", :area_code => "9122") + AreaCode.create(:country => germany, :name => "Lauf a d Pegnitz", :area_code => "9123") + AreaCode.create(:country => germany, :name => "Eckental", :area_code => "9126") + AreaCode.create(:country => germany, :name => "Rosstal Mittelfr", :area_code => "9127") + AreaCode.create(:country => germany, :name => "Feucht", :area_code => "9128") + AreaCode.create(:country => germany, :name => "Wendelstein", :area_code => "9129") + AreaCode.create(:country => germany, :name => "Erlangen", :area_code => "9131") + AreaCode.create(:country => germany, :name => "Herzogenaurach", :area_code => "9132") + AreaCode.create(:country => germany, :name => "Baiersdorf Mittelfr", :area_code => "9133") + AreaCode.create(:country => germany, :name => "Neunkirchen a Brand", :area_code => "9134") + AreaCode.create(:country => germany, :name => "Heßdorf Mittelfr", :area_code => "9135") + AreaCode.create(:country => germany, :name => "Weißenburg i Bay", :area_code => "9141") + AreaCode.create(:country => germany, :name => "Treuchtlingen", :area_code => "9142") + AreaCode.create(:country => germany, :name => "Pappenheim Mittelfr", :area_code => "9143") + AreaCode.create(:country => germany, :name => "Pleinfeld", :area_code => "9144") + AreaCode.create(:country => germany, :name => "Solnhofen", :area_code => "9145") + AreaCode.create(:country => germany, :name => "Markt Berolzheim", :area_code => "9146") + AreaCode.create(:country => germany, :name => "Nennslingen", :area_code => "9147") + AreaCode.create(:country => germany, :name => "Ettenstatt", :area_code => "9148") + AreaCode.create(:country => germany, :name => "Weissenburg-Suffersheim", :area_code => "9149") + AreaCode.create(:country => germany, :name => "Hersbruck", :area_code => "9151") + AreaCode.create(:country => germany, :name => "Hartenstein Mittelfr", :area_code => "9152") + AreaCode.create(:country => germany, :name => "Schnaittach", :area_code => "9153") + AreaCode.create(:country => germany, :name => "Pommelsbrunn", :area_code => "9154") + AreaCode.create(:country => germany, :name => "Simmelsdorf", :area_code => "9155") + AreaCode.create(:country => germany, :name => "Neuhaus a d Pegnitz", :area_code => "9156") + AreaCode.create(:country => germany, :name => "Alfeld Mittelfr", :area_code => "9157") + AreaCode.create(:country => germany, :name => "Offenhausen Mittelfr", :area_code => "9158") + AreaCode.create(:country => germany, :name => "Neustadt a d Aisch", :area_code => "9161") + AreaCode.create(:country => germany, :name => "Scheinfeld", :area_code => "9162") + AreaCode.create(:country => germany, :name => "Dachsbach", :area_code => "9163") + AreaCode.create(:country => germany, :name => "Langenfeld Mittelfr", :area_code => "9164") + AreaCode.create(:country => germany, :name => "Sugenheim", :area_code => "9165") + AreaCode.create(:country => germany, :name => "Münchsteinach", :area_code => "9166") + AreaCode.create(:country => germany, :name => "Oberscheinfeld", :area_code => "9167") + AreaCode.create(:country => germany, :name => "Schwanstetten", :area_code => "9170") + AreaCode.create(:country => germany, :name => "Roth Mittelfr", :area_code => "9171") + AreaCode.create(:country => germany, :name => "Georgensgmünd", :area_code => "9172") + AreaCode.create(:country => germany, :name => "Thalmässing", :area_code => "9173") + AreaCode.create(:country => germany, :name => "Hilpoltstein", :area_code => "9174") + AreaCode.create(:country => germany, :name => "Spalt", :area_code => "9175") + AreaCode.create(:country => germany, :name => "Allersberg", :area_code => "9176") + AreaCode.create(:country => germany, :name => "Heideck", :area_code => "9177") + AreaCode.create(:country => germany, :name => "Abenberg Mittelfr", :area_code => "9178") + AreaCode.create(:country => germany, :name => "Freystadt", :area_code => "9179") + AreaCode.create(:country => germany, :name => "Pyrbaum", :area_code => "9180") + AreaCode.create(:country => germany, :name => "Neumarkt i d Opf", :area_code => "9181") + AreaCode.create(:country => germany, :name => "Velburg", :area_code => "9182") + AreaCode.create(:country => germany, :name => "Burgthann", :area_code => "9183") + AreaCode.create(:country => germany, :name => "Deining Oberpf", :area_code => "9184") + AreaCode.create(:country => germany, :name => "Mühlhausen Oberpf", :area_code => "9185") + AreaCode.create(:country => germany, :name => "Lauterhofen Oberpf", :area_code => "9186") + AreaCode.create(:country => germany, :name => "Altdorf b Nürnberg", :area_code => "9187") + AreaCode.create(:country => germany, :name => "Postbauer-Heng", :area_code => "9188") + AreaCode.create(:country => germany, :name => "Berg b Neumarkt i d Opf", :area_code => "9189") + AreaCode.create(:country => germany, :name => "Heroldsbach", :area_code => "9190") + AreaCode.create(:country => germany, :name => "Forchheim Oberfr", :area_code => "9191") + AreaCode.create(:country => germany, :name => "Gräfenberg", :area_code => "9192") + AreaCode.create(:country => germany, :name => "Höchstadt a d Aisch", :area_code => "9193") + AreaCode.create(:country => germany, :name => "Ebermannstadt", :area_code => "9194") + AreaCode.create(:country => germany, :name => "Adelsdorf Mittelfr", :area_code => "9195") + AreaCode.create(:country => germany, :name => "Wiesenttal", :area_code => "9196") + AreaCode.create(:country => germany, :name => "Egloffstein", :area_code => "9197") + AreaCode.create(:country => germany, :name => "Heiligenstadt i Ofr", :area_code => "9198") + AreaCode.create(:country => germany, :name => "Kunreuth", :area_code => "9199") + AreaCode.create(:country => germany, :name => "Gesees", :area_code => "9201") + AreaCode.create(:country => germany, :name => "Waischenfeld", :area_code => "9202") + AreaCode.create(:country => germany, :name => "Neudrossenfeld", :area_code => "9203") + AreaCode.create(:country => germany, :name => "Plankenfels", :area_code => "9204") + AreaCode.create(:country => germany, :name => "Vorbach", :area_code => "9205") + AreaCode.create(:country => germany, :name => "Mistelgau-Obernsees", :area_code => "9206") + AreaCode.create(:country => germany, :name => "Königsfeld Oberfr", :area_code => "9207") + AreaCode.create(:country => germany, :name => "Bindlach", :area_code => "9208") + AreaCode.create(:country => germany, :name => "Emtmannsberg", :area_code => "9209") + AreaCode.create(:country => germany, :name => "Bayreuth", :area_code => "921") + AreaCode.create(:country => germany, :name => "Kasendorf-Azendorf", :area_code => "9220") + AreaCode.create(:country => germany, :name => "Kulmbach", :area_code => "9221") + AreaCode.create(:country => germany, :name => "Presseck", :area_code => "9222") + AreaCode.create(:country => germany, :name => "Rugendorf", :area_code => "9223") + AreaCode.create(:country => germany, :name => "Stadtsteinach", :area_code => "9225") + AreaCode.create(:country => germany, :name => "Neuenmarkt", :area_code => "9227") + AreaCode.create(:country => germany, :name => "Thurnau", :area_code => "9228") + AreaCode.create(:country => germany, :name => "Mainleus", :area_code => "9229") + AreaCode.create(:country => germany, :name => "Marktredwitz", :area_code => "9231") + AreaCode.create(:country => germany, :name => "Wunsiedel", :area_code => "9232") + AreaCode.create(:country => germany, :name => "Arzberg Oberfr", :area_code => "9233") + AreaCode.create(:country => germany, :name => "Neusorg", :area_code => "9234") + AreaCode.create(:country => germany, :name => "Thierstein", :area_code => "9235") + AreaCode.create(:country => germany, :name => "Nagel", :area_code => "9236") + AreaCode.create(:country => germany, :name => "Röslau", :area_code => "9238") + AreaCode.create(:country => germany, :name => "Pegnitz", :area_code => "9241") + AreaCode.create(:country => germany, :name => "Gößweinstein", :area_code => "9242") + AreaCode.create(:country => germany, :name => "Pottenstein", :area_code => "9243") + AreaCode.create(:country => germany, :name => "Betzenstein", :area_code => "9244") + AreaCode.create(:country => germany, :name => "Obertrubach", :area_code => "9245") + AreaCode.create(:country => germany, :name => "Pegnitz-Trockau", :area_code => "9246") + AreaCode.create(:country => germany, :name => "Münchberg", :area_code => "9251") + AreaCode.create(:country => germany, :name => "Helmbrechts", :area_code => "9252") + AreaCode.create(:country => germany, :name => "Weissenstadt", :area_code => "9253") + AreaCode.create(:country => germany, :name => "Gefrees", :area_code => "9254") + AreaCode.create(:country => germany, :name => "Marktleugast", :area_code => "9255") + AreaCode.create(:country => germany, :name => "Stammbach", :area_code => "9256") + AreaCode.create(:country => germany, :name => "Zell Oberfr", :area_code => "9257") + AreaCode.create(:country => germany, :name => "Wilhelmsthal Oberfr", :area_code => "9260") + AreaCode.create(:country => germany, :name => "Kronach", :area_code => "9261") + AreaCode.create(:country => germany, :name => "Wallenfels", :area_code => "9262") + AreaCode.create(:country => germany, :name => "Ludwigsstadt", :area_code => "9263") + AreaCode.create(:country => germany, :name => "Küps", :area_code => "9264") + AreaCode.create(:country => germany, :name => "Pressig", :area_code => "9265") + AreaCode.create(:country => germany, :name => "Mitwitz", :area_code => "9266") + AreaCode.create(:country => germany, :name => "Nordhalben", :area_code => "9267") + AreaCode.create(:country => germany, :name => "Teuschnitz", :area_code => "9268") + AreaCode.create(:country => germany, :name => "Tettau Kr Kronach", :area_code => "9269") + AreaCode.create(:country => germany, :name => "Creussen", :area_code => "9270") + AreaCode.create(:country => germany, :name => "Thurnau-Alladorf", :area_code => "9271") + AreaCode.create(:country => germany, :name => "Fichtelberg", :area_code => "9272") + AreaCode.create(:country => germany, :name => "Bad Berneck i Fichtelgebirge", :area_code => "9273") + AreaCode.create(:country => germany, :name => "Hollfeld", :area_code => "9274") + AreaCode.create(:country => germany, :name => "Speichersdorf", :area_code => "9275") + AreaCode.create(:country => germany, :name => "Bischofsgrün", :area_code => "9276") + AreaCode.create(:country => germany, :name => "Warmensteinach", :area_code => "9277") + AreaCode.create(:country => germany, :name => "Weidenberg", :area_code => "9278") + AreaCode.create(:country => germany, :name => "Mistelgau", :area_code => "9279") + AreaCode.create(:country => germany, :name => "Selbitz Oberfr", :area_code => "9280") + AreaCode.create(:country => germany, :name => "Hof Saale", :area_code => "9281") + AreaCode.create(:country => germany, :name => "Naila", :area_code => "9282") + AreaCode.create(:country => germany, :name => "Rehau", :area_code => "9283") + AreaCode.create(:country => germany, :name => "Schwarzenbach a d Saale", :area_code => "9284") + AreaCode.create(:country => germany, :name => "Kirchenlamitz", :area_code => "9285") + AreaCode.create(:country => germany, :name => "Oberkotzau", :area_code => "9286") + AreaCode.create(:country => germany, :name => "Selb", :area_code => "9287") + AreaCode.create(:country => germany, :name => "Bad Steben", :area_code => "9288") + AreaCode.create(:country => germany, :name => "Schwarzenbach a Wald", :area_code => "9289") + AreaCode.create(:country => germany, :name => "Konradsreuth", :area_code => "9292") + AreaCode.create(:country => germany, :name => "Berg Oberfr", :area_code => "9293") + AreaCode.create(:country => germany, :name => "Regnitzlosau", :area_code => "9294") + AreaCode.create(:country => germany, :name => "Töpen", :area_code => "9295") + AreaCode.create(:country => germany, :name => "Rottendorf Unterfr", :area_code => "9302") + AreaCode.create(:country => germany, :name => "Eibelstadt", :area_code => "9303") + AreaCode.create(:country => germany, :name => "Estenfeld", :area_code => "9305") + AreaCode.create(:country => germany, :name => "Kist", :area_code => "9306") + AreaCode.create(:country => germany, :name => "Altertheim", :area_code => "9307") + AreaCode.create(:country => germany, :name => "Würzburg", :area_code => "931") + AreaCode.create(:country => germany, :name => "Kitzingen", :area_code => "9321") + AreaCode.create(:country => germany, :name => "Iphofen", :area_code => "9323") + AreaCode.create(:country => germany, :name => "Dettelbach", :area_code => "9324") + AreaCode.create(:country => germany, :name => "Kleinlangheim", :area_code => "9325") + AreaCode.create(:country => germany, :name => "Markt Einersheim", :area_code => "9326") + AreaCode.create(:country => germany, :name => "Ochsenfurt", :area_code => "9331") + AreaCode.create(:country => germany, :name => "Marktbreit", :area_code => "9332") + AreaCode.create(:country => germany, :name => "Sommerhausen", :area_code => "9333") + AreaCode.create(:country => germany, :name => "Giebelstadt", :area_code => "9334") + AreaCode.create(:country => germany, :name => "Aub Kr Würzburg", :area_code => "9335") + AreaCode.create(:country => germany, :name => "Bütthard", :area_code => "9336") + AreaCode.create(:country => germany, :name => "Gaukönigshofen", :area_code => "9337") + AreaCode.create(:country => germany, :name => "Röttingen Unterfr", :area_code => "9338") + AreaCode.create(:country => germany, :name => "Ippesheim", :area_code => "9339") + AreaCode.create(:country => germany, :name => "Königheim-Brehmen", :area_code => "9340") + AreaCode.create(:country => germany, :name => "Tauberbischofsheim", :area_code => "9341") + AreaCode.create(:country => germany, :name => "Wertheim", :area_code => "9342") + AreaCode.create(:country => germany, :name => "Lauda-Königshofen", :area_code => "9343") + AreaCode.create(:country => germany, :name => "Gerchsheim", :area_code => "9344") + AreaCode.create(:country => germany, :name => "Külsheim Baden", :area_code => "9345") + AreaCode.create(:country => germany, :name => "Grünsfeld", :area_code => "9346") + AreaCode.create(:country => germany, :name => "Wittighausen", :area_code => "9347") + AreaCode.create(:country => germany, :name => "Werbach-Gamburg", :area_code => "9348") + AreaCode.create(:country => germany, :name => "Werbach-Wenkheim", :area_code => "9349") + AreaCode.create(:country => germany, :name => "Eussenheim-Hundsbach", :area_code => "9350") + AreaCode.create(:country => germany, :name => "Gemünden a Main", :area_code => "9351") + AreaCode.create(:country => germany, :name => "Lohr a Main", :area_code => "9352") + AreaCode.create(:country => germany, :name => "Karlstadt", :area_code => "9353") + AreaCode.create(:country => germany, :name => "Rieneck", :area_code => "9354") + AreaCode.create(:country => germany, :name => "Frammersbach", :area_code => "9355") + AreaCode.create(:country => germany, :name => "Burgsinn", :area_code => "9356") + AreaCode.create(:country => germany, :name => "Gräfendorf Bay", :area_code => "9357") + AreaCode.create(:country => germany, :name => "Gössenheim", :area_code => "9358") + AreaCode.create(:country => germany, :name => "Karlstadt-Wiesenfeld", :area_code => "9359") + AreaCode.create(:country => germany, :name => "Thüngen", :area_code => "9360") + AreaCode.create(:country => germany, :name => "Arnstein Unterfr", :area_code => "9363") + AreaCode.create(:country => germany, :name => "Zellingen", :area_code => "9364") + AreaCode.create(:country => germany, :name => "Rimpar", :area_code => "9365") + AreaCode.create(:country => germany, :name => "Geroldshausen Unterfr", :area_code => "9366") + AreaCode.create(:country => germany, :name => "Unterpleichfeld", :area_code => "9367") + AreaCode.create(:country => germany, :name => "Uettingen", :area_code => "9369") + AreaCode.create(:country => germany, :name => "Miltenberg", :area_code => "9371") + AreaCode.create(:country => germany, :name => "Klingenberg a Main", :area_code => "9372") + AreaCode.create(:country => germany, :name => "Amorbach", :area_code => "9373") + AreaCode.create(:country => germany, :name => "Eschau", :area_code => "9374") + AreaCode.create(:country => germany, :name => "Freudenberg Baden", :area_code => "9375") + AreaCode.create(:country => germany, :name => "Collenberg", :area_code => "9376") + AreaCode.create(:country => germany, :name => "Freudenberg-Boxtal", :area_code => "9377") + AreaCode.create(:country => germany, :name => "Eichenbühl-Riedern", :area_code => "9378") + AreaCode.create(:country => germany, :name => "Volkach", :area_code => "9381") + AreaCode.create(:country => germany, :name => "Gerolzhofen", :area_code => "9382") + AreaCode.create(:country => germany, :name => "Wiesentheid", :area_code => "9383") + AreaCode.create(:country => germany, :name => "Schwanfeld", :area_code => "9384") + AreaCode.create(:country => germany, :name => "Kolitzheim", :area_code => "9385") + AreaCode.create(:country => germany, :name => "Prosselsheim", :area_code => "9386") + AreaCode.create(:country => germany, :name => "Marktheidenfeld", :area_code => "9391") + AreaCode.create(:country => germany, :name => "Faulbach Unterfr", :area_code => "9392") + AreaCode.create(:country => germany, :name => "Rothenfels Unterfr", :area_code => "9393") + AreaCode.create(:country => germany, :name => "Esselbach", :area_code => "9394") + AreaCode.create(:country => germany, :name => "Triefenstein", :area_code => "9395") + AreaCode.create(:country => germany, :name => "Urspringen b Lohr", :area_code => "9396") + AreaCode.create(:country => germany, :name => "Wertheim-Dertingen", :area_code => "9397") + AreaCode.create(:country => germany, :name => "Birkenfeld b Würzburg", :area_code => "9398") + AreaCode.create(:country => germany, :name => "Neutraubling", :area_code => "9401") + AreaCode.create(:country => germany, :name => "Regenstauf", :area_code => "9402") + AreaCode.create(:country => germany, :name => "Donaustauf", :area_code => "9403") + AreaCode.create(:country => germany, :name => "Nittendorf", :area_code => "9404") + AreaCode.create(:country => germany, :name => "Bad Abbach", :area_code => "9405") + AreaCode.create(:country => germany, :name => "Mintraching", :area_code => "9406") + AreaCode.create(:country => germany, :name => "Wenzenbach", :area_code => "9407") + AreaCode.create(:country => germany, :name => "Altenthann", :area_code => "9408") + AreaCode.create(:country => germany, :name => "Pielenhofen", :area_code => "9409") + AreaCode.create(:country => germany, :name => "Regensburg", :area_code => "941") + AreaCode.create(:country => germany, :name => "Feldkirchen Niederbay", :area_code => "9420") + AreaCode.create(:country => germany, :name => "Straubing", :area_code => "9421") + AreaCode.create(:country => germany, :name => "Bogen Niederbay", :area_code => "9422") + AreaCode.create(:country => germany, :name => "Geiselhöring", :area_code => "9423") + AreaCode.create(:country => germany, :name => "Strasskirchen", :area_code => "9424") + AreaCode.create(:country => germany, :name => "Oberschneiding", :area_code => "9426") + AreaCode.create(:country => germany, :name => "Leiblfing", :area_code => "9427") + AreaCode.create(:country => germany, :name => "Kirchroth", :area_code => "9428") + AreaCode.create(:country => germany, :name => "Rain Niederbay", :area_code => "9429") + AreaCode.create(:country => germany, :name => "Schwandorf", :area_code => "9431") + AreaCode.create(:country => germany, :name => "Nabburg", :area_code => "9433") + AreaCode.create(:country => germany, :name => "Bodenwöhr", :area_code => "9434") + AreaCode.create(:country => germany, :name => "Schwarzenfeld", :area_code => "9435") + AreaCode.create(:country => germany, :name => "Nittenau", :area_code => "9436") + AreaCode.create(:country => germany, :name => "Fensterbach", :area_code => "9438") + AreaCode.create(:country => germany, :name => "Neunburg-Kemnath", :area_code => "9439") + AreaCode.create(:country => germany, :name => "Kelheim", :area_code => "9441") + AreaCode.create(:country => germany, :name => "Riedenburg", :area_code => "9442") + AreaCode.create(:country => germany, :name => "Abensberg", :area_code => "9443") + AreaCode.create(:country => germany, :name => "Siegenburg", :area_code => "9444") + AreaCode.create(:country => germany, :name => "Neustadt a d Donau", :area_code => "9445") + AreaCode.create(:country => germany, :name => "Altmannstein", :area_code => "9446") + AreaCode.create(:country => germany, :name => "Essing", :area_code => "9447") + AreaCode.create(:country => germany, :name => "Hausen Niederbay", :area_code => "9448") + AreaCode.create(:country => germany, :name => "Schierling", :area_code => "9451") + AreaCode.create(:country => germany, :name => "Langquaid", :area_code => "9452") + AreaCode.create(:country => germany, :name => "Thalmassing", :area_code => "9453") + AreaCode.create(:country => germany, :name => "Aufhausen Oberpf", :area_code => "9454") + AreaCode.create(:country => germany, :name => "Roding", :area_code => "9461") + AreaCode.create(:country => germany, :name => "Falkenstein Oberpf", :area_code => "9462") + AreaCode.create(:country => germany, :name => "Wald Oberpf", :area_code => "9463") + AreaCode.create(:country => germany, :name => "Walderbach", :area_code => "9464") + AreaCode.create(:country => germany, :name => "Neukirchen-Balbini", :area_code => "9465") + AreaCode.create(:country => germany, :name => "Stamsried", :area_code => "9466") + AreaCode.create(:country => germany, :name => "Michelsneukirchen", :area_code => "9467") + AreaCode.create(:country => germany, :name => "Zell Oberpf", :area_code => "9468") + AreaCode.create(:country => germany, :name => "Roding-Neubäu", :area_code => "9469") + AreaCode.create(:country => germany, :name => "Burglengenfeld", :area_code => "9471") + AreaCode.create(:country => germany, :name => "Hohenfels Oberpf", :area_code => "9472") + AreaCode.create(:country => germany, :name => "Kallmünz", :area_code => "9473") + AreaCode.create(:country => germany, :name => "Schmidmühlen", :area_code => "9474") + AreaCode.create(:country => germany, :name => "Sünching", :area_code => "9480") + AreaCode.create(:country => germany, :name => "Pfatter", :area_code => "9481") + AreaCode.create(:country => germany, :name => "Wörth a d Donau", :area_code => "9482") + AreaCode.create(:country => germany, :name => "Brennberg", :area_code => "9484") + AreaCode.create(:country => germany, :name => "Hemau", :area_code => "9491") + AreaCode.create(:country => germany, :name => "Parsberg", :area_code => "9492") + AreaCode.create(:country => germany, :name => "Beratzhausen", :area_code => "9493") + AreaCode.create(:country => germany, :name => "Breitenbrunn Oberpf", :area_code => "9495") + AreaCode.create(:country => germany, :name => "Seubersdorf i d Opf", :area_code => "9497") + AreaCode.create(:country => germany, :name => "Laaber", :area_code => "9498") + AreaCode.create(:country => germany, :name => "Painten", :area_code => "9499") + AreaCode.create(:country => germany, :name => "Frensdorf", :area_code => "9502") + AreaCode.create(:country => germany, :name => "Oberhaid Oberfr", :area_code => "9503") + AreaCode.create(:country => germany, :name => "Stadelhofen", :area_code => "9504") + AreaCode.create(:country => germany, :name => "Litzendorf", :area_code => "9505") + AreaCode.create(:country => germany, :name => "Bamberg", :area_code => "951") + AreaCode.create(:country => germany, :name => "Hassfurt", :area_code => "9521") + AreaCode.create(:country => germany, :name => "Eltmann", :area_code => "9522") + AreaCode.create(:country => germany, :name => "Hofheim i Ufr", :area_code => "9523") + AreaCode.create(:country => germany, :name => "Zeil a Main", :area_code => "9524") + AreaCode.create(:country => germany, :name => "Königsberg i Bay", :area_code => "9525") + AreaCode.create(:country => germany, :name => "Riedbach", :area_code => "9526") + AreaCode.create(:country => germany, :name => "Knetzgau", :area_code => "9527") + AreaCode.create(:country => germany, :name => "Donnersdorf", :area_code => "9528") + AreaCode.create(:country => germany, :name => "Oberaurach", :area_code => "9529") + AreaCode.create(:country => germany, :name => "Ebern", :area_code => "9531") + AreaCode.create(:country => germany, :name => "Maroldsweisach", :area_code => "9532") + AreaCode.create(:country => germany, :name => "Untermerzbach", :area_code => "9533") + AreaCode.create(:country => germany, :name => "Burgpreppach", :area_code => "9534") + AreaCode.create(:country => germany, :name => "Pfarrweisach", :area_code => "9535") + AreaCode.create(:country => germany, :name => "Kirchlauter", :area_code => "9536") + AreaCode.create(:country => germany, :name => "Schesslitz", :area_code => "9542") + AreaCode.create(:country => germany, :name => "Hirschaid", :area_code => "9543") + AreaCode.create(:country => germany, :name => "Baunach", :area_code => "9544") + AreaCode.create(:country => germany, :name => "Buttenheim", :area_code => "9545") + AreaCode.create(:country => germany, :name => "Burgebrach", :area_code => "9546") + AreaCode.create(:country => germany, :name => "Zapfendorf", :area_code => "9547") + AreaCode.create(:country => germany, :name => "Mühlhausen Mittelfr", :area_code => "9548") + AreaCode.create(:country => germany, :name => "Lisberg", :area_code => "9549") + AreaCode.create(:country => germany, :name => "Burgwindheim", :area_code => "9551") + AreaCode.create(:country => germany, :name => "Burghaslach", :area_code => "9552") + AreaCode.create(:country => germany, :name => "Ebrach Oberfr", :area_code => "9553") + AreaCode.create(:country => germany, :name => "Untersteinbach Unterfr", :area_code => "9554") + AreaCode.create(:country => germany, :name => "Schlüsselfeld-Aschbach", :area_code => "9555") + AreaCode.create(:country => germany, :name => "Geiselwind", :area_code => "9556") + AreaCode.create(:country => germany, :name => "Grub a Forst", :area_code => "9560") + AreaCode.create(:country => germany, :name => "Coburg", :area_code => "9561") + AreaCode.create(:country => germany, :name => "Sonnefeld", :area_code => "9562") + AreaCode.create(:country => germany, :name => "Rödental", :area_code => "9563") + AreaCode.create(:country => germany, :name => "Bad Rodach", :area_code => "9564") + AreaCode.create(:country => germany, :name => "Untersiemau", :area_code => "9565") + AreaCode.create(:country => germany, :name => "Meeder", :area_code => "9566") + AreaCode.create(:country => germany, :name => "Seßlach-Gemünda", :area_code => "9567") + AreaCode.create(:country => germany, :name => "Neustadt b Coburg", :area_code => "9568") + AreaCode.create(:country => germany, :name => "Sesslach", :area_code => "9569") + AreaCode.create(:country => germany, :name => "Lichtenfels Bay", :area_code => "9571") + AreaCode.create(:country => germany, :name => "Burgkunstadt", :area_code => "9572") + AreaCode.create(:country => germany, :name => "Staffelstein Oberfr", :area_code => "9573") + AreaCode.create(:country => germany, :name => "Marktzeuln", :area_code => "9574") + AreaCode.create(:country => germany, :name => "Weismain", :area_code => "9575") + AreaCode.create(:country => germany, :name => "Lichtenfels-Isling", :area_code => "9576") + AreaCode.create(:country => germany, :name => "Neustadt a d Waldnaab", :area_code => "9602") + AreaCode.create(:country => germany, :name => "Floss", :area_code => "9603") + AreaCode.create(:country => germany, :name => "Wernberg-Köblitz", :area_code => "9604") + AreaCode.create(:country => germany, :name => "Weiherhammer", :area_code => "9605") + AreaCode.create(:country => germany, :name => "Pfreimd", :area_code => "9606") + AreaCode.create(:country => germany, :name => "Luhe-Wildenau", :area_code => "9607") + AreaCode.create(:country => germany, :name => "Kohlberg Oberpf", :area_code => "9608") + AreaCode.create(:country => germany, :name => "Weiden i d Opf", :area_code => "961") + AreaCode.create(:country => germany, :name => "Amberg Oberpf", :area_code => "9621") + AreaCode.create(:country => germany, :name => "Hirschau Oberpf", :area_code => "9622") + AreaCode.create(:country => germany, :name => "Ensdorf Oberpf", :area_code => "9624") + AreaCode.create(:country => germany, :name => "Kastl b Amberg", :area_code => "9625") + AreaCode.create(:country => germany, :name => "Hohenburg", :area_code => "9626") + AreaCode.create(:country => germany, :name => "Freudenberg Oberpf", :area_code => "9627") + AreaCode.create(:country => germany, :name => "Ursensollen", :area_code => "9628") + AreaCode.create(:country => germany, :name => "Tirschenreuth", :area_code => "9631") + AreaCode.create(:country => germany, :name => "Waldsassen", :area_code => "9632") + AreaCode.create(:country => germany, :name => "Mitterteich", :area_code => "9633") + AreaCode.create(:country => germany, :name => "Wiesau", :area_code => "9634") + AreaCode.create(:country => germany, :name => "Bärnau", :area_code => "9635") + AreaCode.create(:country => germany, :name => "Plößberg", :area_code => "9636") + AreaCode.create(:country => germany, :name => "Falkenberg Oberpf", :area_code => "9637") + AreaCode.create(:country => germany, :name => "Neualbenreuth", :area_code => "9638") + AreaCode.create(:country => germany, :name => "Mähring", :area_code => "9639") + AreaCode.create(:country => germany, :name => "Grafenwöhr", :area_code => "9641") + AreaCode.create(:country => germany, :name => "Kemnath Stadt", :area_code => "9642") + AreaCode.create(:country => germany, :name => "Auerbach i d Opf", :area_code => "9643") + AreaCode.create(:country => germany, :name => "Pressath", :area_code => "9644") + AreaCode.create(:country => germany, :name => "Eschenbach i d Opf", :area_code => "9645") + AreaCode.create(:country => germany, :name => "Freihung", :area_code => "9646") + AreaCode.create(:country => germany, :name => "Kirchenthumbach", :area_code => "9647") + AreaCode.create(:country => germany, :name => "Neustadt a Kulm", :area_code => "9648") + AreaCode.create(:country => germany, :name => "Vohenstrauss", :area_code => "9651") + AreaCode.create(:country => germany, :name => "Waidhaus", :area_code => "9652") + AreaCode.create(:country => germany, :name => "Eslarn", :area_code => "9653") + AreaCode.create(:country => germany, :name => "Pleystein", :area_code => "9654") + AreaCode.create(:country => germany, :name => "Tännesberg", :area_code => "9655") + AreaCode.create(:country => germany, :name => "Moosbach b Vohenstrauß", :area_code => "9656") + AreaCode.create(:country => germany, :name => "Waldthurn", :area_code => "9657") + AreaCode.create(:country => germany, :name => "Georgenberg", :area_code => "9658") + AreaCode.create(:country => germany, :name => "Leuchtenberg", :area_code => "9659") + AreaCode.create(:country => germany, :name => "Sulzbach-Rosenberg", :area_code => "9661") + AreaCode.create(:country => germany, :name => "Vilseck", :area_code => "9662") + AreaCode.create(:country => germany, :name => "Neukirchen b Sulzbach-Rosenberg", :area_code => "9663") + AreaCode.create(:country => germany, :name => "Hahnbach", :area_code => "9664") + AreaCode.create(:country => germany, :name => "Königstein Oberpf", :area_code => "9665") + AreaCode.create(:country => germany, :name => "Illschwang", :area_code => "9666") + AreaCode.create(:country => germany, :name => "Oberviechtach", :area_code => "9671") + AreaCode.create(:country => germany, :name => "Neunburg vorm Wald", :area_code => "9672") + AreaCode.create(:country => germany, :name => "Tiefenbach Oberpf", :area_code => "9673") + AreaCode.create(:country => germany, :name => "Schönsee", :area_code => "9674") + AreaCode.create(:country => germany, :name => "Altendorf a Nabburg", :area_code => "9675") + AreaCode.create(:country => germany, :name => "Winklarn", :area_code => "9676") + AreaCode.create(:country => germany, :name => "Oberviechtach-Pullenried", :area_code => "9677") + AreaCode.create(:country => germany, :name => "Windischeschenbach", :area_code => "9681") + AreaCode.create(:country => germany, :name => "Erbendorf", :area_code => "9682") + AreaCode.create(:country => germany, :name => "Friedenfels", :area_code => "9683") + AreaCode.create(:country => germany, :name => "Sandberg Unterfr", :area_code => "9701") + AreaCode.create(:country => germany, :name => "Euerdorf", :area_code => "9704") + AreaCode.create(:country => germany, :name => "Bad Bocklet", :area_code => "9708") + AreaCode.create(:country => germany, :name => "Bad Kissingen", :area_code => "971") + AreaCode.create(:country => germany, :name => "Üchtelhausen", :area_code => "9720") + AreaCode.create(:country => germany, :name => "Schweinfurt", :area_code => "9721") + AreaCode.create(:country => germany, :name => "Werneck", :area_code => "9722") + AreaCode.create(:country => germany, :name => "Röthlein", :area_code => "9723") + AreaCode.create(:country => germany, :name => "Stadtlauringen", :area_code => "9724") + AreaCode.create(:country => germany, :name => "Poppenhausen Unterfr", :area_code => "9725") + AreaCode.create(:country => germany, :name => "Euerbach", :area_code => "9726") + AreaCode.create(:country => germany, :name => "Schonungen-Marktsteinach", :area_code => "9727") + AreaCode.create(:country => germany, :name => "Wülfershausen Unterfr", :area_code => "9728") + AreaCode.create(:country => germany, :name => "Grettstadt", :area_code => "9729") + AreaCode.create(:country => germany, :name => "Hammelburg", :area_code => "9732") + AreaCode.create(:country => germany, :name => "Münnerstadt", :area_code => "9733") + AreaCode.create(:country => germany, :name => "Burkardroth", :area_code => "9734") + AreaCode.create(:country => germany, :name => "Massbach", :area_code => "9735") + AreaCode.create(:country => germany, :name => "Oberthulba", :area_code => "9736") + AreaCode.create(:country => germany, :name => "Wartmannsroth", :area_code => "9737") + AreaCode.create(:country => germany, :name => "Rottershausen", :area_code => "9738") + AreaCode.create(:country => germany, :name => "Bad Brückenau", :area_code => "9741") + AreaCode.create(:country => germany, :name => "Kalbach Rhön", :area_code => "9742") + AreaCode.create(:country => germany, :name => "Zeitlofs-Detter", :area_code => "9744") + AreaCode.create(:country => germany, :name => "Wildflecken", :area_code => "9745") + AreaCode.create(:country => germany, :name => "Zeitlofs", :area_code => "9746") + AreaCode.create(:country => germany, :name => "Geroda Bay", :area_code => "9747") + AreaCode.create(:country => germany, :name => "Motten", :area_code => "9748") + AreaCode.create(:country => germany, :name => "Oberbach Unterfr", :area_code => "9749") + AreaCode.create(:country => germany, :name => "Bad Königshofen i Grabfeld", :area_code => "9761") + AreaCode.create(:country => germany, :name => "Saal a d Saale", :area_code => "9762") + AreaCode.create(:country => germany, :name => "Sulzdorf a d Lederhecke", :area_code => "9763") + AreaCode.create(:country => germany, :name => "Höchheim", :area_code => "9764") + AreaCode.create(:country => germany, :name => "Trappstadt", :area_code => "9765") + AreaCode.create(:country => germany, :name => "Grosswenkheim", :area_code => "9766") + AreaCode.create(:country => germany, :name => "Bad Neustadt a d Saale", :area_code => "9771") + AreaCode.create(:country => germany, :name => "Bischofsheim a d Rhön", :area_code => "9772") + AreaCode.create(:country => germany, :name => "Unsleben", :area_code => "9773") + AreaCode.create(:country => germany, :name => "Oberelsbach", :area_code => "9774") + AreaCode.create(:country => germany, :name => "Schönau a d Brend", :area_code => "9775") + AreaCode.create(:country => germany, :name => "Mellrichstadt", :area_code => "9776") + AreaCode.create(:country => germany, :name => "Ostheim v d Rhön", :area_code => "9777") + AreaCode.create(:country => germany, :name => "Fladungen", :area_code => "9778") + AreaCode.create(:country => germany, :name => "Nordheim v d Rhön", :area_code => "9779") + AreaCode.create(:country => germany, :name => "Ansbach-Katterbach", :area_code => "9802") + AreaCode.create(:country => germany, :name => "Colmberg", :area_code => "9803") + AreaCode.create(:country => germany, :name => "Aurach", :area_code => "9804") + AreaCode.create(:country => germany, :name => "Burgoberbach", :area_code => "9805") + AreaCode.create(:country => germany, :name => "Ansbach", :area_code => "981") + AreaCode.create(:country => germany, :name => "Lehrberg", :area_code => "9820") + AreaCode.create(:country => germany, :name => "Bechhofen a d Heide", :area_code => "9822") + AreaCode.create(:country => germany, :name => "Leutershausen", :area_code => "9823") + AreaCode.create(:country => germany, :name => "Dietenhofen", :area_code => "9824") + AreaCode.create(:country => germany, :name => "Herrieden", :area_code => "9825") + AreaCode.create(:country => germany, :name => "Weidenbach Mittelfr", :area_code => "9826") + AreaCode.create(:country => germany, :name => "Lichtenau Mittelfr", :area_code => "9827") + AreaCode.create(:country => germany, :name => "Rügland", :area_code => "9828") + AreaCode.create(:country => germany, :name => "Flachslanden", :area_code => "9829") + AreaCode.create(:country => germany, :name => "Gunzenhausen", :area_code => "9831") + AreaCode.create(:country => germany, :name => "Wassertrüdingen", :area_code => "9832") + AreaCode.create(:country => germany, :name => "Heidenheim Mittelfr", :area_code => "9833") + AreaCode.create(:country => germany, :name => "Theilenhofen", :area_code => "9834") + AreaCode.create(:country => germany, :name => "Ehingen Mittelfr", :area_code => "9835") + AreaCode.create(:country => germany, :name => "Gunzenhausen-Cronheim", :area_code => "9836") + AreaCode.create(:country => germany, :name => "Haundorf", :area_code => "9837") + AreaCode.create(:country => germany, :name => "Bad Windsheim", :area_code => "9841") + AreaCode.create(:country => germany, :name => "Uffenheim", :area_code => "9842") + AreaCode.create(:country => germany, :name => "Burgbernheim", :area_code => "9843") + AreaCode.create(:country => germany, :name => "Obernzenn", :area_code => "9844") + AreaCode.create(:country => germany, :name => "Oberdachstetten", :area_code => "9845") + AreaCode.create(:country => germany, :name => "Ipsheim", :area_code => "9846") + AreaCode.create(:country => germany, :name => "Ergersheim", :area_code => "9847") + AreaCode.create(:country => germany, :name => "Simmershofen", :area_code => "9848") + AreaCode.create(:country => germany, :name => "Dinkelsbühl", :area_code => "9851") + AreaCode.create(:country => germany, :name => "Feuchtwangen", :area_code => "9852") + AreaCode.create(:country => germany, :name => "Wilburgstetten", :area_code => "9853") + AreaCode.create(:country => germany, :name => "Wittelshofen", :area_code => "9854") + AreaCode.create(:country => germany, :name => "Dentlein a Forst", :area_code => "9855") + AreaCode.create(:country => germany, :name => "Dürrwangen", :area_code => "9856") + AreaCode.create(:country => germany, :name => "Schopfloch Mittelfr", :area_code => "9857") + AreaCode.create(:country => germany, :name => "Rothenburg ob der Tauber", :area_code => "9861") + AreaCode.create(:country => germany, :name => "Adelshofen Mittelfr", :area_code => "9865") + AreaCode.create(:country => germany, :name => "Geslau", :area_code => "9867") + AreaCode.create(:country => germany, :name => "Schillingsfürst", :area_code => "9868") + AreaCode.create(:country => germany, :name => "Wettringen Mittelfr", :area_code => "9869") + AreaCode.create(:country => germany, :name => "Windsbach", :area_code => "9871") + AreaCode.create(:country => germany, :name => "Heilsbronn", :area_code => "9872") + AreaCode.create(:country => germany, :name => "Abenberg-Wassermungenau", :area_code => "9873") + AreaCode.create(:country => germany, :name => "Neuendettelsau", :area_code => "9874") + AreaCode.create(:country => germany, :name => "Wolframs-Eschenbach", :area_code => "9875") + AreaCode.create(:country => germany, :name => "Rohr Mittelfr", :area_code => "9876") + AreaCode.create(:country => germany, :name => "Hengersberg Bay", :area_code => "9901") + AreaCode.create(:country => germany, :name => "Schöllnach", :area_code => "9903") + AreaCode.create(:country => germany, :name => "Lalling", :area_code => "9904") + AreaCode.create(:country => germany, :name => "Bernried Niederbay", :area_code => "9905") + AreaCode.create(:country => germany, :name => "Mariaposching", :area_code => "9906") + AreaCode.create(:country => germany, :name => "Zenting", :area_code => "9907") + AreaCode.create(:country => germany, :name => "Schöfweg", :area_code => "9908") + AreaCode.create(:country => germany, :name => "Deggendorf", :area_code => "991") + AreaCode.create(:country => germany, :name => "Bischofsmais", :area_code => "9920") + AreaCode.create(:country => germany, :name => "Regen", :area_code => "9921") + AreaCode.create(:country => germany, :name => "Zwiesel", :area_code => "9922") + AreaCode.create(:country => germany, :name => "Teisnach", :area_code => "9923") + AreaCode.create(:country => germany, :name => "Bodenmais", :area_code => "9924") + AreaCode.create(:country => germany, :name => "Bayerisch Eisenstein", :area_code => "9925") + AreaCode.create(:country => germany, :name => "Frauenau", :area_code => "9926") + AreaCode.create(:country => germany, :name => "Kirchberg Wald", :area_code => "9927") + AreaCode.create(:country => germany, :name => "Kirchdorf i Wald", :area_code => "9928") + AreaCode.create(:country => germany, :name => "Ruhmannsfelden", :area_code => "9929") + AreaCode.create(:country => germany, :name => "Plattling", :area_code => "9931") + AreaCode.create(:country => germany, :name => "Osterhofen", :area_code => "9932") + AreaCode.create(:country => germany, :name => "Wallersdorf", :area_code => "9933") + AreaCode.create(:country => germany, :name => "Stephansposching", :area_code => "9935") + AreaCode.create(:country => germany, :name => "Wallerfing", :area_code => "9936") + AreaCode.create(:country => germany, :name => "Oberpöring", :area_code => "9937") + AreaCode.create(:country => germany, :name => "Moos Niederbay", :area_code => "9938") + AreaCode.create(:country => germany, :name => "Kötzting", :area_code => "9941") + AreaCode.create(:country => germany, :name => "Viechtach", :area_code => "9942") + AreaCode.create(:country => germany, :name => "Lam Oberpf", :area_code => "9943") + AreaCode.create(:country => germany, :name => "Miltach", :area_code => "9944") + AreaCode.create(:country => germany, :name => "Arnbruck", :area_code => "9945") + AreaCode.create(:country => germany, :name => "Hohenwarth b Kötzing", :area_code => "9946") + AreaCode.create(:country => germany, :name => "Neukirchen b Hl Blut", :area_code => "9947") + AreaCode.create(:country => germany, :name => "Eschlkam", :area_code => "9948") + AreaCode.create(:country => germany, :name => "Landau a d Isar", :area_code => "9951") + AreaCode.create(:country => germany, :name => "Eichendorf", :area_code => "9952") + AreaCode.create(:country => germany, :name => "Pilsting", :area_code => "9953") + AreaCode.create(:country => germany, :name => "SimbachNiederbay", :area_code => "9954") + AreaCode.create(:country => germany, :name => "Mamming", :area_code => "9955") + AreaCode.create(:country => germany, :name => "Eichendorf-Aufhausen", :area_code => "9956") + AreaCode.create(:country => germany, :name => "Mitterfels", :area_code => "9961") + AreaCode.create(:country => germany, :name => "Schwarzach Niederbay", :area_code => "9962") + AreaCode.create(:country => germany, :name => "Konzell", :area_code => "9963") + AreaCode.create(:country => germany, :name => "Stallwang", :area_code => "9964") + AreaCode.create(:country => germany, :name => "Sankt Englmar", :area_code => "9965") + AreaCode.create(:country => germany, :name => "Wiesenfelden", :area_code => "9966") + AreaCode.create(:country => germany, :name => "Cham", :area_code => "9971") + AreaCode.create(:country => germany, :name => "Waldmünchen", :area_code => "9972") + AreaCode.create(:country => germany, :name => "Furth i Wald", :area_code => "9973") + AreaCode.create(:country => germany, :name => "Traitsching", :area_code => "9974") + AreaCode.create(:country => germany, :name => "Waldmünchen-Geigant", :area_code => "9975") + AreaCode.create(:country => germany, :name => "Rötz", :area_code => "9976") + AreaCode.create(:country => germany, :name => "Arnschwang", :area_code => "9977") + AreaCode.create(:country => germany, :name => "Schönthal Oberpf", :area_code => "9978") +end
\ No newline at end of file diff --git a/lib/tasks/populate_area_codes_pl.rake b/lib/tasks/populate_area_codes_pl.rake new file mode 100644 index 0000000..a3f725a --- /dev/null +++ b/lib/tasks/populate_area_codes_pl.rake @@ -0,0 +1,59 @@ +# encoding: UTF-8 + +desc "Populate database with Polish pre 2005 (now defunct) area codes" +task :areacodes_poland => :environment do + if ! poland = Country.where( :name => 'Poland' ).first + $stderr.puts "Error. Country not found." + exit 1 + end + + AreaCode.create(:country => poland, :name => "Biała Podlaska", :area_code => "83") + AreaCode.create(:country => poland, :name => "Białystok", :area_code => "85") + AreaCode.create(:country => poland, :name => "Bielsko-Biała", :area_code => "33") + AreaCode.create(:country => poland, :name => "Bydgoszcz", :area_code => "52") + AreaCode.create(:country => poland, :name => "Chełm", :area_code => "82") + AreaCode.create(:country => poland, :name => "Ciechanów", :area_code => "23") + AreaCode.create(:country => poland, :name => "Częstochowa", :area_code => "34") + AreaCode.create(:country => poland, :name => "Elbląg", :area_code => "55") + AreaCode.create(:country => poland, :name => "Gdańsk", :area_code => "58") + AreaCode.create(:country => poland, :name => "Gorzów Wielkopolski", :area_code => "95") + AreaCode.create(:country => poland, :name => "Jelenia Góra", :area_code => "75") + AreaCode.create(:country => poland, :name => "Kalisz", :area_code => "62") + AreaCode.create(:country => poland, :name => "Katowice", :area_code => "32") + AreaCode.create(:country => poland, :name => "Kielce", :area_code => "41") + AreaCode.create(:country => poland, :name => "Konin", :area_code => "63") + AreaCode.create(:country => poland, :name => "Koszalin", :area_code => "94") + AreaCode.create(:country => poland, :name => "Kraków", :area_code => "12") + AreaCode.create(:country => poland, :name => "Krosno", :area_code => "13") + AreaCode.create(:country => poland, :name => "Legnica", :area_code => "76") + AreaCode.create(:country => poland, :name => "Leszno", :area_code => "65") + AreaCode.create(:country => poland, :name => "Łódź", :area_code => "42") + AreaCode.create(:country => poland, :name => "Łomża", :area_code => "86") + AreaCode.create(:country => poland, :name => "Lublin", :area_code => "81") + AreaCode.create(:country => poland, :name => "Nowy Sącz", :area_code => "18") + AreaCode.create(:country => poland, :name => "Olsztyn", :area_code => "89") + AreaCode.create(:country => poland, :name => "Opole", :area_code => "77") + AreaCode.create(:country => poland, :name => "Ostrołęka", :area_code => "29") + AreaCode.create(:country => poland, :name => "Piła", :area_code => "67") + AreaCode.create(:country => poland, :name => "Piotrków Trybunalski", :area_code => "44") + AreaCode.create(:country => poland, :name => "Płock", :area_code => "24") + AreaCode.create(:country => poland, :name => "Poznań", :area_code => "61") + AreaCode.create(:country => poland, :name => "Przemyśl", :area_code => "16") + AreaCode.create(:country => poland, :name => "Radom", :area_code => "48") + AreaCode.create(:country => poland, :name => "Rzeszów", :area_code => "17") + AreaCode.create(:country => poland, :name => "Siedlce", :area_code => "25") + AreaCode.create(:country => poland, :name => "Sieradz", :area_code => "43") + AreaCode.create(:country => poland, :name => "Skierniewice", :area_code => "46") + AreaCode.create(:country => poland, :name => "Słupsk", :area_code => "59") + AreaCode.create(:country => poland, :name => "Suwałki", :area_code => "87") + AreaCode.create(:country => poland, :name => "Szczecin", :area_code => "91") + AreaCode.create(:country => poland, :name => "Tarnobrzeg", :area_code => "15") + AreaCode.create(:country => poland, :name => "Tarnów", :area_code => "14") + AreaCode.create(:country => poland, :name => "Toruń", :area_code => "56") + AreaCode.create(:country => poland, :name => "Wałbrzych", :area_code => "74") + AreaCode.create(:country => poland, :name => "Warszawa", :area_code => "22") + AreaCode.create(:country => poland, :name => "Włocławek", :area_code => "54") + AreaCode.create(:country => poland, :name => "Wrocław", :area_code => "71") + AreaCode.create(:country => poland, :name => "Zamość", :area_code => "84") + AreaCode.create(:country => poland, :name => "Zielona Góra", :area_code => "68") +end diff --git a/lib/tasks/populate_country_codes.rake b/lib/tasks/populate_country_codes.rake new file mode 100644 index 0000000..1129f81 --- /dev/null +++ b/lib/tasks/populate_country_codes.rake @@ -0,0 +1,244 @@ +# encoding: UTF-8 + +desc "Populate database with country codes" +task :country_codes => :environment do + + Country.create(:name => "American Samoa", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Anguilla", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Antigua and Barbuda", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Bahamas", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Barbados", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Bermuda", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "British Virgin Islands", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Canada", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Cayman Islands", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Dominica", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Dominican Republic", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Grenada", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Guam", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Jamaica", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Montserrat", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Northern Mariana Islands", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Puerto Rico", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Saint Kitts and Nevis", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Saint Lucia", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Saint Vincent and the Grenadines", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Trinidad and Tobago", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Turks and Caicos Islands", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "United States of America", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "United States Virgin Islands", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + Country.create(:name => "Egypt", :country_code => "20", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Morocco", :country_code => "212", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Algeria", :country_code => "213", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Tunisia", :country_code => "216", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Libya", :country_code => "218", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Gambia", :country_code => "220", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Senegal", :country_code => "221", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Mauritania", :country_code => "222", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Mali", :country_code => "223", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Guinea", :country_code => "224", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Côte d'Ivoire", :country_code => "225", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Burkina Faso", :country_code => "226", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Niger", :country_code => "227", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Togolese Republic", :country_code => "228", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Benin", :country_code => "229", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Mauritius", :country_code => "230", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Liberia", :country_code => "231", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Sierra Leone", :country_code => "232", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Ghana", :country_code => "233", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Nigeria", :country_code => "234", :international_call_prefix => "009", :trunk_prefix => "" ) + Country.create(:name => "Chad", :country_code => "235", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Central African Republic", :country_code => "236", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Cameroon", :country_code => "237", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Cape Verde", :country_code => "238", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Sao Tome and Principe", :country_code => "239", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Equatorial Guinea", :country_code => "240", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Gabonese Republic", :country_code => "241", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Congo", :country_code => "242", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Democratic Republic of the Congo", :country_code => "243", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Angola", :country_code => "244", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Guinea-Bissau", :country_code => "245", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Diego Garcia", :country_code => "246", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Ascension", :country_code => "247", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Seychelles", :country_code => "248", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Sudan", :country_code => "249", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Rwanda", :country_code => "250", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Ethiopia", :country_code => "251", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Somali Democratic Republic", :country_code => "252", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Djibouti", :country_code => "253", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Kenya", :country_code => "254", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Tanzania", :country_code => "255", :international_call_prefix => "000", :trunk_prefix => "" ) + Country.create(:name => "Uganda", :country_code => "256", :international_call_prefix => "000", :trunk_prefix => "" ) + Country.create(:name => "Burundi", :country_code => "257", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Mozambique", :country_code => "258", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Zambia", :country_code => "260", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Madagascar", :country_code => "261", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Reunion", :country_code => "262", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Zimbabwe", :country_code => "263", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Namibia", :country_code => "264", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Malawi", :country_code => "265", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Lesotho", :country_code => "266", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Botswana", :country_code => "267", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Swaziland", :country_code => "268", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Comoros", :country_code => "269", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Mayotte", :country_code => "269", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "South Africa", :country_code => "27", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Saint Helena", :country_code => "290", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Eritrea", :country_code => "291", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Aruba", :country_code => "297", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Faroe Islands", :country_code => "298", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Greenland", :country_code => "299", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Greece", :country_code => "30", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Netherlands", :country_code => "31", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Belgium", :country_code => "32", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "France", :country_code => "33", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Spain", :country_code => "34", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Gibraltar", :country_code => "350", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Portugal", :country_code => "351", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Luxembourg", :country_code => "352", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Ireland", :country_code => "353", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Iceland", :country_code => "354", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Albania", :country_code => "355", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Malta", :country_code => "356", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Cyprus", :country_code => "357", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Finland", :country_code => "358", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Bulgaria", :country_code => "359", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Hungary", :country_code => "36", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Lithuania", :country_code => "370", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Latvia", :country_code => "371", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Estonia", :country_code => "372", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Moldova", :country_code => "373", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Armenia", :country_code => "374", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Belarus", :country_code => "375", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Andorra", :country_code => "376", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Monaco", :country_code => "377", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "San Marino", :country_code => "378", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Vatican City State", :country_code => "379", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Ukraine", :country_code => "380", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Serbia and Montenegro", :country_code => "381", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Croatia", :country_code => "385", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Slovenia", :country_code => "386", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Bosnia and Herzegovina", :country_code => "387", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Group of countries, shared code", :country_code => "388", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "The Former Yugoslav Republic of Macedonia", :country_code => "389", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Italy", :country_code => "39", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Vatican City State", :country_code => "39", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Romania", :country_code => "40", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Switzerland", :country_code => "41", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Czech Republic", :country_code => "420", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Slovak Republic", :country_code => "421", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Liechtenstein", :country_code => "423", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Austria", :country_code => "43", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "United Kingdom of Great Britain and Northern Ireland", :country_code => "44", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Denmark", :country_code => "45", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Sweden", :country_code => "46", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Norway", :country_code => "47", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Poland", :country_code => "48", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Germany", :country_code => "49", :international_call_prefix => "00", :trunk_prefix => "0" ) + Country.create(:name => "Falkland Islands", :country_code => "500", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Belize", :country_code => "501", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Guatemala", :country_code => "502", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "El Salvador", :country_code => "503", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Honduras", :country_code => "504", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Nicaragua", :country_code => "505", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Costa Rica", :country_code => "506", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Panama", :country_code => "507", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Saint Pierre and Miquelon", :country_code => "508", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Haiti", :country_code => "509", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Peru", :country_code => "51", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Mexico", :country_code => "52", :international_call_prefix => "00", :trunk_prefix => "01" ) + Country.create(:name => "Cuba", :country_code => "53", :international_call_prefix => "119", :trunk_prefix => "" ) + Country.create(:name => "Argentine Republic", :country_code => "54", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Brazil", :country_code => "55", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Chile", :country_code => "56", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Colombia", :country_code => "57", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Venezuela", :country_code => "58", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Guadeloupe", :country_code => "590", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Bolivia", :country_code => "591", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Guyana", :country_code => "592", :international_call_prefix => "001", :trunk_prefix => "" ) + Country.create(:name => "Ecuador", :country_code => "593", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "French Guiana", :country_code => "594", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Paraguay", :country_code => "595", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Martinique", :country_code => "596", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Suriname", :country_code => "597", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Uruguay", :country_code => "598", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Netherlands Antilles", :country_code => "599", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Malaysia", :country_code => "60", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Australia", :country_code => "61", :international_call_prefix => "0011", :trunk_prefix => "" ) + Country.create(:name => "Indonesia", :country_code => "62", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Philippines", :country_code => "63", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "New Zealand", :country_code => "64", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Singapore", :country_code => "65", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Thailand", :country_code => "66", :international_call_prefix => "001", :trunk_prefix => "" ) + Country.create(:name => "Democratic Republic of Timor-Leste", :country_code => "670", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Australian External Territories", :country_code => "672", :international_call_prefix => "0011", :trunk_prefix => "" ) + Country.create(:name => "Brunei Darussalam", :country_code => "673", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Nauru", :country_code => "674", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Papua New Guinea", :country_code => "675", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Tonga", :country_code => "676", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Solomon Islands", :country_code => "677", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Vanuatu", :country_code => "678", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Fiji", :country_code => "679", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Palau", :country_code => "680", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Wallis and Futuna", :country_code => "681", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Cook Islands", :country_code => "682", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Niue", :country_code => "683", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Samoa", :country_code => "685", :international_call_prefix => "0", :trunk_prefix => "" ) + Country.create(:name => "Kiribati", :country_code => "686", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "New Caledonia", :country_code => "687", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Tuvalu", :country_code => "688", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "French Polynesia", :country_code => "689", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Tokelau", :country_code => "690", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Micronesia", :country_code => "691", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Marshall Islands", :country_code => "692", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Kazakhstan", :country_code => "7", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Russian Federation", :country_code => "7", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "International Freephone Service", :country_code => "800", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "International Shared Cost Service", :country_code => "808", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Japan", :country_code => "81", :international_call_prefix => "010", :trunk_prefix => "" ) + Country.create(:name => "Korea", :country_code => "82", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Viet Nam", :country_code => "84", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Democratic People's Republic of Korea", :country_code => "850", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Hong Kong, China", :country_code => "852", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Macao, China", :country_code => "853", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Cambodia", :country_code => "855", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Lao People's Democratic Republic", :country_code => "856", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "China", :country_code => "86", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Inmarsat SNAC", :country_code => "870", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Inmarsat", :country_code => "871", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Inmarsat", :country_code => "872", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Inmarsat", :country_code => "873", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Inmarsat", :country_code => "874", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Universal Personal Telecommunication Service", :country_code => "878", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Bangladesh", :country_code => "880", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Turkey", :country_code => "90", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "India", :country_code => "91", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Pakistan", :country_code => "92", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Afghanistan", :country_code => "93", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Sri Lanka", :country_code => "94", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Myanmar", :country_code => "95", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Maldives", :country_code => "960", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Lebanon", :country_code => "961", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Jordan", :country_code => "962", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Syrian Arab Republic", :country_code => "963", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Iraq", :country_code => "964", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Kuwait", :country_code => "965", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Saudi Arabia", :country_code => "966", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Yemen", :country_code => "967", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Oman", :country_code => "968", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "United Arab Emirates", :country_code => "971", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Israel", :country_code => "972", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Bahrain", :country_code => "973", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Qatar", :country_code => "974", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Bhutan", :country_code => "975", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Mongolia", :country_code => "976", :international_call_prefix => "001", :trunk_prefix => "" ) + Country.create(:name => "Nepal", :country_code => "977", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "International Premium Rate Service", :country_code => "979", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Iran", :country_code => "98", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Tajikistan", :country_code => "992", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Turkmenistan", :country_code => "993", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Azerbaijani Republic", :country_code => "994", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Georgia", :country_code => "995", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Kyrgyz Republic", :country_code => "996", :international_call_prefix => "00", :trunk_prefix => "" ) + Country.create(:name => "Uzbekistan", :country_code => "998", :international_call_prefix => "00", :trunk_prefix => "" ) +end diff --git a/lib/tasks/send_fax_notifications.rake b/lib/tasks/send_fax_notifications.rake new file mode 100644 index 0000000..039c509 --- /dev/null +++ b/lib/tasks/send_fax_notifications.rake @@ -0,0 +1,49 @@ +# encoding: UTF-8 + +desc "Import inbound fax" + +task :send_fax_notifications => :environment do + FaxDocument.where(:state => 'received').each do |fax_document| + TIFF_FUFFIX = ".tiff" + PDF_SUFFIX = ".pdf" + TMP_DIR = "/tmp/" + + tiff_file = File.basename(fax_document.tiff.to_s) + + if !File.exists?( "#{TMP_DIR}#{tiff_file}" ) + fax_document.state = 'unsuccessful' + fax_document.save + next + end + + paper_size = "letter" + pdf_file = "#{TMP_DIR}#{File.basename(tiff_file, TIFF_FUFFIX)}#{PDF_SUFFIX}" + + system "tiff2pdf \\ + -o \"#{pdf_file}\" \\ + -p #{paper_size} \\ + -a \"#{fax_document.remote_station_id}\" \\ + -c \"AMOOMA Gemeinschaft version #{GEMEINSCHAFT_VERSION}\" \\ + -t \"#{fax_document.remote_station_id}\" \"#{TMP_DIR}#{tiff_file}\"" + + if !File.exists?( pdf_file ) + fax_document.state = 'unsuccessful' + fax_document.save + next + end + + fax_document.document = File.open(pdf_file) + fax_document.state = 'successful' + + if fax_document.save + Notifications.new_fax(fax_document).deliver + File.delete("#{TMP_DIR}#{tiff_file}"); + File.delete(pdf_file); + fax_document.tiff = nil + fax_document.save + else + fax_document.state = 'unsuccessful' + fax_document.save + end + end +end diff --git a/lib/tasks/send_voicemail_notifications.rake b/lib/tasks/send_voicemail_notifications.rake new file mode 100644 index 0000000..214d832 --- /dev/null +++ b/lib/tasks/send_voicemail_notifications.rake @@ -0,0 +1,52 @@ +# encoding: UTF-8 + +desc "Import inbound voicemail" + +task :send_voicemail_notifications => :environment do + VoicemailMessage.where(:notification => nil).each do |message| + + message.notification = false + message.save + if !File.exists?( message.file_path ) + $stderr.puts "File \"#{message.file_path}\" does not exist" + next + end + + sip_account = SipAccount.where(:auth_name => message.username).first + if ! sip_account + $stderr.puts "SipAccount \"#{message.username}\" does not exist" + next + end + + user = sip_account.sip_accountable + if user.class != User + next + end + + if user.email.blank? + $stderr.puts "no email address" + next + end + + voicemail_settings = sip_account.voicemail_setting + if !voicemail_settings + voicemail_settings = VoicemailSetting.new(:notify => user.send_voicemail_as_email_attachment, :attachment => user.send_voicemail_as_email_attachment, :mark_read => user.send_voicemail_as_email_attachment) + end + + message.notification = voicemail_settings.notify + if voicemail_settings.notify + if Notifications.new_voicemail(message, voicemail_settings.attachment).deliver + if voicemail_settings.purge + message.delete + next + end + message.save + if voicemail_settings.mark_read + message.mark_read + end + end + else + message.save + end + end +end diff --git a/lib/templates/erb/scaffold/_form.html.erb b/lib/templates/erb/scaffold/_form.html.erb new file mode 100644 index 0000000..24a1768 --- /dev/null +++ b/lib/templates/erb/scaffold/_form.html.erb @@ -0,0 +1,13 @@ +<%%= simple_form_for(@<%= singular_table_name %>) do |f| %> + <%%= f.error_notification %> + + <div class="inputs"> + <%- attributes.each do |attribute| -%> + <%%= f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %> %> + <%- end -%> + </div> + + <div class="actions"> + <%%= f.button :submit %> + </div> +<%% end %> diff --git a/lib/uacsta.rb b/lib/uacsta.rb new file mode 100644 index 0000000..9303092 --- /dev/null +++ b/lib/uacsta.rb @@ -0,0 +1,83 @@ +class Uacsta + + def send(sip_account, domain, body) + require 'freeswitch_event' + + event = FreeswitchEvent.new("NOTIFY") + event.add_header("profile", "gemeinschaft") + event.add_header("event-string", "uaCSTA") + event.add_header("user", sip_account) + event.add_header("host", domain) + event.add_header("content-type", "application/csta+xml") + event.add_body(body); + + return event.fire(); + end + + def make_call(sip_account, domain, number) + body = '<?xml version="1.0" encoding="UTF-8"?> + <MakeCall xmlns="http://www.ecma-international.org/standards/ecma-323/csta/ed4"> + <callingDevice>' + sip_account.to_s + '</callingDevice> + <calledDirectoryNumber>' + number.to_s + '</calledDirectoryNumber> + <autoOriginate>doNotPrompt</autoOriginate> + </MakeCall>' + + self.send(sip_account, domain, body); + end + + def answer_call(sip_account, domain) + body = '<?xml version="1.0" encoding="UTF-8"?> + <AnswerCall xmlns="http://www.ecma-international.org/standards/ecma-323/csta/ed4"> + <callToBeAnswered> + <deviceID>' + sip_account + '</deviceID> + </callToBeAnswered> + </AnswerCall>' + + self.send(sip_account, domain, body) + end + + def set_microphone_mute(sip_account, domain, value) + body = '<?xml version="1.0" encoding="UTF-8"?> + <SetMicrophoneMute xmlns="http://www.ecma-international.org/standards/ecma-323/csta/ed3"> + <device>' + sip_account + '</device> + <auditoryApparatus>1</auditoryApparatus> + <microphoneMuteOn>' + value.to_s + '</microphoneMuteOn> + </SetMicrophoneMute>' + + self.send(sip_account, domain, body) + end + + def set_speaker_volume(sip_account, domain, value) + body = '<?xml version="1.0" encoding="UTF-8"?> + <SetSpeakerVolume xmlns="http://www.ecma-international.org/standards/ecma-323/csta/ed3"> + <device>' + sip_account + '</device> + <auditoryApparatus>1</auditoryApparatus> + <speakerVolume>' + value.to_s + '</speakerVolume> + </SetSpeakerVolume>' + + self.send(sip_account, domain, body) + end + + def set_do_not_disturb(sip_account, domain, value) + body = '<?xml version="1.0" encoding="UTF-8"?> + <SetDoNotDisturb xmlns="http://www.ecma-international.org/standards/ecma-323/csta/ed3"> + <device>' + sip_account + '</device> + <doNotDisturbOn>' + value.to_s + '</doNotDisturbOn> + </SetDoNotDisturb>' + + self.send(sip_account, domain, body) + end + + def set_forwarding(sip_account, domain, forwarding_type, number, activate) + forwarding_types = [ "forwardImmediate", "forwardBusy", "forwardNoAns" ] + body = '<?xml version="1.0" encoding="UTF-8"?> + <SetForwarding xmlns="http://www.ecma-international.org/standards/ecma-323/csta/ed3"> + <device>' + sip_account + '</device> + <forwardingType>' + forwarding_types[forwarding_type.to_i] + '</forwardingType> + <forwardDN>' + number.to_s + '</forwardDN> + <activateForward>' + activate.to_s + '</activateForward> + </SetForwarding>' + + self.send(sip_account, domain, body) + end +end diff --git a/log/.gitkeep b/log/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/log/.gitkeep diff --git a/misc/TODO-Liste.txt b/misc/TODO-Liste.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/misc/TODO-Liste.txt diff --git a/misc/etc/cron.d/logout_phones b/misc/etc/cron.d/logout_phones new file mode 100644 index 0000000..86b0ffd --- /dev/null +++ b/misc/etc/cron.d/logout_phones @@ -0,0 +1,3 @@ +# Logout tagged phones +23 1 * * * root /opt/GS5/script/logout_phones.sh + diff --git a/misc/etc/ssl/amooma/server.pem b/misc/etc/ssl/amooma/server.pem new file mode 100644 index 0000000..d34e8db --- /dev/null +++ b/misc/etc/ssl/amooma/server.pem @@ -0,0 +1,36 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDC9G3px4ew18PHIm8HJ3yXc9rxqM5uSn1qhjdoWM0zC0Qcue9k +V+5ZUq356yjBbs5wXi3SEWfucXxhnwnzeIqvMeO6y0BiUVsClbqziRCho/hgHPTJ +tZzjt6Mpl3D/9yeFKbah5lJ5qNm0T00ybpcSXC7w2Xv9ckji1DDtGo62fQIDAQAB +AoGAHM0jl9AEednGcJrjsDjjLTTOebkolh6nHJ+re9zyo8HcVCob9cUPz15pmWxm +Xv1RvkQLnOc5ZX6ak4l9XNzIEAvQXNRFXwCOyfpffx/8QhfrG0v2G+K2QG52VxQj +tqnRdLf8HEhCmrJCMvMEAuQkAiirIMTFcaaP1CBbCilr8nkCQQDnSYxEfXMYi4iq +9Xjwn8Ayh1koXFUY+5/0u9SGqzTeTxW1QN2hGhehsd0vlv4cJppcuL4Z+2VYqLQc +zXDZo/MLAkEA18kSHLp+HCd1BW/JEoIQqWlTw6SRx+IsUN7UmnSZS4C+UPRbtq5I +nzgzonZufOEmzoMdwbe9EHAl087f0UfxlwJBAKNIBhGYKvgqEdr3n2Dotuw1J1la +De2sPpmtPPWxyoojdOTYHV8Np59MjSV6yHyhOBq7heGb3EmCGF25H7FWkE8CQGUN +aakAgPxoUfn4zp4XQPxFMhAF6qtDtOMuZzvp7LwaD4ZT2PtlBOdjZ3LmqXlb61N8 +vZuxkx22l1BoqhIU8gMCQQDJsQr5y8UWamYZrNv5YRNnm8aGgJ83Gx3n5b8bKqjh +TM8hqJfFTIfHr90GhHyok1aVkjF+sUtydX1R85IHTDz5 +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIDfjCCAuegAwIBAgIJAKG1XaHFZ4gEMA0GCSqGSIb3DQEBBQUAMIGHMQswCQYD +VQQGEwJERTEYMBYGA1UECBMPUmhlaW5sYW5kLVBmYWx6MRAwDgYDVQQHEwdOZXV3 +aWVkMRQwEgYDVQQKEwtBTU9PTUEgR21iSDEXMBUGA1UEAxMOR2VtZWluc2NoYWZ0 +IDQxHTAbBgkqhkiG9w0BCQEWDmluZm9AYW1vb21hLmRlMB4XDTExMDcyMjE0NDAy +OFoXDTIxMDcxOTE0NDAyOFowgYcxCzAJBgNVBAYTAkRFMRgwFgYDVQQIEw9SaGVp +bmxhbmQtUGZhbHoxEDAOBgNVBAcTB05ldXdpZWQxFDASBgNVBAoTC0FNT09NQSBH +bWJIMRcwFQYDVQQDEw5HZW1laW5zY2hhZnQgNDEdMBsGCSqGSIb3DQEJARYOaW5m +b0BhbW9vbWEuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAML0benHh7DX +w8cibwcnfJdz2vGozm5KfWqGN2hYzTMLRBy572RX7llSrfnrKMFuznBeLdIRZ+5x +fGGfCfN4iq8x47rLQGJRWwKVurOJEKGj+GAc9Mm1nOO3oymXcP/3J4UptqHmUnmo +2bRPTTJulxJcLvDZe/1ySOLUMO0ajrZ9AgMBAAGjge8wgewwHQYDVR0OBBYEFATl +qWtGbyeBIN9mR/4GV9jO7ON7MIG8BgNVHSMEgbQwgbGAFATlqWtGbyeBIN9mR/4G +V9jO7ON7oYGNpIGKMIGHMQswCQYDVQQGEwJERTEYMBYGA1UECBMPUmhlaW5sYW5k +LVBmYWx6MRAwDgYDVQQHEwdOZXV3aWVkMRQwEgYDVQQKEwtBTU9PTUEgR21iSDEX +MBUGA1UEAxMOR2VtZWluc2NoYWZ0IDQxHTAbBgkqhkiG9w0BCQEWDmluZm9AYW1v +b21hLmRlggkAobVdocVniAQwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOB +gQCBIgWoP8YiP6tm8rhb81k6myP4ONO4vOaUz9bsadHDWNCTjiQxvo4uVYqTMLKa +Bc7S0VpyvSg7/eGsSWxIUwdn6dUPdo51juGnnJ9dK9DuiNPHb0HP3UJo1gCWgs1v +EnVGfKDdu9FfdNcQtIb28UfF8Pw8WA6mmhQOOh0M9d3ayQ== +-----END CERTIFICATE----- diff --git a/misc/etc/ssl/amooma/server_cert.pem b/misc/etc/ssl/amooma/server_cert.pem new file mode 100644 index 0000000..021ad28 --- /dev/null +++ b/misc/etc/ssl/amooma/server_cert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDfjCCAuegAwIBAgIJAKG1XaHFZ4gEMA0GCSqGSIb3DQEBBQUAMIGHMQswCQYD +VQQGEwJERTEYMBYGA1UECBMPUmhlaW5sYW5kLVBmYWx6MRAwDgYDVQQHEwdOZXV3 +aWVkMRQwEgYDVQQKEwtBTU9PTUEgR21iSDEXMBUGA1UEAxMOR2VtZWluc2NoYWZ0 +IDQxHTAbBgkqhkiG9w0BCQEWDmluZm9AYW1vb21hLmRlMB4XDTExMDcyMjE0NDAy +OFoXDTIxMDcxOTE0NDAyOFowgYcxCzAJBgNVBAYTAkRFMRgwFgYDVQQIEw9SaGVp +bmxhbmQtUGZhbHoxEDAOBgNVBAcTB05ldXdpZWQxFDASBgNVBAoTC0FNT09NQSBH +bWJIMRcwFQYDVQQDEw5HZW1laW5zY2hhZnQgNDEdMBsGCSqGSIb3DQEJARYOaW5m +b0BhbW9vbWEuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAML0benHh7DX +w8cibwcnfJdz2vGozm5KfWqGN2hYzTMLRBy572RX7llSrfnrKMFuznBeLdIRZ+5x +fGGfCfN4iq8x47rLQGJRWwKVurOJEKGj+GAc9Mm1nOO3oymXcP/3J4UptqHmUnmo +2bRPTTJulxJcLvDZe/1ySOLUMO0ajrZ9AgMBAAGjge8wgewwHQYDVR0OBBYEFATl +qWtGbyeBIN9mR/4GV9jO7ON7MIG8BgNVHSMEgbQwgbGAFATlqWtGbyeBIN9mR/4G +V9jO7ON7oYGNpIGKMIGHMQswCQYDVQQGEwJERTEYMBYGA1UECBMPUmhlaW5sYW5k +LVBmYWx6MRAwDgYDVQQHEwdOZXV3aWVkMRQwEgYDVQQKEwtBTU9PTUEgR21iSDEX +MBUGA1UEAxMOR2VtZWluc2NoYWZ0IDQxHTAbBgkqhkiG9w0BCQEWDmluZm9AYW1v +b21hLmRlggkAobVdocVniAQwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOB +gQCBIgWoP8YiP6tm8rhb81k6myP4ONO4vOaUz9bsadHDWNCTjiQxvo4uVYqTMLKa +Bc7S0VpyvSg7/eGsSWxIUwdn6dUPdo51juGnnJ9dK9DuiNPHb0HP3UJo1gCWgs1v +EnVGfKDdu9FfdNcQtIb28UfF8Pw8WA6mmhQOOh0M9d3ayQ== +-----END CERTIFICATE----- + diff --git a/misc/etc/ssl/amooma/server_key.pem b/misc/etc/ssl/amooma/server_key.pem new file mode 100644 index 0000000..0c8e74e --- /dev/null +++ b/misc/etc/ssl/amooma/server_key.pem @@ -0,0 +1,16 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDC9G3px4ew18PHIm8HJ3yXc9rxqM5uSn1qhjdoWM0zC0Qcue9k +V+5ZUq356yjBbs5wXi3SEWfucXxhnwnzeIqvMeO6y0BiUVsClbqziRCho/hgHPTJ +tZzjt6Mpl3D/9yeFKbah5lJ5qNm0T00ybpcSXC7w2Xv9ckji1DDtGo62fQIDAQAB +AoGAHM0jl9AEednGcJrjsDjjLTTOebkolh6nHJ+re9zyo8HcVCob9cUPz15pmWxm +Xv1RvkQLnOc5ZX6ak4l9XNzIEAvQXNRFXwCOyfpffx/8QhfrG0v2G+K2QG52VxQj +tqnRdLf8HEhCmrJCMvMEAuQkAiirIMTFcaaP1CBbCilr8nkCQQDnSYxEfXMYi4iq +9Xjwn8Ayh1koXFUY+5/0u9SGqzTeTxW1QN2hGhehsd0vlv4cJppcuL4Z+2VYqLQc +zXDZo/MLAkEA18kSHLp+HCd1BW/JEoIQqWlTw6SRx+IsUN7UmnSZS4C+UPRbtq5I +nzgzonZufOEmzoMdwbe9EHAl087f0UfxlwJBAKNIBhGYKvgqEdr3n2Dotuw1J1la +De2sPpmtPPWxyoojdOTYHV8Np59MjSV6yHyhOBq7heGb3EmCGF25H7FWkE8CQGUN +aakAgPxoUfn4zp4XQPxFMhAF6qtDtOMuZzvp7LwaD4ZT2PtlBOdjZ3LmqXlb61N8 +vZuxkx22l1BoqhIU8gMCQQDJsQr5y8UWamYZrNv5YRNnm8aGgJ83Gx3n5b8bKqjh +TM8hqJfFTIfHr90GhHyok1aVkjF+sUtydX1R85IHTDz5 +-----END RSA PRIVATE KEY----- + diff --git a/misc/example/apache-gs5.conf b/misc/example/apache-gs5.conf new file mode 100644 index 0000000..ef81952 --- /dev/null +++ b/misc/example/apache-gs5.conf @@ -0,0 +1,24 @@ + + LoadModule passenger_module /usr/local/rvm/gems/ruby-1.9.2-p290/gems/passenger-3.0.11/ext/apache2/mod_passenger.so + PassengerRoot /usr/local/rvm/gems/ruby-1.9.2-p290/gems/passenger-3.0.11 + PassengerRuby /usr/local/rvm/wrappers/ruby-1.9.2-p290/ruby + + + + <VirtualHost *:443> + ErrorLog "|/usr/bin/logger -t apache -i -p local6.notice" + CustomLog "|/usr/bin/logger -t apache -i -p local6.notice" combined + + + DocumentRoot /opt/GS5/public + PassengerAppRoot /opt/GS5 + RailsEnv development + <Directory /opt/GS5/public> + AllowOverride all + Options -MultiViews + Options FollowSymLinks + </Directory> + SSLEngine on + SSLCertificateFile /etc/ssl/amooma/server_cert.pem + SSLCertificateKeyFile /etc/ssl/amooma/server_key.pem + </VirtualHost> diff --git a/misc/example/nginx b/misc/example/nginx new file mode 100644 index 0000000..ccb2bbb --- /dev/null +++ b/misc/example/nginx @@ -0,0 +1,73 @@ +#!/bin/sh + +##################################################################### +# nginx +# Start Script +# (c) AMOOMA GmbH 2012 +##################################################################### + +### BEGIN INIT INFO +# Provides: nginx +# Required-Start: freeswitch +# Required-Stop: freeswitch +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: starts nginx +# Description: starts nginx +# +### END INIT INFO + +DAEMON=/opt/nginx/sbin/nginx +EXECUTABLE=`basename 'nginx'` +NAME=nginx +DESC=nginx +ARGS="" + +if ! [ -x $DAEMON ] ; then + echo "ERROR: $DAEMON not found" + exit 1 +fi + +set -e + +. /lib/lsb/init-functions + +case "$1" in + start) + echo -n "Starting $DESC: " + start-stop-daemon --start --quiet --pidfile /opt/nginx/logs/$NAME.pid \ + --exec $DAEMON -- $DAEMON_OPTS + echo "$NAME." + ;; + stop) + echo -n "Stopping $DESC: " + start-stop-daemon --stop --quiet --pidfile /opt/nginx/logs/$NAME.pid \ + --exec $DAEMON + echo "$NAME." + ;; + restart|force-reload) + echo -n "Restarting $DESC: " + start-stop-daemon --stop --quiet --pidfile \ + /opt/nginx/logs/$NAME.pid --exec $DAEMON + sleep 1 + start-stop-daemon --start --quiet --pidfile \ + /opt/nginx/logs/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS + echo "$NAME." + ;; + reload) + echo -n "Reloading $DESC configuration: " + start-stop-daemon --stop --signal HUP --quiet --pidfile /opt/nginx/logs/$NAME.pid \ + --exec $DAEMON + echo "$NAME." + ;; + status) + status_of_proc -p /opt/nginx/logs/$NAME.pid "$DAEMON" nginx + ;; + *) + N=/etc/init.d/$NAME + echo "Usage: $N {start|stop|restart|reload|force-reload|status}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/misc/example/xml-interface.txt b/misc/example/xml-interface.txt new file mode 100644 index 0000000..9f05540 --- /dev/null +++ b/misc/example/xml-interface.txt @@ -0,0 +1,25 @@ +User anlegen +============ + +echo '<row><UserName>2222</UserName><LastName>Meyer</LastName><FirstName>Fritz</FirstName><PhoneOffice>+49 228 1234567</PhoneOffice><VoipNr>665544</VoipNr><CellPhone>+49 171 123456</CellPhone><Fax>+49 228 1234444</Fax><Email>fritz.meier@example.com</Email><PIN>123456</PIN><PIN_LastUpdate>2010-11-22T07:09:07.5256939</PIN_LastUpdate><Photo>example.jpg</Photo></row>' | curl -X POST -H 'Content-type: text/xml' -d @- http://0.0.0.0:3000/api/rows + +Userdaten abrufen +================= +curl -H 'Content-type: text/xml' http://0.0.0.0:3000/api/rows/1.xml + +User löschen +============ + +curl -i -H "Accept: application/xml" -X DELETE http://0.0.0.0:3000/api/rows/1 + +If only the user name is known: +curl -i -H "Accept: application/xml" -X DELETE http://0.0.0.0:3000/api/rows/999999?user_name=12345 + + +User updaten +============ + +curl -i -X PUT -H 'Content-Type: application/xml' -d '<row><PhoneOffice>+49 228 5555555</PhoneOffice><VoipNr>665544</VoipNr></row>' http://localhost:3000/api/rows/1 + +If only the user name is known: +curl -i -X PUT -H 'Content-Type: application/xml' -d '<row><PhoneOffice>+49 228 5555555</PhoneOffice><VoipNr>665544</VoipNr></row>' http://localhost:3000/api/rows/999999?user_name=12345
\ No newline at end of file diff --git a/misc/freeswitch/conf/freeswitch.xml b/misc/freeswitch/conf/freeswitch.xml new file mode 100644 index 0000000..04369a7 --- /dev/null +++ b/misc/freeswitch/conf/freeswitch.xml @@ -0,0 +1,759 @@ +<?xml version="1.0" encoding="UTF-8"?> +<document type="freeswitch/xml"> + <X-PRE-PROCESS cmd="set" data="sound_prefix=/opt/freeswitch/sounds/en/us/callie"/> + <X-PRE-PROCESS cmd="set" data="hold_music=local_stream://moh"/> + <X-PRE-PROCESS cmd="set" data="use_profile=internal"/> + <X-PRE-PROCESS cmd="set" data="send_silence_when_idle=400"/> + <X-PRE-PROCESS cmd="set" data="de-ring=%(1000,4000,425.0)"/> + <section name="languages" description="Language Management"> + <language name="en" say-module="en" sound-prefix="/opt/freeswitch/sounds/en/us/callie"> + <phrases> + <macros> + <macro name="voicemail_hello"> + <input pattern="(.*)"> + <match> + <action function="play-file" data="voicemail/vm-hello.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_enter_id"> + <input pattern="(.*)"> + <match> + <action function="play-file" data="voicemail/vm-enter_id.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="voicemail_enter_pass"> + <input pattern="(.*)"> + <match> + <action function="play-file" data="voicemail/vm-enter_pass.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="voicemail_fail_auth"> + <input pattern="(.*)"> + <match> + <action function="play-file" data="voicemail/vm-fail_auth.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_goodbye"> + <input pattern="(.*)"> + <match> + <action function="play-file" data="voicemail/vm-goodbye.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_abort"> + <input pattern="(.*)"> + <match> + <action function="play-file" data="voicemail/vm-abort.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_message_count"> + <input pattern="^(1):(.*)$" break_on_match="true"> + <match> + <action function="play-file" data="voicemail/vm-you_have.wav"/> + <action function="say" data="$1" method="pronounced" type="items"/> + <action function="play-file" data="voicemail/vm-$2.wav"/> + <action function="play-file" data="voicemail/vm-message.wav"/> + </match> + </input> + <input pattern="^(\d+):(.*)$"> + <match> + <action function="play-file" data="voicemail/vm-you_have.wav"/> + <action function="say" data="$1" method="pronounced" type="items"/> + <action function="play-file" data="voicemail/vm-$2.wav"/> + <action function="play-file" data="voicemail/vm-messages.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_menu"> + <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$"> + <match> + <action function="play-file" data="voicemail/vm-listen_new.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + <action function="execute" data="sleep(100)"/> + <action function="play-file" data="voicemail/vm-listen_saved.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$2" method="pronounced" type="name_spelled"/> + <action function="execute" data="sleep(100)"/> + <action function="play-file" data="voicemail/vm-advanced.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$3" method="pronounced" type="name_spelled"/> + <action function="execute" data="sleep(100)"/> + <action function="play-file" data="voicemail/vm-to_exit.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$4" method="pronounced" type="name_phonetic"/> + </match> + </input> + </macro> + <macro name="voicemail_config_menu"> + <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$"> + <match> + <action function="play-file" data="voicemail/vm-to_record_greeting.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + <action function="execute" data="sleep(100)"/> + <action function="play-file" data="voicemail/vm-choose_greeting.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$2" method="pronounced" type="name_spelled"/> + <action function="execute" data="sleep(100)"/> + <action function="play-file" data="voicemail/vm-record_name2.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$3" method="pronounced" type="name_spelled"/> + <action function="execute" data="sleep(100)"/> + <action function="play-file" data="voicemail/vm-change_password.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$4" method="pronounced" type="name_spelled"/> + <action function="execute" data="sleep(100)"/> + <action function="play-file" data="voicemail/vm-main_menu.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$5" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="voicemail_record_name"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="voicemail/vm-record_name1.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_record_file_check"> + <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$"> + <match> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$2" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-save_recording.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$3" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-rerecord.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_record_urgent_check"> + <input pattern="^([0-9#*]):([0-9#*])$"> + <match> + <action function="play-file" data="voicemail/vm-mark-urgent.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-continue.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$2" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="voicemail_forward_prepend"> + <input pattern="^([0-9#*]):([0-9#*])$"> + <match> + <action function="play-file" data="voicemail/vm-forward_add_intro.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-send_message_now.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$2" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="voicemail_forward_message_enter_extension"> + <input pattern="^([0-9#*])$"> + <match> + <action function="play-file" data="voicemail/vm-forward_enter_ext.wav"/> + <action function="play-file" data="voicemail/vm-followed_by.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="voicemail_invalid_extension"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="voicemail/vm-that_was_an_invalid_ext.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_listen_file_check"> + <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):(.*)$"> + <match> + <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-save_recording.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$2" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-delete_recording.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$3" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-forward_to_email.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$4" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-return_call.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$5" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-to_forward.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$6" method="pronounced" type="name_spelled"/> + </match> + </input> + <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$"> + <match> + <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-save_recording.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$2" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-delete_recording.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$3" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-return_call.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$5" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-to_forward.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$6" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="voicemail_choose_greeting"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="voicemail/vm-choose_greeting_choose.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_choose_greeting_fail"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="voicemail/vm-choose_greeting_fail.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_record_greeting"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="voicemail/vm-record_greeting.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_record_message"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="voicemail/vm-record_message.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_greeting_selected"> + <input pattern="^(\d+)$"> + <match> + <action function="play-file" data="voicemail/vm-greeting.wav"/> + <action function="say" data="$1" method="pronounced" type="items"/> + <action function="play-file" data="voicemail/vm-selected.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_play_greeting"> + <input pattern="^(\d+)$"> + <match> + <action function="play-file" data="voicemail/vm-person.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-not_available.wav"/> + </match> + </input> + <input pattern="^name:(.+)$"> + <match> + <action function="play-file" data="$1"/> + <action function="play-file" data="voicemail/vm-not_available.wav"/> + </match> + </input> + <input pattern="^greeting:(.+)$"> + <match> + <action function="play-file" data="$1"/> + </match> + </input> + </macro> + <macro name="voicemail_say_number"> + <input pattern="^(\d+)$"> + <match> + <action function="say" data="$1" method="pronounced" type="items"/> + </match> + </input> + </macro> + <macro name="voicemail_say_message_number"> + <input pattern="^([a-z]+):(\d+)$"> + <match> + <action function="play-file" data="voicemail/vm-$1.wav"/> + <action function="play-file" data="voicemail/vm-message_number.wav"/> + <action function="say" data="$2" method="pronounced" type="items"/> + </match> + </input> + </macro> + <macro name="voicemail_say_phone_number"> + <input pattern="^(.*)$"> + <match> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="voicemail_say_name"> + <input pattern="^(.*)$"> + <match> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="voicemail_ack"> + <input pattern="^(too-small)$"> + <match> + <action function="play-file" data="voicemail/vm-too-small.wav"/> + </match> + </input> + <input pattern="^(deleted)$"> + <match> + <action function="play-file" data="voicemail/vm-message.wav"/> + <action function="play-file" data="voicemail/vm-$1.wav"/> + </match> + </input> + <input pattern="^(saved)$"> + <match> + <action function="play-file" data="voicemail/vm-message.wav"/> + <action function="play-file" data="voicemail/vm-$1.wav"/> + </match> + </input> + <input pattern="^(emailed)$"> + <match> + <action function="play-file" data="voicemail/vm-message.wav"/> + <action function="play-file" data="voicemail/vm-$1.wav"/> + </match> + </input> + <input pattern="^(marked-urgent)$"> + <match> + <action function="play-file" data="voicemail/vm-message.wav"/> + <action function="play-file" data="voicemail/vm-$1.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_say_date"> + <input pattern="^(.*)$"> + <match> + <action function="say" data="$1" method="pronounced" type="current_date_time"/> + </match> + </input> + </macro> + <macro name="voicemail_disk_quota_exceeded"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="voicemail/vm-mailbox_full.wav"/> + </match> + </input> + </macro> + <macro name="valet_announce_ext"> + <input pattern="^([^\:]+):(.*)$"> + <match> + <action function="say" data="$2" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="valet_lot_full"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="tone_stream://%(275,10,600);%(275,100,300)"/> + </match> + </input> + </macro> + <macro name="valet_lot_empty"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="tone_stream://%(275,10,600);%(275,100,300)"/> + </match> + </input> + </macro> + <macro name="logged_in"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="ivr/ivr-you_are_now_logged_in.wav"/> + </match> + </input> + </macro> + <macro name="logged_out"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="ivr/ivr-you_are_now_logged_out.wav"/> + </match> + </input> + </macro> + <macro name="acd_announce_position_enter"> + <input pattern="^([0-9]+)$"> + <match> + <action function="play-file" data="ivr/ivr-you_are_number.wav"/> + <action function="say" data="$1" method="pronounced" type="number"/> + <action function="play-file" data="ivr/ivr-in_line.wav"/> + </match> + </input> + </macro> + <macro name="acd_announce_position_change"> + <input pattern="^1$"> + <match> + <action function="play-file" data="ivr/ivr-you_are_number.wav"/> + <action function="say" data="1" method="pronounced" type="number"/> + <action function="play-file" data="ivr/ivr-in_line.wav"/> + <action function="break"/> + </match> + </input> + <input pattern="^([0-9]+)$"> + <match> + <action function="play-file" data="ivr/ivr-you_are_number.wav"/> + <action function="say" data="$1" method="pronounced" type="number"/> + <action function="play-file" data="ivr/ivr-in_line.wav"/> + <action function="play-file" data="ivr/ivr-thank_you_for_holding.wav"/> + </match> + </input> + </macro> + <macro name="acd_announce_position_periodic"> + <input pattern="^1$"> + <match> + <action function="play-file" data="ivr/ivr-you_are_number.wav"/> + <action function="say" data="1" method="pronounced" type="number"/> + <action function="play-file" data="ivr/ivr-in_line.wav"/> + <action function="break"/> + </match> + </input> + <input pattern="^([0-9]+)$"> + <match> + <action function="play-file" data="ivr/ivr-you_are_number.wav"/> + <action function="say" data="$1" method="pronounced" type="number"/> + <action function="play-file" data="ivr/ivr-in_line.wav"/> + <action function="play-file" data="ivr/ivr-thank_you_for_holding.wav"/> + </match> + </input> + </macro> + <macro name="acd_announce_call_agents"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="$1"/> + </match> + </input> + </macro> + <macro name="acd_greeting"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="$1"/> + </match> + </input> + </macro> + <macro name="acd_goodbye"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="$1"/> + </match> + </input> + </macro> + <macro name="acd_agent_status"> + <input pattern="^active$"> + <match> + <action function="play-file" data="ivr/ivr-you_are_now_logged_in.wav"/> + </match> + </input> + <input pattern="^inactive$"> + <match> + <action function="play-file" data="ivr/ivr-you_are_now_logged_out.wav"/> + </match> + </input> + </macro> + </macros> + </phrases> + </language> + </section> + <section name="configuration" description="Gemeinschaft5 FreeSwitch configuration"> + <configuration name="acl.conf" description="Network Lists"> + <network-lists> + <list name="domains" default="deny"> + <node type="allow" cidr="127.0.0.1/32"/> + </list> + </network-lists> + </configuration> + <configuration name="console.conf" description="Console Logger"> + <mappings> + <map name="all" value="console,debug,info,notice,warning,err,crit,alert"/> + </mappings> + <settings> + <param name="colorize" value="true"/> + <param name="loglevel" value="info"/> + </settings> + </configuration> + <configuration name="event_socket.conf" description="Socket Client"> + <settings> + <param name="nat-map" value="false"/> + <param name="listen-ip" value="127.0.0.1"/> + <param name="listen-port" value="8021"/> + </settings> + </configuration> + <configuration name="fifo.conf" description="FIFO Configuration"> + <settings> + <param name="delete-all-outbound-member-on-startup" value="false"/> + </settings> + <fifos> + </fifos> + </configuration> + <configuration name="local_stream.conf" description="stream files from local dir"> + <directory name="default" path="/opt/freeswitch/sounds/music/16000"> + <param name="rate" value="16000"/> + <param name="shuffle" value="true"/> + <param name="channels" value="1"/> + <param name="interval" value="20"/> + <param name="timer-name" value="soft"/> + </directory> + <directory name="moh" path="/opt/freeswitch/sounds/music/16000"> + <param name="rate" value="16000"/> + <param name="shuffle" value="true"/> + <param name="channels" value="1"/> + <param name="interval" value="20"/> + <param name="timer-name" value="soft"/> + </directory> + <directory name="mohl" path="/opt/freeswitch/sounds/music/8000"> + <param name="rate" value="8000"/> + <param name="shuffle" value="true"/> + <param name="channels" value="1"/> + <param name="interval" value="20"/> + <param name="timer-name" value="soft"/> + </directory> + </configuration> + <configuration name="logfile.conf" description="File Logging"> + <settings> + <param name="rotate-on-hup" value="true"/> + </settings> + <profiles> + <profile name="default"> + <settings> + <param name="logfile" value="/var/log/freeswitch/freeswitch.log"/> + <param name="rollover" value="10485760"/> + </settings> + <mappings> + <map name="all" value="debug,info,notice,warning,err,crit,alert"/> + </mappings> + </profile> + </profiles> + </configuration> + <configuration name="xml_rpc.conf" description="XML RPC"> + <settings> + <param name="http-port" value="228080"/> + <param name="auth-realm" value="gemeinschaft"/> + <param name="auth-user" value="7ff020f74d99a1b88bd2"/> + <param name="auth-pass" value="85d13b5a56c55f7261cc"/> + </settings> + </configuration> + <configuration name="switch.conf" description="Core Configuration"> + <cli-keybindings> + </cli-keybindings> + <settings> + <param name="colorize-console" value="true"/> + <param name="max-sessions" value="1000"/> + <param name="sessions-per-second" value="30"/> + <param name="loglevel" value="debug"/> + <param name="mailer-app" value="sendmail"/> + <param name="mailer-app-args" value="-t"/> + <param name="dump-cores" value="yes"/> + <param name="auto-create-schemas" value="true"/> + <param name="rtp-enable-zrtp" value="false"/> + <param name="rtp-start-port" value="16384" /> + <param name="rtp-end-port" value="32768" /> + <param name="core-db-dsn" value="gemeinschaft:gemeinschaft:gemeinschaft"/> + </settings> + </configuration> + <configuration name="spandsp.conf" description="Tone detector descriptors"> + <descriptors> + <descriptor name="1"> + <tone name="CED_TONE"> + <element freq1="2100" freq2="0" min="500" max="0"/> + </tone> + <tone name="SIT"> + <element freq1="950" freq2="0" min="256" max="400"/> + <element freq1="1400" freq2="0" min="256" max="400"/> + <element freq1="1800" freq2="0" min="256" max="400"/> + </tone> + <tone name="REORDER_TONE"> + <element freq1="480" freq2="620" min="224" max="272"/> + <element freq1="0" freq2="0" min="224" max="272"/> + </tone> + <tone name="BUSY_TONE"> + <element freq1="480" freq2="620" min="464" max="516"/> + <element freq1="0" freq2="0" min="464" max="516"/> + </tone> + </descriptor> + <descriptor name="44"> + <tone name="CED_TONE"> + <element freq1="2100" freq2="0" min="500" max="0"/> + </tone> + <tone name="SIT"> + <element freq1="950" freq2="0" min="256" max="400"/> + <element freq1="1400" freq2="0" min="256" max="400"/> + <element freq1="1800" freq2="0" min="256" max="400"/> + </tone> + <tone name="REORDER_TONE"> + <element freq1="400" freq2="0" min="368" max="416"/> + <element freq1="0" freq2="0" min="336" max="368"/> + <element freq1="400" freq2="0" min="256" max="288"/> + <element freq1="0" freq2="0" min="512" max="544"/> + </tone> + <tone name="BUSY_TONE"> + <element freq1="400" freq2="0" min="352" max="384"/> + <element freq1="0" freq2="0" min="352" max="384"/> + <element freq1="400" freq2="0" min="352" max="384"/> + <element freq1="0" freq2="0" min="352" max="384"/> + </tone> + </descriptor> + <descriptor name="49"> + <tone name="CED_TONE"> + <element freq1="2100" freq2="0" min="500" max="0"/> + </tone> + <tone name="SIT"> + <element freq1="900" freq2="0" min="256" max="400"/> + <element freq1="1400" freq2="0" min="256" max="400"/> + <element freq1="1800" freq2="0" min="256" max="400"/> + </tone> + <tone name="REORDER_TONE"> + <element freq1="425" freq2="0" min="224" max="272"/> + <element freq1="0" freq2="0" min="224" max="272"/> + </tone> + <tone name="BUSY_TONE"> + <element freq1="425" freq2="0" min="464" max="516"/> + <element freq1="0" freq2="0" min="464" max="516"/> + </tone> + </descriptor> + </descriptors> + </configuration> + <configuration name="fax.conf" description="FAX application configuration"> + <settings> + <param name="use-ecm" value="true"/> + <param name="verbose" value="true"/> + <param name="disable-v17" value="false"/> + <param name="ident" value=""/> + <param name="header" value="GS4"/> + <param name="spool-dir" value="/opt/GS5/misc/fax"/> + <param name="file-prefix" value="fax_in_"/> + </settings> + </configuration> + <configuration name="modules.conf" description="Modules"> + <modules> + <load module="mod_console"/> + <load module="mod_logfile"/> + <load module="mod_lua"/> + <!-- <load module="mod_xml_rpc"/> --> + <!-- <load module="mod_cdr_csv"/> --> + <load module="mod_event_socket"/> + <load module="mod_sofia"/> + <load module="mod_loopback"/> + <load module="mod_commands"/> + <load module="mod_conference"/> + <load module="mod_dptools"/> + <load module="mod_expr"/> + <!-- <load module="mod_fifo"/> --> + <load module="mod_voicemail"/> + <!-- <load module="mod_esf"/> --> + <!-- <load module="mod_fsv"/> --> + <load module="mod_valet_parking"/> + <!-- <load module="mod_curl"/> --> + <load module="mod_dialplan_xml"/> + <load module="mod_sndfile"/> + <load module="mod_native_file"/> + <load module="mod_local_stream"/> + <load module="mod_tone_stream"/> + <load module="mod_say_en"/> + <load module="mod_spandsp"/> + </modules> + </configuration> + <configuration name="post_load_modules.conf" description="Modules"> + <modules> + </modules> + </configuration> + <configuration name="voicemail.conf" description="Voicemail"> + <settings> + </settings> + <profiles> + <profile name="default"> + <param name="file-extension" value="wav"/> + <param name="terminator-key" value="#"/> + <param name="max-login-attempts" value="3"/> + <param name="digit-timeout" value="10000"/> + <param name="min-record-len" value="3"/> + <param name="max-record-len" value="300"/> + <param name="max-retries" value="3"/> + <param name="tone-spec" value="%(1000, 0, 640)"/> + <param name="callback-dialplan" value="XML"/> + <param name="callback-context" value="default"/> + <param name="play-new-messages-key" value="1"/> + <param name="play-saved-messages-key" value="2"/> + <param name="login-keys" value="0"/> + <param name="main-menu-key" value="0"/> + <param name="config-menu-key" value="5"/> + <param name="record-greeting-key" value="1"/> + <param name="choose-greeting-key" value="2"/> + <param name="change-pass-key" value="6"/> + <param name="record-name-key" value="3"/> + <param name="record-file-key" value="3"/> + <param name="listen-file-key" value="1"/> + <param name="save-file-key" value="2"/> + <param name="delete-file-key" value="7"/> + <param name="undelete-file-key" value="8"/> + <param name="email-key" value="4"/> + <param name="pause-key" value="0"/> + <param name="restart-key" value="1"/> + <param name="ff-key" value="6"/> + <param name="rew-key" value="4"/> + <param name="skip-greet-key" value="#"/> + <param name="record-silence-threshold" value="200"/> + <param name="record-silence-hits" value="2"/> + <param name="web-template-file" value="web-vm.tpl"/> + <param name="operator-extension" value="operator XML default"/> + <param name="operator-key" value="9"/> + <param name="vmain-extension" value="vmain XML default"/> + <param name="vmain-key" value="*"/> + <param name="odbc-dsn" value="gemeinschaft:gemeinschaft:gemeinschaft"/> + <email> + <param name="notify-template-file" value="notify-voicemail.tpl"/> + <param name="template-file" value="voicemail.tpl"/> + <param name="date-fmt" value="%A, %B %d %Y, %I %M %p"/> + <param name="email-from" value="${voicemail_account}@${voicemail_domain}"/> + </email> + </profile> + </profiles> + </configuration> + <configuration name="lua.conf" description="LUA Configuration"> + <settings> + <param name="script-directory" value="$${base_dir}/scripts/?.lua"/> + <param name="xml-handler-script" value="configuration.lua"/> + <param name="xml-handler-bindings" value="directory|configuration"/> + <param name="startup-script" value="fax_daemon.lua"/> + <param name="startup-script" value="event_manager.lua"/> + </settings> + </configuration> + </section> + <section name="dialplan" description="Regex/XML dialplan"> + <context name="default"> + <extension name="invoke_default_dialplan" continue="true"> + <condition> + <action application="set" data="script=${lua(dialplan_default.lua)}"/> + </condition> + </extension> + <extension name="transfer_loop" continue="false"> + <condition field="endpoint_disposition" expression="BLIND_TRANSFER"> + <action application="transfer" data=" XML default"/> + </condition> + </extension> + </context> + <context name="redirected"> + <extension name="redirected" continue="true"> + <condition> + <action application="transfer" data="${sip_redirect_contact_user_0} XML default"/> + </condition> + </extension> + </context> + </section> +</document> diff --git a/misc/freeswitch/scripts/acd_wait.lua b/misc/freeswitch/scripts/acd_wait.lua new file mode 100644 index 0000000..fd16bea --- /dev/null +++ b/misc/freeswitch/scripts/acd_wait.lua @@ -0,0 +1,45 @@ +-- Gemeinschaft 5: acd call handler +-- (c) AMOOMA GmbH 2012 +-- + +local caller_uuid = argv[1]; +local acd_id = tonumber(argv[2]); +local timeout = tonumber(argv[3]); +local retry_timeout = tonumber(argv[4]); +local acd_caller_id = tonumber(argv[5]); + +-- initialize logging +require 'common.log' +local log = common.log.Log:new{ prefix = '### [' .. caller_uuid .. '] ' }; + +if not acd_id then + log:error('ACD_WAIT - automaticcalldistributor=', acd_id, ' not specified'); + return; +end + +-- connect to database +require 'common.database' +local database = common.database.Database:new{ log = log }:connect(); +if not database:connected() then + log:critical('ACD_WAIT - database connect failed'); + database:release(); + return; +end + +require 'dialplan.acd' +local acd = dialplan.acd.AutomaticCallDistributor:new{ log = log, database = database }:find_by_id(acd_id); + +if not acd then + log:error('ACD_WAIT - automaticcalldistributor=', acd_id, ' not found'); + database:release(); + return; +end + +log:debug('ACD_WAIT ', acd_id, ' - start'); +acd:wait_turn(caller_uuid, acd_caller_id, timeout, retry_timeout); +log:debug('ACD_WAIT ', acd_id, ' - end'); + +-- release database +if database then + database:release(); +end diff --git a/misc/freeswitch/scripts/common/call_forwarding.lua b/misc/freeswitch/scripts/common/call_forwarding.lua new file mode 100644 index 0000000..3942d05 --- /dev/null +++ b/misc/freeswitch/scripts/common/call_forwarding.lua @@ -0,0 +1,47 @@ +-- Gemeinschaft 5 module: call forwarding class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +CallForwarding = {} + +-- Create CallForwarding object +function CallForwarding.new(self, arg, object) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self + self.log = arg.log; + self.database = arg.database; + self.record = arg.record; + self.domain = arg.domain; + return object; +end + +-- Find call forwarding by id +function CallForwarding.find_by_id(self, id) + local sql_query = 'SELECT * FROM `call_forwards` WHERE `id`= ' .. tonumber(id) .. ' LIMIT 1'; + local record = nil + + self.database:query(sql_query, function(entry) + record = entry; + end) + + if record then + call_forwarding = CallForwarding:new(self) + call_forwarding.record = record + return call_forwarding + end + + return nil +end + +function CallForwarding.presence_set(self, presence_state) + require 'dialplan.presence' + local presence = dialplan.presence.Presence:new(); + + presence:init{log = self.log, accounts = { 'f-cftg-' .. tostring(self.record.id) }, domain = self.domain, uuid = 'call_forwarding_' .. tostring(self.record.id)}; + + return presence:set(presence_state); +end diff --git a/misc/freeswitch/scripts/common/call_history.lua b/misc/freeswitch/scripts/common/call_history.lua new file mode 100644 index 0000000..c5bc0bf --- /dev/null +++ b/misc/freeswitch/scripts/common/call_history.lua @@ -0,0 +1,140 @@ +-- Gemeinschaft 5 module: call_history class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + + +function camelize_type(account_type) + ACCOUNT_TYPES = { + sipaccount = 'SipAccount', + conference = 'Conference', + faxaccount = 'FaxAccount', + callthrough = 'Callthrough', + huntgroup = 'HuntGroup', + automaticcalldistributor = 'AutomaticCallDistributor', + } + + return ACCOUNT_TYPES[account_type] or account_type; +end + + +CallHistory = {} + +-- Create CallHistory object +function CallHistory.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.class = 'callhistory'; + self.log = arg.log; + self.database = arg.database; + return object; +end + + +function CallHistory.insert_entry(self, call_history) + local keys = {} + local values = {} + + call_history.created_at = 'NOW()'; + call_history.updated_at = 'NOW()'; + + for key, value in pairs(call_history) do + table.insert(keys, key); + table.insert(values, value); + end + + local sql_query = 'INSERT INTO `call_histories` (`' .. table.concat(keys, "`, `") .. '`) VALUES (' .. table.concat(values, ", ") .. ')'; + local result = self.database:query(sql_query); + if not result then + self.log:error('[', call_history.caller_channel_uuid, '] CALL_HISTORY_SAVE - SQL: ', sql_query); + end + return result; +end + + +function CallHistory.insert_event(self, uuid, account_type, account_id, entry_type, event) + require 'common.str' + local call_history = {} + + call_history.entry_type = common.str.to_sql(entry_type); + call_history.call_historyable_type = common.str.to_sql(camelize_type(account_type)); + call_history.call_historyable_id = common.str.to_sql(account_id); + call_history.caller_channel_uuid = common.str.to_sql(uuid); + call_history.duration = common.str.to_sql(event:getHeader('variable_billsec')); + call_history.caller_id_number = common.str.to_sql(event:getHeader('variable_effective_caller_id_number')); + call_history.caller_id_name = common.str.to_sql(event:getHeader('variable_effective_caller_id_name')); + call_history.callee_id_number = common.str.to_sql(event:getHeader('variable_effective_callee_id_number')); + call_history.callee_id_name = common.str.to_sql(event:getHeader('variable_effective_callee_id_name')); + call_history.result = common.str.to_sql(event:getHeader('variable_hangup_cause')); + call_history.start_stamp = 'FROM_UNIXTIME(' .. math.floor(common.str.to_i(event:getHeader('Caller-Channel-Created-Time')) / 1000000) .. ')'; + call_history.caller_account_type = common.str.to_sql(camelize_type(event:getHeader('variable_gs_caller_account_type') or event:getHeader('variable_gs_account_type'))); + call_history.caller_account_id = common.str.to_sql(event:getHeader('variable_gs_caller_account_id') or event:getHeader('variable_gs_account_id')); + call_history.auth_account_type = common.str.to_sql(camelize_type(event:getHeader('variable_gs_auth_account_type'))); + call_history.auth_account_id = common.str.to_sql(event:getHeader('variable_gs_auth_account_id')); + call_history.callee_account_type = common.str.to_sql(camelize_type(event:getHeader('variable_gs_destination_type'))); + call_history.callee_account_id = common.str.to_sql(event:getHeader('variable_gs_destination_id')); + call_history.destination_number = common.str.to_sql(event:getHeader('variable_gs_destination_number')); + call_history.forwarding_service = common.str.to_sql(event:getHeader('variable_gs_forwarding_service')); + + if common.str.to_s(event:getHeader('variable_gs_call_service')) == 'pickup' then + call_history.forwarding_service = common.str.to_sql('pickup'); + end + + self.log:info('[', uuid,'] CALL_HISTORY_SAVE ', entry_type,' - account: ', account_type, '=', account_id, + ', caller: ', call_history.caller_id_number, ' ', call_history.caller_id_name, + ', callee: ', call_history.callee_id_number, ' ', call_history.callee_id_name, + ', result: ', call_history.result + ); + + return self:insert_entry(call_history); +end + + +function CallHistory.insert_forwarded(self, uuid, account_type, account_id, caller, destination, result) + require 'common.str' + + local call_history = {} + + call_history.entry_type = common.str.to_sql('forwarded'); + call_history.call_historyable_type = common.str.to_sql(camelize_type(account_type)); + call_history.call_historyable_id = common.str.to_sql(account_id); + call_history.caller_channel_uuid = common.str.to_sql(uuid); + + call_history.duration = common.str.to_sql(caller:to_i('billsec')); + call_history.caller_id_number = common.str.to_sql(caller.caller_id_number); + call_history.caller_id_name = common.str.to_sql(caller.caller_id_name); + call_history.callee_id_number = common.str.to_sql(caller.callee_id_number); + call_history.callee_id_name = common.str.to_sql(caller.callee_id_name); + call_history.result = common.str.to_sql(result.cause or 'UNSPECIFIED'); + call_history.start_stamp = 'FROM_UNIXTIME(' .. math.floor(caller:to_i('created_time') / 1000000) .. ')'; + + if caller.account then + call_history.caller_account_type = common.str.to_sql(camelize_type(caller.account.class)); + call_history.caller_account_id = common.str.to_sql(caller.account.id); + end + + if caller.auth_account then + call_history.auth_account_type = common.str.to_sql(camelize_type(caller.auth_account.class)); + call_history.auth_account_id = common.str.to_sql(caller.auth_account.id); + end + + if destination then + call_history.callee_account_type = common.str.to_sql(camelize_type(destination.type)); + call_history.callee_account_id = common.str.to_sql(destination.id); + call_history.destination_number = common.str.to_sql(destination.number); + end + + call_history.forwarding_service = common.str.to_sql(caller.forwarding_service); + + self.log:info('CALL_HISTORY_SAVE forwarded - account: ', account_type, '=', account_id, + ', service: ', call_history.forwarding_service, + ', caller: ', call_history.caller_id_number, ' ', call_history.caller_id_name, + ', callee: ', call_history.callee_id_number, ' ', call_history.callee_id_name, + ', result: ', call_history.result + ); + + return self:insert_entry(call_history); +end diff --git a/misc/freeswitch/scripts/common/conference.lua b/misc/freeswitch/scripts/common/conference.lua new file mode 100644 index 0000000..d2bf829 --- /dev/null +++ b/misc/freeswitch/scripts/common/conference.lua @@ -0,0 +1,239 @@ +-- Gemeinschaft 5 module: conference class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +Conference = {} + +MEMBERS_MAX = 100; +PIN_LENGTH_MAX = 10; +PIN_LENGTH_MIN = 2; +PIN_TIMEOUT = 4000; +ANNOUNCEMENT_MAX_LEN = 10 +ANNOUNCEMENT_SILENCE_THRESHOLD = 500 +ANNOUNCEMENT_SILENCE_LEN = 3 + +-- create conference object +function Conference.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.class = 'conference'; + self.log = arg.log; + self.database = arg.database; + self.record = arg.record; + self.max_members = 0; + return object; +end + +-- find conference by id +function Conference.find_by_id(self, id) + local sql_query = 'SELECT * FROM `conferences` WHERE `id`= ' .. tonumber(id) .. ' LIMIT 1'; + local conference = nil; + + self.database:query(sql_query, function(conference_entry) + conference = Conference:new(self); + conference.record = conference_entry; + conference.id = tonumber(conference_entry.id); + conference.uuid = conference_entry.uuid; + conference.max_members = tonumber(conference.record.max_members) or MEMBERS_MAX; + end) + + return conference; +end + +-- find invitee by phone numbers +function Conference.find_invitee_by_numbers(self, phone_numbers) + if not self.record then + return false + end + + local sql_query = string.format( + "SELECT `conference_invitees`.`pin` AS `pin`, `conference_invitees`.`speaker` AS `speaker`, `conference_invitees`.`moderator` AS `moderator` " .. + "FROM `conference_invitees` JOIN `phone_numbers` ON `phone_numbers`.`phone_numberable_id` = `conference_invitees`.`id` " .. + "WHERE `phone_numbers`.`phone_numberable_type` = 'ConferenceInvitee' AND `conference_invitees`.`conference_id` = %d " .. + "AND `phone_numbers`.`number` IN ('%s') LIMIT 1", self.record.id, table.concat(phone_numbers, "','")); + + local invitee = nil; + + self.database:query(sql_query, function(conference_entry) + invitee = conference_entry; + end) + + return invitee; +end + +function Conference.count(self) + return tonumber(self.caller:result('conference ' .. self.record.id .. ' list count')) or 0; +end + +-- Try to enter a conference +function Conference.enter(self, caller, domain) + local cause = "NORMAL_CLEARING"; + local pin = nil; + local flags = {'waste'}; + + self.caller = caller; + + require "common.phone_number" + local phone_number_class = common.phone_number.PhoneNumber:new{log = self.log, database = self.database} + local phone_numbers = phone_number_class:list_by_owner(self.record.id, "Conference"); + + -- Set conference presence + require "dialplan.presence" + local presence = dialplan.presence.Presence:new(); + presence:init{ log = log, accounts = phone_numbers, domain = domain, uuid = "conference_" .. self.record.id }; + + local conference_count = self:count(); + + -- Check if conference is full + if conference_count >= self.max_members then + presence:early(); + self.log:debug(string.format("full conference %s (\"%s\"), members: %d, members allowed: %d", self.record.id, self.record.name, conference_count, self.max_members)); + + if (tonumber(self.record.conferenceable_id) == caller.account_owner_id) + and (self.record.conferenceable_type == caller.account_owner_type) then + self.log:debug("Allow owner of this conterence to enter a full conference"); + else + cause = "CALL_REJECTED"; + caller:hangup(cause); + return cause; + end; + end + + -- Check if conference is within time frame + if self.record.start and self.record['end'] then + local d = {} + _,_,d.year,d.month,d.day,d.hour,d.min,d.sec=string.find(self.record.start, "(%d+)-(%d+)-(%d+) (%d+):(%d+):(%d+)"); + + local conference_start = os.time(d); + _,_,d.year,d.month,d.day,d.hour,d.min,d.sec=string.find(self.record['end'], "(%d+)-(%d+)-(%d+) (%d+):(%d+):(%d+)"); + local conference_end = os.time(d); + local now = os.time(os.date("!*t", os.time())); + + log:debug("conference - open: " .. os.date("%c",conference_start) .. " by " .. os.date("%c",conference_end) .. ", now: " .. os.date("%c",now)); + + if now < conference_start or now > conference_end then + cause = "CALL_REJECTED"; + caller:hangup(cause); + return cause; + end + end + + require 'common.str' + -- Owner ist always moderator + if (tonumber(self.record.conferenceable_id) == caller.account_owner_id) and (self.record.conferenceable_type == caller.account_owner_type) then + table.insert(flags, 'moderator'); + log:debug("is owner - conference: " .. self.record.id .. ", owner: " .. caller.account_owner_type .. ":" .. caller.account_owner_id); + else + local invitee = self:find_invitee_by_numbers(caller.caller_phone_numbers); + + if not common.str.to_b(self.record.open_for_anybody) and not invitee then + log:debug(string.format("conference %s (\"%s\"), caller %s not allowed to enter this conference", self.record.id, self.record.name, caller.caller_phone_number)); + cause = "CALL_REJECTED"; + caller:hangup(cause); + return cause; + end + + if invitee then + log:debug("conference " .. self.record.id .. " member invited - speaker: " .. invitee.speaker .. ", moderator: " .. invitee.moderator); + if common.str.to_b(invitee.moderator) then + table.insert(flags, 'moderator'); + end + if not common.str.to_b(invitee.speaker) then + table.insert(flags, 'mute'); + end + pin = invitee.pin; + else + log:debug("conference " .. self.record.id .. " caller not invited"); + end + end + + if not pin and self.record.pin then + pin = self.record.pin + end + + caller:answer(); + caller:sleep(1000); + caller.session:streamFile('conference/conf-welcome.wav'); + + if pin and pin ~= "" then + local digits = ""; + for i = 1, 3, 1 do + if digits == pin then + break + elseif digits ~= "" then + caller.session:streamFile('conference/conf-bad-pin.wav'); + end + digits = caller.session:read(PIN_LENGTH_MIN, PIN_LENGTH_MAX, 'conference/conf-enter_conf_pin.wav', PIN_TIMEOUT, '#'); + end + if digits ~= pin then + caller.session:streamFile("conference/conf-goodbye.wav"); + return "CALL_REJECTED"; + end + end + + self.log:debug(string.format("entering conference %s - name: \"%s\", flags: %s, members: %d, max. members: %d", + self.record.id, self.record.name, table.concat(flags, ','), conference_count, self.max_members)); + + -- Members count will be incremented in a few milliseconds, set presence + if (conference_count + 1) >= self.max_members then + presence:early(); + else + presence:confirmed(); + end + + -- Enter the conference + local name_file = nil; + + -- Record caller's name + if common.str.to_b(self.record.announce_new_member_by_name) or common.str.to_b(self.record.announce_left_member_by_name) then + local uid = session:get_uuid(); + name_file = "/tmp/conference_caller_name_" .. uid .. ".wav"; + caller.session:streamFile("voicemail/vm-record_name1.wav"); + caller.session:execute("playback", "tone_stream://%(1000,0,500)"); + session:recordFile(name_file, ANNOUNCEMENT_MAX_LEN, ANNOUNCEMENT_SILENCE_THRESHOLD, ANNOUNCEMENT_SILENCE_LEN); + caller.session:streamFile(name_file); + end + + -- Play entering caller's name if recorded + if name_file and (self:count() > 0) and common.str.to_b(self.record.announce_new_member_by_name) then + caller.session:execute('set',"result=${conference(" .. self.record.id .. " play ".. name_file .. ")}"); + caller.session:execute('set',"result=${conference(" .. self.record.id .. " play conference/conf-has_joined.wav)}"); + else + -- Ensure a surplus "#" digit is not passed to the conference + caller.session:read(1, 1, '', 1000, "#"); + end + + local result = caller.session:execute('conference', self.record.id .. "@profile_" .. self.record.id .. "++flags{" .. table.concat(flags, '|') .. "}"); + self.log:debug('exited conference - result: ' .. tostring(result)); + caller.session:streamFile("conference/conf-goodbye.wav") + + -- Play leaving caller's name if recorded + if name_file then + if (self:count() > 0) and common.str.to_b(self.record.announce_left_member_by_name) then + if (self:count() == 1) then + caller.session:sleep(3000); + end + caller.session:execute('set',"result=${conference(" .. self.record.id .. " play ".. name_file .. ")}"); + caller.session:execute('set',"result=${conference(" .. self.record.id .. " play conference/conf-has_left.wav)}"); + end + os.remove(name_file); + end + + -- Set presence according to member count + conference_count = self:count(); + if conference_count >= self.max_members then + presence:early(); + elseif conference_count > 0 then + presence:confirmed(); + else + presence:terminated(); + end + + cause = "NORMAL_CLEARING"; + caller.session:hangup(cause); + return cause; +end diff --git a/misc/freeswitch/scripts/common/configuration_file.lua b/misc/freeswitch/scripts/common/configuration_file.lua new file mode 100644 index 0000000..67e1f3b --- /dev/null +++ b/misc/freeswitch/scripts/common/configuration_file.lua @@ -0,0 +1,70 @@ +-- Gemeinschaft 5 module: configuration file +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +function ignore_comments(line) + return line:gsub(';+([^;]*)', function(entry) + return ''; + end); +end + +-- parse configuration +function parse(lines, filter_section_name) + require 'common.str' + local section = {} + local root = { [true] = section } + + for line in lines do + if line then + local ignore_line = false; + line = ignore_comments(line); + + line:gsub('^%s*%[(.-)%]%s*$', function(section_name) + if tostring(section_name):match('%=false$') then + section = {} + else + root[common.str.strip(section_name)] = {}; + section = root[common.str.strip(section_name)]; + end + ignore_line = true; + end); + + if not ignore_line then + key, value = common.str.partition(line, '='); + if value and key and not common.str.strip(key):match('%s') then + section[common.str.strip(key)] = common.str.strip(value); + else + line = common.str.strip(line); + if not common.str.blank(line) then + if line:match(',') then + table.insert(section, common.str.strip_to_a(line, ',')); + else + table.insert(section, line); + end + end + end + end + end + end + + if filter_section_name == false then + root[true] = nil; + elseif filter_section_name then + return root[filter_section_name]; + end + + return root; +end + +-- retrieve configuration from file +function get(file_name, filter_section_name) + local file = io.open(file_name); + + if file then + local result = parse(file:lines(), filter_section_name); + file:close(); + return result; + end +end diff --git a/misc/freeswitch/scripts/common/database.lua b/misc/freeswitch/scripts/common/database.lua new file mode 100644 index 0000000..3692f84 --- /dev/null +++ b/misc/freeswitch/scripts/common/database.lua @@ -0,0 +1,151 @@ +-- Gemeinschaft 5 module: database class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +Database = {} + +DATABASE_DRIVER = 'mysql' + +function Database.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.class = 'database'; + self.log = arg.log; + self.conn = nil; + return object; +end + + +function Database.connect(self, database_name, user_name, password, host_name) + local database_driver = nil; + if not (database_name and user_name and password) then + require 'common.configuration_file' + local config = common.configuration_file.get('/opt/freeswitch/scripts/ini/database.ini'); + if config then + database_driver = config[true].driver + database_name = config[database_driver].database + user_name = config[database_driver].user + password = config[database_driver].password + host_name = config[database_driver].host + end + end + + host_name = host_name or 'localhost'; + database_driver = database_driver or DATABASE_DRIVER; + + if database_driver == 'mysql' then + require "luasql.mysql" + self.env = luasql.mysql(); + elseif database_driver == 'odbc' then + require "luasql.odbc" + self.env = luasql.odbc(); + end + + self.conn = self.env:connect(database_name, user_name, password, host_name); + self.conn_id = tostring(self.conn); + self.database_name = database_name; + self.user_name = user_name; + self.password = password; + self.host_name = host_name; + + -- self.log:debug('DATABASE_CONNECT - connection: ', self.conn_id, ', environment: ', self.env); + + return self; +end + + +function Database.reconnect(self) + self.conn = self.env:connect(self.database_name, self.user_name, self.password, self.host_name); + self.conn_id = tostring(self.conn); + + if self.log then + self.log:info('DATABASE_RECONNECT - connection: ', self.conn_id, ', environment: ', self.env); + end + + return self; +end + + +function Database.connected(self) + return self.conn; +end + + +function Database.query(self, sql_query, call_function) + local cursor = self.conn:execute(sql_query); + + if cursor == nil and not self.conn:execute('SELECT @@VERSION') then + if self.log then + self.log:error('DATABASE_QUERY - lost connection: ', self.conn_id, ', environment: ', self.env, ', query: ', sql_query); + end + self:reconnect(); + + if call_function then + cursor = self.conn:execute(sql_query); + self.log:notice('DATABASE_QUERY - retry: ', sql_query); + end + end + + if cursor and call_function then + repeat + row = cursor:fetch({}, 'a'); + if row then + call_function(row); + end + until not row; + end + + if type(cursor) == 'userdata' then + cursor:close(); + end + + return cursor; +end + + +function Database.query_return_value(self, sql_query) + local cursor = self.conn:execute(sql_query); + + if cursor == nil and not self.conn:execute('SELECT @@VERSION') then + if self.log then + self.log:error('DATABASE_QUERY - lost connection: ', self.conn_id, ', environment: ', self.env, ', query: ', sql_query); + end + self:reconnect(); + cursor = self.conn:execute(sql_query); + self.log:notice('DATABASE_QUERY - retry: ', sql_query); + end + + if type(cursor) == 'userdata' then + local row = cursor:fetch({}, 'n'); + cursor:close(); + + if not row then + return row; + else + return row[1]; + end + end + + return cursor; +end + + +function Database.last_insert_id(self) + return self:query_return_value('SELECT LAST_INSERT_ID()'); +end + + +function Database.release(self, sql_query, call_function) + if self.conn then + self.conn:close(); + end + if self.env then + self.env:close(); + end + + -- self.log:debug('DATABASE_RELEASE - connection: ', self.conn_id, ', status: ', self.env, ', ', self.conn); +end diff --git a/misc/freeswitch/scripts/common/fapi.lua b/misc/freeswitch/scripts/common/fapi.lua new file mode 100644 index 0000000..0a05155 --- /dev/null +++ b/misc/freeswitch/scripts/common/fapi.lua @@ -0,0 +1,80 @@ +-- Gemeinschaft 5 module: FS api class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +FApi = {} + +-- create fapi object +function FApi.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.class = 'fapi'; + self.log = arg.log; + self.uuid = arg.uuid; + self.fs_api = freeswitch.API(); + return object; +end + + +function FApi.return_result(self, result, positive, negative, unspecified) + if not result then + return negative; + end + result = tostring(result); + + if result:match('^-ERR') then + return negative; + elseif result:match('^_undef_') then + return negative; + elseif result:match('^+OK') then + return positive; + else + return unspecified; + end +end + + +function FApi.sleep(self, value) + freeswitch.msleep(value); +end + + +function FApi.channel_exists(self, uuid) + require 'common.str' + uuid = uuid or self.uuid; + return common.str.to_b(freeswitch.API():execute('uuid_exists', tostring(uuid))); +end + + +function FApi.get_variable(self, variable_name) + local result = freeswitch.API():execute('uuid_getvar', tostring(self.uuid) .. ' ' .. tostring(variable_name)); + return self:return_result(result, result, nil, result); +end + + +function FApi.set_variable(self, variable_name, value) + value = value or ''; + + local result = freeswitch.API():execute('uuid_setvar', tostring(self.uuid) .. ' ' .. tostring(variable_name) .. ' ' .. tostring(value)); + return self:return_result(result, true); +end + + +function FApi.continue(self) + local result = freeswitch.API():execute('break', tostring(self.uuid)); + return self:return_result(result, true, false); +end + +function FApi.create_uuid(self, uuid) + local result = self.fs_api:execute('create_uuid', uuid); + return result; +end + +function FApi.execute(self, function_name, function_parameters) + local result = self.fs_api:execute(function_name, function_parameters); + return self:return_result(result, true); +end diff --git a/misc/freeswitch/scripts/common/ipcalc.lua b/misc/freeswitch/scripts/common/ipcalc.lua new file mode 100644 index 0000000..5c19d20 --- /dev/null +++ b/misc/freeswitch/scripts/common/ipcalc.lua @@ -0,0 +1,27 @@ +-- Gemeinschaft 5 module: ip calculation functions +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +function ipv4_to_i(ip_address_str) + local octet4, octet3, octet2, octet1 = ip_address_str:match('(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)'); + if octet4 and octet3 and octet2 and octet1 then + return (2^24*octet4 + 2^16*octet3 + 2^8*octet2 + octet1); + end +end + +function ipv4_to_network_netmask(ip_address_str) + local octet4, octet3, octet2, octet1, netmask = ip_address_str:match('(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)/(%d%d?)'); + if octet4 and octet3 and octet2 and octet1 and netmask then + return (2^24*octet4 + 2^16*octet3 + 2^8*octet2 + octet1), tonumber(netmask); + end +end + +function ipv4_network(ip_address, netmask) + return math.floor(ip_address / 2^(32-netmask)); +end + +function ipv4_in_network(ip_address, network, netmask) + return ipv4_network(ip_address, netmask) == ipv4_network(network, netmask); +end diff --git a/misc/freeswitch/scripts/common/log.lua b/misc/freeswitch/scripts/common/log.lua new file mode 100644 index 0000000..d0d13dc --- /dev/null +++ b/misc/freeswitch/scripts/common/log.lua @@ -0,0 +1,69 @@ +-- Gemeinschaft 5 module: log +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +Log = {} + +-- Create logger object +function Log.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.prefix = arg.prefix or '### '; + + self.level_console = arg.level_console or 0; + self.level_alert = arg.level_alert or 1; + self.level_critical = arg.level_critical or 2; + self.level_error = arg.level_error or 3; + self.level_warning = arg.level_warning or 4; + self.level_notice = arg.level_notice or 5; + self.level_info = arg.level_info or 6; + self.level_debug = arg.level_debug or 7; + + return object; +end + +function Log.message(self, log_level, message_arguments ) + local message = tostring(self.prefix); + for index, value in pairs(message_arguments) do + if type(index) == 'number' then + message = message .. tostring(value); + end + end + freeswitch.consoleLog(log_level, message .. '\n'); +end + +function Log.console(self, ...) + self:message(self.level_console, arg); +end + +function Log.alert(self, ...) + self:message(self.level_alert, arg); +end + +function Log.critical(self, ...) + self:message(self.level_critical, arg); +end + +function Log.error(self, ...) + self:message(self.level_error, arg); +end + +function Log.warning(self, ...) + self:message(self.level_warning, arg); +end + +function Log.notice(self, ...) + self:message(self.level_notice, arg); +end + +function Log.info(self, ...) + self:message(self.level_info, arg); +end + +function Log.debug(self, ...) + self:message(self.level_debug, arg); +end diff --git a/misc/freeswitch/scripts/common/node.lua b/misc/freeswitch/scripts/common/node.lua new file mode 100644 index 0000000..544ede9 --- /dev/null +++ b/misc/freeswitch/scripts/common/node.lua @@ -0,0 +1,73 @@ +-- CommonModule: Node +-- +module(...,package.seeall) + +Node = {} + +-- Create Node object +function Node.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self) + self.__index = self + self.log = arg.log + self.database = arg.database + self.record = arg.record + self.session = arg.session + return object +end + +-- Find Node account by name +function Node.find_by_id(self, node_id) + + if not tonumber(node_id) then + return nil + end + + local sql_query = 'SELECT * FROM `gs_nodes` WHERE `id`= ' .. node_id .. ' LIMIT 1'; + local record = nil + + self.database:query(sql_query, function(node_entry) + record = node_entry + end) + + if record then + local node_object = Node:new(self); + node_object.record = record + + return node_object + end + + return nil +end + +-- Find Node account by name +function Node.find_by_address(self, address) + local sql_query = 'SELECT * FROM `gs_nodes` WHERE `ip_address`= "' .. tostring(address):gsub('[^A-F0-9%.%:]', '') .. '" LIMIT 1'; + local record = nil + + self.database:query(sql_query, function(node_entry) + record = node_entry + end) + + if record then + local node_object = Node:new(self); + node_object.record = record + + return node_object + end + + return nil +end + +-- List Nodes +function Node.all(self) + local sql_query = 'SELECT * FROM `gs_nodes`'; + nodes = {}; + + self.database:query(sql_query, function(node_entry) + nodes[tonumber(node_entry.id)] = node_entry; + end) + + return nodes +end
\ No newline at end of file diff --git a/misc/freeswitch/scripts/common/phone_number.lua b/misc/freeswitch/scripts/common/phone_number.lua new file mode 100644 index 0000000..f4f4bfe --- /dev/null +++ b/misc/freeswitch/scripts/common/phone_number.lua @@ -0,0 +1,359 @@ +-- Gemeinschaft 5 module: phone number class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +PhoneNumber = {} + +PHONE_NUMBER_INTERNAL_TYPES = { 'SipAccount', 'Conference', 'FaxAccount', 'Callthrough', 'HuntGroup', 'AutomaticCallDistributor' } + +-- create phone number object +function PhoneNumber.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.class = 'phonenumber'; + self.log = arg.log; + self.database = arg.database; + self.record = arg.record; + self.domain = arg.domain; + self.DEFAULT_CALL_FORWARDING_DEPTH = 20; + return object; +end + +-- find phone number by id +function PhoneNumber.find_by_id(self, id) + local sql_query = 'SELECT * FROM `phone_numbers` WHERE `id`= ' .. tonumber(id) .. ' LIMIT 1'; + + local phone_number = nil; + + self.database:query(sql_query, function(number_entry) + phone_number = PhoneNumber:new(self); + phone_number.record = number_entry; + phone_number.id = tonumber(number_entry.id); + phone_number.uuid = number_entry.uuid; + end) + + return phone_number; +end + +-- find phone number by number +function PhoneNumber.find_by_number(self, number, phone_numberable_types) + require 'common.str' + + phone_numberable_types = phone_numberable_types or PHONE_NUMBER_INTERNAL_TYPES + + local sql_query = 'SELECT * FROM `phone_numbers` \ + WHERE `number`= ' .. common.str.to_sql(number) .. ' \ + AND `phone_numberable_type` IN ("' .. table.concat(phone_numberable_types, '","') .. '") \ + AND `state` = "active" LIMIT 1'; + + local phone_number = nil; + + self.database:query(sql_query, function(number_entry) + phone_number = PhoneNumber:new(self); + phone_number.record = number_entry; + end) + + return phone_number; +end + +-- Find numbers by owner id and type +function PhoneNumber.find_all_by_owner(self, owner_id, owner_type) + local sql_query = 'SELECT * FROM `phone_numbers` WHERE `phone_numberable_type`="' .. owner_type .. '" AND `phone_numberable_id`= ' .. tonumber(owner_id) ..' ORDER BY `position`'; + local phone_numbers = {} + + self.database:query(sql_query, function(number_entry) + phone_numbers[tonumber(number_entry.id)] = PhoneNumber:new(self); + phone_numbers[tonumber(number_entry.id)].record = number_entry; + end) + + return phone_numbers; +end + +-- List numbers by owner id and type +function PhoneNumber.list_by_owner(self, owner_id, owner_type) + local sql_query = 'SELECT * FROM `phone_numbers` WHERE `phone_numberable_type`="' .. owner_type .. '" AND `phone_numberable_id`= ' .. tonumber(owner_id) ..' ORDER BY `position`'; + local phone_numbers = {} + + self.database:query(sql_query, function(number_entry) + table.insert(phone_numbers, number_entry.number) + end) + + return phone_numbers; +end + +-- List numbers by same owner +function PhoneNumber.list_by_same_owner(self, number, owner_types) + local phone_number = self:find_by_number(number, owner_types) + + if phone_number then + return self:list_by_owner(phone_number.record.phone_numberable_id, phone_number.record.phone_numberable_type); + end +end + +-- Retrieve call forwarding +function PhoneNumber.call_forwarding(self, sources) + require 'common.str' + + sources = sources or {}; + table.insert(sources, ''); + + local sql_query = 'SELECT \ + `a`.`destination` AS `number`, \ + `a`.`call_forwardable_id` AS `id`, \ + `a`.`call_forwardable_type` AS `type`, \ + `a`.`timeout`, `a`.`depth`, \ + `b`.`value` AS `service` \ + FROM `call_forwards` `a` JOIN `call_forward_cases` `b` ON `a`.`call_forward_case_id` = `b`.`id` \ + WHERE `a`.`phone_number_id`= ' .. tonumber(self.record.id) .. ' \ + AND `a`.`active` IS TRUE \ + AND (`a`.`source` IS NULL OR `a`.`source` IN ("' .. table.concat( sources, '","') .. '"))'; + + local call_forwarding = {} + + self.database:query(sql_query, function(forwarding_entry) + call_forwarding[forwarding_entry.service] = forwarding_entry; + self.log:debug('CALL_FORWARDING_GET - PhoneNumber=', self.record.id, '/', self.record.uuid, '@', self.record.gs_node_id, + ', number: ', self.record.number, + ', service: ', forwarding_entry.service, + ', destination: ',forwarding_entry.type, '=', forwarding_entry.id, + ', number: ', forwarding_entry.number); + end) + + return call_forwarding; +end + + +function PhoneNumber.call_forwarding_effective(self, service, source) + local conditions = {} + table.insert(conditions, '`phone_number_id` = ' .. self.record.id); + + if source then + table.insert(conditions, '`source` = "' .. source); + else + table.insert(conditions, '(`source` = "" OR `source` IS NULL)'); + end + + if service then + table.insert(conditions, '`call_forward_case_id` IN (SELECT `id` FROM `call_forward_cases` WHERE `value` = "' .. service .. '")'); + end + + -- get call forwarding entry + local sql_query = 'SELECT `destination`,`active`,`timeout`,`call_forwardable_type`, `call_forwardable_id` FROM `call_forwards` WHERE ' .. table.concat(conditions, ' AND ') .. ' ORDER BY `active` DESC LIMIT 1'; + local call_forwarding = nil; + + self.database:query(sql_query, function(entry) + call_forwarding = entry; + end) + + return call_forwarding; +end + + +function PhoneNumber.call_forwarding_off(self, service, source, delete) + local conditions = {} + table.insert(conditions, '`phone_number_id` = ' .. self.record.id); + + if source then + table.insert(conditions, '`source` = "' .. source); + else + table.insert(conditions, '(`source` = "" OR `source` IS NULL)'); + end + + if service then + table.insert(conditions, '`call_forward_case_id` IN (SELECT `id` FROM `call_forward_cases` WHERE `value` = "' .. service .. '")'); + end + + self.log:info('PHONE_NUMBER_CALL_FORWARDING_OFF - service: ', service, ', number: ', self.record.number); + + local call_forwarding_ids = {} + + local sql_query = 'SELECT `id` FROM `call_forwards` WHERE ' .. table.concat(conditions, ' AND '); + self.database:query(sql_query, function(record) + table.insert(call_forwarding_ids, record.id); + end) + + require 'common.call_forwarding' + local call_forwarding_class = common.call_forwarding.CallForwarding:new{ log = self.log, database = self.database, domain = self.domain }; + + for index, call_forwarding_id in ipairs(call_forwarding_ids) do + if tonumber(call_forwarding_id) then + local call_forwarding = call_forwarding_class:find_by_id(call_forwarding_id); + call_forwarding:presence_set('terminated'); + end + end + + -- set call forwarding entry inactive + local sql_query = 'UPDATE `call_forwards` SET `active` = FALSE, `updated_at` = NOW() WHERE ' .. table.concat(conditions, ' AND '); + + local call_forwards = {}; + + -- or delete call forwarding entry + if delete then + sql_query = 'SELECT * FROM `call_forwards` WHERE ' .. table.concat(conditions, ' AND '); + self.database:query(sql_query, function(forwarding_entry) + table.insert(call_forwards, forwarding_entry) + end) + sql_query = 'DELETE FROM `call_forwards` WHERE ' .. table.concat(conditions, ' AND '); + end + + if not self.database:query(sql_query) then + self.log:notice('PHONE_NUMBER_CALL_FORWARDING_OFF - call forwarding could not be deactivated - number: ', self.record.number); + return false; + end + + if delete then + require 'common.sync_log' + local sync_log_class = common.sync_log.SyncLog:new{ log = self.log, database = self.database, homebase_ip_address = '' } + + for index, call_forward in ipairs(call_forwards) do + sync_log_class:insert('CallForward', call_forward, 'destroy', nil); + end + end + + return true; +end + + +function PhoneNumber.call_forwarding_on(self, service, destination, destination_type, timeout, source) + require 'common.str' + if call_forwarding_service == 'noanswer' then + timeout = tonumber(timeout) or '30'; + else + timeout = 'NULL'; + end + + if source then + sql_query = 'SELECT `id`, `destination`, `call_forwardable_type`, `call_forward_case_id` FROM `call_forwards` \ + WHERE `phone_number_id` = ' .. self.record.id .. ' \ + AND `call_forward_case_id` IN (SELECT `id` FROM `call_forward_cases` WHERE `value` = "' .. service .. '") \ + AND `source` = "' .. source .. '" AND `phone_number_id` = ' .. self.record.id .. ' ORDER BY `active` DESC LIMIT 1'; + else + sql_query = 'SELECT `id`, `destination`, `call_forwardable_type`, `call_forward_case_id` FROM `call_forwards` \ + WHERE `phone_number_id` = ' .. self.record.id .. ' \ + AND `call_forward_case_id` IN (SELECT `id` FROM `call_forward_cases` WHERE `value` = "' .. service .. '") \ + AND (`source` = "" OR `source` IS NULL) AND `phone_number_id` = ' .. self.record.id .. ' ORDER BY `active` DESC LIMIT 1'; + end + + destination_type = destination_type or ''; + destination = destination or ''; + local service_id = nil; + local entry_id = 'NULL'; + + self.database:query(sql_query, function(record) + entry_id = record.id; + service_id = record.call_forward_case_id; + if common.str.blank(destination) then + if not common.str.blank(record.call_forwardable_type) then + destination_type = common.str.downcase(record.call_forwardable_type); + end + if not common.str.blank(record.destination) then + destination = record.destination; + end + end + end) + + if destination == '' and destination_type:lower() ~= 'voicemail' then + self.log:notice('PHONE_NUMBER_CALL_FORWARDING_ON - destination not specified - destination: ', destination, ', type: ', destination_type,', number: ' .. self.record.number); + return false; + end + + if destination_type == '' then + destination_type = 'PhoneNumber'; + end + + self.log:info('PHONE_NUMBER_CALL_FORWARDING_ON - service: ', service, ', number: ', self.record.number, ', destination: ', destination, ', type: ', destination_type, ', timeout: ', timeout); + + if not service_id then + sql_query = 'SELECT `id` FROM `call_forward_cases` WHERE `value` = "' .. service .. '"'; + self.database:query(sql_query, function(record) + service_id = tonumber(record.id); + end); + end + + sql_query = 'REPLACE INTO `call_forwards` \ + (`active`, `uuid`, `depth`, `updated_at`, `id`, `phone_number_id`, `call_forward_case_id`, `destination`, `call_forwardable_type`, `timeout`) \ + VALUES \ + (TRUE, UUID(), ' .. self.DEFAULT_CALL_FORWARDING_DEPTH .. ', NOW(), ' .. entry_id .. ', ' .. self.record.id .. ', ' .. service_id .. ', "' .. destination .. '", "' .. destination_type .. '", ' .. timeout .. ')' + + if not self.database:query(sql_query) then + self.log:error('PHONE_NUMBER_CALL_FORWARDING_ON - could not be activated - destination: ', destination, ', type: ', destination_type,', number: ' .. self.record.number); + return false; + end + + require 'common.call_forwarding' + local call_forwarding_class = common.call_forwarding.CallForwarding:new{ log = self.log, database = self.database, domain = self.domain }; + if tonumber(entry_id) then + local call_forwarding = call_forwarding_class:find_by_id(entry_id); + end + + if call_forwarding then + if destination_type:lower() == 'voicemail' then + call_forwarding:presence_set('early'); + else + call_forwarding:presence_set('confirmed'); + end + end + + return true; +end + + +function PhoneNumber.call_forwarding_toggle(self, service, source) + local call_forwarding = self:call_forwarding_effective(service, source); + + -- no call_forwarding entry: all forwarding is deactivated + if not call_forwarding then + return false; + end + + if tostring(call_forwarding.active) == '1' then + if self:call_forwarding_off(service, source) then + return {destination = call_forwarding.destination, destination_type = call_forwarding.destination_type, active = false}; + end + end + + if self:call_forwarding_on(service, call_forwarding.destination, call_forwarding.destination_type, call_forwarding.timeout, source) then + return {destination = call_forwarding.destination, destination_type = call_forwarding.destination_type, active = true}; + end + + return nil; +end + + +function PhoneNumber.call_forwarding_presence_set(self, presence_state, service) + service = service or 'always'; + local dialplan_function = 'f-cfutg'; + + if service == 'assistant' then + dialplan_function = 'f-cfatg'; + end + + require "dialplan.presence" + local presence = dialplan.presence.Presence:new(); + + presence:init{log = self.log, accounts = { dialplan_function .. '-' .. tostring(self.record.id) }, domain = self.domain, uuid = 'call_forwarding_number_' .. tostring(self.record.id)}; + + return presence:set(presence_state); +end + + +-- Retrieve ringtone +function PhoneNumber.ringtone(self, id) + id = id or self.record.id; + if not id then + return false; + end + + local sql_query = "SELECT * FROM `ringtones` WHERE `ringtoneable_type` = \"PhoneNumber\" AND `ringtoneable_id`=" .. self.record.id .. " LIMIT 1"; + local ringtone = nil; + + self.database:query(sql_query, function(entry) + ringtone = entry; + end) + + return ringtone; +end diff --git a/misc/freeswitch/scripts/common/routing_tables.lua b/misc/freeswitch/scripts/common/routing_tables.lua new file mode 100644 index 0000000..34d0143 --- /dev/null +++ b/misc/freeswitch/scripts/common/routing_tables.lua @@ -0,0 +1,66 @@ +-- Gemeinschaft 5 module: routing table functions +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +function expand_variables(line, variables_list) + variables_list = variables_list or {}; + + return (line:gsub('{([%a%d_]+)}', function(captured) + return variables_list[captured] or ''; + end)) +end + + +function match_route(entry, search_str, variables_list) + if not entry or not search_str then + return { error = 'No input values' }; + end + + local result = nil; + local success = nil; + success, result = pcall(string.find, search_str, entry[1]); + + if not success then + return { error = result, line = line } + elseif result then + local route = { + pattern = entry[1], + value = search_str:gsub(entry[1], expand_variables(entry[#entry], variables_list)), + } + + for index = 2, #entry-1 do + local attribute = entry[index]:match('^(.-)%s*='); + if attribute then + route[attribute] = entry[index]:match('=%s*(.-)$'); + end + end + + return route; + end + + return {}; +end + + +function match_caller_id(entry, search_str, variables_list) + if not entry or not search_str then + return { error = 'No input values' }; + end + local result = nil; + local success = nil; + success, result = pcall(string.find, search_str, entry[1]); + if not success then + return { error = result, line = line } + elseif result then + return { + value = search_str:gsub(entry[1], expand_variables(entry[4], variables_list)), + class = entry[2], + endpoint = entry[3], + pattern = entry[1], + } + end + + return {}; +end diff --git a/misc/freeswitch/scripts/common/sip_account.lua b/misc/freeswitch/scripts/common/sip_account.lua new file mode 100644 index 0000000..28a00df --- /dev/null +++ b/misc/freeswitch/scripts/common/sip_account.lua @@ -0,0 +1,137 @@ +-- Gemeinschaft 5 module: sip account class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +SipAccount = {} + +-- Create SipAccount object +function SipAccount.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.class = 'sipaccount'; + self.log = arg.log; + self.database = arg.database; + self.record = arg.record; + return object; +end + + +function SipAccount.find_by_sql(self, where) + local sql_query = 'SELECT \ + `a`.`id`, \ + `a`.`uuid`, \ + `a`.`auth_name`, \ + `a`.`caller_name`, \ + `a`.`password`, \ + `a`.`voicemail_pin`, \ + `a`.`tenant_id`, \ + `a`.`sip_domain_id`, \ + `a`.`call_waiting`, \ + `a`.`clir`, \ + `a`.`clip`, \ + `a`.`clip_no_screening`, \ + `a`.`sip_accountable_type`, \ + `a`.`sip_accountable_id`, \ + `a`.`hotdeskable`, \ + `a`.`gs_node_id`, \ + `b`.`host` \ + FROM `sip_accounts` `a` JOIN `sip_domains` `b` ON `a`.`sip_domain_id` = `b`.`id` \ + WHERE ' .. where .. ' LIMIT 1'; + + local sip_account = nil; + self.database:query(sql_query, function(account_entry) + sip_account = SipAccount:new(self); + sip_account.record = account_entry; + sip_account.id = tonumber(account_entry.id); + sip_account.uuid = account_entry.uuid; + end) + + return sip_account; +end + + +-- find sip account by id +function SipAccount.find_by_id(self, id) + local sql_query = '`a`.`id`= ' .. tonumber(id); + return self:find_by_sql(sql_query); +end + +-- find sip account by uuid +function SipAccount.find_by_uuid(self, uuid) + local sql_query = '`a`.`uuid`= "' .. uuid .. '"'; + return self:find_by_sql(sql_query); +end + +-- Find SIP Account by auth_name +function SipAccount.find_by_auth_name(self, auth_name, domain) + local sql_query = '`a`.`auth_name`= "' .. auth_name .. '"'; + + if domain then + sql_query = sql_query .. ' AND `b`.`host` = "' .. domain .. '"'; + end + + return self:find_by_sql(sql_query); +end + +-- retrieve Phone Numbers for SIP Account +function SipAccount.phone_numbers(self, id) + id = id or self.record.id; + if not id then + return false; + end + + local sql_query = "SELECT * FROM `phone_numbers` WHERE `phone_numberable_type` = \"SipAccount\" AND `phone_numberable_id`=" .. self.record.id; + local phone_numbers = {} + + self.database:query(sql_query, function(entry) + table.insert(phone_numbers,entry.number); + end) + + return phone_numbers; +end + +-- retrieve Ringtone for SIP Account +function SipAccount.ringtone(self, id) + id = id or self.record.id; + if not id then + return false; + end + + local sql_query = "SELECT * FROM `ringtones` WHERE `ringtoneable_type` = \"SipAccount\" AND `ringtoneable_id`=" .. self.record.id .. " LIMIT 1"; + local ringtone = nil; + + self.database:query(sql_query, function(entry) + ringtone = entry; + end) + + return ringtone; +end + +function SipAccount.send_text(self, text) + local event = freeswitch.Event("NOTIFY"); + event:addHeader("profile", "gemeinschaft"); + event:addHeader("event-string", "text"); + event:addHeader("user", self.record.auth_name); + event:addHeader("host", self.record.host); + event:addHeader("content-type", "text/plain"); + event:addBody(text); + event:fire(); +end + + +function SipAccount.call_state(self) + local state = nil + local sql_query = "SELECT `callstate` FROM `channels` \ + WHERE `name` LIKE (\"\%" .. self.record.auth_name .. "@%\") \ + OR `name` LIKE (\"\%" .. self.record.auth_name .. "@%\") LIMIT 1"; + + self.database:query(sql_query, function(channel_entry) + state = channel_entry.callstate; + end) + + return state; +end diff --git a/misc/freeswitch/scripts/common/str.lua b/misc/freeswitch/scripts/common/str.lua new file mode 100644 index 0000000..b19f299 --- /dev/null +++ b/misc/freeswitch/scripts/common/str.lua @@ -0,0 +1,136 @@ +-- Gemeinschaft 5 module: string functions +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +function try(array, arguments) + local argument = arguments:match('^(.-)%.') or arguments; + local remaining_arguments = arguments:match('%.(.-)$'); + + if argument and type(array) == 'table' then + if remaining_arguments then + if type(array[argument]) == 'table' then + return try(array[argument], remaining_arguments); + else + return nil; + end + else + return array[argument]; + end + end + + return nil; +end + +-- to number +function to_n(value) + value = tostring(value):gsub('[^%d%.%+%-]', ''); + return tonumber(value) or 0; +end + +-- to integer +function to_i(value) + return math.floor(to_n(value)); +end + +-- to string +function to_s(value) + if value == nil then + return ''; + end + + return tostring(value); +end + +-- to boolean +function to_b(value) + if type(value) == 'boolean' then + return value; + elseif tonumber(value) then + return (tonumber(value) > 0); + else + return (tostring(value) == 'yes' or tostring(value) == 'true'); + end +end + +-- to array +function to_a(line, separator) + line = line or ''; + separator = separator or ';'; + local result = {} + line:gsub('([^' .. separator .. ']+)', function(entry) + table.insert(result, entry); + end); + + return result; +end + +-- stripped to array +function strip_to_a(line, separator) + + local result = {} + line:gsub('([^' .. separator .. ']+)', function(entry) + table.insert(result, (entry:gsub('^%s+', ''):gsub('%s+$', ''))); + end); + + return result; +end + +-- downcase +function downcase(value) + if value == nil then + return ''; + end + + return tostring(value):lower(); +end + +-- remove special characters +function to_ascii(value) + return (to_s(value):gsub('[^A-Za-z0-9%-%_ %(%)]', '')); +end + +-- to SQL +function to_sql(value) + if type(value) == 'boolean' then + return tostring(value):upper(); + elseif type(value) == 'number' then + return tostring(value); + elseif type(value) == 'string' then + return '"' .. value:gsub('"', '\\"'):gsub("'", "\\'") .. '"'; + else + return 'NULL'; + end +end + +-- to JSON +function to_json(value) + if type(value) == 'boolean' then + return tostring(value):lower(); + elseif type(value) == 'number' then + return tostring(value); + elseif type(value) == 'string' then + return '"' .. value:gsub('"', '\\"'):gsub("'", "\\'") .. '"'; + else + return 'null'; + end +end + +-- remove leading/trailing whitespace +function strip(value) + return (tostring(value):gsub('^%s+', ''):gsub('%s+$', '')); +end + +-- split string +function partition(value, separator) + value = tostring(value); + separator = separator or ':' + + return value:match('^(.-)' .. separator), value:match(separator .. '(.-)$'); +end + +-- check if value is empty string or nil +function blank(value) + return (value == nil or to_s(value) == ''); +end diff --git a/misc/freeswitch/scripts/common/sync_log.lua b/misc/freeswitch/scripts/common/sync_log.lua new file mode 100644 index 0000000..05b0dcf --- /dev/null +++ b/misc/freeswitch/scripts/common/sync_log.lua @@ -0,0 +1,39 @@ +-- Gemeinschaft 5 module: sync log class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +SyncLog = {} + +-- create sync log object +function SyncLog.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.database = arg.database; + self.homebase_ip_address = arg.homebase_ip_address; + return object; +end + +-- create new entry +function SyncLog.insert(self, entry_name, entry_record, action, history_entries) + local content = {} + for key, value in pairs(entry_record) do + require 'common.str' + table.insert(content, '"'.. key ..'":' .. common.str.to_json(value)); + end + + local history = ''; + if action == 'update' then + history = 'Changed: ["' .. table.concat(history_entries, '","') .. '"]'; + end + + local sql_query = 'INSERT INTO `gs_cluster_sync_log_entries` (`waiting_to_be_synced`,`created_at`,`updated_at`,`class_name`,`action`,`content`,`history`,`homebase_ip_address`) \ + VALUES \ + (TRUE, NOW(), NOW(), \'' .. entry_name .. '\', \'' .. action .. '\', \'{' .. table.concat(content, ',') .. '}\', \'' .. history .. '\', \'' .. self.homebase_ip_address .. '\')'; + + return self.database:query(sql_query); +end diff --git a/misc/freeswitch/scripts/configuration.lua b/misc/freeswitch/scripts/configuration.lua new file mode 100644 index 0000000..906d3f8 --- /dev/null +++ b/misc/freeswitch/scripts/configuration.lua @@ -0,0 +1,229 @@ +-- Gemeinschaft 5 dynamic freeswitch configuration +-- (c) AMOOMA GmbH 2012 +-- + +function nodes(database, local_node_id) + local gateways_xml = ''; + + require 'common.node' + for node_id, node_record in pairs(common.node.Node:new{log=log, database=database}:all()) do + if node_id ~= local_node_id then + local node_parameters = {} + node_parameters['username'] = node_record.name; + node_parameters['password'] = 'gemeinschaft'; + node_parameters['proxy'] = node_record.ip_address; + node_parameters['register'] = 'false'; + log:debug('NODE_GATEWAY ', node_record.id, ' - name: ', node_record.name, ', address: ', node_record.ip_address); + gateways_xml = gateways_xml .. xml:gateway(node_record.name, node_parameters); + end + end + + return gateways_xml; +end + +function gateways(profile_name) + local gateways_xml = ''; + local gateways = common.configuration_file.get('/opt/freeswitch/scripts/ini/gateways.ini', false); + + if not gateways then + return ''; + end + + for sofia_gateway, gateway_parameters in pairs(gateways) do + if tostring(gateway_parameters.profile) == profile_name then + log:debug('GATEWAY - name: ', sofia_gateway, ', address: ', gateway_parameters.proxy); + gateways_xml = gateways_xml .. xml:gateway(sofia_gateway, gateway_parameters); + end + end + + return gateways_xml; +end + +function profile(database, sofia_ini, profile_name, index, domains, node_id) + local profile_parameters = sofia_ini['profile:' .. profile_name]; + + if not profile_parameters then + log:error('SOFIA_PROFILE ', index,' - name: ', profile_name, ' - no parameters'); + return ''; + end + -- set local bind address + if domains[index] then + profile_parameters['sip-ip'] = domains[index]['host']; + profile_parameters['rtp-ip'] = domains[index]['host']; + profile_parameters['force-register-domain'] = domains[index]['host']; + profile_parameters['force-subscription-domain'] = domains[index]['host']; + profile_parameters['force-register-db-domain'] = domains[index]['host']; + log:debug('SOFIA_PROFILE ', index,' - name: ', profile_name, ', domain: ', domains[index]['host'], ', sip_bind: ', profile_parameters['sip-ip'], ':', profile_parameters['sip-port']); + else + log:error('SOFIA_PROFILE ', index,' - name: ', profile_name, ' - no domains'); + end + + local gateways_xml = gateways(profile_name); + + if index == 1 then + gateways_xml = gateways_xml .. nodes(database, node_id); + end + + return xml:sofia_profile(profile_name, profile_parameters, gateways_xml); +end + +-- generate sofia.conf +function conf_sofia(database) + local sofia_profile = "gemeinschaft"; + + require 'common.configuration_file' + local sofia_ini = common.configuration_file.get('/opt/freeswitch/scripts/ini/sofia.ini'); + local dialplan_parameters = common.configuration_file.get('/opt/freeswitch/scripts/ini/dialplan.ini', 'parameters'); + + local local_node_id = tonumber(dialplan_parameters['node_id']) or 1; + + require 'configuration.sip' + local domains = configuration.sip.Sip:new{ log = log, database = database}:domains(); + + sofia_profiles_xml = ''; + for index, profile_name in ipairs(sofia_ini.profiles) do + sofia_profiles_xml = sofia_profiles_xml .. profile(database, sofia_ini, profile_name, index, domains, local_node_id); + end + + XML_STRING = xml:document(xml:sofia(sofia_ini.parameters, sofia_profiles_xml)) +end + +function conf_conference(database) + XML_STRING = xml:document(xml:conference()); + + require 'common.configuration_file' + local conference_ini = common.configuration_file.get('/opt/freeswitch/scripts/ini/conferences.ini'); + local conference_parameters = conference_ini.parameters; + + local event_name = params:getHeader("Event-Name") + if event_name == 'COMMAND' then + local conf_name = params:getHeader('conf_name'); + local profile_name = params:getHeader('profile_name'); + + if conf_name then + require 'common.conference' + conference = common.conference.Conference:new{log=log, database=database}:find_by_id(conf_name); + if conference then + log:debug('CONFIG_CONFERENCE ', conf_name, ' name: ', conference.record.name, ', profile: ', profile_name); + conference_parameters['caller-id-name'] = conference.record.name or ''; + XML_STRING = xml:document(xml:conference(xml:conference_profile(profile_name, conference_parameters))); + else + log:error('CONFIG_CONFERENCE ', conf_name, ' - conference not found'); + end + else + log:notice('CONFIG_CONFERENCE - no conference name'); + end + else + log:debug('CONFIG_CONFERENCE ', conf_name, ' - event: ', event_name); + end +end + + +function directory_sip_account(database) + local key = params:getHeader('key'); + local auth_name = params:getHeader('user'); + local domain = params:getHeader('domain'); + local purpose = params:getHeader('purpose'); + + if auth_name and auth_name ~= '' then + -- sip account or gateway + if string.len(auth_name) > 3 and auth_name:sub(1, 3) == 'gw+' then + local gateway_name = auth_name:sub(4); + domain = domain or freeswitch.API():execute('global_getvar', 'domain'); + require 'configuration.sip' + log:notice('DATABASE: ', database); + local sip_gateway = configuration.sip.Sip:new{ log = log, database = database}:find_gateway_by_name(gateway_name); + if sip_gateway ~= nil and next(sip_gateway) ~= nil then + log:debug('DIRECTORY_GATEWAY - name: ', gateway_name, ', auth_name: ', auth_name); + XML_STRING = xml:document(xml:directory(xml:gateway_user(sip_gateway, gateway_name, auth_name), domain)); + else + log:debug('DIRECTORY_GATEWAY - gateway not found - name: ', gateway_name, ', auth_name: ', auth_name); + end + else + require 'common.sip_account' + local sip_account = common.sip_account.SipAccount:new{ log = log, database = database}:find_by_auth_name(auth_name, domain); + if sip_account ~= nil then + if tostring(purpose) == 'publish-vm' then + log:debug('DIRECTORY_SIP_ACCOUNT - purpose: VoiceMail, auth_name: ', sip_account.record.auth_name, ', caller_name: ', sip_account.record.caller_name, ', domain: ', domain); + XML_STRING = xml:document(xml:directory(xml:group_default(xml:user(sip_account.record)), domain)); + else + log:debug('DIRECTORY_SIP_ACCOUNT - auth_name: ', sip_account.record.auth_name, ', caller_name: ', sip_account.record.caller_name, ', domain: ', domain); + XML_STRING = xml:document(xml:directory(xml:user(sip_account.record), domain)); + end + else + log:debug('DIRECTORY_SIP_ACCOUNT - sip account not found - auth_name: ', auth_name, ', domain: ', domain); + -- fake sip_account configuration + sip_account = { + auth_name = auth_name, + id = 0, + uuid = '', + password = tostring(math.random(0, 65534)), + voicemail_pin = '', + state = 'inactive', + caller_name = '', + sip_accountable_type = 'none', + sip_accountable_id = 0, + } + XML_STRING = xml:document(xml:directory(xml:user(sip_account), domain)) + end + end + elseif tostring(XML_REQUEST.key_name) == 'name' and tostring(XML_REQUEST.key_value) ~= '' then + log:debug('DOMAIN_DIRECTORY - domain: ', XML_REQUEST.key_value); + XML_STRING = xml:document(xml:directory(nil, XML_REQUEST.key_value)); + end +end + + +local log_identifier = XML_REQUEST.key_value or 'CONFIG'; + +-- set logger +require 'common.log' +log = common.log.Log:new(); +log.prefix = '#C# [' .. log_identifier .. '] '; + +-- return a valid xml document +require 'configuration.freeswitch_xml' +xml = configuration.freeswitch_xml.FreeSwitchXml:new(); +XML_STRING = xml:document(); + +local database = nil; + +-- log:debug('CONFIG_REQUEST section: ', XML_REQUEST.section, ', tag: ', XML_REQUEST.tag_name, ', key: ', XML_REQUEST.key_value); + +if XML_REQUEST.section == 'configuration' and XML_REQUEST.tag_name == 'configuration' then + -- database connection + require 'common.database' + database = common.database.Database:new{ log = log }:connect(); + if database:connected() == false then + log:error('CONFIG_REQUEST - cannot connect to Gemeinschaft database'); + return false; + end + + if XML_REQUEST.key_value == 'sofia.conf' then + conf_sofia(database); + elseif XML_REQUEST.key_value == "conference.conf" then + conf_conference(database); + end +elseif XML_REQUEST.section == 'directory' and XML_REQUEST.tag_name == '' then + log:debug('SIP_ACCOUNT_DIRECTORY - initialization phase'); +elseif XML_REQUEST.section == 'directory' and XML_REQUEST.tag_name == 'domain' then + if params == nil then + log:error('SIP_ACCOUNT_DIRECTORY - no parameters'); + return false; + end + + require 'common.database' + database = common.database.Database:new{ log = log }:connect(); + if not database:connected() then + log:error('CONFIG_REQUEST - cannot connect to Gemeinschaft database'); + return false; + end + directory_sip_account(database); +else + log:error('CONFIG_REQUEST - no configuration handler, section: ', XML_REQUEST.section, ', tag: ', XML_REQUEST.tag_name); +end + +-- ensure database handler is released on exit +if database then + database:release(); +end diff --git a/misc/freeswitch/scripts/configuration/freeswitch_xml.lua b/misc/freeswitch/scripts/configuration/freeswitch_xml.lua new file mode 100644 index 0000000..c81bf50 --- /dev/null +++ b/misc/freeswitch/scripts/configuration/freeswitch_xml.lua @@ -0,0 +1,307 @@ +-- ConfigurationModule: FreeSwitchXml +-- +module(...,package.seeall) + +FreeSwitchXml = {} + +-- Create FreeSwitchXml object +function FreeSwitchXml.new(self, object) + object = object or {} + setmetatable(object, self) + self.__index = self + return object +end + +function FreeSwitchXml.param(self, name, value) + return '<param name="' .. name .. '" value="' .. value .. '"/>' +end + +function FreeSwitchXml.variable(self, name, value) + return '<variable name="' .. name .. '" value="' .. value .. '"/>' +end + +function FreeSwitchXml.document(self, sections_xml) + if type(sections_xml) == "string" then + sections_xml = { sections_xml } + elseif type(sections_xml) == "nil" then + sections_xml = { "" } + end + + local xml_string= +[[<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<document type="freeswitch/xml"> +]] .. table.concat(sections_xml, "\n") .. [[ + +</document>]] + + return xml_string +end + +function FreeSwitchXml.directory(self, entries_xml, domain) + if type(entries_xml) == "string" then + entries_xml = { entries_xml } + elseif type(entries_xml) == "nil" then + entries_xml = { "" } + end + + local xml_string = +[[ +<section name="directory"> +<domain name="]] .. domain .. [["> +<params> +<param name="dial-string" value="${sofia_contact(${dialed_user}@${dialed_domain})}"/> +</params> +]] .. table.concat(entries_xml, "\n") .. [[ + +</domain> +</section>]] + return xml_string +end + +function FreeSwitchXml.group_default(self, entries_xml) + if type(entries_xml) == "string" then + entries_xml = { entries_xml } + elseif type(entries_xml) == "nil" then + entries_xml = { "" } + end + + local xml_string = +[[ +<groups> +<group name="default"> +<users> +]] .. table.concat(entries_xml, "\n") .. [[ + +</users> +</group> +</groups>]] + return xml_string +end + +function FreeSwitchXml.user(self, user) + require 'common.configuration_file' + local params = common.configuration_file.get('/opt/freeswitch/scripts/ini/sip_accounts.ini', 'parameters'); + + params['password'] = user.password; + params['vm-password'] = user.voicemail_pin; + + local variables = { + user_context = "default", + gs_from_gateway = "false", + gs_account_id = user.id, + gs_account_uuid = user.uuid, + gs_account_type = "SipAccount", + gs_account_state = user.state, + gs_account_caller_name = user.caller_name, + gs_account_owner_type = user.sip_accountable_type, + gs_account_owner_id = user.sip_accountable_id + } + + local params_xml = {} + for name, value in pairs(params) do + params_xml[#params_xml+1] = self:param(name, value) + end + + local variables_xml = {} + for name, value in pairs(variables) do + variables_xml[#variables_xml+1] = self:variable(name, value) + end + + local xml_string = +[[ +<user id="]] .. user.auth_name .. [["> +<params> +]] .. table.concat(params_xml, "\n") .. [[ + +</params> +<variables> +]] .. table.concat(variables_xml, "\n") .. [[ + +</variables> +</user>]] + return xml_string +end + +function FreeSwitchXml.gateway_user(self, user, gateway_name, auth_name) + user.id = user.id or 0 + + local params = { + ['password'] = user.password, + } + + local variables = { + user_context = "default", + gs_from_gateway = "true", + gs_gateway_name = gateway_name, + gs_gateway_id = user.id + } + + local params_xml = {} + for name, value in pairs(params) do + params_xml[#params_xml+1] = self:param(name, value) + end + + local variables_xml = {} + for name, value in pairs(variables) do + variables_xml[#variables_xml+1] = self:variable(name, value) + end + + local xml_string = +[[ +<user id="]] .. auth_name .. [["> +<params> +]] .. table.concat(params_xml, "\n") .. [[ + +</params> +<variables> +]] .. table.concat(variables_xml, "\n") .. [[ + +</variables> +</user>]] + return xml_string +end + +function FreeSwitchXml.sofia(self, parameters, profiles_xml) + if type(profiles_xml) == "string" then + profiles_xml = { profiles_xml } + elseif type(profiles_xml) == "nil" then + profiles_xml = { "" } + end + + local params_xml = {} + for name, value in pairs(parameters) do + params_xml[#params_xml+1] = self:param(name, value) + end + + local xml_string = +[[ +<section name="configuration" description="FreeSwitch configuration for Sofia Profile"> +<configuration name="sofia.conf" description="Sofia SIP Configuration"> +<global_settings> +]] .. table.concat(params_xml, "\n") .. [[ + +</global_settings> +<profiles> +]] .. table.concat(profiles_xml, "\n") .. [[ + +</profiles> +</configuration> +</section>]] + return xml_string +end + +function FreeSwitchXml.sofia_profile(self, profile_name, parameters, gateways_xml) + params_xml = {} + for name, value in pairs(parameters) do + params_xml[#params_xml+1] = self:param(name, value) + end + + if type(gateways_xml) == "string" then + gateways_xml = { gateways_xml } + elseif type(gateways_xml) == "nil" then + gateways_xml = { "" } + end + + local xml_string = +[[ +<profile name="]] .. profile_name .. [["> +<aliases> +</aliases> +<gateways> +]] .. table.concat(gateways_xml, "\n") .. [[ + +</gateways> +<domains> +<domain name="all" alias="true" parse="false"/> +</domains> +<settings> +]] .. table.concat(params_xml, "\n") .. [[ + +</settings> +</profile>]] + return xml_string +end + +function FreeSwitchXml.gateway(self, gateway_name, parameters) + local params_xml = {} + if parameters then + for name, value in pairs(parameters) do + params_xml[#params_xml+1] = self:param(name, value) + end + end + + local xml_string = +[[ +<gateway name="]] .. gateway_name .. [["> +]] .. table.concat(params_xml, "\n") .. [[ + +</gateway>]] + return xml_string +end + +function FreeSwitchXml.conference(self, profiles_xml) + if type(profiles_xml) == "string" then + profiles_xml = { profiles_xml } + elseif type(profiles_xml) == "nil" then + profiles_xml = { "" } + end + + local xml_string = +[[ +<section name="configuration" description="FreeSwitch configuration for Sofia Profile"> +<configuration name="conference.conf" description="Conference configuration"> +<advertise> +</advertise> +<caller-controls> +<group name="speaker"> +<control action="mute"/> +<control action="deaf mute" digits="*"/> +<control action="energy up" digits="9"/> +<control action="energy equ" digits="8"/> +<control action="energy dn" digits="7"/> +<control action="vol talk up" digits="3"/> +<control action="vol talk zero" digits="2"/> +<control action="vol talk dn" digits="1"/> +<control action="vol listen up" digits="6"/> +<control action="vol listen zero" digits="5"/> +<control action="vol listen dn" digits="4"/> +<control action="hangup" digits="#"/> +</group> +<group name="moderator"> +<control action="mute" digits="0"/> +<control action="deaf mute" digits="*"/> +<control action="energy up" digits="9"/> +<control action="energy equ" digits="8"/> +<control action="energy dn" digits="7"/> +<control action="vol talk up" digits="3"/> +<control action="vol talk zero" digits="2"/> +<control action="vol talk dn" digits="1"/> +<control action="vol listen up" digits="6"/> +<control action="vol listen zero" digits="5"/> +<control action="vol listen dn" digits="4"/> +<control action="hangup" digits="#"/> +</group> +</caller-controls> +<profiles> +]] .. table.concat(profiles_xml, "\n") .. [[ + +</profiles> +</configuration> +</section>]] + return xml_string +end + +function FreeSwitchXml.conference_profile(self, profile_name, parameters) + params_xml = {} + for name, value in pairs(parameters) do + params_xml[#params_xml+1] = self:param(name, value) + end + + local xml_string = +[[ +<profile name="]] .. profile_name .. [["> +]] .. table.concat(params_xml, "\n") .. [[ + +</profile>]] + return xml_string +end diff --git a/misc/freeswitch/scripts/configuration/sip.lua b/misc/freeswitch/scripts/configuration/sip.lua new file mode 100644 index 0000000..78143bc --- /dev/null +++ b/misc/freeswitch/scripts/configuration/sip.lua @@ -0,0 +1,37 @@ +-- Gemeinschaft 5 module: sip configuration class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +Sip = {} + +-- create sip configuration object +function Sip.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.database = arg.database; + self.record = arg.record; + return object; +end + +-- find gateway by name +function Sip.find_gateway_by_name(self, name) + require 'common.configuration_file' + return common.configuration_file.get('/opt/freeswitch/scripts/ini/gateways.ini', name); +end + +-- list sip domains +function Sip.domains(self) + local sql_query = 'SELECT * FROM `sip_domains`'; + local sip_domains = {} + + self.database:query(sql_query, function(sip_domain) + table.insert(sip_domains, sip_domain); + end) + + return sip_domains; +end diff --git a/misc/freeswitch/scripts/dialplan/access_authorizations.lua b/misc/freeswitch/scripts/dialplan/access_authorizations.lua new file mode 100644 index 0000000..dbacf20 --- /dev/null +++ b/misc/freeswitch/scripts/dialplan/access_authorizations.lua @@ -0,0 +1,52 @@ +-- CommonModule: AccessAuthorization +-- +module(...,package.seeall) + +AccessAuthorization = {} + +-- Create AccessAuthorization object +function AccessAuthorization.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self) + self.__index = self + self.log = arg.log + self.database = arg.database + self.record = arg.record + self.session = arg.session + return object +end + +-- Find AccessAuthorization by ID +function AccessAuthorization.find_by_id(self, id) + local sql_query = string.format("SELECT * FROM `access_authorizations` WHERE `id`=%d LIMIT 1", id) + local record = nil + + self.database:query(sql_query, function(access_authorization_entry) + record = access_authorization_entry + end) + + if record then + access_authorization = AccessAuthorization:new(self) + access_authorization.record = record + return access_authorization + end + + return nil +end + +-- list accessauthorization by owner +function AccessAuthorization.list_by_owner(self, owner_id, owner_type) + local sql_query = 'SELECT `a`.`id`, `a`.`name`, `a`.`login`, `a`.`pin`, `a`.`sip_account_id`, `b`.`number` AS `phone_number` \ + FROM `access_authorizations` `a` \ + LEFT JOIN `phone_numbers` `b` ON `b`.`phone_numberable_id` = `a`.`id` AND `b`.`phone_numberable_type` = "AccessAuthorization" \ + WHERE `a`.`access_authorizationable_type` = "' .. owner_type .. '" AND `access_authorizationable_id`= ' .. tonumber(owner_id); + + local access_authorizations = {} + + self.database:query(sql_query, function(access_authorization_entry) + table.insert(access_authorizations, access_authorization_entry); + end); + + return access_authorizations; +end diff --git a/misc/freeswitch/scripts/dialplan/acd.lua b/misc/freeswitch/scripts/dialplan/acd.lua new file mode 100644 index 0000000..563d836 --- /dev/null +++ b/misc/freeswitch/scripts/dialplan/acd.lua @@ -0,0 +1,484 @@ +-- Gemeinschaft 5 module: acd class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +AutomaticCallDistributor = {} + +local DEFAULT_AGENT_TIMEOUT = 20; +local DEFAULT_TIME_RES = 5; +local DEFAULT_WAIT_TIMEOUT = 360; +local DEFAULT_RETRY_TIME = 2; +local DEFAULT_MUSIC_ON_WAIT = 'tone_stream://%(2000,4000,440.0,480.0);loops=-1'; + +-- create acd object +function AutomaticCallDistributor.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.class = 'automaticcalldistributor'; + self.log = arg.log; + self.database = arg.database; + self.record = arg.record; + self.acd_caller_id = arg.acd_caller_id; + self.domain = arg.domain; + return object; +end + + +function AutomaticCallDistributor.find_by_sql(self, sql_query) + local acd = nil; + + require 'common.str' + + self.database:query(sql_query, function(entry) + acd = AutomaticCallDistributor:new(self); + acd.record = entry; + acd.id = tonumber(entry.id); + acd.uuid = entry.uuid; + acd.agent_timeout = tonumber(entry.agent_timeout) or DEFAULT_AGENT_TIMEOUT; + acd.announce_position = tonumber(entry.announce_position); + acd.announce_call_agents = common.str.to_s(entry.announce_call_agents); + acd.greeting = common.str.to_s(entry.greeting); + acd.goodbye = common.str.to_s(entry.goodbye); + acd.music = common.str.to_s(entry.music); + acd.strategy = common.str.to_s(entry.strategy); + acd.join = common.str.to_s(entry.join); + acd.leave = common.str.to_s(entry.leave); + end) + + return acd; +end + + +function AutomaticCallDistributor.find_by_id(self, id) + local sql_query = 'SELECT * FROM `automatic_call_distributors` WHERE `id`= '.. tonumber(id) .. ' LIMIT 1'; + return self:find_by_sql(sql_query); +end + + +function AutomaticCallDistributor.find_by_uuid(self, uuid) + local sql_query = 'SELECT * FROM `automatic_call_distributors` WHERE `uuid`= "'.. tostring(uuid) .. '" LIMIT 1'; + return self:find_by_sql(sql_query); +end + + +function AutomaticCallDistributor.callers_count(self) + return self.database:query_return_value('SELECT COUNT(*) FROM `acd_callers` `a` JOIN `channels` `b` ON `a`.`channel_uuid` = `b`.`uuid` WHERE `automatic_call_distributor_id` = ' .. self.id); +end + + +function AutomaticCallDistributor.caller_new(self, uuid) + local sql_query = 'INSERT INTO `acd_callers` \ + (`enter_time`, `created_at`, `updated_at`, `status`, `automatic_call_distributor_id`, `channel_uuid`) \ + VALUES (NOW(), NOW(), NOW(), "enter", ' .. self.id .. ', "' .. uuid .. '")'; + + if self.database:query(sql_query) then + self.acd_caller_id = self.database:last_insert_id(); + end +end + + +function AutomaticCallDistributor.caller_update(self, attributes) + local attributes_sql = { '`updated_at` = NOW()' }; + for key, value in pairs(attributes) do + table.insert(attributes_sql, '`' .. key .. '` = "' .. value .. '"'); + end + + local sql_query = 'UPDATE `acd_callers` \ + SET '.. table.concat(attributes_sql, ',') .. '\ + WHERE `id` = ' .. tonumber(self.acd_caller_id); + return self.database:query(sql_query); +end + + +function AutomaticCallDistributor.caller_delete(self, id) + id = id or self.acd_caller_id; + local sql_query = 'DELETE FROM `acd_callers` \ + WHERE `id` = ' .. tonumber(id); + return self.database:query(sql_query); +end + + +function AutomaticCallDistributor.agent_find_by_acd_and_destination(self, acd_id, destination_type, destination_id) + local sql_query = 'SELECT * FROM `acd_agents` \ + WHERE `automatic_call_distributor_id` = ' .. acd_id .. ' \ + AND `destination_type` = "' .. destination_type .. '" \ + AND `destination_id` = ' .. destination_id; + + local agent = nil; + self.database:query(sql_query, function(entry) + agent = entry; + end) + + return agent; +end + + +function AutomaticCallDistributor.agent_status_presence_set(self, agent_id, presence_state) + require "dialplan.presence" + local presence = dialplan.presence.Presence:new(); + + presence:init{log = self.log, accounts = { 'f-acdmtg-' .. tostring(agent_id) }, domain = self.domain, uuid = 'acd_agent_' .. tostring(agent_id)}; + return presence:set(presence_state); +end + + +function AutomaticCallDistributor.agent_status_get(self, agent_id) + local sql_query = 'SELECT `status` FROM `acd_agents` WHERE `id` = ' .. agent_id; + return self.database:query_return_value(sql_query); +end + + +function AutomaticCallDistributor.agent_status_toggle(self, agent_id, destination_type, destination_id) + local sql_query = 'UPDATE `acd_agents` SET `status` = IF(`status` = "active", "inactive", "active") \ + WHERE `id` = ' .. agent_id .. ' \ + AND `destination_type` = "' .. destination_type .. '" \ + AND `destination_id` = ' .. destination_id; + + if not self.database:query(sql_query) then + return nil; + end + + local status = self:agent_status_get(agent_id); + + if tostring(status) == 'active' then + self:agent_status_presence_set(agent_id, 'confirmed'); + else + self:agent_status_presence_set(agent_id, 'terminated'); + end + + return status; +end + + +function AutomaticCallDistributor.agents_active(self) + local sql_query = 'SELECT * FROM `acd_agents` \ + WHERE `status` = "active" AND destination_type != "SipAccount" AND `automatic_call_distributor_id` = ' .. tonumber(self.id); + + local agents = {} + self.database:query(sql_query, function(entry) + table.insert(agents, entry); + end); + + local sql_query = 'SELECT `a`.* FROM `acd_agents` `a` \ + JOIN `sip_accounts` `b` ON `a`.`destination_id` = `b`.`id` \ + JOIN `sip_registrations` `c` ON `b`.`auth_name` = `c`.`sip_user` \ + WHERE `a`.`status` = "active" AND `a`.destination_type = "SipAccount" AND `a`.`automatic_call_distributor_id` = ' .. tonumber(self.id); + + self.database:query(sql_query, function(entry) + table.insert(agents, entry); + end); + + return agents; +end + + +function AutomaticCallDistributor.agents_available(self, strategy) + local order_by = '`a`.`id` DESC'; + + if strategy then + if strategy == 'round_robin' then + order_by = '`a`.`last_call` ASC, `a`.`id` DESC'; + end + end + + local sql_query = 'SELECT `a`.`id`, `a`.`name`, `a`.`destination_type`, `a`.`destination_id`, `b`.`auth_name`, `b`.`gs_node_id`, `c`.`callstate` \ + FROM `acd_agents` `a` LEFT JOIN `sip_accounts` `b` ON `a`.`destination_id` = `b`.`id` \ + JOIN `sip_registrations` `d` ON `b`.`auth_name` = `d`.`sip_user` \ + LEFT JOIN `channels` `c` ON `c`.`name` LIKE CONCAT("%", `b`.`auth_name`, "@%") \ + WHERE `a`.`status` = "active" AND `a`.`destination_id` IS NOT NULL AND `a`.`automatic_call_distributor_id` = ' .. tonumber(self.id) .. ' \ + ORDER BY ' .. order_by; + + local accounts = {} + self.database:query(sql_query, function(entry) + if not entry.callstate then + table.insert(accounts, entry); + end + end); + + return accounts; +end + + +function AutomaticCallDistributor.agent_update_call(self, agent_id) + + local sql_query = 'UPDATE `acd_agents` \ + SET `last_call` = NOW(), `calls_answered` = IFNULL(`calls_answered`, 0) + 1 \ + WHERE `id` = ' .. tonumber(agent_id); + return self.database:query(sql_query); +end + + +function AutomaticCallDistributor.call_position(self) + local sql_query = 'SELECT COUNT(*) FROM `acd_callers` `a` JOIN `channels` `b` ON `a`.`channel_uuid` = `b`.`uuid` \ + WHERE `automatic_call_distributor_id` = ' .. tonumber(self.id) .. ' AND `status` = "waiting" AND `id` < ' .. tonumber(self.acd_caller_id); + + return tonumber(self.database:query_return_value(sql_query)); +end + + +function AutomaticCallDistributor.wait_turn(self, caller_uuid, acd_caller_id, timeout, retry_timeout) + self.acd_caller_id = acd_caller_id or self.acd_caller_id; + timeout = timeout or DEFAULT_WAIT_TIMEOUT; + local available_agents = {}; + local active_agents = {}; + local position = self:call_position(); + + self.log:info('ACD ', self.id, ' WAIT - timeout: ', timeout, ', res: ', DEFAULT_TIME_RES, ', retry_timeout: ', retry_timeout, ', position: ', position + 1); + + require 'common.fapi' + local fapi = common.fapi.FApi:new{ log = self.log, uuid = caller_uuid } + + local acd_status = nil; + local start_time = os.time(); + local exit_time = start_time + timeout; + + if tonumber(retry_timeout) then + self.log:info('ACD ', self.id, ' WAIT - retry_timeout: ', retry_timeout); + fapi:sleep(retry_timeout * 1000); + end + + while (exit_time > os.time() and fapi:channel_exists()) do + available_agents = self:agents_available(); + active_agents = self:agents_active(); + local current_position = self:call_position(); + + if position ~= current_position then + position = current_position; + self.log:info('ACD ', self.id, ' WAIT - agents: ', #available_agents, '/', #active_agents, ', position: ', position + 1, ', wait_time: ', os.time()-start_time); + + if tostring(self.announce_position) >= '0' and position > 0 then + acd_status = 'announce_position'; + fapi:set_variable('acd_position', position + 1); + break; + end + else + self.log:debug('ACD ', self.id, ' WAIT - agents: ', #available_agents, '/', #active_agents, ', position: ', position + 1, ', wait_time: ', os.time()-start_time); + end + + if #available_agents == 0 and self.leave:find('no_agents_available') then + acd_status = 'no_agents'; + break; + elseif #active_agents == 0 and self.leave:find('no_agents_active') then + acd_status = 'no_agents'; + break; + elseif position == 0 and #available_agents > 0 then + acd_status = 'call_agents'; + break; + end + + if tonumber(self.announce_position) and tonumber(self.announce_position) > 0 and tonumber(self.announce_position) <= os.time()-start_time then + acd_status = 'announce_position'; + fapi:set_variable('acd_position', position + 1); + break; + end + + fapi:sleep(DEFAULT_TIME_RES * 1000); + end + + if not acd_status then + if (exit_time <= os.time()) then + acd_status = 'timeout'; + else + acd_status = 'unspecified'; + end + end + + self.log:info('ACD ', self.id, ' WAIT END - status: ', acd_status, ', agents: ', #available_agents, '/', #active_agents, ', position: ', position + 1, ', wait_time: ', os.time()-start_time); + + fapi:set_variable('acd_status', acd_status); + if tostring(fapi:get_variable('acd_waiting')) == 'true' then + fapi:continue(); + end +end + + +function AutomaticCallDistributor.wait_play_music(self, caller, timeout, retry_timeout, music) + local result = caller:result('luarun(acd_wait.lua ' .. caller.uuid .. ' ' .. tostring(self.id) .. ' ' .. tostring(timeout) .. ' ' .. tostring(retry_timeout) .. ' ' .. self.acd_caller_id .. ')'); + if not tostring(result):match('^+OK') then + self.log:error('ACD ', self.id,' WAIT_PLAY_MUSIC - error starting acd thread'); + return 'error'; + end + + caller:set_variable('acd_waiting', true); + caller.session:streamFile(music or DEFAULT_MUSIC_ON_WAIT); + caller:set_variable('acd_waiting', false); + + local acd_status = caller:to_s('acd_status'); + if acd_status == '' then + acd_status = 'abandoned'; + end + + return acd_status; +end + + +function AutomaticCallDistributor.on_answer(self, destination) + self.log:info('ACD ', self.id, ' ANSWERED - agent: ', destination.type, '=', destination.id, '/', destination.uuid) + self:caller_update({status = 'answered'}); +end + + +function AutomaticCallDistributor.call_agents(self, dialplan_object, caller, destination) + local available_agents = self:agents_available(self.strategy); + + self.log:info('ACD ', self.id, ' CALL_AGENTS - strategy: ', self.strategy, ', available_agents: ', #available_agents); + + caller:set_variable('ring_ready', true); + + local destinations = {} + for index, agent in ipairs(available_agents) do + self.log:info('ACD ', self.id, ' AGENT - name: ', agent.name, ', destination: ', agent.destination_type, '=', agent.destination_id, '@', agent.gs_node_id, ', local_node: ', dialplan_object.node_id); + table.insert(destinations, dialplan_object:destination_new{ type = agent.destination_type, id = agent.destination_id, node_id = agent.gs_node_id, data = agent.id }); + end + + local result = { continue = false }; + local start_time = os.time(); + + require 'dialplan.sip_call' + if self.strategy == 'ring_all' then + result = dialplan.sip_call.SipCall:new{ log = self.log, database = self.database, caller = caller, calling_object = self, on_answer = self.on_answer }:fork(destinations, + { + callee_id_number = destination.number, + timeout = self.agent_timeout, + send_ringing = ( dialplan_object.send_ringing_to_gateways and caller.from_gateway ), + }); + self.log:info('ACD ', self.id, ' CALL_AGENTS - success, fork_index: ', result.fork_index); + if result.fork_index then + result.destination = destinations[result.fork_index]; + end + return result; + else + for index, destination in ipairs(destinations) do + if os.time() > (self.start_time + self.timeout) and caller.session:ready() then + self.log:info('ACD ', self.id, ' CALL_AGENTS - timeout'); + return { disposition = 'ACD_TIMEOUT', code = 480, phrase = 'Timeout' } + end + + self.log:info('ACD ', self.id, ' CALL_AGENT - ', destination.type, '=', destination.id, ', timeout: ', self.agent_timeout); + result = dialplan.sip_call.SipCall:new{ log = self.log, database = self.database, caller = caller, calling_object = self, on_answer = self.on_answer }:fork({ destination }, + { + callee_id_number = destination.number, + timeout = self.agent_timeout, + send_ringing = ( dialplan_object.send_ringing_to_gateways and caller.from_gateway ), + }); + if result.disposition == 'SUCCESS' then + self.log:info('ACD ', self.id, ' CALL_AGENTS - success, agent_id: ', destination.data); + self:agent_update_call(destination.data); + result.destination = destination; + return result; + end + end + end + + return { disposition = 'ACD_NO_AGENTS', code = 480, phrase = 'No active agents' } +end + + +function AutomaticCallDistributor.run(self, dialplan_object, caller, destination) + require 'common.str' + + local callers_count = self:callers_count(); + local active_agents = self:agents_active(); + local available_agents = self:agents_available(); + local position = self:call_position(); + + if self.leave:find('timeout') then + self.timeout = dialplan_object.dial_timeout_active; + else + self.timeout = 86400; + end + + self.log:info('ACD ', self.id,' - ', self.class, '=', self.id, '/', self.uuid, ', acd_caller=', self.acd_caller_id, ', callers: ', callers_count, ', agents: ', #available_agents, '/', #active_agents, ', position: ', position + 1, ', music: ', tostring(self.music)); + + if self.join == 'agents_active' and #active_agents == 0 then + self.log:info('ACD ', self.id, ' - no agents active'); + return { disposition = 'ACD_NO_AGENTS', code = 480, phrase = 'No agents' } + end + + if self.join == 'agents_available' and #available_agents == 0 then + self.log:info('ACD ', self.id, ' - no agents available'); + return { disposition = 'ACD_NO_AGENTS', code = 480, phrase = 'All agents busy' } + end + + if not common.str.blank(self.music) then + caller:set_variable('ringback', self.music); + else + self.music = false; + end + + if self.music then + caller.session:answer(); + else + caller:set_variable('instant_ringback', true); + end + + self.start_time = os.time(); + caller:sleep(500); + local acd_status = 'waiting'; + self:caller_update({status = acd_status}); + + local retry_timeout = nil; + local result = { disposition = 'ACD_NO_AGENTS', code = 480, phrase = 'No active agents' } + + if self.greeting then + caller.session:sayPhrase('acd_greeting', self.greeting); + end + + if self.announce_position then + local current_position = self:call_position(); + if tonumber(current_position) then + caller.session:sayPhrase('acd_announce_position_enter', tonumber(current_position) + 1); + end + end + + while acd_status == 'waiting' and caller.session:ready() do + acd_status = self:wait_play_music(caller, self.timeout - (os.time() - self.start_time), retry_timeout, self.music); + self.log:info('ACD ', self.id, ' PROCESS - status: ', acd_status, ', wait_time: ', (os.time() - self.start_time)); + + if not caller.session:ready() then + acd_status = 'abandoned'; + break; + elseif os.time() >= (self.start_time + self.timeout) then + acd_status = 'timeout'; + break; + elseif acd_status == 'no_agents' then + break; + elseif acd_status == 'call_agents' then + if self.announce_call_agents ~= '' then + caller.session:sayPhrase('acd_announce_call_agents', self.announce_call_agents); + end + + result = self:call_agents(dialplan_object, caller, destination); + self.log:info('ACD ', self.id, ' PROCESS - result: ', result.disposition, ', code: ', result.code, ', wait_time: ', (os.time() - self.start_time)); + + if result.disposition == 'SUCCESS' then + acd_status = 'success'; + break; + elseif os.time() < (self.start_time + self.timeout) then + acd_status = 'waiting'; + else + break; + end + elseif acd_status == 'announce_position' then + acd_status = 'waiting'; + if tostring(self.announce_position) == '0' then + caller.session:sayPhrase('acd_announce_position_change', caller:to_i('acd_position')); + else + caller.session:sayPhrase('acd_announce_position_periodic', caller:to_i('acd_position')); + end + end + + retry_timeout = tonumber(self.record.retry_timeout); + end + + if self.goodbye and caller.session:ready() then + caller.session:sayPhrase('acd_goodbye', self.goodbye); + end + self.log:info('ACD ', self.id, ' EXIT - status: ', acd_status, ', wait_time: ', (os.time() - self.start_time)); + + return result; +end diff --git a/misc/freeswitch/scripts/dialplan/callthrough.lua b/misc/freeswitch/scripts/dialplan/callthrough.lua new file mode 100644 index 0000000..69a0611 --- /dev/null +++ b/misc/freeswitch/scripts/dialplan/callthrough.lua @@ -0,0 +1,148 @@ +-- CommonModule: Callthrough +-- +module(...,package.seeall) + +Callthrough = {} + +-- Create Callthrough object +function Callthrough.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self) + self.__index = self + self.log = arg.log + self.database = arg.database + self.record = arg.record + self.access_authorizations = arg.access_authorizations + return object +end + +-- Find Callthrough by ID +function Callthrough.find_by_id(self, id) + local sql_query = string.format("SELECT * FROM `callthroughs` WHERE `id`=%d LIMIT 1", id) + local record = nil + + self.database:query(sql_query, function(callthrough_entry) + record = callthrough_entry + end) + + if record then + local callthrough = Callthrough:new(self); + callthrough.record = record + + require 'dialplan.access_authorizations' + callthrough.access_authorizations = dialplan.access_authorizations.AccessAuthorization:new{ log = self.log, database = self.database }:list_by_owner(record.id, 'Callthrough'); + return callthrough + end + + return nil +end + +function Callthrough.authenticate(self, caller) + local authorizations = {} + local logins = {} + local pins = {} + + caller:answer(); + caller:sleep(1000); + + if not self.access_authorizations or table.getn(self.access_authorizations) == 0 then + self.log:debug('CALLTHROUGH_AUTHENTICATE - authorization disabled'); + return true; + end + + self.log:debug('CALLTHROUGH_AUTHENTICATE - access_authorizations: ', #self.access_authorizations); + for index, authorization in ipairs(self.access_authorizations) do + if authorization.phone_number then + if authorization.phone_number == caller.caller_phone_number then + if authorization.pin and authorization.pin ~= "" then + if caller.session:read(authorization.pin:len(), authorization.pin:len(), "ivr/ivr-please_enter_pin_followed_by_pound.wav", 3000, "#") ~= authorization.pin then + self.log:debug("CALLTHROUGH_AUTHENTICATE - Wrong PIN"); + return false; + else + self.log:debug("CALLTHROUGH_AUTHENTICATE - Caller was authenticated by caller id: " .. caller.caller_phone_number .. " and PIN"); + return authorization; + end + end + self.log:debug("CALLTHROUGH_AUTHENTICATE - Caller was authenticated by caller id: " .. caller.caller_phone_number); + return authorization; + end + else + self.log:debug('CALLTHROUGH_AUTHENTICATE - access_authorization=', authorization.id); + if authorization.id then + authorizations[authorization.id] = authorization; + if authorization.login and authorization.login ~= "" then + logins[authorization.login] = authorization; + elseif authorization.pin and authorization.pin ~= "" then + pins[authorization.pin] = authorization; + end + end + end + end + + local login = nil; + local pin = nil; + + + if next(logins) ~= nil then + + caller.session:streamFile('ivr/ivr-please_enter_the.wav'); + caller.session:streamFile('ivr/ivr-id_number.wav'); + login = caller.session:read(2, 10, 'ivr/ivr-followed_by_pound.wav', 3000, '#'); + end + + if login and logins[tostring(login)] then + if not logins[tostring(login)].pin or logins[tostring(login)].pin == '' then + self.log:debug("CALLTHROUGH_AUTHENTICATE - Caller was authenticated by login: " .. login .. " without PIN"); + return logins[tostring(login)]; + end + pin = caller.session:read(2, 10, "ivr/ivr-please_enter_pin_followed_by_pound.wav", 3000, "#"); + if logins[tostring(login)].pin == pin then + self.log:debug("CALLTHROUGH_AUTHENTICATE - Caller was authenticated by login: " .. login .. " and PIN"); + return logins[tostring(login)]; + else + self.log:debug("CALLTHROUGH_AUTHENTICATE - Wrong PIN"); + return false + end + end + + if next(pins) ~= nil then + pin = caller.session:read(2, 10, "ivr/ivr-please_enter_pin_followed_by_pound.wav", 3000, "#"); + end + + self.log:debug("CALLTHROUGH_AUTHENTICATE - No such login, try PIN"); + + if pin and pins[tostring(pin)] then + self.log:debug("CALLTHROUGH_AUTHENTICATE - Caller was authenticated by PIN"); + return pins[tostring(pin)]; + end + + self.log:debug("CALLTHROUGH_AUTHENTICATE - No login, wrong PIN - giving up"); + + return false; +end + +function Callthrough.whitelist(self, number) + local sql_query = 'SELECT `id` FROM `whitelists` WHERE `whitelistable_type` = "Callthrough" AND `whitelistable_id` = ' .. self.record.id; + local whitelist_ids = {} + + self.database:query(sql_query, function(entry) + table.insert(whitelist_ids, entry.id); + end) + + if next(whitelist_ids) == nil then + return true; + end + + -- OPTIMIZE Make sure number contains only valid characters + local sql_query = 'SELECT `id` FROM `phone_numbers` WHERE \ + `number` = "' .. number .. '" AND \ + `phone_numberable_type` = "Whitelist" AND `phone_numberable_id` IN (' .. table.concat(whitelist_ids, ',') .. ') LIMIT 1'; + + local authorized = false + self.database:query(sql_query, function(entry) + authorized = true + end) + + return authorized; +end diff --git a/misc/freeswitch/scripts/dialplan/cdr.lua b/misc/freeswitch/scripts/dialplan/cdr.lua new file mode 100644 index 0000000..55a7889 --- /dev/null +++ b/misc/freeswitch/scripts/dialplan/cdr.lua @@ -0,0 +1,71 @@ +-- Gemeinschaft 5 module: cdr class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +Cdr = {} + +local DEFAULT_MEMBER_TIMEOUT = 20; + +-- Create Cdr object +function Cdr.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.database = arg.database; + return object; +end + + +function Cdr.save(self, caller, destination) + require 'common.str' + local cdr = {} + cdr.uuid = common.str.to_sql(caller.uuid); + cdr.bleg_uuid = common.str.to_sql(caller:to_s('bridge_uuid')); + cdr.dialed_number = common.str.to_sql(caller.called_number); + cdr.destination_number = common.str.to_sql(destination.number); + cdr.caller_id_number = common.str.to_sql(caller:to_s('effective_caller_id_number')); + cdr.caller_id_name = common.str.to_sql(caller:to_s('effective_caller_id_name')); + cdr.callee_id_number = common.str.to_sql(caller:to_s('effective_callee_id_number')); + cdr.callee_id_name = common.str.to_sql(caller:to_s('effective_callee_id_name')); + cdr.start_stamp = 'FROM_UNIXTIME(' .. math.floor(caller:to_i('created_time') / 1000000) .. ')'; + cdr.answer_stamp = 'FROM_UNIXTIME(' .. math.floor(caller:to_i('answered_time') / 1000000) .. ')'; + cdr.end_stamp = 'NOW()'; + cdr.duration = 'UNIX_TIMESTAMP(NOW()) - ' .. math.floor(caller:to_i('created_time') / 1000000); + cdr.hangup_cause = common.str.to_sql(caller.session:hangupCause()); + cdr.dialstatus = common.str.to_sql(caller:to_s('DIALSTATUS')); + cdr.forwarding_number = common.str.to_sql(caller.forwarding_number); + cdr.forwarding_service = common.str.to_sql(caller.forwarding_service); + + if caller.auth_account then + cdr.forwarding_account_id = common.str.to_sql(caller.auth_account.id); + cdr.forwarding_account_type = common.str.to_sql(caller.auth_account.class); + end + + if caller.account then + cdr.account_id = common.str.to_sql(caller.account.id); + cdr.account_type = common.str.to_sql(caller.account.class); + end + + if caller:to_i('answered_time') > 0 then + cdr.billsec = 'UNIX_TIMESTAMP(NOW()) - ' .. math.floor(caller:to_i('answered_time') / 1000000); + end + + cdr.bleg_account_id = common.str.to_sql(tonumber(destination.id)); + cdr.bleg_account_type = common.str.to_sql(destination.type); + + local keys = {} + local values = {} + + for key, value in pairs(cdr) do + table.insert(keys, key); + table.insert(values, value); + end + + local sql_query = 'INSERT INTO `cdrs` (`' .. table.concat(keys, "`, `") .. '`) VALUES (' .. table.concat(values, ", ") .. ')'; + self.log:info('CDR_SAVE - caller: ', cdr.account_type, '=', cdr.account_id, ', callee: ',cdr.bleg_account_type, '=', cdr.bleg_account_id,', dialstatus: ', cdr.dialstatus); + return self.database:query(sql_query); +end diff --git a/misc/freeswitch/scripts/dialplan/dialplan.lua b/misc/freeswitch/scripts/dialplan/dialplan.lua new file mode 100644 index 0000000..f4dca9e --- /dev/null +++ b/misc/freeswitch/scripts/dialplan/dialplan.lua @@ -0,0 +1,996 @@ +-- Gemeinschaft 5 module: dialplan class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +Dialplan = {} + +-- local constants +local CONFIG_FILE_NAME = '/opt/freeswitch/scripts/ini/dialplan.ini'; +local DIAL_TIMEOUT = 120; +local MAX_LOOPS = 20; +local DIALPLAN_FUNCTION_PATTERN = '^f[_%-].*'; +local CALL_FORWARDING_SERVICES = { + USER_BUSY = 'busy', + CALL_REJECTED = 'busy', + NO_ANSWER = 'noanswer', + USER_NOT_REGISTERED = 'offline', + HUNT_GROUP_EMPTY = 'offline', + ACD_NO_AGENTS = 'offline', + ACD_TIMEOUT = 'noanswer', +} + +-- create dialplan object +function Dialplan.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.database = arg.database; + self.caller = arg.caller; + + return object; +end + + +function Dialplan.domain_get(self, domain) + require 'common.str' + local global_domain = freeswitch.API():execute('global_getvar', 'domain'); + + if common.str.blank(global_domain) then + if common.str.blank(domain) then + require 'common.database' + local database = common.database.Database:new{ log = self.log }:connect(); + if not database:connected() then + self.log:error('[', uuid,'] DIALPLAN_DOMAIN - cannot connect to Gemeinschaft database'); + else + require 'configuration.sip' + local domains = configuration.sip.Sip:new{ log = self.log, database = database }:domains(); + if domains[1] then + domain = domains[1]['host']; + end + end + end + + if database then + database:release(); + end + + if not common.str.blank(domain) then + self.log:notice('DIALPLAN_DOMAIN - setting default domain: ', domain); + freeswitch.API():execute('global_setvar', 'domain=' .. tostring(domain)); + end + else + domain = global_domain; + end + + if common.str.blank(domain) then + self.log:error('DIALPLAN_DOMAIN - no domain found'); + end + + return domain; +end + + +function Dialplan.configuration_read(self, file_name) + require 'common.str' + require 'common.configuration_file' + + -- dialplan configuration + self.config = common.configuration_file.get(file_name or CONFIG_FILE_NAME); + self.node_id = common.str.to_i(self.config.parameters.node_id); + self.domain = self:domain_get(self.config.parameters.domain); + self.dial_timeout = tonumber(self.config.parameters.dial_timeout) or DIAL_TIMEOUT; + self.max_loops = tonumber(self.config.parameters.max_loops) or MAX_LOOPS; + self.user_image_url = common.str.to_s(self.config.parameters.user_image_url); + self.phone_book_entry_image_url = common.str.to_s(self.config.parameters.phone_book_entry_image_url); + self.phonebook_number_lookup = common.str.to_b(self.config.parameters.phonebook_number_lookup); + self.geo_number_lookup = common.str.to_b(self.config.parameters.geo_number_lookup); + self.default_language = self.config.parameters.default_language or 'en'; + self.send_ringing_to_gateways = common.str.to_b(self.config.parameters.send_ringing_to_gateways); + + if tonumber(self.config.parameters.default_ringtone) then + self.default_ringtone = 'http://amooma.de;info=Ringer' .. self.config.parameters.default_ringtone .. ';x-line-id=0'; + else + self.default_ringtone = 'http://amooma.de;info=Ringer1;x-line-id=0'; + end + + return (self.config ~= nil); +end + + +function Dialplan.hangup(self, code, phrase, cause) + if self.caller:ready() then + if tonumber(code) then + self.caller:respond(code, phrase or 'Thank you for flying Gemeinschaft5'); + end + self.caller:hangup(cause or 16); + else + self.log:info('HANGUP - caller sesson down - cause: ', self.caller.session:hangupCause()); + end +end + + +function Dialplan.check_auth(self) + local authenticated = false; + + require 'common.str' + if self.caller.from_node then + self.log:info('AUTH_FIRST_STAGE - node authenticated - node_id: ', self.caller.node_id); + authenticated = true; + elseif not common.str.blank(self.caller.auth_account_type) then + self.log:info('AUTH_FIRST_STAGE - sipaccount autheticated by name/password: ', self.caller.auth_account_type, '=', self.caller.account_id, '/', self.caller.account_uuid); + authenticated = true; + elseif self.caller.from_gateway then + self.log:info('AUTH_FIRST_STAGE - gateway autheticated by name/password: gateway=', self.caller.gateway_id, ', name: ', self.caller.gateway_name); + authenticated = true; + else + local gateways = common.configuration_file.get('/opt/freeswitch/scripts/ini/gateways.ini', false); + if not gateways then + return false; + end + for gateway, gateway_parameters in pairs(gateways) do + if common.str.to_s(gateway_parameters.proxy) == self.caller.sip_contact_host then + self.caller.gateway_name = gateway; + self.caller.from_gateway = true; + self.log:info('AUTH_FIRST_STAGE - gateway autheticated by ip: gateway=', self.caller.gateway_id, ', name: ', self.caller.gateway_name, ', ip: ', self.caller.sip_contact_host); + authenticated = true; + end + end + end + + return authenticated; +end + + +function Dialplan.check_auth_node(self) + require 'common.node' + local node = common.node.Node:new{ log = self.log, database = self.database }:find_by_address(self.caller.sip_contact_host); + + return (node ~= nil); +end + + +function Dialplan.check_auth_ip(self) + self.log:info('AUTH - node: ', self.caller.from_node, ', auth_account: ', self.caller.auth_account_type, ', gateway: ', self.caller.from_gateway); + require 'common.str' + if self.caller.from_node then + return true; + elseif not common.str.blank(self.caller.auth_account_type) then + return true; + elseif self.caller.from_gateway then + return true; + else + return nil; + end +end + + +function Dialplan.object_find(self, class, identifier, auth_name) + require 'common.str' + class = common.str.downcase(class); + + if class == 'user' then + require 'dialplan.user' + local user = nil; + if type(identifier) == 'number' then + user = dialplan.user.User:new{ log = self.log, database = self.database }:find_by_id(identifier); + else + user = dialplan.user.User:new{ log = self.log, database = self.database }:find_by_uuid(identifier); + end + + if user then + user.groups = user:list_groups(); + end + + return user; + elseif class == 'tenant' then + require 'dialplan.tenant' + local tenant = nil; + if type(identifier) == 'number' then + tenant = dialplan.tenant.Tenant:new{ log = self.log, database = self.database }:find_by_id(identifier); + else + tenant = dialplan.tenant.Tenant:new{ log = self.log, database = self.database }:find_by_uuid(identifier); + end + + return tenant; + elseif class == 'sipaccount' then + require 'common.sip_account' + local sip_account = nil; + if auth_name then + sip_account = common.sip_account.SipAccount:new{ log = self.log, database = self.database }:find_by_auth_name(auth_name, identifier); + elseif type(identifier) == 'number' then + sip_account = common.sip_account.SipAccount:new{ log = self.log, database = self.database }:find_by_id(identifier); + else + sip_account = common.sip_account.SipAccount:new{ log = self.log, database = self.database }:find_by_uuid(identifier); + end + if sip_account then + sip_account.owner = self:object_find(sip_account.record.sip_accountable_type, tonumber(sip_account.record.sip_accountable_id)); + end + return sip_account; + elseif class == 'huntgroup' then + require 'dialplan.hunt_group' + + local hunt_group = nil; + if type(identifier) == 'number' then + hunt_group = dialplan.hunt_group.HuntGroup:new{ log = self.log, database = self.database }:find_by_id(identifier); + else + hunt_group = dialplan.hunt_group.HuntGroup:new{ log = self.log, database = self.database }:find_by_uuid(identifier); + end + + if hunt_group then + hunt_group.owner = self:object_find('tenant', tonumber(hunt_group.record.tenant_id)); + end + + return hunt_group; + elseif class == 'automaticcalldistributor' then + require 'dialplan.acd' + + local acd = nil; + if type(identifier) == 'number' then + acd = dialplan.acd.AutomaticCallDistributor:new{ log = self.log, database = self.database, domain = self.domain }:find_by_id(identifier); + else + acd = dialplan.acd.AutomaticCallDistributor:new{ log = self.log, database = self.database, domain = self.domain }:find_by_uuid(identifier); + end + + if acd then + acd.owner = self:object_find(acd.record.automatic_call_distributorable_type, tonumber(acd.record.automatic_call_distributorable_id)); + end + + return acd; + elseif class == 'faxaccount' then + require 'dialplan.fax' + local fax_account = nil; + if type(identifier) == 'number' then + fax_account = dialplan.fax.Fax:new{ log = self.log, database = self.database }:find_by_id(identifier); + else + fax_account = dialplan.fax.Fax:new{ log = self.log, database = self.database }:find_by_uuid(identifier); + end + if fax_account then + fax_account.owner = self:object_find(fax_account.record.fax_accountable_type, tonumber(fax_account.record.fax_accountable_id)); + end + + return fax_account; + end +end + + +function Dialplan.retrieve_caller_data(self) + self.caller.caller_phone_numbers_hash = {} + + require 'common.str' + + local dialed_sip_user = self.caller:to_s('dialed_user'); + + -- TODO: Set auth_account on transfer initiated by calling party + if not common.str.blank(dialed_sip_user) then + self.caller.auth_account = self:object_find('sipaccount', self.caller:to_s('dialed_domain'), dialed_sip_user); + self.caller:set_auth_account(self.caller.auth_account); + elseif not common.str.blank(self.caller.auth_account_type) and not common.str.blank(self.caller.auth_account_uuid) then + self.caller.auth_account = self:object_find(self.caller.auth_account_type, self.caller.auth_account_uuid); + self.caller:set_auth_account(self.caller.auth_account); + end + + if self.caller.auth_account then + self.log:info('CALLER_DATA - auth account: ', self.caller.auth_account.class, '=', self.caller.auth_account.id, '/', self.caller.auth_account.uuid); + if self.caller.auth_account.owner then + self.log:info('CALLER_DATA - auth owner: ', self.caller.auth_account.owner.class, '=', self.caller.auth_account.owner.id, '/', self.caller.auth_account.owner.uuid); + else + self.log:error('CALLER_DATA - auth owner not found'); + end + else + self.log:info('CALLER_DATA - no data - unauthenticated call: ', self.caller.auth_account_type, '/', self.caller.auth_account_uuid); + end + + if not common.str.blank(self.caller.account_type) and not common.str.blank(self.caller.account_uuid) then + self.caller.account = self:object_find(self.caller.account_type, self.caller.account_uuid); + if self.caller.account then + require 'common.phone_number' + self.caller.caller_phone_numbers = common.phone_number.PhoneNumber:new{ log = self.log, database = self.database }:list_by_owner(self.caller.account.id, self.caller.account.class); + for index, caller_number in ipairs(self.caller.caller_phone_numbers) do + self.caller.caller_phone_numbers_hash[caller_number] = true; + end + self.log:info('CALLER_DATA - caller account: ', self.caller.account.class, '=', self.caller.account.id, '/', self.caller.account.uuid, ', phone_numbers: ', #self.caller.caller_phone_numbers); + if self.caller.account.owner then + self.log:info('CALLER_DATA - caller owner: ', self.caller.account.owner.class, '=', self.caller.account.owner.id, '/', self.caller.account.owner.uuid); + else + self.log:error('CALLER_DATA - caller owner not found'); + end + + if not self.caller.clir then + self.caller:set_caller_id(self.caller.caller_phone_numbers[1], self.caller.account.record.caller_name or self.caller.account.record.name); + end + else + self.log:error('CALLER_DATA - caller account not found: ', self.caller.account_type, '/', self.caller.account_uuid); + end + end +end + + +function Dialplan.destination_new(self, arg) + require 'common.str' + + local destination = { + number = arg.number or '', + type = arg.type or 'unknown', + id = common.str.to_i(arg.id), + uuid = arg.uuid or '', + phone_number = arg.phone_number, + node_id = common.str.to_i(arg.node_id), + call_forwarding = {}, + data = arg.data, + } + + destination.type = common.str.downcase(destination.type); + + if not common.str.blank(destination.number) then + if destination.type == 'unknown' and destination.number:find(DIALPLAN_FUNCTION_PATTERN) then + destination.type = 'dialplanfunction'; + elseif destination.type == 'phonenumber' or destination.type == 'unknown' then + require 'common.phone_number' + destination.phone_number = common.phone_number.PhoneNumber:new{ log = self.log, database = self.database }:find_by_number(destination.number); + + if destination.phone_number then + destination.type = common.str.downcase(destination.phone_number.record.phone_numberable_type); + destination.id = common.str.to_i(destination.phone_number.record.phone_numberable_id); + destination.uuid = common.str.to_s(destination.phone_number.record.phone_numberable_uuid); + destination.node_id = common.str.to_i(destination.phone_number.record.gs_node_id); + if self.caller then + destination.call_forwarding = destination.phone_number:call_forwarding(self.caller.caller_phone_numbers); + end + elseif destination.type == 'unknown' then + require 'common.sip_account' + destination.account = common.sip_account.SipAccount:new{ log = self.log, database = self.database }:find_by_auth_name(destination.number); + if destination.account then + destination.type = 'sipaccount'; + destination.id = common.str.to_i(destination.account.record.id); + destination.uuid = common.str.to_s(destination.account.record.uuid); + destination.node_id = common.str.to_i(destination.account.record.gs_node_id); + end + end + end + end + + if destination.node_id == 0 then + destination.node_id = self.node_id; + destination.node_local = true; + else + destination.node_local = (destination.node_id == self.node_id); + end + + self.log:info('DESTINATION_NEW - ', destination.type, '=', destination.id, '/', destination.uuid,'@', destination.node_id, ', number: ', destination.number); + + return destination; +end + + +function Dialplan.routes_get(self, destination) + require 'dialplan.route' + return dialplan.route.Route:new{ log = self.log, database = self.database, routing_table = self.routes }:outbound(self.caller, destination.number); +end + + +function Dialplan.set_caller_picture(self, entry_id, entry_type, image) + entry_type = entry_type:lower(); + if entry_type == 'user' then + require 'dialplan.user' + local user = dialplan.user.User:new{ log = self.log, database = self.database }:find_by_id(entry_id); + if user then + self.caller:set_variable('sip_h_Call-Info', '<' .. self.user_image_url .. '/' .. tonumber(entry_id) .. '/snom_caller_picture_' .. tostring(user.record.image) .. '>;purpose=icon'); + end + elseif entry_type == 'phonebookentry' and image then + self.caller:set_variable('sip_h_Call-Info', '<' .. self.phone_book_entry_image_url .. '/' .. tonumber(entry_id) .. '/snom_caller_picture_' .. tostring(image) .. '>;purpose=icon'); + end +end + + +function Dialplan.dial(self, destination) + require 'common.str' + destination.caller_id_number = destination.caller_id_number or self.caller.caller_phone_numbers[1]; + + if not self.caller.clir then + if destination.node_local and destination.type == 'sipaccount' then + local user_id = nil; + local tenant_id = nil; + + destination.account = self:object_find(destination.type, destination.id); + if destination.account then + if destination.account.class == 'sipaccount' then + destination.callee_id_name = destination.account.record.caller_name; + self.caller:set_callee_id(destination.number, destination.account.record.caller_name); + end + end + + if destination.account and destination.account.owner then + if destination.account.owner.class == 'user' then + user_id = destination.account.owner.id; + tenant_id = tonumber(destination.account.owner.record.current_tenant_id); + elseif destination.account.owner.class == 'tenant' then + tenant_id = destination.account.owner.id; + end + end + + if user_id or tenant_id then + require 'common.str' + local phone_book_entry = nil; + + if self.phonebook_number_lookup then + require 'dialplan.phone_book' + phone_book_entry = dialplan.phone_book.PhoneBook:new{ log = self.log, database = self.database }:find_entry_by_number_user_tenant(self.caller.caller_phone_numbers, user_id, tenant_id); + end + + if phone_book_entry then + self.log:info('PHONE_BOOK_ENTRY - phone_book=', phone_book_entry.phone_book_id, ' (', phone_book_entry.phone_book_name, '), caller_id_name: ', phone_book_entry.caller_id_name, ', ringtone: ', phone_book_entry.bellcore_id); + destination.caller_id_name = common.str.to_ascii(phone_book_entry.caller_id_name); + if tonumber(phone_book_entry.bellcore_id) then + self.log:debug('RINGTONE - phonebookentry, index: ', phone_book_entry.bellcore_id); + self.caller:export_variable('alert_info', 'http://amooma.de;info=Ringer' .. phone_book_entry.bellcore_id .. ';x-line-id=0'); + end + if phone_book_entry.image then + self:set_caller_picture(phone_book_entry.id, 'phonebookentry', phone_book_entry.image); + elseif self.caller.account and self.caller.account.owner then + self:set_caller_picture(self.caller.account.owner.id, self.caller.account.owner.class); + end + elseif self.caller.account and self.caller.account.owner then + self:set_caller_picture(self.caller.account.owner.id, self.caller.account.owner.class); + elseif self.geo_number_lookup then + require 'dialplan.geo_number' + local geo_number = dialplan.geo_number.GeoNumber:new{ log = self.log, database = self.database }:find(destination.caller_id_number); + if geo_number then + self.log:info('GEO_NUMBER - found: ', geo_number.name, ', ', geo_number.country); + if geo_number.name then + destination.caller_id_name = common.str.to_ascii(geo_number.name) .. ', ' .. common.str.to_ascii(geo_number.country); + else + destination.caller_id_name = common.str.to_ascii(geo_number.country); + end + end + end + end + end + self.caller:set_caller_id(destination.caller_id_number, destination.caller_id_name or self.caller.caller_id_name); + else + self.caller:set_caller_id('anonymous', 'Unknown'); + self.caller:set_privacy(true); + end + + local destinations = { destination }; + + if self.caller.forwarding_service == 'assistant' and self.caller.auth_account and self.caller.auth_account.class == 'sipaccount' then + self.caller.auth_account.type = self.caller.auth_account.class; + local forwarding_destination = self:destination_new( self.caller.auth_account ); + if forwarding_destination then + forwarding_destination.alert_info = 'http://amooma.com;info=Ringer0;x-line-id=0' + table.insert(destinations, forwarding_destination); + end + end + + if common.str.to_b(self.config.parameters.bypass_media) then + self.caller:set_variable('bypass_media', true); + end + + require 'dialplan.sip_call' + return dialplan.sip_call.SipCall:new{ log = self.log, database = self.database, caller = self.caller }:fork( + destinations, + { timeout = self.dial_timeout_active, + send_ringing = ( self.send_ringing_to_gateways and self.caller.from_gateway ), + bypass_media_network = self.config.parameters.bypass_media_network, + } + ); +end + + +function Dialplan.huntgroup(self, destination) + local hunt_group = self:object_find('huntgroup', tonumber(destination.id)); + + if not hunt_group then + self.log:error('DIALPLAN_HUNTGROUP - huntgroup not found'); + return { continue = true, code = 404, phrase = 'Huntgroup not found' } + end + + self.caller:set_callee_id(destination.number, hunt_group.record.name); + destination.caller_id_number = destination.caller_id_number or self.caller.caller_phone_numbers[1]; + + if not self.caller.clir then + self.caller:set_caller_id(destination.caller_id_number, tostring(hunt_group.record.name) .. ' '.. tostring(self.caller.caller_id_name)); + if self.caller.account and self.caller.account.owner then + self:set_caller_picture(self.caller.account.owner.id, self.caller.account.owner.class); + end + else + self.caller:set_caller_id('anonymous', tostring(hunt_group.record.name)); + self.caller:set_privacy(true); + end + + self.caller.auth_account = hunt_group; + self.caller:set_auth_account(self.caller.auth_account); + self.caller.forwarding_number = destination.number; + self.caller.forwarding_service = 'huntgroup'; + self.caller:set_variable('gs_forwarding_service', self.caller.forwarding_service); + self.caller:set_variable('gs_forwarding_number', self.caller.forwarding_number); + return hunt_group:run(self, self.caller, destination); +end + + +function Dialplan.acd(self, destination) + local acd = self:object_find('automaticcalldistributor', tonumber(destination.id)); + + if not acd then + self.log:error('DIALPLAN_ACD - acd not found'); + return { continue = true, code = 404, phrase = 'ACD not found' } + end + + self.caller:set_callee_id(destination.number, acd.record.name); + destination.caller_id_number = destination.caller_id_number or self.caller.caller_phone_numbers[1]; + + if not self.caller.clir then + self.caller:set_caller_id(destination.caller_id_number, tostring(acd.record.name) .. ' '.. tostring(self.caller.caller_id_name)); + if self.caller.account and self.caller.account.owner then + self:set_caller_picture(self.caller.account.owner.id, self.caller.account.owner.class); + end + else + self.caller:set_caller_id('anonymous', tostring(acd.record.name)); + self.caller:set_privacy(true); + end + + self.caller.auth_account = acd; + self.caller:set_auth_account(self.caller.auth_account); + self.caller.forwarding_number = destination.number; + self.caller.forwarding_service = 'automaticcalldistributor'; + self.caller:set_variable('gs_forwarding_service', self.caller.forwarding_service); + self.caller:set_variable('gs_forwarding_number', self.caller.forwarding_number); + + acd:caller_new(self.caller.uuid); + local result = acd:run(self, self.caller, destination); + acd:caller_delete(); + + return result; +end + + +function Dialplan.conference(self, destination) + -- call local conference + require 'common.conference' + conference = common.conference.Conference:new{ log = self.log, database = self.database }:find_by_id(destination.id); + + if not conference then + return { continue = false, code = 404, phrase = 'Conference not found' } + end + + local cause = conference:enter(self.caller, self.domain); + return { continue = false, cause = cause } +end + + +function Dialplan.faxaccount(self, destination) + require 'dialplan.fax' + local fax_account = dialplan.fax.Fax:new{ log = self.log, database = self.database }:find_by_id(destination.id); + + if not fax_account then + return { continue = false, code = 404, phrase = 'Fax not found' } + end + + self.log:info('FAX_RECEIVE start - fax_account=', fax_account.id, '/', fax_account.uuid, ', name: ', fax_account.record.name, ', station_id: ', fax_account.record.station_id); + + self.caller:set_caller_id(self.caller.caller_phone_number); + self.caller:set_callee_id(destination.number, fax_account.record.name); + + local fax_document = fax_account:receive(self.caller); + + if not fax_document then + self.log:error('FAX_RECEIVE - error receiving fax document - fax_account=', fax_account.id, '/', fax_account.uuid); + return { continue = false, code = 500, phrase = 'Error receiving fax' }; + end + + fax_document.caller_id_number = self.caller.caller_phone_number; + fax_document.caller_id_name = self.caller.caller_id_name; + fax_document.uuid = self.caller.uuid; + + self.log:info('FAX_RECEIVE end - success: ', fax_document.success, + ', remote: ', fax_document.remote_station_id, + ', pages: ', fax_document.transferred_pages, '/', fax_document.total_pages, + ', result: ', fax_document.result_code, ' ', fax_document.result_text); + + if fax_document.success then + self.log:notice('FAX_RECEIVE - saving fax document: ', fax_document.filename ); + if not fax_account:insert_document(fax_document) then + self.log:error('FAX_RECEIVE - error inserting fax document to database - fax_account=', fax_account.id, '/', fax_account.uuid, ', file: ', fax_document.filename); + end + end + + return { continue = false, code = 200, phrase = 'OK' } +end + + +function Dialplan.callthrough(self, destination) + -- Callthrough + require 'dialplan.callthrough' + callthrough = dialplan.callthrough.Callthrough:new{ log = self.log, database = self.database }:find_by_id(destination.id) + + if not callthrough then + self.log:error('CALLTHROUGH - no callthrough for destination number: ', destination.number); + return { continue = false, code = 404, phrase = 'Fax not found' } + end + self.log:info('CALLTHROUGH - number: ' .. destination.number .. ', name: ' .. callthrough.record.name); + + local authorization = callthrough:authenticate(self.caller); + + if not authorization then + self.log:notice('CALLTHROUGH - authentication failed'); + return { continue = false, code = 403, phrase = 'Authentication failed' } + end + + if type(authorization) == 'table' and tonumber(authorization.sip_account_id) and tonumber(authorization.sip_account_id) > 0 then + local auth_account = self:object_find('sipaccount', tonumber(authorization.sip_account_id)); + self.caller.forwarding_number = destination.number; + self.caller.forwarding_service = 'callthrough'; + self.caller:set_variable('gs_forwarding_service', self.caller.forwarding_service); + self.caller:set_variable('gs_forwarding_number', self.caller.forwarding_number); + + if auth_account then + self.caller.auth_account = auth_account; + self.caller:set_auth_account(self.caller.auth_account); + self.log:info('AUTH_ACCOUNT_UPDATE - account: ', self.caller.auth_account.class, '=', self.caller.auth_account.id, '/', self.caller.auth_account.uuid); + if self.caller.auth_account.owner then + self.log:info('AUTH_ACCOUNT_UPDATE - auth owner: ', self.caller.auth_account.owner.class, '=', self.caller.auth_account.owner.id, '/', self.caller.auth_account.owner.uuid); + else + self.log:error('AUTH_ACCOUNT_UPDATE - auth owner not found'); + end + self.log:info('CALLTHROUGH - use sip account: ', auth_account.id, ' (', auth_account.record.caller_name, ')'); + end + else + self.log:info('CALLTHROUGH - no sip account'); + end + + local destination_number = ''; + for i = 1, 3, 1 do + if destination_number ~= '' then + break; + end + destination_number = session:read(2, 16, "ivr/ivr-enter_destination_telephone_number.wav", 3000, "#"); + end + + if destination_number == '' then + self.log:debug("no callthrough destination - hangup call"); + return { continue = false, code = 404, phrase = 'No destination' } + end + + local route = dialplan.route.Route:new{ log = self.log, database = self.database, routing_table = self.routes }:prerouting(self.caller, destination_number); + if route and route.value then + destination_number = route.value; + end + + if not callthrough:whitelist(destination_number) then + self.log:debug('caller not authorized to call destination number: ' .. destination_number .. ' - hangup call'); + return { continue = false, code = 403, phrase = 'Unauthorized' } + end + + return { continue = true, code = 302, number = destination_number } +end + + +function Dialplan.voicemail(self, destination) + if not self.caller.auth_account or self.caller.auth_account.class ~= 'sipaccount' then + self.log:error('VOICEMAIL - incompatible destination'); + return { continue = false, code = 404, phrase = 'Mailbox not found' } + end + + require 'dialplan.voicemail' + local voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_sip_account_id(self.caller.auth_account.id); + + if not voicemail_account then + self.log:error('VOICEMAIL - no mailbox'); + return { continue = false, code = 404, phrase = 'Mailbox not found' } + end + + voicemail_account:leave(self.caller, self.caller.forwarding_number); + + if self.caller:to_s("voicemail_message_len") == '' then + self.log:info('VOICEMAIL - no message saved'); + end + + return { continue = false, code = 200 } +end + + +function Dialplan.dialplanfunction(self, destination) + require 'dialplan.functions' + return dialplan.functions.Functions:new{ log = self.log, database = self.database, domain = self.domain }:dialplan_function(self.caller, destination.number); +end + + +function Dialplan.switch(self, destination) + require 'common.str' + local result = nil; + self.dial_timeout_active = self.dial_timeout; + + if not destination.node_local then + return self:dial(destination); + end + + for service, call_forwarding in pairs(destination.call_forwarding) do + if self.caller.caller_phone_numbers_hash[call_forwarding.number] then + self.log:info('CALL_FORWARDING - caller number equals destination: ', call_forwarding.number,' - ignore service: ', service); + destination.call_forwarding[service] = nil; + end + end + + if destination.call_forwarding.noanswer then + self.dial_timeout_active = tonumber(destination.call_forwarding.noanswer.timeout) or self.dial_timeout; + end + + if destination.call_forwarding.always then + return { continue = true, call_forwarding = destination.call_forwarding.always } + elseif destination.call_forwarding.assistant then + if common.str.downcase(destination.call_forwarding.assistant.type) == 'huntgroup' then + require 'dialplan.hunt_group' + local hunt_group = dialplan.hunt_group.HuntGroup:new{ log = self.log, database = self.database }:find_by_id(destination.call_forwarding.assistant.id); + self.log:info('CALL_FORWARDING - huntgroup - auth_account: ', self.caller.auth_account_type, '=', self.caller.auth_account_uuid); + if hunt_group and (hunt_group:is_member_by_numbers(self.caller.caller_phone_numbers)) then + self.log:info('CALL_FORWARDING - caller is huntgroup member - ignore service: ', destination.call_forwarding.assistant.service); + else + return { continue = true, call_forwarding = destination.call_forwarding.assistant } + end + else + return { continue = true, call_forwarding = destination.call_forwarding.assistant } + end + end + + -- reset ringtone + self.caller:export_variable('alert_info', self.default_ringtone); + + if destination.phone_number then + local ringtone = destination.phone_number:ringtone(); + if ringtone and ringtone.bellcore_id then + self.log:debug('RINGTONE - ', ringtone.ringtoneable_type .. ', index: ' .. ringtone.bellcore_id); + self.caller:export_variable('alert_info', 'http://amooma.de;info=Ringer' .. tonumber(ringtone.bellcore_id) .. ';x-line-id=0'); + end + end + + if destination.type == 'sipaccount' then + result = self:dial(destination); + if CALL_FORWARDING_SERVICES[result.disposition] then + result.call_forwarding = destination.call_forwarding[CALL_FORWARDING_SERVICES[result.disposition]]; + if result.call_forwarding then + result.continue = true; + end + end + return result; + elseif destination.type == 'conference' then + return self:conference(destination); + elseif destination.type == 'faxaccount' then + return self:faxaccount(destination); + elseif destination.type == 'callthrough' then + return self:callthrough(destination); + elseif destination.type == 'huntgroup' then + result = self:huntgroup(destination); + if CALL_FORWARDING_SERVICES[result.disposition] then + result.call_forwarding = destination.call_forwarding[CALL_FORWARDING_SERVICES[result.disposition]]; + if result.call_forwarding then + result.continue = true; + end + end + return result; + elseif destination.type == 'automaticcalldistributor' then + result = self:acd(destination); + if CALL_FORWARDING_SERVICES[result.disposition] then + result.call_forwarding = destination.call_forwarding[CALL_FORWARDING_SERVICES[result.disposition]]; + if result.call_forwarding then + result.continue = true; + end + end + return result; + elseif destination.type == 'voicemail' then + return self:voicemail(destination); + elseif destination.type == 'dialplanfunction' then + return self:dialplanfunction(destination); + elseif not common.str.blank(destination.number) then + local result = { continue = false, code = 404, phrase = 'No route' } + local routes = self:routes_get(destination); + + if not routes or #routes == 0 then + self.log:notice('SWITCH - no route - number: ', destination.number); + return { continue = false, code = 404, phrase = 'No route' } + end + + destination.callee_id_number = destination.number; + destination.callee_id_name = nil; + + if self.phonebook_number_lookup then + require 'common.str' + local user_id = common.str.try(self.caller, 'account.owner.id'); + local tenant_id = common.str.try(self.caller, 'account.owner.record.current_tenant_id'); + + if user_id or tenant_id then + require 'dialplan.phone_book' + local phone_book_entry = dialplan.phone_book.PhoneBook:new{ log = self.log, database = self.database }:find_entry_by_number_user_tenant({ destination.number }, user_id, tenant_id); + if phone_book_entry then + self.log:info('PHONE_BOOK_ENTRY - phone_book=', phone_book_entry.phone_book_id, ' (', phone_book_entry.phone_book_name, '), callee_id_name: ', common.str.to_ascii(phone_book_entry.caller_id_name)); + destination.callee_id_name = common.str.to_ascii(phone_book_entry.caller_id_name); + end + end + end + + if self.geo_number_lookup and not destination.callee_id_name then + require 'dialplan.geo_number' + local geo_number = dialplan.geo_number.GeoNumber:new{ log = self.log, database = self.database }:find(destination.number); + if geo_number then + require 'common.str' + self.log:info('GEO_NUMBER - found: ', geo_number.name, ', ', geo_number.country); + if geo_number.name then + destination.callee_id_name = common.str.to_ascii(geo_number.name) .. ', ' .. common.str.to_ascii(geo_number.country); + else + destination.callee_id_name = common.str.to_ascii(geo_number.country); + end + end + end + + self.caller:set_callee_id(destination.callee_id_number, destination.callee_id_name); + + for index, route in ipairs(routes) do + if route.class == 'hangup' then + return { continue = false, code = route.endpoint, phrase = route.phrase, cause = route.value } + end + if route.class == 'forward' then + return { continue = true, call_forwarding = { number = route.value, service = 'route', type = 'phonenumber' }} + end + destination.gateway = route.endpoint; + destination.type = route.class; + destination.number = route.value; + destination.caller_id_number = route.caller_id_number; + destination.caller_id_name = route.caller_id_name; + result = self:dial(destination); + + if result.continue == false then + break; + end + + if common.str.to_b(self.routes.failover[tostring(result.code)]) == true then + self.log:info('SWITCH - failover - code: ', result.code); + elseif common.str.to_b(self.routes.failover[tostring(result.cause)]) == true then + self.log:info('SWITCH - failover - cause: ', result.cause); + else + self.log:info('SWITCH - no failover - cause: ', result.cause, ', code: ', result.code); + break; + end + end + + return result; + end + + self.log:error('SWITCH - destination not found - type: ', destination.type); + return { continue = true, code = 404, phrase = destination.type .. ' not found' } +end + + +function Dialplan.run(self, destination) + self.caller:set_variable('hangup_after_bridge', false); + self.caller:set_variable('ringback', self.config.parameters.ringback); + self.caller:set_variable('bridge_early_media', 'true'); + self.caller:set_variable('send_silence_when_idle', 0); + self.caller:set_variable('default_language', self.default_language); + self.caller:set_variable('gs_save_cdr', true); + self.caller:set_variable('gs_call_service', 'dial'); + self.caller.session:setAutoHangup(false); + + self.routes = common.configuration_file.get('/opt/freeswitch/scripts/ini/routes.ini'); + self.caller.domain_local = self.domain; + self:retrieve_caller_data(); + + if not destination or destination.type == 'unknown' then + require 'dialplan.route' + local route = nil; + + if self.caller.from_gateway then + local route_object = dialplan.route.Route:new{ log = self.log, database = self.database, routing_table = self.routes }; + route = route_object:inbound(self.caller, self.caller.destination_number); + local inbound_caller_id_number = route_object:inbound_cid_number(self.caller, self.caller.gateway_name, 'gateway'); + route_object.expandable.caller_id_number = inbound_caller_id_number; + local inbound_caller_id_name = route_object:inbound_cid_name(self.caller, self.caller.gateway_name, 'gateway'); + self.log:info('INBOUND_CALLER_ID_REWRITE - number: ', inbound_caller_id_number, ', name: ', inbound_caller_id_name); + self.caller.caller_id_number = inbound_caller_id_number or self.caller.caller_id_number; + self.caller.caller_id_name = inbound_caller_id_name or self.caller.caller_id_name; + self.caller.caller_phone_numbers[1] = self.caller.caller_id_number; + else + route = dialplan.route.Route:new{ log = self.log, database = self.database, routing_table = self.routes }:prerouting(self.caller, self.caller.destination_number); + end + + if route then + destination = self:destination_new{ number = route.value } + self.caller.destination_number = destination.number; + self.caller.destination = destination; + elseif not destination or destination.type == 'unknown' then + destination = self:destination_new{ number = self.caller.destination_number } + self.caller.destination = destination; + end + end + + self.log:info('DIALPLAN start - caller_id: ',self.caller.caller_id_number, ' "', self.caller.caller_id_name,'"', + ', number: ', destination.number); + + local result = { continue = false }; + local loop = self.caller.loop_count; + while self.caller:ready() and loop < self.max_loops do + loop = loop + 1; + self.caller.loop_count = loop; + + self.log:info('LOOP ', loop, + ' - destination: ', destination.type, '=', destination.id, '/', destination.uuid,'@', destination.node_id, + ', number: ', destination.number); + + self.caller:set_variable('gs_destination_type', destination.type); + self.caller:set_variable('gs_destination_id', destination.id); + self.caller:set_variable('gs_destination_uuid', destination.uuid); + self.caller:set_variable('gs_destination_number', destination.number); + self.caller:set_variable('gs_destination_node_local', destination.node_local); + + result = self:switch(destination); + result = result or { continue = false, code = 502, cause = 'DESTINATION_OUT_OF_ORDER', phrase = 'Destination out of order' } + + if result.call_service then + self.caller:set_variable('gs_call_service', result.call_service); + end + + if not result.continue then + break; + end + + if result.call_forwarding then + self.log:info('LOOP ', loop, ' CALL_FORWARDING - service: ', result.call_forwarding.service, + ', destination: ', result.call_forwarding.type, '=', result.call_forwarding.id, + ', number: ', result.call_forwarding.number); + + local auth_account = self:object_find(destination.type, destination.id); + self.caller.forwarding_number = destination.number; + self.caller.forwarding_service = result.call_forwarding.service; + self.caller:set_variable('gs_forwarding_service', self.caller.forwarding_service); + self.caller:set_variable('gs_forwarding_number', self.caller.forwarding_number); + + if auth_account then + self.caller.auth_account = auth_account; + self.caller:set_auth_account(self.caller.auth_account); + self.log:info('AUTH_ACCOUNT_UPDATE - account: ', self.caller.auth_account.class, '=', self.caller.auth_account.id, '/', self.caller.auth_account.uuid); + if self.caller.auth_account.owner then + self.log:info('AUTH_ACCOUNT_UPDATE - auth owner: ', self.caller.auth_account.owner.class, '=', self.caller.auth_account.owner.id, '/', self.caller.auth_account.owner.uuid); + else + self.log:error('AUTH_ACCOUNT_UPDATE - auth owner not found'); + end + end + + destination = self:destination_new(result.call_forwarding); + self.caller.destination = destination; + + if not result.no_cdr and auth_account then + require 'common.call_history' + common.call_history.CallHistory:new{ log = self.log, database = self.database }:insert_forwarded( + self.caller.uuid, + auth_account.class, + auth_account.id, + self.caller, + destination, + result + ); + end + end + + if result.number then + self.log:info('LOOP ', loop, ' NEW_DESTINATION_NUMBER - number: ', result.number ); + destination = self:destination_new{ number = result.number } + self.caller.destination = destination; + end + end + + if loop >= self.max_loops then + result = { continue = false, code = 483, cause = 'EXCHANGE_ROUTING_ERROR', phrase = 'Too many hops' } + end + + self.log:info('DIALPLAN end - caller_id: ',self.caller.caller_id_number, ' "', self.caller.caller_id_name,'"', + ', destination: ', destination.type, '=', destination.id, '/', destination.uuid,'@', destination.node_id, + ', number: ', destination.number, ', result: ', result.code, ' ', result.phrase); + + if self.caller:ready() then + self:hangup(result.code, result.phrase, result.cause); + end + + self.caller:set_variable('gs_save_cdr', not result.no_cdr); +end diff --git a/misc/freeswitch/scripts/dialplan/fax.lua b/misc/freeswitch/scripts/dialplan/fax.lua new file mode 100644 index 0000000..2a40620 --- /dev/null +++ b/misc/freeswitch/scripts/dialplan/fax.lua @@ -0,0 +1,232 @@ +-- Gemeinschaft 5 module: fax class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +FAX_DOCUMENTS_DIRECTORY = '/tmp/' +FAX_PARALLEL_MAX = 8; +Fax = {} + +-- Create Fax object +function Fax.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self) + self.__index = self + self.class = 'faxaccount'; + self.log = arg.log; + self.database = arg.database; + self.record = arg.record; + self.fax_directory = arg.fax_directory or FAX_DOCUMENTS_DIRECTORY; + return object; +end + +-- find fax account by id +function Fax.find_by_id(self, id) + local sql_query = 'SELECT * FROM `fax_accounts` WHERE `id` = ' .. tonumber(id) .. ' LIMIT 1'; + local fax_account = nil; + + self.database:query(sql_query, function(fax_entry) + fax_account = Fax:new(self); + fax_account.record = fax_entry; + fax_account.id = tonumber(fax_entry.id); + fax_account.uuid = fax_entry.uuid; + end) + + return fax_account; +end + + +-- find fax account by uuid +function Fax.find_by_uuid(self, uuid) + local sql_query = 'SELECT * FROM `fax_accounts` WHERE `uuid` = "' .. uuid .. '" LIMIT 1'; + local fax_account = nil; + + self.database:query(sql_query, function(fax_entry) + fax_account = Fax:new(self); + fax_account.record = fax_entry; + fax_account.id = tonumber(fax_entry.id); + fax_account.uuid = fax_entry.uuid; + end) + + return fax_account; +end + + +function Fax.destination_numbers(self, id) + local sql_query = 'SELECT `number` FROM `phone_numbers` WHERE `phone_numberable_type` = "FaxDocument" AND `phone_numberable_id` = ' .. tonumber(id); + local destination_numbers = {} + + self.database:query(sql_query, function(fax_entry) + table.insert(destination_numbers, fax_entry.number); + end) + + return destination_numbers; +end + +function Fax.destination_number(self, id) + local sql_query = 'SELECT `number` FROM `phone_numbers` WHERE `phone_numberable_type` = "FaxDocument" AND `phone_numberable_id`= ' .. tonumber(id) .. ' LIMIT 1'; + local destination_number = nil; + + self.database:query(sql_query, function(fax_entry) + destination_number = fax_entry.number; + end) + + return destination_number; +end + +-- List waiting fax documents +function Fax.queued_for_sending(self, limit) + limit = limit or FAX_PARALLEL_MAX; + local sql_query = 'SELECT * FROM `fax_documents` WHERE `state` IN ("queued_for_sending","unsuccessful") AND `retry_counter` > 0 ORDER BY `sent_at` ASC LIMIT ' .. limit; + local fax_documents = {} + self.database:query(sql_query, function(fax_entry) + fax_entry['destination_numbers'] = Fax:destination_numbers(fax_entry.id) + table.insert(fax_documents, fax_entry); + end) + + return fax_documents; +end + +-- Update fax document sending status +function Fax.document_update(self, id, params) + require 'common.str' + local params_sql = {} + + for name, value in pairs(params) do + table.insert(params_sql, '`' .. name .. '`=' .. common.str.to_sql(value)); + end + + if not params['sent_at'] then + table.insert(params_sql, '`sent_at`=NOW()'); + end + + if not params['updated_at'] then + table.insert(params_sql, '`updated_at`=NOW()'); + end + + local sql_query = 'UPDATE `fax_documents` SET ' .. table.concat(params_sql, ',') .. ' WHERE `id` = ' .. tonumber(id); + + return self.database:query(sql_query); +end + + +function Fax.get_parameters(self, caller) + local fax_parameters = { + bad_rows = caller:to_i('fax_bad_rows'), + total_pages = caller:to_i('fax_document_total_pages'), + transferred_pages = caller:to_i('fax_document_transferred_pages'), + ecm_requested = caller:to_b('fax_ecm_requested'), + ecm_used = caller:to_b('fax_ecm_used'), + filename = caller:to_s('fax_filename'), + image_resolution = caller:to_s('fax_image_resolution'), + image_size = caller:to_i('fax_image_size'), + local_station_id = caller:to_s('fax_local_station_id'), + result_code = caller:to_i('fax_result_code'), + result_text = caller:to_s('fax_result_text'), + remote_station_id = caller:to_s('fax_remote_station_id'), + success = caller:to_b('fax_success'), + transfer_rate = caller:to_i('fax_transfer_rate'), + v17_disabled = caller:to_b('fax_v17_disabled'), + } + + return fax_parameters; +end + +-- Receive Fax +function Fax.receive(self, caller, file_name) + file_name = file_name or self.fax_directory .. 'fax_in_' .. caller.uuid .. '.tiff'; + + caller:set_variable('fax_ident', self.record.station_id) + caller:set_variable('fax_verbose', 'false') + + caller:answer(); + local start_time = os.time(); + caller:execute('rxfax', file_name); + local record = self:get_parameters(caller); + record.transmission_time = os.time() - start_time; + return record; +end + +-- Send Fax +function Fax.send(self, caller, file_name) + caller:set_variable('fax_ident', self.record.station_id) + caller:set_variable('fax_header', self.record.name) + caller:set_variable('fax_verbose', 'false') + local start_time = os.time(); + caller:execute('txfax', file_name); + local record = self:get_parameters(caller); + record.transmission_time = os.time() - start_time; + return record; +end + +-- find fax document by id +function Fax.find_document_by_id(self, id) + local sql_query = 'SELECT * FROM `fax_documents` WHERE `id` = ' .. tonumber(id) .. ' LIMIT 1' + local record = nil + + self.database:query(sql_query, function(fax_entry) + record = fax_entry; + end); + + return record; +end + +-- save fax document to database +function Fax.insert_document(self, record) + require 'common.str' + local sql_query = 'INSERT INTO `fax_documents` ( \ + inbound, \ + retry_counter, \ + fax_resolution_id, \ + state, \ + transmission_time, \ + sent_at, \ + document_total_pages, \ + document_transferred_pages, \ + ecm_requested, \ + ecm_used, \ + image_resolution, \ + image_size, \ + local_station_id, \ + result_code, \ + remote_station_id, \ + success, \ + transfer_rate, \ + created_at, \ + updated_at, \ + fax_account_id, \ + caller_id_number, \ + caller_id_name, \ + tiff, \ + uuid \ + ) VALUES ( \ + true, \ + 0, \ + 1, \ + "received", \ + ' .. common.str.to_sql(record.transmission_time) .. ', \ + NOW(), \ + ' .. common.str.to_sql(record.total_pages) .. ', \ + ' .. common.str.to_sql(record.transferred_pages) .. ', \ + ' .. common.str.to_sql(record.ecm_requested) .. ', \ + ' .. common.str.to_sql(record.ecm_used) .. ', \ + ' .. common.str.to_sql(record.image_resolution) .. ', \ + ' .. common.str.to_sql(record.image_size) .. ', \ + ' .. common.str.to_sql(record.local_station_id) .. ', \ + ' .. common.str.to_sql(record.result_code) .. ', \ + ' .. common.str.to_sql(record.remote_station_id) .. ', \ + ' .. common.str.to_sql(record.success) .. ', \ + ' .. common.str.to_sql(record.transfer_rate) .. ', \ + NOW(), \ + NOW(), \ + ' .. common.str.to_sql(self.id) .. ', \ + ' .. common.str.to_sql(record.caller_id_number) .. ', \ + ' .. common.str.to_sql(record.caller_id_name) .. ', \ + ' .. common.str.to_sql(record.filename) .. ', \ + ' .. common.str.to_sql(record.uuid) .. ' \ + )'; + + return self.database:query(sql_query); +end diff --git a/misc/freeswitch/scripts/dialplan/functions.lua b/misc/freeswitch/scripts/dialplan/functions.lua new file mode 100644 index 0000000..c104f89 --- /dev/null +++ b/misc/freeswitch/scripts/dialplan/functions.lua @@ -0,0 +1,839 @@ +-- DialplanModule: Functions +-- +module(...,package.seeall) + +Functions = {} + +-- Create Functions object +function Functions.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self) + self.__index = self + self.log = arg.log + self.database = arg.database + self.domain = arg.domain + return object +end + +function Functions.ensure_caller_sip_account(self, caller) + if caller.account and caller.account.class == 'sipaccount' then + return caller.account; + end +end + +function Functions.dialplan_function(self, caller, dialed_number) + require 'common.str' + local parameters = common.str.to_a(dialed_number, '%-'); + if not parameters[2] then + return { continue = false, code = 484, phrase = 'Malformed function parameters', no_cdr = true }; + end + local fid = tostring(parameters[2]); + local result = { continue = false, code = 404, phrase = 'Function not found', no_cdr = true }; + + self.log:debug('DIALPLAN_DUNCTION - execute: ', dialed_number); + + if fid == "ta" then + result = self:transfer_all(caller, parameters[3]); + elseif fid == "in" then + result = self:intercept_extensions(caller, parameters[3]); + elseif fid == "ia" then + result = self:intercept_any_extension(caller, parameters[3]); + elseif fid == "anc" then + result = self:account_node_change(caller); + elseif fid == "li" then + result = self:user_login(caller, parameters[3], parameters[4]); + elseif fid == "lo" then + result = self:user_logout(caller); + elseif fid == "lir" then + result = self:user_login_redirect(caller, parameters[3], parameters[4]); + elseif fid == "loaon" then + result = self:user_auto_logout(caller, true); + elseif fid == "loaoff" then + result = self:user_auto_logout(caller, false); + elseif fid == "dcliroff" then + result = self:dial_clir_off(caller, parameters[3]); + elseif fid == "dcliron" then + result = self:dial_clir_on(caller, parameters[3]); + elseif fid == "clipon" then + result = self:clip_on(caller); + elseif fid == "clipoff" then + result = self:clip_off(caller); + elseif fid == "cwaoff" then + result = self:callwaiting_off(caller); + elseif fid == "cwaon" then + result = self:callwaiting_on(caller); + elseif fid == "cfoff" then + result = self:call_forwarding_off(caller); + elseif fid == "cfdel" then + result = self:call_forwarding_off(caller, nil, true); + elseif fid == "cfu" then + result = self:call_forwarding_on(caller, 'always', parameters[3], 'PhoneNumber'); + elseif fid == "cfuoff" then + result = self:call_forwarding_off(caller, 'always'); + elseif fid == "cfudel" then + result = self:call_forwarding_off(caller, 'always', true); + elseif fid == "cfutg" then + result = self:call_forwarding_toggle(caller, 'always', parameters[3]); + elseif fid == "cfn" then + result = self:call_forwarding_on(caller, 'noanswer', parameters[3], 'PhoneNumber', parameters[4]); + elseif fid == "cfnoff" then + result = self:call_forwarding_off(caller, 'noanswer'); + elseif fid == "cfndel" then + result = self:call_forwarding_off(caller, 'noanswer', true); + elseif fid == "cfo" then + result = self:call_forwarding_on(caller, 'offline', parameters[3], 'PhoneNumber'); + elseif fid == "cfooff" then + result = self:call_forwarding_off(caller, 'offline'); + elseif fid == "cfodel" then + result = self:call_forwarding_off(caller, 'offline', true); + elseif fid == "cfb" then + result = self:call_forwarding_on(caller, 'busy', parameters[3], 'PhoneNumber'); + elseif fid == "cfboff" then + result = self:call_forwarding_off(caller, 'busy'); + elseif fid == "cfbdel" then + result = self:call_forwarding_off(caller, 'busy', true); + elseif fid == "vmleave" then + result = self:voicemail_message_leave(caller, parameters[3]); + elseif fid == "vmcheck" then + result = self:voicemail_check(caller, parameters[3]); + elseif fid == "vmtg" then + result = self:call_forwarding_toggle(caller, nil, parameters[3]); + elseif fid == "acdmtg" then + result = self:acd_membership_toggle(caller, parameters[3], parameters[4]); + elseif fid == "e164" then + result = "+" .. tostring(parameters[3]); + elseif fid == "hangup" then + result = self:hangup(caller, parameters[3], parameters[4]); + end + + return result; +end + +-- Transfer all calls to a conference +function Functions.transfer_all(self, caller, destination_number) + self.log:info('TRANSFER_ALL - caller: ', caller.account_type, '/', caller.account_uuid, ' number: ', destination_number); + + local caller_sip_account = self:ensure_caller_sip_account(caller); + if not caller_sip_account then + self.log:error('TRANSFER_ALL - incompatible caller'); + return { continue = false, code = 403, phrase = 'Incompatible caller' } + end + + -- Query call and channel table for channel IDs + local sql_query = 'SELECT `b`.`name` AS `caller_chan_name`, `a`.`caller_uuid`, `a`.`callee_uuid` \ + FROM `calls` `a` JOIN `channels` `b` ON `a`.`caller_uuid` = `b`.`uuid` JOIN `channels` `c` \ + ON `a`.`callee_uuid` = `c`.`uuid` WHERE `b`.`name` LIKE ("%' .. caller_sip_account.record.auth_name .. '@%") \ + OR `c`.`name` LIKE ("%' .. caller_sip_account.record.auth_name .. '@%") LIMIT 100'; + + self.database:query(sql_query, function(call_entry) + local uid = nil + if call_entry.caller_chan_name:find(caller_sip_account.record.auth_name .. "@") then + uid = call_entry.callee_uuid; + self.log:debug("Transfering callee channel with uid: " .. uid); + else + uid = call_entry.caller_uuid; + self.log:debug("Transfering caller channel with uid: " .. uid); + end + freeswitch.API():execute("uuid_transfer", uid .. " " .. destination_number); + end) + + return destination_number; +end + +-- Intercept Extensions +function Functions.intercept_extensions(self, caller, destination_numbers) + if type(destination_numbers) == "string" then + destination_numbers = "\"" .. destination_numbers .. "\""; + else + destination_numbers = "\"" .. table.concat(destination_numbers, "\",\"") .. "\""; + end + + self.log:debug("Intercept call to number(s): " .. destination_numbers); + + if caller.account_type ~= "SipAccount" then + self.log:error("caller is not a SipAccount"); + return { continue = false, code = 403, phrase = 'Incompatible caller' } + end + + local sql_query = 'SELECT * FROM `channels` WHERE `callstate` IN ("EARLY", "ACTIVE") AND `dest` IN (' .. destination_numbers .. ') LIMIT 1'; + + self.database:query(sql_query, function(call_entry) + self.log:debug("intercepting call with uid: " .. call_entry.uuid); + caller:intercept(call_entry.uuid); + end) + + return nil; +end + +-- intercept call to destination (e.g. sip_account) +function Functions.intercept_destination(self, caller, destination) + self.log:debug("Intercept call to destination " .. destination); + local result = false; + local sql_query = 'SELECT `call_uuid`, `uuid` FROM `channels` WHERE `callstate` = "RINGING" AND `dest` = "' .. destination .. '" LIMIT 1'; + + caller:set_caller_id(caller.caller_phone_numbers[1] ,caller.caller_id_name); + self.database:query(sql_query, function(call_entry) + if call_entry.call_uuid and tostring(call_entry.call_uuid) then + self.log:debug("intercepting call - uuid: " .. call_entry.call_uuid); + caller:intercept(call_entry.call_uuid); + result = { continue = false, code = 200, call_service = 'pickup' } + require 'common.str' + require 'common.fapi' + local fapi = common.fapi.FApi:new{ log = self.log, uuid = call_entry.call_uuid } + if fapi:channel_exists() then + caller:set_caller_id( + common.str.to_s(fapi:get_variable('effective_caller_id_number')), + common.str.to_s(fapi:get_variable('effective_caller_id_name')) + ); + caller:set_callee_id( + common.str.to_s(fapi:get_variable('effective_callee_id_number')), + common.str.to_s(fapi:get_variable('effective_callee_id_name')) + ); + + caller:set_variable('gs_destination_type', fapi:get_variable('gs_destination_type')); + caller:set_variable('gs_destination_id', fapi:get_variable('gs_destination_id')); + caller:set_variable('gs_destination_uuid', fapi:get_variable('gs_destination_uuid')); + + caller:set_variable('gs_caller_account_type', fapi:get_variable('gs_account_type')); + caller:set_variable('gs_caller_account_id', fapi:get_variable('gs_account_id')); + caller:set_variable('gs_caller_account_uuid', fapi:get_variable('gs_account_uuid')); + + caller:set_variable('gs_auth_account_type', fapi:get_variable('gs_auth_account_type')); + caller:set_variable('gs_auth_account_id', fapi:get_variable('gs_auth_account_id')); + caller:set_variable('gs_auth_account_uuid', fapi:get_variable('gs_auth_account_uuid')); + end + else + self.log:error('FUNCTION - failed to intercept call - no caller uuid for callee uuid: ', call_entry.uuid); + end + end) + + return result; +end + +-- intercept call to owner of destination_number +function Functions.intercept_any_extension(self, caller, destination_number) + require 'common.phone_number' + local phone_number_object = common.phone_number.PhoneNumber:new{ log = self.log, database = self.database }:find_by_number(destination_number); + + if not phone_number_object or not phone_number_object.record then + self.log:notice("unallocated number: " .. tostring(destination_number)); + return false; + end + + if phone_number_object.record.phone_numberable_type == 'SipAccount' then + require "common.sip_account" + local sip_account_class = common.sip_account.SipAccount:new{ log = self.log, database = self.database } + local sip_account = sip_account_class:find_by_id(phone_number_object.record.phone_numberable_id) + if sip_account then + return self:intercept_destination(caller, sip_account.record.auth_name); + end + end +end + + +function Functions.account_node_change(self, caller) + self.log:info('NODE_CHANGE - caller: ', caller.account_type, '/', caller.account_uuid, ', caller_id: ', caller.caller_id_number); + + -- find caller's sip account + local caller_sip_account = caller.account; + if not caller_sip_account or not caller_sip_account.class == 'sipaccount' then + self.log:notice('LOGIN - caller sip_account not found'); + return { continue = false, code = 404, phrase = 'Account not found', no_cdr = true } + end + + require 'phones.phone' + local phone_class = phones.phone.Phone:new{log = self.log, database = self.database} + + -- logout caller phones if caller account is hot-deskable + local caller_phones = phone_class:find_all_hot_deskable_by_account(caller_sip_account.record.id); + for index, phone_caller in ipairs(caller_phones) do + phone_caller:logout(caller_sip_account.record.id); + end + + self:update_node_change(caller_sip_account, caller.local_node_id); + caller:answer(); + caller:send_display('Change successful'); + caller.session:sayPhrase('logged_in'); + + -- resync caller phones + for index, phone_caller in ipairs(caller_phones) do + local result = phone_caller:resync{ auth_name = caller_sip_account.auth_name, domain = caller.domain }; + self.log:info('NODE_CHANGE - resync phone - mac: ', phone_caller.record.mac_address, ', ip_address: ', phone_caller.record.ip_address, ', result: ', result); + end + + return { continue = false, code = 200, phrase = 'OK', no_cdr = true } +end + + +function Functions.user_login(self, caller, number, pin) + require 'common.str' + + local PHONE_NUMBER_LEN_MIN = 4; + local PHONE_NUMBER_LEN_MAX = 12; + local PIN_LEN_MIN = 4; + local PIN_LEN_MAX = 12; + + caller:set_variable('destination_number', 'f-li-' .. common.str.to_s(number) .. '-PIN'); + self.log:info('LOGIN - caller: ', caller.account_type, '/', caller.account_uuid, ', caller_id: ', caller.caller_id_number, ', number: ', number); + + if common.str.blank(number) then + number = caller.session:read(PHONE_NUMBER_LEN_MIN, PHONE_NUMBER_LEN_MAX, 'ivr/ivr-please_enter_extension_followed_by_pound.wav', 3000, '#'); + end + + -- find caller's sip account + local caller_sip_account = caller.account; + if not caller_sip_account or not caller_sip_account.class == 'sipaccount' then + self.log:notice('LOGIN - caller sip_account not found'); + return { continue = false, code = 404, phrase = 'Caller not found', no_cdr = true } + end + + require 'phones.phone' + local phone_class = phones.phone.Phone:new{log = self.log, database = self.database} + + local caller_phones = phone_class:find_all_hot_deskable_by_account(caller_sip_account.id); + local caller_phone = caller_phones[1]; + + if not caller_phone then + self.log:notice('LOGIN - caller phone not found or not hot-deskable'); + return { continue = false, code = 403, phrase = 'Phone not hot-deskable', no_cdr = true } + end + + require 'common.phone_number' + local phone_number = common.phone_number.PhoneNumber:new{log = self.log, database = self.database}:find_by_number(number, {"SipAccount"}); + + if not phone_number then + self.log:notice('LOGIN - number not found or not linked to a sip account - number: ', number); + return { continue = false, code = 404, phrase = 'Account not found', no_cdr = true } + end + + require 'common.sip_account' + local destination_sip_account = common.sip_account.SipAccount:new{ log = self.log, database = self.database }:find_by_id(phone_number.record.phone_numberable_id); + + if not destination_sip_account then + self.log:notice('LOGIN - account not found - ', phone_number.record.phone_numberable_type, '=', phone_number.record.phone_numberable_id, ', number: ', number); + return { continue = false, code = 404, phrase = 'Account not found', no_cdr = true } + end + + self.log:info('LOGIN - destination: ', phone_number.record.phone_numberable_type, '=', destination_sip_account.record.id, + ', caller_name: ', destination_sip_account.record.caller_name, ', hotdeskable: ', destination_sip_account.record.hotdeskable); + + if not common.str.to_b(destination_sip_account.record.hotdeskable) then + self.log:notice('LOGIN - destination sip_account not hot-deskable'); + return { continue = false, code = 404, phrase = 'Destination not hot-deskable', no_cdr = true } + end + + require 'dialplan.user' + local user = dialplan.user.User:new{ log = self.log, database = self.database }:find_by_id(destination_sip_account.record.sip_accountable_id); + + if common.str.blank(pin) then + pin = caller.session:read(PIN_LEN_MIN, PIN_LEN_MAX, 'ivr/ivr-please_enter_pin_followed_by_pound.wav', 3000, '#'); + end + + if not user then + self.log:notice('LOGIN - user not found - ', destination_sip_account.record.sip_accountable_type, '=',destination_sip_account.record.sip_accountable_id); + return { continue = false, code = 403, phrase = 'Authentication failed', no_cdr = true } + end + + if not user:check_pin(pin) then + self.log:notice('LOGIN - authentication failed'); + return { continue = false, code = 403, phrase = 'Authentication failed', no_cdr = true } + end + + -- logout caller phones if caller account is hot-deskable + if common.str.to_b(caller_sip_account.record.hotdeskable) then + for index, phone_caller in ipairs(caller_phones) do + phone_caller:logout(caller_sip_account.record.id); + end + end + + local destination_phones = phone_class:find_all_hot_deskable_by_account(destination_sip_account.record.id); + -- logout destination phones + for index, phone_destination in ipairs(destination_phones) do + phone_destination:logout(destination_sip_account.record.id); + end + + local result = caller_phone:login(destination_sip_account.record.id, destination_sip_account.record.sip_accountable_id, destination_sip_account.record.sip_accountable_type); + self.log:info('LOGIN - account login - mac: ', caller_phone.record.mac_address, ', ip_address: ', caller_phone.record.ip_address, ', result: ', result); + + if not result then + return { continue = false, code = 403, phrase = 'Login failed', no_cdr = true } + end + + caller:answer(); + caller:send_display('Login successful'); + + self:update_node_change(destination_sip_account, caller.local_node_id); + caller:sleep(1000); + + -- resync destination phones + for index, phone_destination in ipairs(destination_phones) do + local result = phone_destination:resync{ auth_name = destination_sip_account.auth_name, domain = caller.domain_local }; + self.log:info('LOGIN - resync destination phone - mac: ', phone_destination.record.mac_address, ', ip_address: ', phone_destination.record.ip_address, ', result: ', result); + end + + -- resync caller phones + for index, phone_caller in ipairs(caller_phones) do + local result = phone_caller:resync{ auth_name = caller_sip_account.auth_name, domain = caller.domain }; + self.log:info('LOGIN - resync caller phone - mac: ', phone_caller.record.mac_address, ', ip_address: ', phone_caller.record.ip_address, ', result: ', result); + end + + return { continue = false, code = 200, phrase = 'OK', no_cdr = true } +end + + +function Functions.user_logout(self, caller) + require 'common.str' + self.log:info('LOGOUT - caller: ', caller.account_type, '/', caller.account_uuid, ', caller_id: ', caller.caller_id_number); + + -- find caller's sip account + local caller_sip_account = caller.account; + if not caller_sip_account or not caller_sip_account.class == 'sipaccount' then + self.log:notice('LOGOUT - caller sip_account not found'); + return { continue = false, code = 404, phrase = 'Caller not found', no_cdr = true } + end + + if not common.str.to_b(caller_sip_account.record.hotdeskable) then + self.log:notice('LOGOUT - caller sip_account not hot-deskable'); + return { continue = false, code = 404, phrase = 'Caller not hot-deskable', no_cdr = true } + end + + require 'phones.phone' + local phone_class = phones.phone.Phone:new{log = self.log, database = self.database} + + local caller_phones = phone_class:find_all_hot_deskable_by_account(caller_sip_account.id); + + if caller_phones == 0 then + self.log:notice('LOGOUT - caller phones not found or not hot-deskable'); + return { continue = false, code = 403, phrase = 'Phone not hot-deskable', no_cdr = true } + end + + local result = false; + for index, phone_caller in ipairs(caller_phones) do + result = phone_caller:logout(caller_sip_account.record.id); + self.log:info('LOGOUT - account logout - mac: ', phone_caller.record.mac_address, ', ip_address: ', phone_caller.record.ip_address, ', result: ', result); + end + + caller:answer(); + caller:send_display('Logout successful'); + caller:sleep(1000); + + -- resync caller phones + for index, phone_caller in ipairs(caller_phones) do + local result = phone_caller:resync{ auth_name = caller_sip_account.auth_name, domain = caller.domain }; + self.log:info('LOGIN - resync caller phone - mac: ', phone_caller.record.mac_address, ', ip_address: ', phone_caller.record.ip_address, ', result: ', result); + end + + return { continue = false, code = 200, phrase = 'OK', no_cdr = true } +end + + +function Functions.update_node_change(self, sip_account, node_id) + require 'common.sync_log' + local sync_log_class = common.sync_log.SyncLog:new{ log = self.log, database = self.database, homebase_ip_address = sip_account.record.host } + + if tostring(sip_account.record.gs_node_id) ~= tostring(node_id) then + self.log:info('UPDATE_NODE - from: ', sip_account.record.gs_node_id, ', to: ', node_id, ', sipaccount=', sip_account.record.id, '/', sip_account.record.uuid, '@', node_id, ', caller_name: ', sip_account.record.caller_name); + sql_query = 'UPDATE `sip_accounts` SET `updated_at` = NOW(), `gs_node_id` = ' .. tonumber(node_id) .. ' WHERE id = ' .. tonumber(sip_account.record.id); + if self.database:query(sql_query) then + sync_log_class:insert('SipAccount', { uuid = sip_account.record.uuid, gs_node_id = tonumber(node_id), updated_at = os.date('!%Y-%m-%d %H:%M:%S %Z') }, 'update', { 'gs_node_id', 'updated_at' }); + end + end + + require 'common.phone_number' + local phone_numbers = common.phone_number.PhoneNumber:new{log = self.log, database = self.database}:find_all_by_owner(sip_account.record.id, 'SipAccount'); + for number_id, phone_number in pairs(phone_numbers) do + if tostring(phone_number.record.gs_node_id) ~= tostring(node_id) then + self.log:info('UPDATE_NODE - from: ', phone_number.record.gs_node_id, ', to: ', node_id, ', phonenumber=', phone_number.record.id, '/', phone_number.record.uuid, '@', node_id, ', number: ', phone_number.record.number); + sql_query = 'UPDATE `phone_numbers` SET `updated_at` = NOW(), `gs_node_id` = ' .. tonumber(node_id) .. ' WHERE id = ' .. tonumber(number_id); + + if self.database:query(sql_query) then + sync_log_class:insert('PhoneNumber', { uuid = phone_number.record.uuid, gs_node_id = tonumber(node_id), updated_at = os.date('!%Y-%m-%d %H:%M:%S %Z') }, 'update', { 'gs_node_id', 'updated_at' }); + end + end + end +end + + +function Functions.user_login_redirect(self, caller, phone_number, pin) + -- Remove PIN from destination_number + caller.session:setVariable("destination_number", "f-li-" .. tostring(phone_number) .. "-PIN"); + + -- Redirect to f-li function + caller.session:execute("redirect", "sip:f-li-" .. tostring(phone_number) .. "-" .. tostring(pin) .. "@" .. caller.domain); +end + +-- Set nightly_reboot flag +function Functions.user_auto_logout(self, caller, auto_logout) + local nightly_reboot = 'FALSE'; + if auto_logout then + nightly_reboot = 'TRUE'; + end + + -- Ensure a valid sip account + local caller_sip_account = caller.account; + if not caller_sip_account or not caller_sip_account.class == 'sipaccount' then + return { continue = false, code = 403, phrase = 'Incompatible caller', no_cdr = true } + end + + require "phones.phone" + local phone_class = phones.phone.Phone:new{log = self.log, database = self.database} + + -- Get caller phone + local caller_phone = phone_class:find_hot_deskable_by_account(caller_sip_account.id); + if not caller_phone then + self.log:notice("Caller phone not found or not hot-deskable"); + return { continue = false, code = 401, phrase = 'Phone not hot-deskable', no_cdr = true } + end + + log:debug("Hot-desking auto log off - caller phone: " .. caller_phone.record.id .. ", mac: " .. caller_phone.record.mac_address); + + sql_query = 'UPDATE `phones` SET `nightly_reboot` = ' .. nightly_reboot .. ' WHERE `id` = ' .. tonumber(caller_phone.record.id); + + if not self.database:query(sql_query) then + self.log:error('Hot-desking auto log off status could not be changed from ' .. tostring(caller_phone.record.nightly_reboot) .. ' to ' .. nightly_reboot); + return { continue = false, code = 401, phrase = 'Value could not be changed', no_cdr = true } + + end + + self.log:debug('Hot-desking auto log off changed from ' .. tostring(caller_phone.record.nightly_reboot) .. ' to ' .. nightly_reboot); + + caller:answer(); + caller:send_display('Logout successful'); + caller:sleep(1000); +end + +function Functions.dial_clir_off(self, caller, phone_number) + -- Ensure a valid sip account + local caller_sip_account = self:ensure_caller_sip_account(caller); + if not caller_sip_account then + return { continue = false, code = 403, phrase = 'Incompatible caller', no_cdr = true } + end + + caller.clir = false; + return { continue = true, number = phone_number } +end + +function Functions.dial_clir_on(self, caller, phone_number) + -- Ensure a valid sip account + local caller_sip_account = self:ensure_caller_sip_account(caller); + if not caller_sip_account then + return { continue = false, code = 403, phrase = 'Incompatible caller', no_cdr = true } + end + + caller.clir = true; + return { continue = true, number = phone_number } +end + +function Functions.callwaiting_on(self, caller) + -- Find caller's SipAccount + local caller_sip_account = self:ensure_caller_sip_account(caller); + if not caller_sip_account then + return { continue = false, code = 403, phrase = 'Incompatible caller', no_cdr = true } + end + + local sql_query = 'UPDATE `sip_accounts` SET `call_waiting` = TRUE WHERE `id` = ' .. caller_sip_account.record.id; + + if not self.database:query(sql_query) then + self.log:notice("Call Waiting could not be set"); + return { continue = false, code = 500, phrase = 'Call Waiting could not be set', no_cdr = true } + end + + caller:answer(); + caller:send_display('Call waiting on'); + caller:sleep(1000); + return { continue = false, code = 200, phrase = 'OK', no_cdr = true } +end + +function Functions.callwaiting_off(self, caller) + -- Find caller's SipAccount + local caller_sip_account = self:ensure_caller_sip_account(caller); + if not caller_sip_account then + return { continue = false, code = 403, phrase = 'Incompatible caller', no_cdr = true } + end + + local sql_query = 'UPDATE `sip_accounts` SET `call_waiting` = FALSE WHERE `id` = ' .. caller_sip_account.record.id; + + if not self.database:query(sql_query) then + self.log:notice("Call Waiting could not be set"); + return { continue = false, code = 500, phrase = 'Call Waiting could not be set', no_cdr = true } + end + + caller:answer(); + caller:send_display('Call waiting off'); + caller:sleep(1000); + return { continue = false, code = 200, phrase = 'OK', no_cdr = true } +end + +function Functions.clip_on(self, caller) + -- Find caller's SipAccount + local caller_sip_account = self:ensure_caller_sip_account(caller); + if not caller_sip_account then + return { continue = false, code = 403, phrase = 'Incompatible caller', no_cdr = true } + end + + local sql_query = 'UPDATE `sip_accounts` SET `clip` = TRUE WHERE `id` = ' .. caller_sip_account.record.id; + + if not self.database:query(sql_query) then + self.log:notice("CLIP could not be set"); + return { continue = false, code = 500, phrase = 'CLIP could not be set', no_cdr = true } + + end + + caller:answer(); + caller:send_display('CLIP on'); + caller:sleep(1000); + return { continue = false, code = 200, phrase = 'OK', no_cdr = true } +end + +function Functions.clip_off(self, caller) + -- Find caller's SipAccount + local caller_sip_account = self:ensure_caller_sip_account(caller); + if not caller_sip_account then + return { continue = false, code = 403, phrase = 'Incompatible caller', no_cdr = true } + end + + local sql_query = 'UPDATE `sip_accounts` SET `clip` = FALSE WHERE `id` = ' .. caller_sip_account.record.id; + + if not self.database:query(sql_query) then + self.log:notice("CLIP could not be set"); + return { continue = false, code = 500, phrase = 'CLIP could not be set', no_cdr = true } + + end + + caller:answer(); + caller:send_display('CLIP off'); + caller:sleep(1000); + return { continue = false, code = 200, phrase = 'OK', no_cdr = true } +end + + +function Functions.call_forwarding_off(self, caller, call_forwarding_service, delete) + local defaults = {log = self.log, database = self.database, domain = caller.domain} + + -- Find caller's SipAccount + local caller_sip_account = self:ensure_caller_sip_account(caller); + if not caller_sip_account then + return { continue = false, code = 403, phrase = 'Incompatible caller', no_cdr = true } + end + + require 'common.phone_number' + local phone_number_class = common.phone_number.PhoneNumber:new{ log = self.log, database = self.database, domain = caller.domain }; + local phone_numbers = phone_number_class:list_by_owner(caller_sip_account.record.id, 'SipAccount'); + + local success = false; + for index, phone_number in pairs(phone_numbers) do + phone_number_object = phone_number_class:find_by_number(phone_number); + if phone_number_object then + if phone_number_object:call_forwarding_off(call_forwarding_service, nil, delete) then + success = true; + end + end + end + + if not success then + self.log:notice("call forwarding could not be deactivated"); + return { continue = false, code = 500, phrase = 'Call Forwarding could not be deactivated', no_cdr = true } + + end + + caller:answer(); + caller:send_display('Call forwarding off'); + caller:sleep(1000); + return { continue = false, code = 200, phrase = 'OK', no_cdr = true } +end + + +function Functions.call_forwarding_on(self, caller, call_forwarding_service, destination, destination_type, timeout) + local defaults = {log = self.log, database = self.database, domain = caller.domain} + + if not call_forwarding_service then + self.log:notice('no call forwarding service specified'); + end + + -- Find caller's SipAccount + local caller_sip_account = self:ensure_caller_sip_account(caller); + if not caller_sip_account then + return { continue = false, code = 403, phrase = 'Incompatible caller', no_cdr = true } + end + + require "common.phone_number" + local phone_number_class = common.phone_number.PhoneNumber:new{ log = self.log, database = self.database, domain = caller.domain }; + local phone_numbers = phone_number_class:list_by_owner(caller_sip_account.record.id, 'SipAccount'); + + local success = false; + for index, phone_number in pairs(phone_numbers) do + phone_number_object = phone_number_class:find_by_number(phone_number); + if phone_number_object then + if phone_number_object:call_forwarding_on(call_forwarding_service, destination, timeout) then + success = true; + end + end + end + + if not success then + self.log:notice("call forwarding could not be activated"); + return { continue = false, code = 500, phrase = 'Call Forwarding could not be activated', no_cdr = true } + + end + + caller:answer(); + caller:send_display('Call forwarding on'); + caller:sleep(1000); + return { continue = false, code = 200, phrase = 'OK', no_cdr = true } +end + + +function Functions.call_forwarding_toggle(self, caller, call_forwarding_service, phone_number_id) + local defaults = {log = self.log, database = self.database, domain = caller.domain} + + -- Find caller's SipAccount + local caller_sip_account = self:ensure_caller_sip_account(caller); + if not caller_sip_account then + return { continue = false, code = 403, phrase = 'Incompatible caller', no_cdr = true } + end + + require "common.phone_number" + local phone_number_class = common.phone_number.PhoneNumber:new{ log = self.log, database = self.database, domain = caller.domain }; + local phone_numbers = phone_number_class:list_by_owner(caller_sip_account.record.id, 'SipAccount'); + + local result = nil; + for index, phone_number in pairs(phone_numbers) do + phone_number_object = phone_number_class:find_by_number(phone_number); + if phone_number_object then + if not result then + result = phone_number_object:call_forwarding_toggle(call_forwarding_service); + elseif result.active then + phone_number_object:call_forwarding_on(call_forwarding_service, result.destination, result.destination_type, result.timeout); + else + phone_number_object:call_forwarding_off(call_forwarding_service); + end + end + end + + if not result then + self.log:notice("call forwarding could not be toggled"); + return { continue = false, code = 500, phrase = 'Call Forwarding could not be toggled', no_cdr = true } + + end + + caller:answer(); + caller:send_display('Call forwarding toggled'); + caller:sleep(1000); + return { continue = false, code = 200, phrase = 'OK', no_cdr = true } +end + + +function Functions.voicemail_message_leave(self, caller, phone_number) + require 'dialplan.voicemail' + local voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_number(phone_number); + + if not voicemail_account then + return { continue = false, code = 404, phrase = 'Mailbox not found', no_cdr = true } + end + + voicemail_account:leave(caller, phone_number); + + if caller:to_s("voicemail_message_len") ~= '' then + voicemail_account:send_notify(caller); + else + self.log:debug("voicemail - no message saved"); + end + + return { continue = false, code = 200, phrase = 'OK' } +end + + +function Functions.voicemail_check(self, caller, phone_number) + local voicemail_account = nil; + local voicemail_authorized = false; + + require 'dialplan.voicemail' + + if phone_number then + voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_number(phone_number); + else + if caller.auth_account_type == 'SipAccount' then + voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_sip_account_id(caller.auth_account.id); + voicemail_authorized = true; + end + end + + if not voicemail_account then + return { continue = false, code = 404, phrase = 'Mailbox not found', no_cdr = true } + end + + voicemail_account:menu(caller, voicemail_authorized); + + return { continue = false, code = 200, phrase = 'OK', no_cdr = true } +end + + +function Functions.acd_membership_toggle(self, caller, agent_id, phone_number) + -- Find caller's SipAccount + local caller_sip_account = self:ensure_caller_sip_account(caller); + if not caller_sip_account then + return { continue = false, code = 403, phrase = 'Incompatible caller', no_cdr = true } + end + + require 'dialplan.acd' + local acd_class = dialplan.acd.AutomaticCallDistributor:new{ log = self.log, database = self.database, domain = self.domain }; + + self.log:info('ACD_MEMBERSHIP_TOGGLE - sipaccount=', caller_sip_account.id, '/', caller_sip_account.uuid, ', agent=', agent_id, ', ACD phone number: ', phone_number); + + if not tonumber(agent_id) or tonumber(agent_id) == 0 then + + if not phone_number then + self.log:notice('ACD_MEMBERSHIP_TOGGLE - neither agent_id nor phone_number specified'); + return { continue = false, code = 404, phrase = 'Agent not found', no_cdr = true } + end + + require "common.phone_number" + local phone_number_object = common.phone_number.PhoneNumber:new{ log = self.log, database = self.database, domain = caller.domain }:find_by_number(phone_number, {'AutomaticCallDistributor'}); + + if not phone_number_object or not tonumber(phone_number_object.record.phone_numberable_id) then + self.log:notice('ACD_MEMBERSHIP_TOGGLE - ACD not found'); + return { continue = false, code = 404, phrase = 'ACD not found', no_cdr = true } + end + + local agent = acd_class:agent_find_by_acd_and_destination(phone_number_object.record.phone_numberable_id, caller_sip_account.class, caller_sip_account.id); + + if not agent or not tonumber(agent.id) then + self.log:notice('ACD_MEMBERSHIP_TOGGLE - agent not found'); + return { continue = false, code = 404, phrase = 'Agent not found', no_cdr = true } + end + + agent_id = agent.id; + end + + local status = acd_class:agent_status_toggle(agent_id, 'sipaccount', caller_sip_account.id); + + if not status then + self.log:error('ACD_MEMBERSHIP_TOGGLE - error toggling ACD membership'); + return { continue = false, code = 500, phrase = 'Error toggling ACD membership', no_cdr = true } + end + + self.log:info('ACD_MEMBERSHIP_TOGGLE - sipaccount=', caller_sip_account.id, '/', caller_sip_account.uuid, ', agent=', agent_id, ', status: ', status); + + caller:answer(); + caller:send_display('ACD membership toggled: ' .. status); + caller:sleep(500); + caller.session:sayPhrase('acd_agent_status', tostring(status)); + return { continue = false, code = 200, phrase = 'OK', no_cdr = true } +end + +function Functions.hangup(self, caller, code, phrase) + require 'common.str' + + if not tonumber(code) then + code = 403; + phrase = 'Forbidden'; + end + + if common.str.blank(phrase) then + phrase = 'Hangup here'; + end + + self.log:info("FUNCTION_HANGUP code: ", code, ', phrase: ', phrase); + return { continue = false, code = code, phrase = phrase:gsub('_', ' '), no_cdr = true } +end diff --git a/misc/freeswitch/scripts/dialplan/geo_number.lua b/misc/freeswitch/scripts/dialplan/geo_number.lua new file mode 100644 index 0000000..06bfd62 --- /dev/null +++ b/misc/freeswitch/scripts/dialplan/geo_number.lua @@ -0,0 +1,89 @@ +-- Gemeinschaft 5 module: geonumber class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +GeoNumber = {} + +-- create phone_book object +function GeoNumber.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.class = 'geonumber'; + self.log = arg.log; + self.database = arg.database; + return object; +end + +function GeoNumber.country(self, phone_number) + if phone_number:match('^%+1') then + return { id = 0, name = 'NANP', country_code = '1' } + end + + local country_codes = {}; + for i = 2, 4, 1 do + table.insert(country_codes, phone_number:sub(2, i)); + end + + local sql_query = 'SELECT * FROM `countries` WHERE `country_code` IN ("' .. table.concat(country_codes, '","') .. '") ORDER BY LENGTH(`country_code`) DESC LIMIT 1'; + + local country = nil; + self.database:query(sql_query, function(entry) + country = entry; + end) + + return country; +end + + +function GeoNumber.area_code(self, phone_number, country_code) + local sql_query = nil; + local area_code = nil; + + if country_code == '1' then + area_code = {} + area_code.area_code, area_code.central_office_code, area_code.subscriber_number, area_code.extension = phone_number:match('%+1(%d%d%d)(%d%d%d)(%d%d%d%d)(%d*)'); + sql_query = 'SELECT `a`.`name`, `b`.`name` AS `country` FROM `area_codes` `a` \ + JOIN `countries` `b` ON `a`.`country_id` = `b`.`id` \ + WHERE `b`.`country_code` = "' .. tostring(country_code) .. '"\ + AND `a`.`area_code` = "' .. tostring(area_code.area_code) .. '" \ + AND `a`.`central_office_code` = "' .. tostring(area_code.central_office_code) .. '" LIMIT 1'; + else + local offset = #country_code; + area_codes = {}; + for i = (3 + offset), (6 + offset), 1 do + table.insert(area_codes, phone_number:sub((2 + offset), i)); + end + + sql_query = 'SELECT `a`.`name`, `b`.`name` AS `country` FROM `area_codes` `a` \ + JOIN `countries` `b` ON `a`.`country_id` = `b`.`id` \ + WHERE `b`.`country_code` = "' .. country_code .. '"\ + AND `a`.`area_code` IN ("' .. table.concat(area_codes, '","') .. '") ORDER BY LENGTH(`a`.`area_code`) DESC LIMIT 1'; + end + + self.database:query(sql_query, function(entry) + area_code = entry; + end) + + return area_code; +end + + +function GeoNumber.find(self, phone_number) + if not phone_number:match('^%+%d+') then + return nil; + end + + local country = self:country(phone_number); + if country then + local area_code = self:area_code(phone_number, country.country_code); + if area_code then + return area_code; + else + return { country = country.name }; + end + end +end diff --git a/misc/freeswitch/scripts/dialplan/hunt_group.lua b/misc/freeswitch/scripts/dialplan/hunt_group.lua new file mode 100644 index 0000000..87f86f1 --- /dev/null +++ b/misc/freeswitch/scripts/dialplan/hunt_group.lua @@ -0,0 +1,202 @@ +-- Gemeinschaft 5 module: hunt group class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +HuntGroup = {} + +local DEFAULT_MEMBER_TIMEOUT = 20; + +-- Create HuntGroup object +function HuntGroup.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.class = 'huntgroup'; + self.log = arg.log; + self.database = arg.database; + self.record = arg.record; + return object; +end + + +function HuntGroup.find_by_id(self, id) + local sql_query = 'SELECT * FROM `hunt_groups` WHERE `id`= '.. tonumber(id) .. ' LIMIT 1'; + local hunt_group = nil; + + self.database:query(sql_query, function(entry) + hunt_group = HuntGroup:new(self); + hunt_group.record = entry; + hunt_group.id = tonumber(entry.id); + hunt_group.uuid = entry.uuid; + end) + + return hunt_group; +end + + +function HuntGroup.find_by_uuid(self, uuid) + local sql_query = 'SELECT * FROM `hunt_groups` WHERE `id`= "'.. uuid .. '" LIMIT 1'; + local hunt_group = nil; + + self.database:query(sql_query, function(entry) + hunt_group = HuntGroup:new(self); + hunt_group.record = entry; + hunt_group.id = tonumber(entry.id); + hunt_group.uuid = entry.uuid; + end) + + return hunt_group; +end + + +function HuntGroup.list_active_members(self) + local sql_query = 'SELECT `a`.`number`, `b`.`name` \ + FROM `phone_numbers` `a` \ + LEFT JOIN `hunt_group_members` `b` ON `a`.`phone_numberable_type` = "huntgroupmember" AND `a`.`phone_numberable_id` = `b`.`id` \ + WHERE `a`.`phone_numberable_type` = "huntgroupmember" \ + AND `b`.`active` IS TRUE \ + AND `b`.`hunt_group_id` = ' .. self.record.id; + + local hunt_group_members = {} + + self.database:query(sql_query, function(hunt_group_members_entry) + table.insert(hunt_group_members, hunt_group_members_entry); + end) + + return hunt_group_members; +end + + +function HuntGroup.is_member_by_numbers(self, numbers) + local sql_query = 'SELECT `a`.`number`, `b`.`name` \ + FROM `phone_numbers` `a` \ + LEFT JOIN `hunt_group_members` `b` ON `a`.`phone_numberable_type` = "huntgroupmember" AND `a`.`phone_numberable_id` = `b`.`id` \ + WHERE `a`.`phone_numberable_type` = "huntgroupmember" \ + AND `b`.`active` IS TRUE \ + AND `b`.`hunt_group_id` = ' .. self.record.id .. '\ + AND `a`.`number` IN ("' .. table.concat( numbers, '","') .. '") LIMIT 1'; + + local hunt_group_member = false; + + self.database:query(sql_query, function(hunt_group_members_entry) + hunt_group_member = true; + end) + + return hunt_group_member; +end + + +function HuntGroup.run(self, dialplan_object, caller, destination) + local hunt_group_members = self:list_active_members(); + + if #hunt_group_members == 0 then + return { disposition = 'HUNT_GROUP_EMPTY', code = 480, phrase = 'No active users' } + end + + self.log:info('HUNTGROUP ', self.record.id, ' - name: ', self.record.name, ', strategy: ', self.record.strategy,', members: ', #hunt_group_members); + + local destinations = {} + for index, hunt_group_member in ipairs(hunt_group_members) do + local destination = dialplan_object:destination_new{ number = hunt_group_member.number }; + if destination.type == 'unknown' then + require 'dialplan.route' + local routes = dialplan.route.Route:new{ log = self.log, database = self.database, routing_table = dialplan_object.routes }:outbound(caller, destination.number); + if routes and #routes > 0 then + destination.callee_id_number = destination.number; + destination.callee_id_name = nil; + local route = routes[1]; + destination.gateway = route.endpoint; + destination.type = route.class; + destination.number = route.value; + destination.caller_id_number = route.caller_id_number; + destination.caller_id_name = route.caller_id_name; + table.insert(destinations, destination); + end + else + table.insert(destinations, destination); + end + end + + local forwarding_destination = nil; + if caller.forwarding_service == 'assistant' and caller.auth_account then + forwarding_destination = dialplan_object:destination_new{ type = caller.auth_account.class, id = caller.auth_account.id, number = forwarding_number } + forwarding_destination.alert_info = 'http://amooma.com;info=Ringer0;x-line-id=0'; + end + + local result = { continue = false }; + local start_time = os.time(); + if self.record.strategy == 'ring_recursively' then + local member_timeout = tonumber(self.record.seconds_between_jumps) or DEFAULT_MEMBER_TIMEOUT; + local run_queue = true; + while run_queue do + for index, member_destination in ipairs(destinations) do + local recursive_destinations = { member_destination } + if forwarding_destination then + table.insert(recursive_destinations, forwarding_destination); + end + require 'dialplan.sip_call' + result = dialplan.sip_call.SipCall:new{ log = self.log, database = self.database, caller = caller }:fork( recursive_destinations, { callee_id_number = destination.number, timeout = member_timeout }); + if result.disposition == 'SUCCESS' then + if result.fork_index then + result.destination = recursive_destinations[result.fork_index]; + end + run_queue = false; + break; + elseif os.time() > start_time + dialplan_object.dial_timeout_active then + run_queue = false; + break; + elseif not caller:ready() then + run_queue = false; + break; + end + end + if tostring(result.code) == '486' then + self.log:info('HUNTGROUP ', self.record.id, ' - all members busy'); + run_queue = false; + end + end + else + if forwarding_destination then + table.insert(destinations, forwarding_destination); + end + + require 'dialplan.sip_call' + result = dialplan.sip_call.SipCall:new{ log = self.log, database = self.database, caller = caller }:fork( destinations, + { + callee_id_number = destination.number, + timeout = dialplan_object.dial_timeout_active, + send_ringing = ( dialplan_object.send_ringing_to_gateways and caller.from_gateway ), + }); + if result.fork_index then + result.destination = destinations[result.fork_index]; + end + + return result; + end + + return result; +end + + +function HuntGroup.list_destination_numbers(self) + require "common.phone_number" + local phone_number_class = common.phone_number.PhoneNumber:new(defaults) + + local sql_query = string.format("SELECT * FROM `phone_numbers` WHERE `state`='active' AND `phone_numberable_type` = 'HuntGroupMember' AND `phone_numberable_id` IN ( \ + SELECT `id` FROM `hunt_group_members` WHERE `active` IS TRUE AND `hunt_group_id`=%d ) ORDER BY `position` ASC", tonumber(self.record.id)); + local phone_numbers = {} + + self.database:query(sql_query, function(hunt_group_number_entry) + local number_object = phone_number_class:find_by_number(hunt_group_number_entry.number) + if number_object and number_object.record then + table.insert(phone_numbers, {number = hunt_group_number_entry.number, destination_type = number_object.record.phone_numberable_type, destination_id = number_object.record.phone_numberable_id}); + else + table.insert(phone_numbers, {number = hunt_group_number_entry.number}); + end + end) + + return phone_numbers ; +end diff --git a/misc/freeswitch/scripts/dialplan/phone_book.lua b/misc/freeswitch/scripts/dialplan/phone_book.lua new file mode 100644 index 0000000..089f115 --- /dev/null +++ b/misc/freeswitch/scripts/dialplan/phone_book.lua @@ -0,0 +1,63 @@ +-- Gemeinschaft 5 module: phone book class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +PhoneBook = {} + +-- create phone_book object +function PhoneBook.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.class = 'phonebook'; + self.log = arg.log; + self.database = arg.database; + return object; +end + + +function PhoneBook.find_entry_by_number_user_tenant(self, numbers, user_id, tenant_id) + user_id = tonumber(user_id) or 0; + tenant_id = tonumber(tenant_id) or 0; + + if not numbers or #numbers == 0 then + return nil; + end + local numbers_sql = '"' .. table.concat(numbers, '","') .. '"'; + + local sql_query = 'SELECT `a`.`name` AS `number_name`, \ + `a`.`number`, \ + `b`.`id`, \ + `b`.`value_of_to_s`, \ + `b`.`phone_book_id`, \ + `b`.`image`, \ + `c`.`name` AS `phone_book_name`, \ + `d`.`bellcore_id` \ + FROM `phone_numbers` `a` \ + JOIN `phone_book_entries` `b` ON `a`.`phone_numberable_id` = `b`.`id` AND `a`.`phone_numberable_type` = "PhoneBookENtry" \ + JOIN `phone_books` `c` ON `b`.`phone_book_id` = `c`.`id` \ + LEFT JOIN `ringtones` `d` ON `a`.`id` = `d`.`ringtoneable_id` AND `d`.`ringtoneable_type` = "PhoneNumber" \ + WHERE ((`c`.`phone_bookable_type` = "User" AND `c`.`phone_bookable_id` = ' .. user_id .. ') \ + OR (`c`.`phone_bookable_type` = "Tenant" AND `c`.`phone_bookable_id` = ' .. tenant_id .. ')) \ + AND `a`.`number` IN (' .. numbers_sql .. ') \ + AND `a`.`state` = "active" \ + AND `b`.`state` = "active" \ + AND `c`.`state` = "active" \ + ORDER BY `c`.`phone_bookable_type` DESC LIMIT 1'; + + local phone_book_entry = nil; + + self.database:query(sql_query, function(entry) + phone_book_entry = entry; + if entry.number_name then + phone_book_entry.caller_id_name = tostring(entry.value_of_to_s) .. ' (' .. entry.number_name .. ')'; + else + phone_book_entry.caller_id_name = entry.value_of_to_s; + end + end) + + return phone_book_entry; +end diff --git a/misc/freeswitch/scripts/dialplan/presence.lua b/misc/freeswitch/scripts/dialplan/presence.lua new file mode 100644 index 0000000..234b908 --- /dev/null +++ b/misc/freeswitch/scripts/dialplan/presence.lua @@ -0,0 +1,84 @@ +-- Gemeinschaft 5 module: presence class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +Presence = {} + +-- Create Presence object +function Presence.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self) + self.__index = self + self.log = arg.log; + self.domain = arg.domain; + self.uuid = arg.uuid; + self.inbound = arg.inbound; + self.accounts = arg.accounts; + + return object +end + + +function Presence.init(self, arg) + self.log = arg.log or self.log; + self.domain = arg.domain or self.domain; + self.uuid = arg.uuid or self.uuid; + self.inbound = arg.inbound or self.inbound; + self.accounts = arg.accounts or self.accounts; +end + + +function Presence.set(self, state, caller_number) + if not self.accounts or #self.accounts == 0 then + return nil; + end + + state = state or "terminated"; + local direction = "outbound"; + + if self.inbound then + direction = "inbound"; + end + + for index, account in pairs(self.accounts) do + if account ~= '' then + local event = freeswitch.Event('PRESENCE_IN'); + event:addHeader('proto', 'sip'); + event:addHeader('from', account .. '@' .. self.domain); + event:addHeader('event_type', 'presence'); + event:addHeader('alt_event_type', 'dialog'); + event:addHeader('presence-call-direction', direction); + event:addHeader('answer-state', state); + event:addHeader('unique-id', self.uuid); + if caller_number then + if self.inbound then + event:addHeader('Caller-Destination-Number', caller_number); + else + event:addHeader('Other-Leg-Caller-ID-Number', caller_number); + end + end + event:fire(); + self.log:debug('PRESENCE - account: ' .. account .. '@' .. self.domain .. ', state: ' .. state .. ', direction: ' .. direction .. ', uid: ' ..self.uuid); + end + end + + return true; +end + + +function Presence.early(self, caller_number) + return self:set("early", caller_number); +end + + +function Presence.confirmed(self, caller_number) + return self:set("confirmed", caller_number); +end + + +function Presence.terminated(self, caller_number) + return self:set("terminated", caller_number); +end diff --git a/misc/freeswitch/scripts/dialplan/route.lua b/misc/freeswitch/scripts/dialplan/route.lua new file mode 100644 index 0000000..2243cbe --- /dev/null +++ b/misc/freeswitch/scripts/dialplan/route.lua @@ -0,0 +1,265 @@ +-- Gemeinschaft 5 module: routing class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +Route = {} + +-- create route object +function Route.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.database = arg.database; + self.routing_table = arg.routing_table; + self.expandable = arg.expandable or {}; + return object; +end + +-- find matching routes +function Route.prerouting(self, caller, number) + require 'common.routing_tables' + + for index, routing_entry in pairs(self.routing_table.prerouting) do + local route = common.routing_tables.match_route(routing_entry, number); + if route.error then + self.log:error('PREROUTE - error: ', route.error); + elseif route.value then + self.log:info('ROUTE_PREROUTING - called number: ', number, ', value: ', route.value, ', pattern: ', route.pattern); + return route; + end + end +end + +-- find matching routes +function Route.outbound(self, caller, number) + local routes = {}; + require 'common.routing_tables' + require 'common.str' + + local ignore_arguments = { + class=true, + endpoint=true, + pattern=true, + value=true, + group=true, + phrase=true, + } + + local clip_no_screening = common.str.try(caller, 'account.record.clip_no_screening'); + local caller_id_numbers = {} + if not common.str.blank(clip_no_screening) then + for index, number in ipairs(common.str.strip_to_a(clip_no_screening, ',')) do + table.insert(caller_id_numbers, number); + end + end + for index, number in ipairs(caller.caller_phone_numbers) do + table.insert(caller_id_numbers, number); + end + self.log:info('CALLER_ID_NUMBER - caller_id_numbers: ', table.concat(caller_id_numbers, ',')); + + for index, routing_entry in pairs(self.routing_table.outbound) do + local route = common.routing_tables.match_route(routing_entry, number); + if route.error then + self.log:error('ROUTE_OUTBOUND - error: ', route.error); + elseif route.value then + local valid_route = true; + + for argument, value in pairs(route) do + if not ignore_arguments[argument] then + local table_value = common.str.downcase(tostring(common.str.try(caller, argument))); + value = common.str.downcase(tostring(value)); + if table_value:match(value) then + self.log:info('ROUTE_OUTBOUND_POSITIVE - ', argument, '=', value, ' ~ ', table_value, ', pattern: ', route.pattern); + else + self.log:info('ROUTE_OUTBOUND_NEGATIVE - ', argument, '=', value, ' !~ ', table_value, ', pattern: ', route.pattern); + valid_route = false; + end + end + end + + if route.group then + if common.str.try(caller.auth_account, 'owner.groups.' .. tostring(route.group)) then + self.log:info('ROUTE_OUTBOUND_POSITIVE - group=', route.group, ', pattern: ', route.pattern); + else + self.log:info('ROUTE_OUTBOUND_NEGATIVE - group=', route.group, ', pattern: ', route.pattern); + valid_route = false; + end + end + + if route.cidn then + if caller.caller_id_number:match(route.cidn) then + self.log:info('ROUTE_OUTBOUND_POSITIVE - cidn=', route.cidn, ' ~ ', caller.caller_id_number,', pattern: ', route.pattern); + else + self.log:info('ROUTE_OUTBOUND_NEGATIVE - cidn=', route.cidn, ' !~ ', caller.caller_id_number, ', pattern: ', route.pattern); + valid_route = false; + end + end + + if valid_route then + if route.class ~= 'hangup' then + route.caller_id_number = self:outbound_cid_number(caller, caller_id_numbers, route.endpoint, route.class); + self.expandable.caller_id_number = route.caller_id_number; + route.caller_id_name = self:outbound_cid_name(caller, route.endpoint, route.class); + end + table.insert(routes, route); + self.log:info('ROUTE_OUTBOUND ', #routes,' - ', route.class, '=', route.endpoint, ', value: ', route.value, ', caller_id_number: ', route.caller_id_number, ', caller_id_name: ', route.caller_id_name); + end + end + end + + return routes; +end + + +function Route.inbound(self, caller, number) + require 'common.routing_tables' + + local ignore_arguments = { + class=true, + endpoint=true, + pattern=true, + value=true, + group=true, + phrase=true, + } + + for index, routing_entry in pairs(self.routing_table.inbound) do + local route = common.routing_tables.match_route(routing_entry, number); + if route.error then + self.log:error('ROUTE_INBOUND - error: ', route.error); + elseif route.value then + local valid_route = true; + + for argument, value in pairs(route) do + if not ignore_arguments[argument] then + local table_value = common.str.downcase(tostring(common.str.try(caller, argument))); + value = common.str.downcase(tostring(value)); + if table_value:match(value) then + self.log:info('ROUTE_INBOUND_POSITIVE - ', argument, '=', value, ' ~ ', table_value, ', pattern: ', route.pattern); + else + self.log:info('ROUTE_INBOUND_NEGATIVE - ', argument, '=', value, ' !~ ', table_value, ', pattern: ', route.pattern); + valid_route = false; + end + end + end + + if route.class and route.endpoint then + if route.class == 'gateway' and caller.gateway_name:match(route.endpoint) then + self.log:info('ROUTE_INBOUND_POSITIVE - ', route.class, '=', route.endpoint, ' ~ ', caller.gateway_name, ', pattern: ', route.pattern); + else + self.log:info('ROUTE_INBOUND_NEGATIVE - ', route.class, '=', route.endpoint, ' !~ ', caller.gateway_name, ', pattern: ', route.pattern); + valid_route = false; + end + end + + if valid_route then + self.log:info('ROUTE_INBOUND - called number: ', number, ', value: ', route.value, ', pattern: ', route.pattern); + return route; + end + end + end +end + +-- find caller id +function Route.caller_id(self, caller, cid_entry, search_str, endpoint, class) + local ignore_arguments = { + class=true, + endpoint=true, + pattern=true, + value=true, + group=true, + phrase=true, + } + + local route = common.routing_tables.match_route(cid_entry, search_str, self.expandable); + if route.error then + self.log:error('CALLER_ID - error: ', route.error); + elseif route.value then + local valid_route = true; + + for argument, value in pairs(route) do + if not ignore_arguments[argument] then + local table_value = common.str.downcase(tostring(common.str.try(caller, argument))); + value = common.str.downcase(tostring(value)); + if table_value:match(value) then + self.log:debug('CALLER_ID_POSITIVE - ', argument, '=', value, ' ~ ', table_value, ', pattern: ', route.pattern); + else + self.log:debug('CALLER_ID_NEGATIVE - ', argument, '=', value, ' !~ ', table_value, ', pattern: ', route.pattern); + valid_route = false; + end + end + end + + if route.group then + if common.str.try(caller.auth_account, 'owner.groups.' .. tostring(route.group)) then + self.log:debug('CALLER_ID_POSITIVE - group=', route.group, ', pattern: ', route.pattern); + else + self.log:debug('CALLER_ID_NEGATIVE - group=', route.group, ', pattern: ', route.pattern); + valid_route = false; + end + end + + endpoint = tostring(endpoint); + if route.class and route.endpoint then + if route.class == 'gateway' and endpoint:match(route.endpoint) then + self.log:debug('CALLER_ID_POSITIVE - ', route.class, '=', route.endpoint, ' ~ ', endpoint, ', pattern: ', route.pattern); + else + self.log:debug('CALLER_ID_NEGATIVE - ', route.class, '=', route.endpoint, ' !~ ', endpoint, ', pattern: ', route.pattern); + valid_route = false; + end + end + + if valid_route then + self.log:debug('CALLER_ID ', route.class, '=', route.endpoint, ', value: ', route.value); + return route.value; + end + end + + return nil; +end + +-- find matching caller id number +function Route.outbound_cid_number(self, caller, caller_id_numbers, endpoint, class) + for route_index, cid_entry in pairs(self.routing_table.outbound_cid_number) do + for index, number in ipairs(caller_id_numbers) do + local route = self:caller_id(caller, cid_entry, number, endpoint, class); + if route then + return route; + end + end + end +end + +-- find matching caller id name +function Route.outbound_cid_name(self, caller, endpoint, class) + for route_index, cid_entry in pairs(self.routing_table.outbound_cid_name) do + local route = self:caller_id(caller, cid_entry, caller.caller_id_name, endpoint, class); + if route then + return route; + end + end +end + +-- find matching caller id number +function Route.inbound_cid_number(self, caller, endpoint, class) + for route_index, cid_entry in pairs(self.routing_table.inbound_cid_number) do + local route = self:caller_id(caller, cid_entry, caller.caller_id_number, endpoint, class); + if route then + return route; + end + end +end + +-- find matching caller id name +function Route.inbound_cid_name(self, caller, endpoint, class) + for route_index, cid_entry in pairs(self.routing_table.inbound_cid_name) do + local route = self:caller_id(caller, cid_entry, caller.caller_id_name, endpoint, class); + if route then + return route; + end + end +end diff --git a/misc/freeswitch/scripts/dialplan/session.lua b/misc/freeswitch/scripts/dialplan/session.lua new file mode 100644 index 0000000..7174b24 --- /dev/null +++ b/misc/freeswitch/scripts/dialplan/session.lua @@ -0,0 +1,224 @@ +-- Gemeinschaft 5 module: caller session class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +Session = {} + +-- create session object +function Session.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.session = arg.session; + + if not self.session then + return nil; + end + + return object; +end + +function Session.init_channel_variables(self) + self.cause = "UNSPECIFIED" + + self.uuid = self.session:get_uuid(); + self.destination_number = self:expand_variables(self:to_s('destination_number')); + self.called_number = self.destination_number; + + self.caller_id_number = self:to_s('caller_id_number'); + self.caller_id_name = self:to_s('caller_id_name'); + self.caller_phone_number = self.caller_id_number; + self.caller_phone_numbers = {self.caller_id_number}; + + self.domain = self:to_s('domain_name'); + self.gateway_name = self:to_s('sip_gateway'); + self.from_gateway = self:to_b('gs_from_gateway'); + if self.from_gateway then + self.gateway_name = self:to_s('gs_gateway_name'); + elseif self.gateway_name ~= '' then + self.from_gateway = true; + end + + self.account_uuid = self:to_s('gs_account_uuid'); + self.account_type = self:to_s('gs_account_type'); + self.sip_contact_host = self:to_s('sip_contact_host'); + self.clir = self:to_b('gs_clir'); + self.call_timeout = self:to_i('gs_call_timeout'); + self.auth_account_type = self:to_s('gs_auth_account_type'); + self.auth_account_uuid = self:to_s('gs_auth_account_uuid'); + + self.node_id = self:to_i('sip_h_X-GS_node_id'); + self.loop_count = self:to_i('sip_h_X-GS_loop_count'); + + if self.node_id > 0 and self.node_id ~= self.local_node_id then + self.from_node = true; + else + self.from_node = false; + end + self:set_variable('gs_account_node_local', not self.from_node); + + if self.from_node then + self.account_uuid = self:to_s('sip_h_X-GS_account_uuid'); + self.account_type = self:to_s('sip_h_X-GS_account_type'); + self.auth_account_uuid = self:to_s('sip_h_X-GS_auth_account_uuid'); + self.auth_account_type = self:to_s('sip_h_X-GS_auth_account_type'); + end + + if self.auth_account_type == '' then + self.auth_account_type = self.account_type; + self.auth_account_uuid = self.account_uuid; + end + + self.forwarding_number = nil; + self.forwarding_service = nil; + + return true; +end + + +-- Cast channel variable to string +function Session.to_s(self, variable_name) + require 'common.str' + return common.str.to_s(self.session:getVariable(variable_name)); +end + +-- Cast channel variable to integer +function Session.to_i(self, variable_name) + require 'common.str' + return common.str.to_i(self.session:getVariable(variable_name)); +end + +-- Cast channel variable to boolean +function Session.to_b(self, variable_name) + require 'common.str' + return common.str.to_b(self.session:getVariable(variable_name)); +end + +-- Split channel variable to table +function Session.to_a(self, variable_name) + require 'common.str' + return common.str.to_a(self.session:getVariable(variable_name)); +end + +-- Check if session is active +function Session.ready(self, command, parameters) + return self.session:ready(); +end + +-- Wait milliseconds +function Session.sleep(self, milliseconds) + return self.session:sleep(milliseconds); +end + +-- Execute command +function Session.execute(self, command, parameters) + parameters = parameters or ''; + self.session:execute(command, parameters); +end + +-- Execute and return result +function Session.result(self, command_line) + self.session:execute('set', 'result=${' .. command_line .. '}'); + return self.session:getVariable('result'); +end + +-- Set cause code +function Session.set_cause(self, cause) + self.cause = cause +end + +-- Set channel variable +function Session.set_variable(self, name, value) + self.session:setVariable(name, tostring(value)); +end + +-- Set and export channel variable +function Session.export_variable(self, name, value) + self.session:execute('export', tostring(name) .. '=' .. tostring(value)); +end + +-- Set SIP header +function Session.set_header(self, name, value) + self.session:setVariable('sip_h_' .. name, tostring(value)); +end + +-- Hangup a call +function Session.hangup(self, cause) + return self.session:hangup(cause); +end + +-- Respond a call +function Session.respond(self, code, text) + self.session:execute('respond', tostring(code) .. ' ' .. text); + return self.session:hangupCause(); +end + +-- Answer a call +function Session.answer(self) + return self.session:answer(); +end + +function Session.intercept(self, uid) + self.session:execute("intercept", uid); +end + +function Session.send_display(self, ... ) + self:execute('send_display', table.concat( arg, '|')); +end + +-- Set caller ID +function Session.set_caller_id(self, number, name) + if number then + self.caller_id_number = tostring(number); + self.session:setVariable('effective_caller_id_number', tostring(number)) + end + if name then + self.caller_id_name = tostring(name); + self.session:setVariable('effective_caller_id_name', tostring(name)) + end +end + +-- Set callee ID +function Session.set_callee_id(self, number, name) + if number ~= nil then + self.callee_id_number = tostring(number); + self.session:execute('export', 'effective_callee_id_number=' .. number); + end + if name ~= nil then + self.callee_id_name = tostring(name); + self.session:execute('export', 'effective_callee_id_name=' .. name); + end +end + +-- Set caller Privacy header +function Session.set_privacy(self, privacy) + if privacy then + self.session:setVariable('cid_type', 'none'); + self.session:setVariable('sip_h_Privacy', 'id'); + else + self.session:setVariable('cid_type', 'none'); + self.session:setVariable('sip_h_Privacy', 'none'); + end +end + + +function Session.set_auth_account(self, auth_account) + if auth_account then + self:set_variable('gs_auth_account_type', auth_account.class); + self:set_variable('gs_auth_account_id', auth_account.id); + self:set_variable('gs_auth_account_uuid', auth_account.uuid); + end + + return auth_account; +end + + +function Session.expand_variables(self, line) + return (line:gsub('{([%a%d_-]+)}', function(captured) + return self.session:getVariable(captured) or ''; + end)) +end diff --git a/misc/freeswitch/scripts/dialplan/sip_call.lua b/misc/freeswitch/scripts/dialplan/sip_call.lua new file mode 100644 index 0000000..57f92c6 --- /dev/null +++ b/misc/freeswitch/scripts/dialplan/sip_call.lua @@ -0,0 +1,266 @@ +-- Gemeinschaft 5 module: sip call class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall); + +SipCall = {} + +-- Create SipCall object +function SipCall.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.session = arg.session; + self.record = arg.record; + self.database = arg.database; + self.domain = arg.domain; + self.caller = arg.caller; + self.on_answer = arg.on_answer; + self.calling_object = arg.calling_object; + return object; +end + + +function SipCall.wait_answer(self, caller_session, callee_session, timeout, start_time) + if caller_session:ready() and callee_session:ready() then + callee_session:waitForAnswer(caller_session); + end + + while true do + if not caller_session:ready() then + return 'ORIGINATOR_CANCEL'; + elseif not callee_session:ready() then + return 'UNSPECIFIED'; + elseif (os.time() - start_time) > timeout then + return 'NO_ANSWER'; + elseif callee_session:answered() then + return 'SUCCESS'; + end + + self.caller:sleep(500); + end +end + + +function SipCall.wait_hangup(self, caller_session, callee_session) + local hangup_on = { + CS_HANGUP = true, + CS_DESTROY = true, + } + + while true do + local state_caller = caller_session:getState(); + local state_callee = callee_session:getState(); + if hangup_on[state_caller] or hangup_on[state_callee] then + break; + end + caller_session:sleep(500); + end +end + + +function SipCall.call_waiting_busy(self, sip_account) + require 'common.str' + if common.str.to_b(sip_account.record.call_waiting) then + self.log:info('CALL_WAITING - status: enabled'); + return false; + else + local state = sip_account:call_state(); + self.log:info('CALL_WAITING - status: disabled, sip_account state: ', state); + return state; + end +end + + +function SipCall.fork(self, destinations, arg ) + local dial_strings = {} + + require 'common.sip_account' + local sip_account_class = common.sip_account.SipAccount:new{ log = self.log, database = self.database }; + + local call_result = { code = 404, phrase = 'No destination' }; + local some_destinations_busy = false; + + for index, destination in ipairs(destinations) do + local origination_variables = { 'gs_fork_index=' .. index } + + self.log:info('FORK ', index, '/', #destinations, ' - ', destination.type, '=', destination.id, '/', destination.gateway or destination.uuid, '@', destination.node_id, ', number: ', destination.number); + if not destination.node_local or destination.type == 'node' then + require 'common.node' + local node = nil; + if tonumber(destination.gateway) then + node = common.node.Node:new{ log = self.log, database = self.database }:find_by_id(tonumber(destination.gateway)); + else + node = common.node.Node:new{ log = self.log, database = self.database }:find_by_id(destination.node_id); + end + if node then + table.insert(origination_variables, 'sip_h_X-GS_node_id=' .. self.caller.local_node_id); + table.insert(dial_strings, '[' .. table.concat(origination_variables , ',') .. ']sofia/gateway/' .. node.record.name .. '/' .. destination.number); + end + elseif destination.type == 'sipaccount' then + local callee_id_params = ''; + local sip_account = sip_account_class:find_by_id(destination.id); + local call_waiting = self:call_waiting_busy(sip_account); + if not call_waiting then + destinations[index].numbers = sip_account:phone_numbers(); + + if not arg.callee_id_name then + table.insert(origination_variables, "effective_callee_id_name='" .. sip_account.record.caller_name .. "'"); + end + if not arg.callee_id_number then + table.insert(origination_variables, "effective_callee_id_number='" .. destination.number .. "'"); + end + if destination.alert_info then + table.insert(origination_variables, "alert_info='" .. destination.alert_info .. "'"); + end + table.insert(dial_strings, '[' .. table.concat(origination_variables , ',') .. ']user/' .. sip_account.record.auth_name); + else + some_destinations_busy = true; + call_result = { code = 486, phrase = 'User busy', disposition = 'USER_BUSY' }; + end + elseif destination.type == 'gateway' then + if destination.caller_id_number then + table.insert(origination_variables, "origination_caller_id_number='" .. destination.caller_id_number .. "'"); + end + if destination.caller_id_name then + table.insert(origination_variables, "origination_caller_id_name='" .. destination.caller_id_name .. "'"); + end + table.insert(dial_strings, '[' .. table.concat(origination_variables , ',') .. ']sofia/gateway/' .. destination.gateway .. '/' .. destination.number); + elseif destination.type == 'dial' then + if destination.caller_id_number then + table.insert(origination_variables, "origination_caller_id_number='" .. destination.caller_id_number .. "'"); + end + if destination.caller_id_name then + table.insert(origination_variables, "origination_caller_id_name='" .. destination.caller_id_name .. "'"); + end + table.insert(dial_strings, '[' .. table.concat(origination_variables , ',') .. ']' .. destination.number); + else + self.log:info('FORK ', index, '/', #destinations, ' - unhandled destination type: ', destination.type, ', number: ', destination.number); + end + end + + if #dial_strings == 0 then + self.log:notice('FORK - no active destinations - result: ', call_result.code, ' ', call_result.phrase); + return call_result; + end + + self.caller:set_callee_id(arg.callee_id_number, arg.callee_id_name); + self.caller:set_header('X-GS_account_uuid', self.caller.account_uuid); + self.caller:set_header('X-GS_account_type', self.caller.account_type); + self.caller:set_header('X-GS_auth_account_type', self.caller.auth_account_type); + self.caller:set_header('X-GS_auth_account_uuid', self.caller.auth_account_uuid); + self.caller:set_header('X-GS_loop_count', self.caller.loop_count); + + self.caller:set_variable('call_timeout', arg.timeout ); + self.log:info('FORK DIAL - destinations: ', #dial_strings, ', timeout: ', arg.timeout); + + if arg.send_ringing then + self.caller:execute('ring_ready'); + end + + local start_time = os.time(); + local session_callee = freeswitch.Session('{local_var_clobber=true}' .. table.concat(dial_strings, ','), self.caller.session); + self.log:debug('FORK SESSION_INIT - dial_time: ', os.time() - start_time); + local answer_result = self:wait_answer(self.caller.session, session_callee, arg.timeout, start_time); + local fork_index = nil; + self.log:info('FORK ANSWER - status: ', answer_result, ', dial_time: ', os.time() - start_time); + if answer_result == 'SUCCESS' then + session_callee:setAutoHangup(false); + fork_index = tonumber(session_callee:getVariable('gs_fork_index')) or 0; + local destination = destinations[fork_index]; + + if arg.bypass_media_network then + local callee_uuid = session_callee:get_uuid(); + + if callee_uuid and self.caller.uuid and freeswitch then + require 'common.ipcalc' + local callee_network_str = self.caller:to_s('bleg_network_addr'); + local caller_network_str = self.caller:to_s('network_addr'); + local callee_network_addr = common.ipcalc.ipv4_to_i(callee_network_str); + local caller_network_addr = common.ipcalc.ipv4_to_i(caller_network_str); + local network, netmask = common.ipcalc.ipv4_to_network_netmask(arg.bypass_media_network); + if network and netmask and callee_network_addr and caller_network_addr + and common.ipcalc.ipv4_in_network(callee_network_addr, network, netmask) + and common.ipcalc.ipv4_in_network(caller_network_addr, network, netmask) then + self.log:info('FORK ', fork_index, ' BYPASS_MEDIA - caller_ip: ', caller_network_str, + ', callee_ip: ', callee_network_str, + ', subnet: ', arg.bypass_media_network, + ', uuid: ', self.caller.uuid, ', bleg_uuid: ', callee_uuid); + freeswitch.API():execute('uuid_media', 'off ' .. self.caller.uuid); + freeswitch.API():execute('uuid_media', 'off ' .. callee_uuid); + end + end + end + + if self.on_answer then + self.on_answer(self.calling_object, destination); + end + + self.caller:set_variable('gs_destination_type', destination.type); + self.caller:set_variable('gs_destination_id', destination.id); + self.caller:set_variable('gs_destination_uuid', destination.uuid); + + self.log:info('FORK ', fork_index, + ' BRIDGE - destination: ', destination.type, '=', destination.id, '/', destination.uuid,'@', destination.node_id, + ', number: ', destination.number, + ', dial_time: ', os.time() - start_time); + freeswitch.bridge(self.caller.session, session_callee); + self:wait_hangup(self.caller.session, session_callee); + end + + -- if session_callee:ready() then + -- self.log:info('FORK - hangup destination channel'); + -- session_callee:hangup('ORIGINATOR_CANCEL'); + -- end + + call_result = {}; + call_result.disposition = session_callee:hangupCause(); + call_result.fork_index = fork_index; + + if some_destinations_busy and call_result.disposition == 'USER_NOT_REGISTERED' then + call_result.phrase = 'User busy'; + call_result.code = 486; + call_result.disposition = 'USER_BUSY'; + elseif call_result.disposition == 'USER_NOT_REGISTERED' then + call_result.phrase = 'User offline'; + call_result.code = 480; + elseif call_result.disposition == 'NO_ANSWER' then + call_result.phrase = 'No answer'; + call_result.code = 408; + elseif call_result.disposition == 'NORMAL_TEMPORARY_FAILURE' then + call_result.phrase = 'User offline'; + call_result.code = 480; + else + call_result.cause = self.caller:to_s('last_bridge_hangup_cause'); + call_result.code = self.caller:to_i('last_bridge_proto_specific_hangup_cause'); + call_result.phrase = self.caller:to_s('sip_hangup_phrase'); + end + + self.log:info('FORK EXIT - disposition: ', call_result.disposition, + ', cause: ', call_result.cause, + ', code: ', call_result.code, + ', phrase: ', call_result.phrase, + ', dial_time: ', os.time() - start_time); + + return call_result; +end + +-- Return call forwarding settngs +function SipCall.conditional_call_forwarding(self, cause, call_forwarding) + local condition_map = {USER_NOT_REGISTERED="offline", NO_ANSWER="noanswer", USER_BUSY="busy"} + local condition = condition_map[cause] + if call_forwarding and condition and call_forwarding[condition] then + log:debug('call forwarding on ' .. condition .. ' - destination: ' .. call_forwarding[condition].destination .. ', type: ' .. call_forwarding[condition].call_forwardable_type); + return call_forwarding[condition] + end +end + +function SipCall.set_callee_variables(self, sip_account) + self.session:setVariable("gs_callee_account_id", sip_account.id); + self.session:setVariable("gs_callee_account_type", "SipAccount"); + self.session:setVariable("gs_callee_account_owner_type", sip_account.sip_accountable_type); + self.session:setVariable("gs_callee_account_owner_id", sip_account.sip_accountable_id); +end diff --git a/misc/freeswitch/scripts/dialplan/tenant.lua b/misc/freeswitch/scripts/dialplan/tenant.lua new file mode 100644 index 0000000..8d6436c --- /dev/null +++ b/misc/freeswitch/scripts/dialplan/tenant.lua @@ -0,0 +1,51 @@ +-- Gemeinschaft 5 module: user class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +Tenant = {} + +-- Create Tenant object +function Tenant.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self) + self.__index = self; + self.class = 'tenant'; + self.log = arg.log; + self.database = arg.database; + self.record = arg.record; + return object; +end + +-- find tenant by id +function Tenant.find_by_id(self, id) + local sql_query = 'SELECT * FROM `tenants` WHERE `id`= ' .. tonumber(id) .. ' LIMIT 1'; + local tenant = nil; + + self.database:query(sql_query, function(account_entry) + tenant = Tenant:new(self); + tenant.record = account_entry; + tenant.id = tonumber(account_entry.id); + tenant.uuid = account_entry.uuid; + end); + + return tenant; +end + +-- find tenant by uuid +function Tenant.find_by_uuid(self, uuid) + tenant_id = tonumber(tenant_id) + local sql_query = 'SELECT * FROM `tenants` WHERE `id`= "' .. uuid .. '" LIMIT 1'; + local tenant = nil; + + self.database:query(sql_query, function(account_entry) + tenant = Tenant:new(self); + tenant.record = account_entry; + tenant.id = tonumber(account_entry.id); + tenant.uuid = account_entry.uuid; + end); + + return tenant; +end diff --git a/misc/freeswitch/scripts/dialplan/user.lua b/misc/freeswitch/scripts/dialplan/user.lua new file mode 100644 index 0000000..3b483c8 --- /dev/null +++ b/misc/freeswitch/scripts/dialplan/user.lua @@ -0,0 +1,91 @@ +-- Gemeinschaft 5 module: user class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +User = {} + +MAX_GROUP_MEMBERSHIPS = 256; + +-- create user object +function User.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.class = 'user'; + self.log = arg.log; + self.database = arg.database; + self.record = arg.record; + return object; +end + +-- find user by id +function User.find_by_id(self, id) + local sql_query = 'SELECT * FROM `users` WHERE `id`= ' .. tonumber(id) .. ' LIMIT 1'; + local user = nil; + + self.database:query(sql_query, function(account_entry) + user = User:new(self); + user.record = account_entry; + user.id = tonumber(account_entry.id); + user.uuid = account_entry.uuid; + end); + + return user; +end + +-- find user by uuid +function User.find_by_uuid(self, uuid) + local sql_query = 'SELECT * FROM `users` WHERE `id`= "' .. uuid .. '" LIMIT 1'; + local user = nil; + + self.database:query(sql_query, function(account_entry) + user = User:new(self); + user.record = account_entry; + user.id = tonumber(account_entry.id); + user.uuid = account_entry.uuid; + end); + + return user; +end + + +function User.list_groups(self, id) + require 'common.str' + id = id or self.id; + local sql_query = 'SELECT `b`.`name` FROM `user_group_memberships` `a` \ + JOIN `user_groups` `b` ON `a`.`user_group_id` = `b`.`id` \ + WHERE `a`.`state` = "active" AND `a`.`user_id`= ' .. tonumber(id) .. ' ORDER BY `b`.`position` LIMIT ' .. MAX_GROUP_MEMBERSHIPS; + + local groups = {}; + + self.database:query(sql_query, function(entry) + groups[common.str.downcase(entry.name)] = true; + end); + + return groups; +end + + +function User.check_pin(self, pin_to_check) + if not self.record then + return nil + end + + local str_to_hash = tostring(self.record.pin_salt) .. tostring(pin_to_check); + + local file = io.popen("echo -n " .. str_to_hash .. "|sha256sum"); + local pin_to_check_hash = file:read("*a"); + file:close(); + + pin_to_check_hash = pin_to_check_hash:sub(1, 64); + + if pin_to_check_hash == self.record.pin_hash then + return true; + end + + return false; +end + diff --git a/misc/freeswitch/scripts/dialplan/voicemail.lua b/misc/freeswitch/scripts/dialplan/voicemail.lua new file mode 100644 index 0000000..b9dab79 --- /dev/null +++ b/misc/freeswitch/scripts/dialplan/voicemail.lua @@ -0,0 +1,155 @@ +-- Gemeinschaft 5 module: voicemail class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +Voicemail = {} + +MESSAGE_LENGTH_MIN = 3; +MESSAGE_LENGTH_MAX = 120; +SILENCE_LENGTH_ABORT = 5; +SILENCE_LEVEL = 500; +BEEP = 'tone_stream://%(1000,0,500)'; +RECORD_FILE_PREFIX = '/tmp/voicemail_'; + +-- create voicemail object +function Voicemail.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.class = 'voicemail'; + self.log = arg.log; + self.database = arg.database; + self.record = arg.record; + return object +end + +-- find voicemail account by sip account id +function Voicemail.find_by_sip_account_id(self, id) + local sql_query = 'SELECT `a`.`id`, `a`.`uuid`, `a`.`auth_name`, `a`.`caller_name`, `b`.`name_path`, `b`.`greeting_path`, `a`.`voicemail_pin`, `b`.`password`, `c`.`host` AS `domain` \ + FROM `sip_accounts` `a` LEFT JOIN `voicemail_prefs` `b` ON `a`.`auth_name` = `b`.`username` \ + JOIN `sip_domains` `c` ON `a`.`sip_domain_id` = `c`.`id` \ + WHERE `a`.`id` = ' .. tonumber(id); + + local voicemail_account = nil; + self.database:query(sql_query, function(entry) + voicemail_account = Voicemail:new(self); + voicemail_account.record = entry; + voicemail_account.id = tonumber(entry.id); + voicemail_account.uuid = entry.uuid; + end) + + return voicemail_account; +end + +-- Find Voicemail account by name +function Voicemail.find_by_name(self, account_name) + id = tonumber(id) or 0; + local sql_query = string.format('SELECT * FROM `voicemail_prefs` WHERE `username`= "%s" LIMIT 1', account_name) + local record = nil + + self.database:query(sql_query, function(voicemail_entry) + record = voicemail_entry + end) + + if voicemail_account then + voicemail_account.account_name = account_name; + if record then + voicemail_account.name_path = record.name_path; + voicemail_account.greeting_path = record.greeting_path; + voicemail_account.password = record.password; + end + end + + return voicemail_account +end + +-- Find Voicemail account by name +function Voicemail.find_by_number(self, phone_number) + local sip_account = nil; + + require "common.phone_number" + local phone_number_class = common.phone_number.PhoneNumber:new{ log = self.log, database = self.database }; + local destination_number_object = phone_number_class:find_by_number(phone_number); + if destination_number_object and destination_number_object.record.phone_numberable_type == "SipAccount" then + return Voicemail:find_by_sip_account_id(destination_number_object.record.phone_numberable_id); + end + + return false; +end + + +function Voicemail.leave(self, caller, phone_number) + require 'common.str' + + self.log:info('VOICEMAIL_LEAVE - account=', self.record.id, '/', self.record.uuid, ', auth_name: ', self.record.auth_name, ', caller_name: ', self.record.caller_name); + + caller:set_callee_id(phone_number, self.record.caller_name); + caller:answer(); + caller:send_display(common.str.to_s(self.record.caller_name), common.str.to_s(phone_number)); + caller:sleep(1000); + + if not common.str.blank(self.record.greeting_path) then + caller.session:sayPhrase('voicemail_play_greeting', 'greeting:' .. tostring(self.record.greeting_path)); + elseif not common.str.blank(self.record.name_path) then + caller.session:sayPhrase('voicemail_play_greeting', 'name:' .. tostring(self.record.name_path)); + elseif not common.str.blank(phone_number) then + caller.session:sayPhrase('voicemail_play_greeting', (tostring(phone_number):gsub('[%D]', ''))); + end + + local record_file_name = RECORD_FILE_PREFIX .. caller.uuid .. '.wav'; + caller.session:streamFile(BEEP); + self.log:info('VOICEMAIL_LEAVE - recording to file: ', tostring(record_file_name)); + local result = caller.session:recordFile(record_file_name, MESSAGE_LENGTH_MAX, SILENCE_LEVEL, SILENCE_LENGTH_ABORT); + local duration = caller:to_i('record_seconds'); + + if duration >= MESSAGE_LENGTH_MIN then + self.log:info('VOICEMAIL_LEAVE - saving recorded message to voicemail, duration: ', duration); + require 'common.fapi' + common.fapi.FApi:new{ log = self.log, uuid = caller.uuid }:execute('vm_inject', + self.record.auth_name .. + '@' .. self.record.domain .. " '" .. + record_file_name .. "' '" .. + caller.caller_id_number .. "' '" .. + caller.caller_id_name .. "' '" .. + caller.uuid .. "'" + ); + caller:set_variable('voicemail_message_len', duration); + else + caller:set_variable('voicemail_message_len'); + end + os.remove(record_file_name); + return true; +end + + +function Voicemail.send_notify(self, caller) + self.log:debug('VOICEMAIL_NOTIFY - account: ' .. self.record.auth_name .. ", id: " .. tostring(caller.uuid)); + + local file = io.popen("/opt/GS5/script/voicemail_new.sh '" .. tostring(self.record.auth_name) .. "' '" .. tostring(caller.uuid) .. "' 2>&1"); + self.log:debug('VOICEMAIL_NOTIFY - result: ' .. tostring(file:read("*a"))); + file:close(); + + return true; +end + + +function Voicemail.menu(self, caller, authorized) + self.log:info('VOICEMAIL_MENU - account: ', self.record.auth_name); + + if authorized then + caller:set_variable('voicemail_authorized', true); + end + + caller:set_callee_id(phone_number, self.record.caller_name); + caller:answer(); + caller:send_display(common.str.to_s(self.record.caller_name), common.str.to_s(phone_number)); + + caller:sleep(1000); + caller:set_variable('skip_greeting', true); + caller:set_variable('skip_instructions', true); + + caller:execute('voicemail', 'check default ' .. self.record.domain .. ' ' .. self.record.auth_name); +end diff --git a/misc/freeswitch/scripts/dialplan_default.lua b/misc/freeswitch/scripts/dialplan_default.lua new file mode 100644 index 0000000..ee4a88f --- /dev/null +++ b/misc/freeswitch/scripts/dialplan_default.lua @@ -0,0 +1,64 @@ +-- Gemeinschaft 5 default dialplan +-- (c) AMOOMA GmbH 2012 +-- + + +function hangup_hook_caller(s, status, arg) + log:info('HANGUP_HOOK: ', status) + if tostring(status) == 'transfer' then + if start_caller and start_caller.destination then + log:info('CALL_TRANSFERRED - destination was: ', start_caller.destination.type, '=', start_caller.destination.id,', number: ' .. tostring(start_caller.destination.number) .. ', to: ' .. start_caller:to_s('sip_refer_to')); + start_caller.auth_account = start_dialplan:object_find(start_caller.destination.type, start_caller.destination.id); + start_caller.forwarding_number = start_caller.destination.number; + start_caller.forwarding_service = 'transfer'; + end + end +end + +-- initialize logging +require 'common.log' +log = common.log.Log:new{ prefix = '### [' .. session:get_uuid() .. '] ' }; + +-- caller session object +require 'dialplan.session' +start_caller = dialplan.session.Session:new{ log = log, session = session }; + +-- dialplan object +require 'dialplan.dialplan' + +start_dialplan = dialplan.dialplan.Dialplan:new{ log = log, caller = start_caller }; +start_dialplan:configuration_read(); +start_caller.local_node_id = start_dialplan.node_id; +start_caller:init_channel_variables(); + +-- session:execute('info','notice'); + +if not start_dialplan:check_auth() then + log:debug('AUTHENTICATION_REQUIRED - domain: ', start_dialplan.domain); + start_dialplan:hangup(407, start_dialplan.domain); + return false; +end + +-- connect to database +require 'common.database' +local database = common.database.Database:new{ log = log }:connect(); +if not database:connected() then + log:critical('DIALPLAN_DEFAULT - database connect failed'); + return; +end + +start_dialplan.database = database; + +if start_caller.from_node and not start_dialplan:check_auth_node() then + log:debug('AUTHENTICATION_REQUIRED_NODE - node_id: ', start_caller.node_id, ', domain: ', start_dialplan.domain); + start_dialplan:hangup(407, start_dialplan.domain); +else + start_destination = { type = 'unknown' } + start_caller.session:setHangupHook('hangup_hook_caller', 'destination_number'); + start_dialplan:run(start_destination); +end + +-- release database handle +if database then + database:release(); +end diff --git a/misc/freeswitch/scripts/event/call_history_save.lua b/misc/freeswitch/scripts/event/call_history_save.lua new file mode 100644 index 0000000..057ca16 --- /dev/null +++ b/misc/freeswitch/scripts/event/call_history_save.lua @@ -0,0 +1,74 @@ +-- Gemeinschaft 5 module: call_history event handler class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +function handler_class() + return CallHistorySave +end + +CallHistorySave = {} + + +function CallHistorySave.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.class = 'callhistorysave' + self.database = arg.database; + self.domain = arg.domain; + + return object; +end + + +function CallHistorySave.event_handlers(self) + return { CHANNEL_DESTROY = { [true] = self.channel_destroy } } +end + + +function CallHistorySave.channel_destroy(self, event) + local uuid = event:getHeader('Unique-ID'); + local direction = event:getHeader('variable_direction'); + + require 'common.str' + local save_cdr = common.str.to_b(event:getHeader('variable_gs_save_cdr')); + + if not save_cdr then + self.log:debug('[', uuid,'] CALL_HISTORY_SAVE - event: CHANNEL_DESTROY, direction: ', direction, ', save_cdr: ', save_cdr); + return false; + end + + require 'common.call_history' + call_history_class = common.call_history.CallHistory:new{ log = self.log, database = self.database } + + -- caller entry + local account_type = event:getHeader('variable_gs_account_type'); + local account_id = common.str.to_i(event:getHeader('variable_gs_account_id')); + + if account_type and account_id > 0 and common.str.to_b(event:getHeader('variable_gs_account_node_local')) then + call_history_class:insert_event(uuid, account_type, account_id, 'dialed', event); + else + self.log:info('[', uuid,'] CALL_HISTORY_SAVE - ignore caller entry - account: ', account_type, '=', account_id, ', local: ', event:getHeader('variable_gs_account_node_local')); + end + + -- callee entry + local account_type = event:getHeader('variable_gs_destination_type'); + local account_id = common.str.to_i(event:getHeader('variable_gs_destination_id')); + + if account_type and account_id > 0 + and common.str.to_b(event:getHeader('variable_gs_destination_node_local')) + and tostring(event:getHeader('variable_gs_call_service')) ~= 'pickup' then + + if tostring(event:getHeader('variable_endpoint_disposition')) == 'ANSWER' then + call_history_class:insert_event(uuid, account_type, account_id, 'received', event); + else + call_history_class:insert_event(uuid, account_type, account_id, 'missed', event); + end + else + self.log:info('[', uuid,'] CALL_HISTORY_SAVE - ignore callee entry - account: ', account_type, '=', account_id, ', local: ', event:getHeader('variable_gs_destination_node_local')); + end +end diff --git a/misc/freeswitch/scripts/event/cdr_save.lua b/misc/freeswitch/scripts/event/cdr_save.lua new file mode 100644 index 0000000..ed53aa3 --- /dev/null +++ b/misc/freeswitch/scripts/event/cdr_save.lua @@ -0,0 +1,105 @@ +-- Gemeinschaft 5 module: cdr event handler class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + + +function handler_class() + return CdrSave +end + + +function camelize_type(account_type) + ACCOUNT_TYPES = { + sipaccount = 'SipAccount', + conference = 'Conference', + faxaccount = 'FaxAccount', + callthrough = 'Callthrough', + huntgroup = 'HuntGroup', + automaticcalldistributor = 'AutomaticCallDistributor', + } + + return ACCOUNT_TYPES[account_type] or account_type; +end + + +CdrSave = {} + + +function CdrSave.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.class = 'cdrsave' + self.database = arg.database; + self.domain = arg.domain; + + return object; +end + + +function CdrSave.event_handlers(self) + return { CHANNEL_DESTROY = { [true] = self.channel_destroy } } +end + + +function CdrSave.channel_destroy(self, event) + local uuid = event:getHeader('Unique-ID'); + local direction = event:getHeader('variable_direction'); + + require 'common.str' + local save_cdr = common.str.to_b(event:getHeader('variable_gs_save_cdr')); + + if not save_cdr then + self.log:debug('[', uuid,'] CDR_SAVE - event: CHANNEL_DESTROY, direction: ', direction, ', save_cdr: ', save_cdr); + return false; + end + + require 'common.str' + local cdr = {} + + cdr.uuid = common.str.to_sql(uuid); + cdr.bleg_uuid = common.str.to_sql(event:getHeader('variable_bridge_uuid')); + cdr.dialed_number = common.str.to_sql(event:getHeader('Caller-Destination-Number')); + cdr.destination_number = common.str.to_sql(event:getHeader('variable_gs_destination_number')); + cdr.caller_id_number = common.str.to_sql(event:getHeader('variable_effective_caller_id_number')); + cdr.caller_id_name = common.str.to_sql(event:getHeader('variable_effective_caller_id_name')); + cdr.callee_id_number = common.str.to_sql(event:getHeader('variable_effective_callee_id_number')); + cdr.callee_id_name = common.str.to_sql(event:getHeader('variable_effective_callee_id_name')); + cdr.start_stamp = 'FROM_UNIXTIME(' .. math.floor(common.str.to_i(event:getHeader('Caller-Channel-Created-Time')) / 1000000) .. ')'; + cdr.answer_stamp = 'FROM_UNIXTIME(' .. math.floor(common.str.to_i(event:getHeader('Caller-Channel-Answered-Time')) / 1000000) .. ')'; + cdr.end_stamp = 'FROM_UNIXTIME(' .. math.floor(common.str.to_i(event:getHeader('Caller-Channel-Hangup-Time')) / 1000000) .. ')'; + cdr.bridge_stamp = common.str.to_sql(event:getHeader('variable_bridge_stamp')); + cdr.duration = common.str.to_sql(event:getHeader('variable_duration')); + cdr.billsec = common.str.to_sql(event:getHeader('variable_billsec')); + cdr.hangup_cause = common.str.to_sql(event:getHeader('variable_hangup_cause')); + cdr.dialstatus = common.str.to_sql(event:getHeader('variable_DIALSTATUS')); + cdr.forwarding_number = common.str.to_sql(event:getHeader('variable_gs_forwarding_number')); + cdr.forwarding_service = common.str.to_sql(event:getHeader('variable_gs_forwarding_service')); + cdr.forwarding_account_id = common.str.to_sql(event:getHeader('variable_gs_auth_account_id')); + cdr.forwarding_account_type = common.str.to_sql(camelize_type(event:getHeader('variable_gs_auth_account_type'))); + cdr.account_id = common.str.to_sql(event:getHeader('variable_gs_account_id')); + cdr.account_type = common.str.to_sql(camelize_type(event:getHeader('variable_gs_account_type'))); + cdr.bleg_account_id = common.str.to_sql(event:getHeader('variable_gs_destination_id')); + cdr.bleg_account_type = common.str.to_sql(camelize_type(event:getHeader('variable_gs_destination_type'))); + + local keys = {} + local values = {} + + for key, value in pairs(cdr) do + table.insert(keys, key); + table.insert(values, value); + end + + self.log:info('[', uuid,'] CDR_SAVE - account: ', cdr.account_type, '=', cdr.account_id, + ', caller: ', cdr.caller_id_number, ' ', cdr.caller_id_name, + ', callee: ', cdr.callee_id_number, ' ', cdr.callee_id_name, + ', cause: ', cdr.hangup_cause + ); + + local sql_query = 'INSERT INTO `cdrs` (`' .. table.concat(keys, "`, `") .. '`) VALUES (' .. table.concat(values, ", ") .. ')'; + return self.database:query(sql_query); +end diff --git a/misc/freeswitch/scripts/event/event.lua b/misc/freeswitch/scripts/event/event.lua new file mode 100644 index 0000000..8e67bc9 --- /dev/null +++ b/misc/freeswitch/scripts/event/event.lua @@ -0,0 +1,109 @@ +-- Gemeinschaft 5 module: event manager class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +EventManager = {} + +-- create event manager object +function EventManager.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.class = 'eventmanager' + self.database = arg.database; + self.domain = arg.domain; + + return object; +end + + +function EventManager.register(self) + self.consumer = freeswitch.EventConsumer('all'); + return (self.consumer ~= nil); +end + + +function EventManager.load_event_modules(self) + local CONFIG_FILE_NAME = '/opt/freeswitch/scripts/ini/events.ini'; + + require 'common.configuration_file' + self.config = common.configuration_file.get(CONFIG_FILE_NAME); + + return self.config.modules; +end + + +function EventManager.load_event_handlers(self, event_modules) + event_handlers = {} + + for index, event_module_name in ipairs(event_modules) do + event_module = require('event.' .. event_module_name); + if event_module then + self.log:info('[event] EVENT_MANAGER - loading handler module: ', event_module_name); + handler_class = event_module.handler_class(); + + if handler_class then + module_event_handlers = handler_class:new{ log = self.log, database = self.database, domain = self.domain }:event_handlers(); + if module_event_handlers then + for event_name, event_subclasses in pairs(module_event_handlers) do + if not event_handlers[event_name] then + event_handlers[event_name] = {}; + end + + for event_subclass, module_event_handler in pairs(event_subclasses) do + if not event_handlers[event_name][event_subclass] then + event_handlers[event_name][event_subclass] = {}; + end + + table.insert(event_handlers[event_name][event_subclass], { class = handler_class, method = module_event_handler } ); + self.log:info('[event] EVENT_MANAGER - module: ', event_module_name, ', handling events: ', event_name, ', subclass:', event_subclass); + end + end + end + end + end + end + + return event_handlers; +end + + +function EventManager.run(self) + + local event_modules = self:load_event_modules(); + local event_handlers = self:load_event_handlers(event_modules); + + if not event_handlers then + self.log:error('[event] EVENT_MANAGER - no handlers specified'); + return nil; + end + + if not self:register() then + return nil; + end + + freeswitch.setGlobalVariable('gs_event_manager', 'true'); + while freeswitch.getGlobalVariable('gs_event_manager') == 'true' do + local event = self.consumer:pop(1, 100); + if event then + local event_type = event:getType(); + local event_subclass = event:getHeader('Event-Subclass'); + if event_handlers[event_type] then + if event_handlers[event_type][event_subclass] and #event_handlers[event_type][event_subclass] > 0 then + for index, event_handler in ipairs(event_handlers[event_type][event_subclass]) do + event_handler.method(event_handler.class, event); + end + end + if event_handlers[event_type][true] and #event_handlers[event_type][true] > 0 then + for index, event_handler in ipairs(event_handlers[event_type][true]) do + event_handler.method(event_handler.class, event); + end + end + end + end + end +end diff --git a/misc/freeswitch/scripts/event/perimeter.lua b/misc/freeswitch/scripts/event/perimeter.lua new file mode 100644 index 0000000..3babba6 --- /dev/null +++ b/misc/freeswitch/scripts/event/perimeter.lua @@ -0,0 +1,106 @@ +-- Gemeinschaft 5 module: cdr event handler class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + + +function handler_class() + return Perimeter +end + + + +Perimeter = {} + +MALICIOUS_CONTACT_COUNT = 20; +MALICIOUS_CONTACT_TIME_SPAN = 2; +BAN_FUTILE = 2; + +function Perimeter.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.class = 'cdrsave' + self.database = arg.database; + self.domain = arg.domain; + + self.ip_address_table = {} + self:init(); + + return object; +end + + +function Perimeter.event_handlers(self) + return { CUSTOM = { ['sofia::pre_register'] = self.sofia_pre_register } } +end + + +function Perimeter.init(self) + local config = common.configuration_file.get('/opt/freeswitch/scripts/ini/perimeter.ini'); + if config and config.general then + self.malicious_contact_count = tonumber(config.general.malicious_contact_count) or MALICIOUS_CONTACT_COUNT; + self.malicious_contact_time_span = tonumber(config.general.malicious_contact_time_span) or MALICIOUS_CONTACT_TIME_SPAN; + self.ban_futile = tonumber(config.general.ban_futile) or BAN_FUTILE; + self.execute = config.general.execute; + end + + self.log:info('[perimeter] PERIMETER - setup perimeter defense - config: ', self.malicious_contact_count, '/', self.malicious_contact_time_span, ', execute: ', self.execute); +end + + +function Perimeter.sofia_pre_register(self, event) + local ip_address = event:getHeader('network-ip'); + self:check_ip(ip_address); +end + + +function Perimeter.check_ip(self, ip_address) + local event_time = os.time(); + + if not self.ip_address_table[ip_address] then + self.ip_address_table[ip_address] = { last_contact = event_time, contact_count = 0, start_stamp = event_time, banned = 0 } + end + + local ip_record = self.ip_address_table[ip_address]; + ip_record.last_contact = event_time; + ip_record.contact_count = ip_record.contact_count + 1; + + if ip_record.contact_count > MALICIOUS_CONTACT_COUNT then + if (event_time - ip_record.start_stamp) <= MALICIOUS_CONTACT_TIME_SPAN then + self.log:warning('[', ip_address, '] PERIMETER - too many registration attempts'); + ip_record.start_stamp = event_time; + ip_record.contact_count = 0; + if ip_record.banned < BAN_FUTILE then + ip_record.banned = ip_record.banned + 1; + self:ban_ip(ip_address); + else + self.log:error('[', ip_address, '] PERIMETER - ban futile'); + end + end + end +end + + +function Perimeter.ban_ip(self, ip_address) + self.ip_address = ip_address; + + if self.execute then + local command = self:expand_variables(self.execute); + self.log:debug('[', ip_address, '] PERIMETER - execute: ', command); + local result = os.execute(command); + if tostring(result) == '0' then + self.log:warning('[', ip_address, '] PERIMETER - IP banned'); + end + end +end + + +function Perimeter.expand_variables(self, line) + return (line:gsub('{([%a%d_-]+)}', function(captured) + return self[captured]; + end)) +end diff --git a/misc/freeswitch/scripts/event/presence_update.lua b/misc/freeswitch/scripts/event/presence_update.lua new file mode 100644 index 0000000..01ec17b --- /dev/null +++ b/misc/freeswitch/scripts/event/presence_update.lua @@ -0,0 +1,199 @@ + +module(...,package.seeall) + +function handler_class() + return PresenceUpdate +end + +ACCOUNT_RECORD_TIMEOUT = 120; + +PresenceUpdate = {} + +function PresenceUpdate.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.class = 'presenceupdate' + self.database = arg.database; + self.domain = arg.domain; + self.presence_accounts = {} + self.account_record = {} + + return object; +end + + +function PresenceUpdate.event_handlers(self) + return { + PRESENCE_PROBE = { [true] = self.presence_probe }, + CUSTOM = { ['sofia::register'] = self.sofia_register, ['sofia::unregister'] = self.sofia_ungerister }, + PRESENCE_IN = { [true] = self.presence_in }, + } +end + + +function PresenceUpdate.presence_probe(self, event) + local DIALPLAN_FUNCTION_PATTERN = '^f[_%-].*'; + + require 'common.str' + local event_to = event:getHeader('to'); + local event_from = event:getHeader('from'); + local probe_type = event:getHeader('probe-type'); + local account, domain = common.str.partition(event_from, '@'); + local subscription, domain = common.str.partition(event_to, '@'); + + self.log:debug('[', account, '] PRESENCE_UPDATE - subscription: ', subscription,', type: ', probe_type); + if (not self.presence_accounts[account] or not self.presence_accounts[account][subscription]) and subscription:find(DIALPLAN_FUNCTION_PATTERN) then + if not self.presence_accounts[account] then + self.presence_accounts[account] = {}; + end + if not self.presence_accounts[account][subscription] then + self.presence_accounts[account][subscription] = {}; + end + self:update_function_presence(account, domain, subscription); + end +end + + +function PresenceUpdate.sofia_register(self, event) + local account = event:getHeader('from-user'); + self.log:debug('[', account, '] PRESENCE_UPDATE - flushing account cache on register'); + self.presence_accounts[account] = nil; +end + + +function PresenceUpdate.sofia_ungerister(self, event) + local account = event:getHeader('from-user'); + self.log:debug('[', account, '] PRESENCE_UPDATE - flushing account cache on unregister'); + self.presence_accounts[account] = nil; +end + + +function PresenceUpdate.presence_in(self, event) + if not event:getHeader('status') then + return + end + + local account, domain = common.str.partition(event:getHeader('from'), '@'); + local direction = tostring(event:getHeader('presence-call-direction')) + local state = event:getHeader('presence-call-info-state'); + local uuid = event:getHeader('Unique-ID'); + local caller_id = event:getHeader('Caller-Caller-ID-Number'); + + if direction == 'inbound' then + self.log:info('[', uuid,'] PRESENCE_INBOUND: account: ', account, ', state: ', state); + self:sip_account(true, account, domain, state, uuid); + elseif direction == 'outbound' then + self.log:info('[', uuid,'] PRESENCE_OUTBOUND: account: ', account, ', state: ', state, ', caller: ', caller_id); + self:sip_account(false, account, domain, state, uuid, caller_id); + end +end + + +function PresenceUpdate.update_function_presence(self, account, domain, subscription) + local parameters = common.str.to_a(subscription, '_%-'); + local fid = parameters[2]; + local function_parameter = parameters[3]; + + if not fid then + self.log:error('[', account, '] PRESENCE_UPDATE - no function specified'); + return; + end + + if fid == 'cftg' and tonumber(function_parameter) then + self:call_forwarding(account, domain, function_parameter); + elseif fid == 'hgmtg' then + self:hunt_group_membership(account, domain, function_parameter); + elseif fid == 'acdmtg' then + self:acd_membership(account, domain, function_parameter); + end + +end + + +function PresenceUpdate.call_forwarding(self, account, domain, call_forwarding_id) + require 'common.call_forwarding' + local call_forwarding = common.call_forwarding.CallForwarding:new{ log=self.log, database=self.database, domain=domain }:find_by_id(call_forwarding_id); + + require 'common.str' + if call_forwarding and common.str.to_b(call_forwarding.record.active) then + local destination_type = tostring(call_forwarding.record.call_forwardable_type):lower() + + self.log:debug('[', account, '] PRESENCE_UPDATE - updating call forwarding presence - id: ', call_forwarding_id, ', destination: ', destination_type); + + if destination_type == 'voicemail' then + call_forwarding:presence_set('early'); + else + call_forwarding:presence_set('confirmed'); + end + end +end + + +function PresenceUpdate.hunt_group_membership(self, account, domain, member_id) + local sql_query = 'SELECT `active` FROM `hunt_group_members` WHERE `active` IS TRUE AND `id`=' .. tonumber(member_id) .. ' LIMIT 1'; + local status = self.database:query_return_value(sql_query); + + if status then + self.log:debug('[', account, '] PRESENCE_UPDATE - updating hunt group membership presence - id: ', member_id); + require 'dialplan.presence' + local presence_class = dialplan.presence.Presence:new{ + log = self.log, + database = self.database, + domain = domain, + accounts = {'f-hgmtg-' .. member_id}, + uuid = 'hunt_group_member_' .. member_id + }:set('confirmed'); + end +end + + +function PresenceUpdate.acd_membership(self, account, domain, member_id) + local sql_query = 'SELECT `status` FROM `acd_agents` WHERE `status` = "active" AND `id`=' .. tonumber(member_id) .. ' LIMIT 1'; + local status = self.database:query_return_value(sql_query); + + if status then + self.log:debug('[', account, '] PRESENCE_UPDATE - updating ACD membership presence - id: ', member_id); + require 'dialplan.presence' + local presence_class = dialplan.presence.Presence:new{ + log = self.log, + database = self.database, + domain = domain, + accounts = {'f-acdmtg-' .. member_id}, + uuid = 'acd_agent_' .. member_id + }:set(status); + end +end + + +function PresenceUpdate.sip_account(self, inbound, account, domain, status, uuid, caller_id) + local status_map = { progressing = 'early', alerting = 'confirmed', active = 'confirmed' } + + if not self.account_record[account] or ((os.time() - self.account_record[account].created_at) > ACCOUNT_RECORD_TIMEOUT) then + self.log:debug('[', uuid,'] PRESENCE - retrieve account data - account: ', account); + + require 'common.sip_account' + local sip_account = common.sip_account.SipAccount:new{ log = self.log, database = self.database }:find_by_auth_name(account); + + if not sip_account then + return + end + + require 'common.phone_number' + local phone_numbers = common.phone_number.PhoneNumber:new{ log = self.log, database = self.database }:list_by_owner(sip_account.id, sip_account.class); + + self.account_record[account] = { id = sip_account.id, class = sip_account.class, phone_numbers = phone_numbers, created_at = os.time() } + end + + require 'dialplan.presence' + local result = dialplan.presence.Presence:new{ + log = self.log, + database = self.database, + inbound = inbound, + domain = domain, + accounts = self.account_record[account].phone_numbers, + uuid = uuid + }:set(status_map[status] or 'terminated', caller_id); +end diff --git a/misc/freeswitch/scripts/event_manager.lua b/misc/freeswitch/scripts/event_manager.lua new file mode 100644 index 0000000..0e3c0e0 --- /dev/null +++ b/misc/freeswitch/scripts/event_manager.lua @@ -0,0 +1,39 @@ +-- Gemeinschaft 5.0 event handler +-- (c) AMOOMA GmbH 2012 +-- + +-- Set logger +require "common.log" +local log = common.log.Log:new() +log.prefix = "#E# " + +log:info('[event] EVENT_MANAGER start'); + +require 'common.database' +local database = common.database.Database:new{ log = log }:connect(); +if not database:connected() then + log:error('[event] EVENT_MANAGER - cannot connect to Gemeinschaft database'); + return; +end + +require "configuration.sip" +local sip = configuration.sip.Sip:new{ log = log, database = database } + +local domain = '127.0.0.1'; +local domains = sip:domains(); +if domains[1] then + domain = domains[1]['host']; +else + log:error('[event] EVENT_MANAGER - No SIP domains found!'); +end + +require 'event.event' +local event_manager = event.event.EventManager:new{ log = log, database = database, domain = domain } +event_manager:run(); + +-- ensure database handle is released on exit +if database then + database:release(); +end + +log:info('[event] EVENT_MANAGER exit'); diff --git a/misc/freeswitch/scripts/fax_daemon.lua b/misc/freeswitch/scripts/fax_daemon.lua new file mode 100644 index 0000000..cfe7c4e --- /dev/null +++ b/misc/freeswitch/scripts/fax_daemon.lua @@ -0,0 +1,42 @@ +-- Gemeinschaft 5.0 fax daemon +-- (c) AMOOMA GmbH 2012 +-- + +local MAIN_LOOP_SLEEP_TIME = 30; + +-- Set logger +require "common.log" +local log = common.log.Log:new() +log.prefix = "### [faxdaemon] " + +log:debug('Starting fax daemon'); + +local database = nil; +local api = freeswitch.API(); + +freeswitch.setGlobalVariable('gs_fax_daemon', 'true'); +while freeswitch.getGlobalVariable("gs_fax_daemon") == 'true' do + require 'common.database' + local database = common.database.Database:new{ log = log }:connect(); + + if not database:connected() then + log:error("connection to Gemeinschaft database lost - retry in " .. MAIN_LOOP_SLEEP_TIME .. " seconds") + else + require 'dialplan.fax' + local fax_documents = dialplan.fax.Fax:new{log=log, database=database}:queued_for_sending(); + + for key, fax_document in pairs(fax_documents) do + if table.getn(fax_document.destination_numbers) > 0 and tonumber(fax_document.retry_counter) > 0 then + log:debug('FAX_DAEMON_LOOP - fax_document=', fax_document.id, '/', fax_document.uuid, ', number: ' .. fax_document.destination_numbers[1]); + local result = api:executeString('luarun send_fax.lua ' .. fax_document.id); + end + end + end + database:release(); + + if freeswitch.getGlobalVariable("gs_fax_daemon") == 'true' then + freeswitch.msleep(MAIN_LOOP_SLEEP_TIME * 1000); + end +end + +log:debug('Exiting fax daemon'); diff --git a/misc/freeswitch/scripts/ini/conferences.ini b/misc/freeswitch/scripts/ini/conferences.ini new file mode 100644 index 0000000..d8d0817 --- /dev/null +++ b/misc/freeswitch/scripts/ini/conferences.ini @@ -0,0 +1,27 @@ +; Gemeinschaft 5 conferences configuration file +; (c) AMOOMA GmbH 2012 +; + +[parameters] +caller-controls = speaker +moderator-controls = moderator +max-members = 100 +rate = 16000 +interval = 20 +energy-level = 300 +sound-prefix = /opt/freeswitch/sounds/en/us/callie +muted-sound = conference/conf-muted.wav +unmuted-sound = conference/conf-unmuted.wav +alone-sound = conference/conf-alone.wav +moh-sound = local_stream://moh +enter-sound = tone_stream://%(200,0,500,600,700) +exit-sound = tone_stream://%(500,0,300,200,100,50,25) +kicked-sound = conference/conf-kicked.wav +locked-sound = conference/conf-locked.wav +is-locked-sound = conference/conf-is-locked.wav +is-unlocked-sound = conference/conf-is-unlocked.wav +pin-sound = conference/conf-pin.wav +bad-pin-sound = conference/conf-bad-pin.wav +caller-id-name = Conference +caller-id-number = +comfort-noise = true diff --git a/misc/freeswitch/scripts/ini/database.ini b/misc/freeswitch/scripts/ini/database.ini new file mode 100644 index 0000000..1652118 --- /dev/null +++ b/misc/freeswitch/scripts/ini/database.ini @@ -0,0 +1,11 @@ +; Gemeinschaft 5 database configuration +; (c) AMOOMA GmbH 2012 +; + +driver = mysql + +[mysql] +host = localhost +database = gemeinschaft +user = gemeinschaft +password = gemeinschaft diff --git a/misc/freeswitch/scripts/ini/dialplan.ini b/misc/freeswitch/scripts/ini/dialplan.ini new file mode 100644 index 0000000..f4a6b66 --- /dev/null +++ b/misc/freeswitch/scripts/ini/dialplan.ini @@ -0,0 +1,11 @@ +; Gemeinschaft 5 dialplan configuration file +; (c) AMOOMA GmbH 2012 +; + +[parameters] +node_id = 1 +phone_book_entry_image_url = http://192.168.0.150/uploads/phone_book_entry/image +user_image_url = http://192.168.0.150/uploads/user/image +ringtone_url = http://192.168.0.150 +ringback = %(2000,4000,440.0,480.0) +tone_busy = %(500,500,480,620);loops=4 diff --git a/misc/freeswitch/scripts/ini/events.ini b/misc/freeswitch/scripts/ini/events.ini new file mode 100644 index 0000000..e63eb73 --- /dev/null +++ b/misc/freeswitch/scripts/ini/events.ini @@ -0,0 +1,8 @@ +; Gemeinschaft 5 routing configuration file +; (c) AMOOMA GmbH 2012 +; + +[modules] +cdr_save +call_history_save +presence_update diff --git a/misc/freeswitch/scripts/ini/gateways.ini.example b/misc/freeswitch/scripts/ini/gateways.ini.example new file mode 100644 index 0000000..b6ae018 --- /dev/null +++ b/misc/freeswitch/scripts/ini/gateways.ini.example @@ -0,0 +1,23 @@ +; Gemeinschaft 5 gateways configuration file +; (c) AMOOMA GmbH 2012 +; + +[gateway1] +profile = gemeinschaft +name = gateway1 +username = gateway1 +realm = gemeinschaft +password = freeswitch +extension = default +proxy = 192.168.0.1 +expire-seconds = 600 +register = true + +[gateway2] +profile = gemeinschaft +name = sipgate +username = 1234567e0 +password = ABCdeF +proxy = sipgate.com +register = true +extension = {sip_to_user} diff --git a/misc/freeswitch/scripts/ini/perimeter.ini b/misc/freeswitch/scripts/ini/perimeter.ini new file mode 100644 index 0000000..ecbb032 --- /dev/null +++ b/misc/freeswitch/scripts/ini/perimeter.ini @@ -0,0 +1,9 @@ +; Gemeinschaft 5 perimeter defense configuration file +; (c) AMOOMA GmbH 2012 +; + +[general] +malicious_contact_count = 20 +malicious_contact_time_span = 2 +ban_futile = 5 +execute = sudo /usr/local/bin/ban_ip.sh {ip_address} diff --git a/misc/freeswitch/scripts/ini/routes.ini b/misc/freeswitch/scripts/ini/routes.ini new file mode 100644 index 0000000..1334e7b --- /dev/null +++ b/misc/freeswitch/scripts/ini/routes.ini @@ -0,0 +1,77 @@ +; Gemeinschaft 5 routing configuration file +; (c) AMOOMA GmbH 2012 +; + +[general] + + +[prerouting] +^%*0%*$ , f-li +^%*0%*(%d+)#*$ , f-li-%1 +^%*0%*(%d+)%*(%d+)#*$ , f-li-%1-%2 +^#0#$ , f-lo +^%*30#$ , f-clipon +^#30#$ , f-clipoff +^%*31#(%d+)$ , f-dclirof, f-%1 +^#31#(%d+)$ , f-dcliron-%1 +^%*43#$ , f-cwaon +^#43#$ , f-cwaoff +^#002#$ , f-cfoff +^##002#$ , f-cfdel +^%*21#$ , f-cfu +^%*21%*(%d+)#$ , f-cfu-%1 +^%*%*21%*(%d+)#$ , f-cfu-%1 +^#21#$ , f-cfuoff +^##21#$ , f-cfudel +^%*61#$ , f-cfn +^%*61%*(%d+)#$ , f-cfn-%1 +^%*%*61%*(%d+)#$ , f-cfn-%1 +^%*61%*(%d+)%*(%d+)#$ , f-cfn-%1-%2 +^%*%*61%*(%d+)%*(%d+)#$ , f-cfn-%1-%2 +^#61#$ , f-cfnoff +^##61#$ , f-cfndel +^%*62#$ , f-cfo +^%*62%*(%d+)#$ , f-cfo-%1 +^%*%*62%*(%d+)#$ , f-cfo-%1 +^#62#$ , f-cfooff +^##62#$ , f-cfodel +^%*67#$ , f-cfb +^%*67%*(%d+)#$ , f-cfb-%1 +^%*%*67%*(%d+)#$ , f-cfb-%1 +^#67#$ , f-cfboff +^##67#$ , f-cfbdel +^%*98$ , f-vmcheck +^%*98#$ , f-vmcheck +^%*98%*(%d+)#$ , f-vmcheck-%1 +^%*1337%*1%*1#$ , f-loaon +^%*1337%*1%*0#$ , f-loaoff + +^00(%d+)$ , +%1 +^0(%d+)$ , +49%1 + + +[outbound] +^%+(%d+)$ , class=gateway, endpoint=gateway1, group=users, %1 +^([1-9]%d+)$ , class=gateway, endpoint=gateway1, group=users, %1 + + +[failover] +UNALLOCATED_NUMBER = true +NORMAL_TEMPORARY_FAILURE = true + + +[outbound_cid_number] + + +[outbound_cid_name] + + +[inbound] +^00(%d+)$ , +%1 +^0(%d+)$ , +49%1 + +[inbound_cid_number] +^00(%d+)$ , +%1 +^0(%d+)$ , +49%1 + +[inbound_cid_name] diff --git a/misc/freeswitch/scripts/ini/sip_accounts.ini b/misc/freeswitch/scripts/ini/sip_accounts.ini new file mode 100644 index 0000000..73a5fae --- /dev/null +++ b/misc/freeswitch/scripts/ini/sip_accounts.ini @@ -0,0 +1,10 @@ +; Gemeinschaft 5 sip accounts default parameters +; (c) AMOOMA GmbH 2012 +; + +[parameters] +vm-enabled = true +vm-email-all-messages = false +vm-attach-file = false +vm-mailto = + diff --git a/misc/freeswitch/scripts/ini/sofia.ini b/misc/freeswitch/scripts/ini/sofia.ini new file mode 100644 index 0000000..9c73990 --- /dev/null +++ b/misc/freeswitch/scripts/ini/sofia.ini @@ -0,0 +1,55 @@ +; Gemeinschaft 5 sofia configuration file +; (c) AMOOMA GmbH 2012 +; + +[profiles] +gemeinschaft + +[parameters] +log-level = 3 +debug-presence = 0 + +[profile:gemeinschaft] +user-agent-string = Gemeinschaft5 +debug = 0 +sip-trace = no +log-auth-failures = false +context = default +rfc2833-pt = 101 +pass-rfc2833 = true +sip-port = 5060 +dialplan = XML +dtmf-duration = 2000 +rtp-timer-name = soft +inbound-codec-prefs = PCMA,G7221@32000h,G7221@16000h,G722,PCMU,GSM +outbound-codec-prefs = PCMA,G7221@32000h,G7221@16000h,G722,PCMU,GSM +inbound-codec-negotiation = greedy +ext-rtp-ip = auto-nat +ext-sip-ip = auto-nat +hold-music = local_stream://moh +manage-presence = true +tls = false +tls-sip-port = 5061 +tls-cert-dir = /opt/freeswitch/conf/ssl +accept-blind-reg = false +accept-blind-auth = false +nonce-ttl = 60 +disable-transcoding = false +manual-redirect = true +disable-transfer = false +disable-register = false +auth-calls = false +inbound-reg-force-matching-username = true +auth-all-packets = false +rtp-timeout-sec = 300 +rtp-hold-timeout-sec = 1800 +force-subscription-expires = 3600 +sip-force-expires = 3000 +sip-expires-max-deviation = 600; +challenge-realm = auto_from +rtp-rewrite-timestamps = true +inbound-use-callid-as-uuid = false +outbound-use-callid-as-uuid = false +context = default +record-template = /${record_file} +odbc-dsn = gemeinschaft:gemeinschaft:gemeinschaft diff --git a/misc/freeswitch/scripts/phones/phone.lua b/misc/freeswitch/scripts/phones/phone.lua new file mode 100644 index 0000000..5cd210b --- /dev/null +++ b/misc/freeswitch/scripts/phones/phone.lua @@ -0,0 +1,114 @@ +-- Gemeinschaft 5 module: phone class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +Phone = {} + +-- create phone object +function Phone.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.database = arg.database; + return object; +end + + + +-- Find a hot-deskable phone by sip-account +function Phone.find_all_hot_deskable_by_account(self, account_id) + require 'common.str' + + local sql_query = 'SELECT \ + `b`.`id`, `b`.`mac_address`, `b`.`ip_address`, `b`.`http_user`, `b`.`http_password`, `b`.`phoneable_type`, `b`.`phoneable_id`, \ + `d`.`ieee_name` \ + FROM `phone_sip_accounts` `a` \ + JOIN `phones` `b` ON `a`.`phone_id` = `b`.`id` \ + JOIN `phone_models` `c` ON `b`.`phone_model_id` = `c`.`id` \ + JOIN `manufacturers` `d` ON `c`.`manufacturer_id` = `d`.`id` \ + WHERE `b`.`hot_deskable` IS TRUE \ + AND `c`.`state` = "active" \ + AND `d`.`state` = "active" \ + AND `a`.`sip_account_id` = ' .. tonumber(account_id); + + local account_phones = {}; + + self.database:query(sql_query, function(account_entry) + local phone = Phone:new(self, {object = parent_class}); + phone.record = account_entry; + phone.record.ieee_name = common.str.downcase(account_entry.ieee_name); + + if phone.record.ieee_name == 'snom technology ag' then + require 'phones.snom' + phone.model = phones.snom.Snom:new(); + elseif account_entry.ieee_name == 'siemens enterprise communicationsgmbh & co. kg' then + require 'phones.siemens' + phone.model = phones.siemens.Siemens:new(); + end + table.insert(account_phones, phone); + end) + + return account_phones; +end + + +function Phone.find_hot_deskable_by_account(self, account_id) + return self:find_all_hot_deskable_by_account(account_id)[1]; +end + + +function Phone.tenant_id_get(self) + local sql_query = 'SELECT `c`.`sip_accountable_id` \ + FROM `phones` `a` LEFT JOIN `phone_sip_accounts` `b` ON `a`.`id` = `b`.`phone_id` \ + JOIN `sip_accounts` `c` ON `b`.`sip_account_id` = `c`.`id` AND `sip_accountable_type` = "Tenant" \ + WHERE `a`.`id` = ' .. tonumber(self.record.id) .. ' LIMIT 1'; + + local tenant_id = nil; + self.database:query(sql_query, function(tenant_entry) + tenant_id = tenant_entry.sip_accountable_id; + end) + + return tenant_id; +end + +function Phone.phoneable_set(self, phoneable_id, phoneable_type) + sql_query = 'UPDATE `phones` SET `phoneable_type` = "' .. phoneable_type ..'", `phoneable_id` = ' .. phoneable_id .. ' \ + WHERE `id` = ' .. tonumber(self.record.id); + self.database:query(sql_query); +end + +function Phone.logout(self, account_id) + local tenant_id = self:tenant_id_get(); + + if not tenant_id then + self.log:info('PHONE_LOGOUT - tenant not found'); + return false; + end + + self:phoneable_set(tenant_id, 'Tenant'); + + sql_query = 'DELETE FROM `phone_sip_accounts` WHERE `sip_account_id` = ' .. tonumber(account_id); + return self.database:query(sql_query); +end + +function Phone.login(self, account_id, owner_id, owner_type) + self:phoneable_set(owner_id, owner_type); + sql_query = 'INSERT INTO `phone_sip_accounts` (`phone_id`, `sip_account_id`, `position`, `created_at`, `updated_at`) \ + VALUES ('.. tonumber(self.record.id) .. ', '.. tonumber(account_id) .. ', 1, NOW(), NOW())'; + + return self.database:query(sql_query); +end + +function Phone.resync(self, arg) + if not self.model then + self.log:notice('PHONE_RESYNC - unsupported phone model'); + return false; + end + + arg.ip_address = arg.ip_address or self.record.ip_address; + return self.model:resync(arg); +end
\ No newline at end of file diff --git a/misc/freeswitch/scripts/phones/siemens.lua b/misc/freeswitch/scripts/phones/siemens.lua new file mode 100644 index 0000000..71bb40a --- /dev/null +++ b/misc/freeswitch/scripts/phones/siemens.lua @@ -0,0 +1,45 @@ +-- Gemeinschaft 5 module: general siemens model class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +Siemens = {} + +-- create siemens object +function Siemens.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.PHONE_HTTP_PORT = 8085; + return object; +end + +-- send reload message to phone +function Siemens.resync(self, arg) + if arg.ip_address then + return self:resync_http(arg.ip_address, arg.http_user, arg.http_password, arg.http_port); + end + + return false; +end + +-- send reload message to ip +function Siemens.resync_http(self, ip_address, http_user, http_password, http_port) + local port_str = ''; + if tonumber(http_port) then + port_str = ':' .. http_port; + end + + get_command = 'wget --no-proxy -q -O /dev/null -o /dev/null -b --tries=2 --timeout=10 --user="' .. (http_user or '') .. '" --password="' .. (http_password or '') .. '"' .. + ' wget http://' .. tostring(ip_address):gsub('[^0-9%.]', '') .. ':' .. (tonumber(http_port) or self.PHONE_HTTP_PORT) .. '/contact_dls.html/ContactDLS' .. + ' 1>>/dev/null 2>>/dev/null &'; + + result = os.execute(get_command); + + if result and tonumber(result) == 0 then + return true; + end +end diff --git a/misc/freeswitch/scripts/phones/snom.lua b/misc/freeswitch/scripts/phones/snom.lua new file mode 100644 index 0000000..80d1fce --- /dev/null +++ b/misc/freeswitch/scripts/phones/snom.lua @@ -0,0 +1,65 @@ +-- Gemeinschaft 5 module: general snom model class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +Snom = {} + +-- Create Snom object +function Snom.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.reboot = arg.reboot or true; + return object; +end + +-- send reload message to phone +function Snom.resync(self, arg) + if arg.reboot == nil then + arg.reboot = self.reboot; + end + + local success = nil; + if arg.auth_name and arg.domain then + success = self:resync_sip(arg.auth_name, arg.domain, arg.reboot); + end + + if arg.ip_address and arg.reboot then + success = self:resync_http(arg.ip_address, arg.http_user, arg.http_password, arg.http_port); + end + + return success; +end + +-- send reload message to sip_account +function Snom.resync_sip(self, sip_account, domain, reboot) + local event = freeswitch.Event('NOTIFY'); + event:addHeader('profile', 'gemeinschaft'); + event:addHeader('event-string', 'check-sync;reboot=' .. tostring(reboot)); + event:addHeader('user', sip_account); + event:addHeader('host', domain); + event:addHeader('content-type', 'application/simple-message-summary'); + return event:fire(); +end + +-- send reload message to ip +function Snom.resync_http(self, ip_address, http_user, http_password, http_port) + local port_str = ''; + if tonumber(http_port) then + port_str = ':' .. http_port; + end + + get_command = 'wget --no-proxy -q -O /dev/null -o /dev/null -b --tries=2 --timeout=10 --user="' .. (http_user or '') .. '" --password="' .. (http_password or '') .. '"' .. + ' wget http://' .. tostring(ip_address):gsub('[^0-9%.]', '') .. port_str .. '/advanced.htm?reboot=Reboot' .. + ' 1>>/dev/null 2>>/dev/null &'; + + result = os.execute(get_command); + + if result and tonumber(result) == 0 then + return true; + end +end diff --git a/misc/freeswitch/scripts/phones/uacsta.lua b/misc/freeswitch/scripts/phones/uacsta.lua new file mode 100644 index 0000000..61cb788 --- /dev/null +++ b/misc/freeswitch/scripts/phones/uacsta.lua @@ -0,0 +1,100 @@ +-- CommonModule: Uacsta +-- +module(...,package.seeall) + +Uacsta = {} + +-- Create Uacsta object +function Uacsta.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self) + self.__index = self + self.log = arg.log; + + return object +end + +function Uacsta.send(self, sip_account, domain, body) + local event = freeswitch.Event("NOTIFY"); + event:addHeader("profile", "gemeinschaft"); + event:addHeader("event-string", "uaCSTA"); + event:addHeader("user", sip_account); + event:addHeader("host", domain); + event:addHeader("content-type", "application/csta+xml"); + event:addBody(body); + event:fire(); +end + +function Uacsta.make_call(self, sip_account, domain, number) + local body = +[[<?xml version="1.0" encoding="UTF-8"?> +<MakeCall xmlns="http://www.ecma-international.org/standards/ecma-323/csta/ed4"> + <callingDevice>]] .. sip_account .. [[</callingDevice> + <calledDirectoryNumber>]] .. number .. [[</calledDirectoryNumber> + <autoOriginate>doNotPrompt</autoOriginate> +</MakeCall>]] + + self:send(sip_account, domain, body); +end + +function Uacsta.answer_call(self, sip_account, domain) + local body = +[[<?xml version="1.0" encoding="UTF-8"?> +<AnswerCall xmlns="http://www.ecma-international.org/standards/ecma-323/csta/ed4"> + <callToBeAnswered> + <deviceID>]] .. sip_account .. [[</deviceID> + </callToBeAnswered> +</AnswerCall>]] + + self:send(sip_account, domain, body); +end + +function Uacsta.set_microphone_mute(self, sip_account, domain, value) + local body = +[[<?xml version="1.0" encoding="UTF-8"?> +<SetMicrophoneMute xmlns="http://www.ecma-international.org/standards/ecma-323/csta/ed3"> + <device>]] .. sip_account .. [[</device> + <auditoryApparatus>1</auditoryApparatus> + <microphoneMuteOn>]] .. tostring(value) .. [[</microphoneMuteOn> +</SetMicrophoneMute>]] + + self:send(sip_account, domain, body); +end + +function Uacsta.set_speaker_volume(self, sip_account, domain, value) + local body = +[[<?xml version="1.0" encoding="UTF-8"?> +<SetSpeakerVolume xmlns="http://www.ecma-international.org/standards/ecma-323/csta/ed3"> + <device>]] .. sip_account .. [[</device> + <auditoryApparatus>1</auditoryApparatus> + <speakerVolume>]] .. tonumber(value) .. [[</speakerVolume> +</SetSpeakerVolume>]] + + self:send(sip_account, domain, body); +end + +function Uacsta.set_do_not_disturb(self, sip_account, domain, value) + local body = +[[<?xml version="1.0" encoding="UTF-8"?> +<SetDoNotDisturb xmlns="http://www.ecma-international.org/standards/ecma-323/csta/ed3"> + <device>]] .. sip_account .. [[</device> + <doNotDisturbOn>]] .. tostring(value) .. [[</doNotDisturbOn> +</SetDoNotDisturb>]] + + self:send(sip_account, domain, body); +end + +function Uacsta.set_forwarding(self, sip_account, domain, forwarding_type, number, activate) + local forwarding_types = { "forwardImmediate", "forwardBusy", "forwardNoAns" } + local body = +[[<?xml version="1.0" encoding="UTF-8"?> +<SetForwarding xmlns="http://www.ecma-international.org/standards/ecma-323/csta/ed3"> + <device>]] .. sip_account .. [[</device> + <forwardingType>]] .. tostring(forwarding_types[tonumber(forwarding_type)]) .. [[</forwardingType> + <forwardDN>]] .. number .. [[</forwardDN> + <activateForward>]] .. tostring(activate) .. [[</activateForward> +</SetForwarding>]] + + self:send(sip_account, domain, body); +end diff --git a/misc/freeswitch/scripts/send_fax.lua b/misc/freeswitch/scripts/send_fax.lua new file mode 100644 index 0000000..321a5b1 --- /dev/null +++ b/misc/freeswitch/scripts/send_fax.lua @@ -0,0 +1,170 @@ +-- Gemeinschaft 5.0 +-- (c) AMOOMA GmbH 2012 +-- + +local FAX_FILE_PATH = "/opt/GS5/public/uploads/fax_document/tiff/"; +local FAX_ANSWERING_TIMEOUT = 20; + +-- Set logger +require "common.log" +local log = common.log.Log:new() +log.prefix = "### [sendfax] " + +local document_id = argv[1]; + +require 'common.database' +local database = common.database.Database:new{ log = log }:connect(); + +if not database:connected() then + log:error('cannot connect to Gemeinschaft database'); + return +end + +if not tonumber(document_id) then + log:error('document id not specified'); + return +end + +local defaults = {log=log, database=database} +require "dialplan.fax" +local fax_class = dialplan.fax.Fax:new(defaults); + +local fax_document = fax_class:find_document_by_id(document_id); + +if not fax_document then + log:error('document ' .. document_id .. ' not found'); + return +end + +if tonumber(fax_document.retry_counter) > 0 then + fax_class:document_update(document_id, {state = 'sending', retry_counter = fax_document.retry_counter - 1}); +else + fax_class:document_update(document_id, {state = 'sending'}); +end + +local fax_account = fax_class:find_by_id(fax_document.fax_account_id); + +if not fax_account then + log:error('fax account ' .. fax_document.fax_account_id .. ' not found'); + return +end + +local destination_number = fax_class:destination_number(document_id); + +if not destination_number or tostring(destination_number) == '' then + log:error('destination number not found'); + return +end + +require 'common.str' +destination_number = common.str.strip(destination_number); + +log:info('FAX_SEND - fax_document=' .. document_id .. ', destination number: ' .. destination_number .. ', retries: ' .. fax_document.retry_counter); + +require "common.phone_number" +local phone_number_class = common.phone_number.PhoneNumber:new(defaults); + +phone_number = phone_number_class:find_by_number(destination_number); + +local origination_variables = { + 'gs_account_id=' .. fax_account.record.id, + 'gs_account_uuid=' .. fax_account.record.uuid, + 'gs_account_type=' .. 'faxaccount', + 'gs_auth_account_id=' .. fax_account.record.id, + 'gs_auth_account_uuid=' .. fax_account.record.uuid, + 'gs_auth_account_type=' .. 'faxaccount', +} + +local session = nil + +if phone_number then + session = freeswitch.Session("[" .. table.concat(origination_variables, ",") .. "]loopback/" .. destination_number .. "/default"); +else + local owner_class = common.str.downcase(fax_account.record.fax_accountable_type); + + local caller = {} + caller.caller_phone_numbers = phone_number_class:list_by_owner(fax_account.record.id, 'FaxAccount'); + caller.account = fax_account; + caller.auth_account = fax_account; + caller.caller_id_name = fax_account.record.station_id; + + if owner_class == 'user' then + require 'dialplan.user' + caller.auth_account.owner = dialplan.user.User:new{ log = log, database = database }:find_by_id(fax_account.record.fax_accountable_id); + if caller.auth_account.owner then + caller.auth_account.owner.groups = caller.auth_account.owner:list_groups(); + end + elseif owner_class == 'tenant' then + require 'dialplan.tenant' + caller.auth_account.owner = dialplan.tenant.Tenant:new{ log = log, database = database }:find_by_id(fax_account.record.fax_accountable_id); + end + + require 'common.configuration_file' + local routing_table = common.configuration_file.get('/opt/freeswitch/scripts/ini/routes.ini'); + require 'dialplan.route' + local routes = dialplan.route.Route:new{ log = log, database = database, routing_table = routing_table }:outbound(caller, destination_number); + + for index, route in ipairs(routes) do + log:info('FAX_SEND - ', route.class, '=', route.endpoint, ', number: ', route.value); + if route.class == 'gateway' then + table.insert(origination_variables, "origination_caller_id_number='" .. (route.caller_id_number or caller.caller_phone_numbers[1]) .. "'"); + table.insert(origination_variables, "origination_caller_id_name='" .. (route.caller_id_name or fax_account.record.station_id) .. "'"); + session = freeswitch.Session('[' .. table.concat(origination_variables, ',') .. ']sofia/gateway/' .. route.endpoint .. '/' .. route.value); + break; + end + end +end + +local loop_count = FAX_ANSWERING_TIMEOUT; +local cause = "UNSPECIFIED" + +while session and session:ready() and not session:answered() and loop_count >= 0 do + log:debug('waiting for answer: ' .. loop_count) + loop_count = loop_count - 1; + freeswitch.msleep(1000); +end + +if session and session:answered() then + log:info('FAX_SEND - sending fax_document=' .. fax_document.id .. ' (' .. fax_document.tiff .. ')'); + + local file_name = FAX_FILE_PATH .. fax_document.id .. "/" .. fax_document.tiff; + + session:setVariable('fax_ident', fax_account.record.station_id) + session:setVariable('fax_header', fax_account.record.name) + session:setVariable('fax_verbose', 'false') + local start_time = os.time(); + session:execute('txfax', file_name); + + fax_state = { + state = nil, + transmission_time = os.time() - start_time, + document_total_pages = common.str.to_i(session:getVariable('fax_document_total_pages')), + document_transferred_pages = common.str.to_i(session:getVariable('fax_document_transferred_pages')), + ecm_requested = common.str.to_b(session:getVariable('fax_ecm_requested')), + ecm_used = common.str.to_b(session:getVariable('fax_ecm_used')), + image_resolution = common.str.to_s(session:getVariable('fax_image_resolution')), + image_size = common.str.to_i(session:getVariable('fax_image_size')), + local_station_id = common.str.to_s(session:getVariable('fax_local_station_id')), + result_code = common.str.to_i(session:getVariable('fax_result_code')), + remote_station_id = common.str.to_s(session:getVariable('fax_remote_station_id')), + success = common.str.to_b(session:getVariable('fax_success')), + transfer_rate = common.str.to_i(session:getVariable('fax_transfer_rate')), + } + + if fax_state.success then + fax_state.state = 'successful'; + else + fax_state.state = 'unsuccessful'; + end + + fax_account:document_update(fax_document.id, fax_state) + + cause = session:hangupCause(); + log:info('FAX_SEND - end - fax_document=', fax_document.id, ', success: ', fax_state.state, ', cause: ', cause, ', result: ', fax_state.result_code, ' ', session:getVariable('fax_result_text')); +else + if session then + cause = session:hangupCause(); + end + log:debug('Destination "', destination_number, '" could not be reached, cause: ', cause) + fax_account:document_update(fax_document.id, {state = 'unsuccessful', result_code = "129"}) +end diff --git a/misc/mon_ami/asterisk.py b/misc/mon_ami/asterisk.py new file mode 100644 index 0000000..ffcff06 --- /dev/null +++ b/misc/mon_ami/asterisk.py @@ -0,0 +1,299 @@ +# -*- coding: utf-8 -*- +# MonAMI Asterisk Manger Interface Server +# Asterisk AMI client connector +# (c) AMOOMA GmbH 2012 + +from threading import Thread, Lock +from log import ldebug, linfo, lwarn, lerror, lcritic +from time import sleep +from traceback import format_exc +from helper import to_hash +import socket + +class AsteriskAMIServer(Thread): + + def __init__(self, client_socket, address, message_queue): + Thread.__init__(self) + self.runthread = True + self.LINE_SEPARATOR = "\r\n" + self.GREETING_STRING = 'Asterisk Call Manager/1.1' + self.ASTERISK_VERSION_STRING = 'Asterisk 1.6.2.9-2' + self.ASTERISK_CHANNEL_STATES = ( + 'Down', + 'Reserved', + 'Offhook', + 'Dialing', + 'Ring', + 'Ringing', + 'Up', + 'Busy', + 'Dialing_Offhook', + 'Pprering', + 'Mute', + ) + self.ASTERISK_PRESENTATION_INDICATOR = ( + 'Presentation allowed', + 'Presentation restricted', + 'Number not available due to interworking', + 'Reserved', + ) + self.ASTERISK_SCREENING_INDICATOR = ( + 'not screened', + 'verified and passed', + 'verified and failed', + 'Network provided', + ) + + self.write_lock = Lock() + self.socket = client_socket + self.address = address + self.message_queue = message_queue + + + def stop(self): + ldebug('thread stop', self) + self.runthread = False + + + def run(self): + ldebug('starting AMI server thread', self) + + data = '' + while self.runthread and self.socket: + try: + recv_buffer = self.socket.recv(128) + except socket.timeout as exception: + # Socket timeout occured + continue + except: + lerror(format_exc(), self) + self.runthread = False + break + + if not recv_buffer: + ldebug('client connection lost', self) + break + + data += recv_buffer + messages = data.split(self.LINE_SEPARATOR * 2) + data = messages.pop() + + for message_str in messages: + if not message_str: + continue + + message = to_hash(message_str.split(self.LINE_SEPARATOR)) + self.message_queue.appendleft({'type': 'ami_client_message', 'body': message}) + + ldebug('exiting AMI server thread', self) + + + def send(self, send_buffer): + try: + self.write_lock.acquire() + self.socket.send(send_buffer) + self.write_lock.release() + return True + except: + return False + + + def send_message(self, *message): + if len(message) == 1 and type(message[0]) == list: + self.send(self.LINE_SEPARATOR.join(message[0]) + (self.LINE_SEPARATOR * 2)) + else: + self.send(self.LINE_SEPARATOR.join(message) + (self.LINE_SEPARATOR * 2)) + + def send_greeting(self): + self.send_message(self.GREETING_STRING) + + def send_message_unknown(self, command): + self.send_message('Response: Error', 'Message: Invalid/unknown command: %s.' % command) + + def send_login_ack(self): + self.send_message('Response: Success', 'Message: Authentication accepted') + + def send_login_nack(self): + self.send_message('Response: Error', 'Message: Authentication failed') + + def send_logout_ack(self): + self.send_message('Response: Goodbye', 'Message: Thank you for flying MonAMI') + + def send_pong(self, action_id): + self.send_message('Response: Pong', "ActionID: %s" % str(action_id), 'Server: localhost') + + def send_asterisk_version(self, action_id): + self.send_message( + 'Response: Follows', + 'Privilege: Command', + "ActionID: %s" % str(action_id), + self.ASTERISK_VERSION_STRING, + '--END COMMAND--' + ) + + def send_hangup_ack(self): + self.send_message('Response: Success', 'Message: Channel Hungup') + + + def send_originate_ack(self, action_id): + self.send_message('Response: Success', "ActionID: %s" % str(action_id), 'Message: Originate successfully queued') + + + def send_status_ack(self, action_id): + self.send_message( + 'Response: Success', + "ActionID: %s" % str(action_id), + 'Message: Channel status will follow' + ) + self.send_message( + 'Event: StatusComplete', + "ActionID: %s" % action_id, + 'Items: 0' + ) + + def send_extension_state(self, action_id, extension, context = 'default', status = -1, hint = ''): + self.send_message( + 'Response: Success', + "ActionID: %s" % str(action_id), + 'Message: Extension Status', + 'Exten: %s' % extension, + 'Context: %s' % context, + 'Hint: %s' % hint, + 'Status: %d' % status, + ) + + + def send_event_newchannel(self, uuid, channel_name, channel_state, caller_id_number = '', caller_id_name = '', destination_number = ''): + self.send_message( + 'Event: Newchannel', + 'Privilege: call,all', + 'Channel: %s' % str(channel_name), + 'ChannelState: %d' % channel_state, + 'ChannelStateDesc: %s' % self.ASTERISK_CHANNEL_STATES[channel_state], + 'CallerIDNum: %s' % str(caller_id_number), + 'CallerIDName: %s' % str(caller_id_name), + 'AccountCode:', + 'Exten: %s' % str(destination_number), + 'Context: default', + 'Uniqueid: %s' % str(uuid), + ) + + + def send_event_newstate(self, uuid, channel_name, channel_state, caller_id_number = '', caller_id_name = ''): + self.send_message( + 'Event: Newstate', + 'Privilege: call,all', + 'Channel: %s' % str(channel_name), + 'ChannelState: %d' % channel_state, + 'ChannelStateDesc: %s' % self.ASTERISK_CHANNEL_STATES[channel_state], + 'CallerIDNum: %s' % str(caller_id_number), + 'CallerIDName: %s' % str(caller_id_name), + 'Uniqueid: %s' % str(uuid), + ) + + + def send_event_newcallerid(self, uuid, channel_name, caller_id_number = '', caller_id_name = '', calling_pres = 0): + + presentation = self.ASTERISK_PRESENTATION_INDICATOR[calling_pres >> 6] + screening = self.ASTERISK_SCREENING_INDICATOR[calling_pres & 3] + + self.send_message( + 'Event: NewCallerid', + 'Privilege: call,all', + 'Channel: %s' % str(channel_name), + 'CallerIDNum: %s' % str(caller_id_number), + 'CallerIDName: %s' % str(caller_id_name), + 'Uniqueid: %s' % str(uuid), + 'CID-CallingPres: %d (%s, %s)' % (calling_pres, presentation, screening), + ) + + + def send_event_hangup(self, uuid, channel_name, caller_id_number = '', caller_id_name = '', cause = 0): + self.send_message( + 'Event: Hangup', + 'Privilege: call,all', + 'Channel: %s' % str(channel_name), + 'CallerIDNum: %s' % str(caller_id_number), + 'CallerIDName: %s' % str(caller_id_name), + 'Cause: %d' % cause, + 'Cause-txt: Unknown', + 'Uniqueid: %s' % str(uuid) + ) + + + def send_event_dial_begin(self, uuid, channel_name, caller_id_number, caller_id_name, destination_channel, destination_uuid, destination_number): + self.send_message( + 'Event: Dial', + 'Privilege: call,all', + 'SubEvent: Begin', + "Channel: %s" % str(channel_name), + "Destination: %s" % str(destination_channel), + 'CallerIDNum: %s' % str(caller_id_number), + 'CallerIDName: %s' % str(caller_id_name), + 'Uniqueid: %s' % str(uuid), + 'DestUniqueid: %s' % str(destination_uuid), + 'Dialstring: %s@default' % str(destination_number) + ) + + + def send_event_dial_end(self, uuid, channel_name, dial_status = 'UNKNOWN'): + self.send_message( + 'Event: Dial', + 'Privilege: call,all', + 'SubEvent: End', + "Channel: %s" % str(channel_name), + 'Uniqueid: %s' % str(uuid), + "DialStatus: %s" % str(dial_status), + ) + + + def send_event_originate_response(self, uuid, channel_name, caller_id_number, caller_id_name, destination_number, action_id, reason): + #reasons: + #0: no such extension or number + #1: no answer + #4: answered + #8: congested or not available + + if reason == 4: + response = 'Success' + else: + response = 'Failure' + + self.send_message( + 'Event: OriginateResponse', + 'Privilege: call,all', + 'ActionID: %s' % str(action_id), + 'Response: %s' % response, + 'Channel: %s' % str(channel_name), + 'Context: default', + 'Exten: %s' % str(destination_number), + 'Reason: %d' % reason, + 'CallerIDNum: %s' % str(caller_id_number), + 'CallerIDName: %s' % str(caller_id_name), + 'Uniqueid: %s' % str(uuid), + ) + + + def send_event_bridge(self, uuid, channel_name, caller_id, o_uuid, o_channel_name, o_caller_id): + self.send_message( + 'Event: Bridge', + 'Privilege: call,all', + 'Bridgestate: Link', + 'Bridgetype: core', + 'Channel1: %s' % str(channel_name), + 'Channel2: %s' % str(o_channel_name), + 'Uniqueid1: %s' % str(uuid), + 'Uniqueid2: %s' % str(o_uuid), + 'CallerID1: %s' % str(caller_id), + 'CallerID2: %s' % str(o_caller_id), + ) + + def send_event_newaccountcode(self, uuid, channel_name): + self.send_message( + 'Event: NewAccountCode', + 'Privilege: call,all', + "Channel: %s" % str(channel_name), + 'Uniqueid: %s' % str(uuid), + 'AccountCode:', + 'OldAccountCode:', + ) diff --git a/misc/mon_ami/freeswitch.py b/misc/mon_ami/freeswitch.py new file mode 100644 index 0000000..eab9bb6 --- /dev/null +++ b/misc/mon_ami/freeswitch.py @@ -0,0 +1,194 @@ +# -*- coding: utf-8 -*- +# MonAMI Asterisk Manger Interface server +# FreeSWITCH event socket interface +# (c) AMOOMA GmbH 2012 + +from threading import Thread, Lock +from log import ldebug, linfo, lwarn, lerror, lcritic +from collections import deque +from time import sleep, time +from random import random +from helper import to_hash +from traceback import format_exc +import socket +import sys +import hashlib + + +class FreeswitchEventSocket(Thread): + + def __init__(self, host, port, password): + Thread.__init__(self) + self.LINE_SEPARATOR = "\n" + self.SOCKET_TIMEOUT = 1 + self.MESSAGE_PIPE_MAX_LENGTH = 128 + self.write_lock = Lock() + self.host = host + self.port = port + self.password = password + self.runthread = True + self.fs = None + self.client_queues = {} + + + def stop(self): + ldebug('thread stop', self) + self.runthread = False + + + def run(self): + ldebug('starting FreeSWITCH event_socket thread', self) + + while self.runthread: + if not self.connect(): + ldebug('could not connect to FreeSWITCH - retry', self) + sleep(self.SOCKET_TIMEOUT) + continue + ldebug('opening event_socket connection', self) + + data = '' + while self.runthread and self.fs: + + try: + recv_buffer = self.fs.recv(128) + except socket.timeout as exception: + # Socket timeout occured + continue + except: + lerror(format_exc(), self) + self.runthread = False + break + + if not recv_buffer: + ldebug('event_socket connection lost', self) + break + + data += recv_buffer + messages = data.split(self.LINE_SEPARATOR * 2) + data = messages.pop() + + for message_str in messages: + if not message_str: + continue + message_body = None + + message = to_hash(message_str.split(self.LINE_SEPARATOR)) + + if not 'Content-Type' in message: + ldebug('message without Content-Type', self) + continue + + if 'Content-Length' in message and int(message['Content-Length']) > 0: + content_length = int(message['Content-Length']) + while len(data) < int(message['Content-Length']): + try: + data += self.fs.recv(content_length - len(data)) + except socket.timeout as exception: + ldebug('Socket timeout in message body', self) + continue + except: + lerror(format_exc(), self) + break + message_body = data.strip() + data = '' + else: + content_length = 0 + + self.process_message(message['Content-Type'], message, content_length, message_body) + + + ldebug('closing event_socket connection', self) + if self.fs: + self.fs.close() + + + def connect(self): + fs = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + fs.connect((self.host, self.port)) + except: + lerror(format_exc(), self) + return False + + fs.settimeout(self.SOCKET_TIMEOUT) + self.fs = fs + return True + + + def authenticate(self): + ldebug('send authentication to FreeSWITCH', self) + self.send_message("auth %s" % self.password) + + + def send(self, send_buffer): + try: + self.write_lock.acquire() + self.fs.send(send_buffer) + self.write_lock.release() + return True + except: + return False + + + def send_message(self, *message): + if len(message) == 1 and type(message[0]) == list: + self.send(self.LINE_SEPARATOR.join(message[0]) + (self.LINE_SEPARATOR * 2)) + else: + self.send(self.LINE_SEPARATOR.join(message) + (self.LINE_SEPARATOR * 2)) + + + def process_message(self, content_type, message_head, content_length, message_body): + + if content_type == 'auth/request': + self.authenticate() + if content_type == 'command/reply': + if 'Reply-Text' in message_head: + ldebug('FreeSWITCH command reply: %s' % message_head['Reply-Text'], self) + elif content_type == 'text/event-plain': + event = to_hash(message_body.split(self.LINE_SEPARATOR)) + + if 'Event-Name' in event and event['Event-Name'] in self.client_queues: + event_type = event['Event-Name'] + for entry_id, message_pipe in self.client_queues[event_type].items(): + if type(message_pipe) == deque: + if len(message_pipe) < self.MESSAGE_PIPE_MAX_LENGTH: + message_pipe.appendleft({'type': 'freeswitch_event', 'body': event}) + else: + lwarn("event queue %d full" % entry_id) + else: + ldebug("force-deregister event queue %d for event type %s" % (entry_id, event_type), self) + del self.client_queues[event_type][entry_id] + + def register_client_queue(self, queue, event_type): + if not event_type in self.client_queues: + self.client_queues[event_type] = {} + self.send_message("event plain all %s" % event_type) + ldebug("we are listening now to events of type: %s" % event_type, self) + self.client_queues[event_type][id(queue)] = queue + ldebug("event queue %d registered for event type: %s" % (id(queue), event_type), self) + + + def deregister_client_queue(self, queue, event_type): + ldebug("deregister event queue %d for event type %s" % (id(queue), event_type), self) + del self.client_queues[event_type][id(queue)] + + def deregister_client_queue_all(self, queue): + for event_type, event_queues in self.client_queues.items(): + if id(queue) in event_queues: + ldebug("deregister event queue %d for all registered event types - event type %s" % (id(queue), event_type), self) + del self.client_queues[event_type][id(queue)] + + + def hangup(self, uuid, hangup_cause = 'NORMAL_CLEARING'): + ldebug('hangup channel: %s' % uuid, self) + self.send_message('SendMsg %s' % uuid, 'call-command: hangup', 'hangup-cause: %s' % hangup_cause) + + return True + + + def originate(self, sip_account, extension, action_id = ''): + uuid = hashlib.md5('%s%f' % (sip_account, random() * 65534)).hexdigest() + ldebug('originate call - from: %s, to: %s, uuid: %s' % (sip_account, extension, uuid), self) + self.send_message('bgapi originate {origination_uuid=%s,origination_action=%s,origination_caller_id_number=%s}user/%s %s' % (uuid, action_id, sip_account, sip_account, extension)) + + return uuid diff --git a/misc/mon_ami/helper.py b/misc/mon_ami/helper.py new file mode 100644 index 0000000..bf286de --- /dev/null +++ b/misc/mon_ami/helper.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# MonAMI Asterisk Manger Interface server +# helper functions +# (c) AMOOMA GmbH 2012 + + +def to_hash(message): + message_hash = {} + for line in message: + keyword, delimeter, value = line.partition(": ") + if (keyword): + message_hash[keyword] = value.strip() + + return message_hash + + +def sval(array, key): + try: + return array[key] + except: + return None diff --git a/misc/mon_ami/log.py b/misc/mon_ami/log.py new file mode 100644 index 0000000..92709ad --- /dev/null +++ b/misc/mon_ami/log.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +# Log library +# (c) AMOOMA GmbH 2012 + +import logging + +def ldebug(entry, initiator = None): + global logger + logger.debug('%s(%d) %s' % (type(initiator).__name__, id(initiator), entry)) + +def lwarn(entry, initiator = None): + global logger + logger.warning('%s(%d) %s' % (type(initiator).__name__, id(initiator), entry)) + +def lerror(entry, initiator = None): + global logger + logger.error('%s(%d) %s' % (type(initiator).__name__, id(initiator), entry)) + +def linfo(entry, initiator = None): + global logger + logger.info('%s(%d) %s' % (type(initiator).__name__, id(initiator), entry)) + +def lcritic(entry, initiator = None): + global logger + logger.critical('%s(%d) %s' % (type(initiator).__name__, id(initiator), entry)) + +def setup_log(file_name = None, loglevel = 5, logformat = None): + from sys import stdout + global logger + + if file_name: + try: + logfile = logging.FileHandler(file_name) + except: + logfile = logging.StreamHandler(stdout) + else: logfile = logging.StreamHandler(stdout) + + loglevel = int(loglevel) + + if (loglevel == 0): + logfile.setLevel(logging.NOTSET) + logger.setLevel(logging.NOTSET) + elif (loglevel == 1): + logfile.setLevel(logging.CRITICAL) + logger.setLevel(logging.CRITICAL) + elif (loglevel == 2): + logfile.setLevel(logging.ERROR) + logger.setLevel(logging.ERROR) + elif (loglevel == 3): + logfile.setLevel(logging.WARNING) + logger.setLevel(logging.WARNING) + elif (loglevel == 4): + logfile.setLevel(logging.INFO) + logger.setLevel(logging.INFO) + elif (loglevel >= 5): + logfile.setLevel(logging.DEBUG) + logger.setLevel(logging.DEBUG) + + if not logformat: + logformat = '%(asctime)s-%(name)s-%(levelname)s-%(message)s' + + try: + format = logging.Formatter(logformat) + logfile.setFormatter(format) + except: + format = logging.Formatter('%(asctime)s-%(name)s-%(levelname)s-%(message)s') + logfile.setFormatter(format) + + logger.addHandler(logfile) + +logger = logging.getLogger('#') diff --git a/misc/mon_ami/mon-ami b/misc/mon_ami/mon-ami new file mode 100755 index 0000000..a630140 --- /dev/null +++ b/misc/mon_ami/mon-ami @@ -0,0 +1,58 @@ +#!/bin/sh + +##################################################################### +# MonAMI Asterisk Manger Interface Emulator +# Start Script +# (c) AMOOMA GmbH 2012 +##################################################################### + +### BEGIN INIT INFO +# Provides: mon_ami +# Required-Start: freeswitch +# Required-Stop: freeswitch +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: MonAMI Asterisk Manger Interface Emulator +# Description: +# +### END INIT INFO + +DAEMON=/opt/GS5/misc/mon_ami/mon_ami +EXECUTABLE=`basename 'mon_ami'` +DESC="MonAMI Asterisk Manger Interface Emulator" +ARGS="--log-file=/var/log/mon_ami.log" + +if ! [ -x $DAEMON ] ; then + echo "ERROR: $DAEMON not found" + exit 1 +fi + +case "$1" in + start) + echo -n "Starting $DESC: " + start-stop-daemon --start --pidfile /var/run/$EXECUTABLE.pid \ + --make-pidfile --background --startas $DAEMON -- $ARGS + echo "$EXECUTABLE." + ;; + + stop) + echo -n "Stopping $DESC: " + start-stop-daemon --stop --quiet --oknodo --retry=TERM/30/KILL/5 \ + --pidfile /var/run/$EXECUTABLE.pid + rm -f /var/run/$NAME.pid + echo "$EXECUTABLE." + ;; + + reload|restart|force-reload) + $0 stop + sleep 2 + $0 start + ;; + + *) + echo "Usage: $0 {start|stop|restart|reload|force-reload}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/misc/mon_ami/mon_ami b/misc/mon_ami/mon_ami new file mode 100755 index 0000000..a212cfe --- /dev/null +++ b/misc/mon_ami/mon_ami @@ -0,0 +1,10 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +# MonAMI Asterisk Manger Interface Server +# (c) AMOOMA GmbH 2012 +from mon_ami_main import main +from sys import exit + +if (__name__ == "__main__"): + result = main() + exit(result) diff --git a/misc/mon_ami/mon_ami_handler.py b/misc/mon_ami/mon_ami_handler.py new file mode 100644 index 0000000..59e9225 --- /dev/null +++ b/misc/mon_ami/mon_ami_handler.py @@ -0,0 +1,434 @@ +# -*- coding: utf-8 -*- +# MonAMI Asterisk Manger Interface Server +# Asterisk AMI Emulator Handler Process +# (c) AMOOMA GmbH 2012 + +from threading import Thread +from log import ldebug, linfo, lwarn, lerror, lcritic +from time import sleep +from traceback import format_exc +from collections import deque +from urllib import unquote +from asterisk import AsteriskAMIServer +from socket import SHUT_RDWR +from helper import sval + + +class MonAMIHandler(Thread): + + def __init__(self, socket, address, event_socket=None): + Thread.__init__(self) + self.runthread = True + self.socket = socket + self.address = address + self.event_socket = event_socket + self.ami = None + self.deregister_at_server = None + self.message_pipe = deque() + self.channels = {} + self.user_password_authentication = None + self.account_name = '' + + + def stop(self): + ldebug('thread stop', self) + self.ami.stop() + self.runthread = False + + + def shutdown(self): + self.deregister_at_server(self) + ldebug('closing connection to %s:%d' % self.address) + try: + self.socket.shutdown(SHUT_RDWR) + self.socket.close() + ldebug('connection closed ', self) + except: + ldebug('connection closed by foreign host', self) + + def run(self): + ldebug('starting MonAMI handler thread', self) + + # starting asterisk AMI thread + self.ami = AsteriskAMIServer(self.socket, self.address, self.message_pipe) + self.ami.start() + self.ami.send_greeting() + + # register for events + self.event_socket.register_client_queue(self.message_pipe, 'CHANNEL_CREATE') + self.event_socket.register_client_queue(self.message_pipe, 'CHANNEL_DESTROY') + self.event_socket.register_client_queue(self.message_pipe, 'CHANNEL_STATE') + self.event_socket.register_client_queue(self.message_pipe, 'CHANNEL_ANSWER') + self.event_socket.register_client_queue(self.message_pipe, 'CHANNEL_BRIDGE') + + while self.runthread and self.ami.isAlive(): + if self.message_pipe: + message = self.message_pipe.pop() + message_type = sval(message, 'type') + if message_type == 'freeswitch_event': + self.handle_fs_event(message['body']) + elif message_type == 'ami_client_message': + self.handle_ami_client_message(message['body']) + else: + sleep(0.1) + + self.event_socket.deregister_client_queue_all(self.message_pipe) + + ldebug('exiting MonAMI handler thread', self) + self.shutdown() + + + def handle_ami_client_message(self, message): + + if 'Action' in message: + action = message['Action'].lower() + + if action == 'login': + if 'UserName' in message: + self.account_name = message['UserName'] + if 'Secret' in message and self.user_password_authentication and self.user_password_authentication(self.account_name, message['Secret']): + self.ami.send_login_ack() + ldebug('AMI connection authenticated - account: %s' % self.account_name, self) + else: + self.ami.send_login_nack() + linfo('AMI authentication failed - account: %s' % sval(message, 'UserName'), self) + self.ami.stop() + self.stop() + elif action == 'logoff': + self.ami.send_logout_ack() + ldebug('AMI logout', self) + self.ami.stop() + self.stop() + elif action == 'ping': + self.ami.send_pong(sval(message, 'ActionID')) + elif action == 'status': + self.ami.send_status_ack(sval(message, 'ActionID')) + elif action == 'command' and sval(message, 'Command') == 'core show version': + self.ami.send_asterisk_version(sval(message, 'ActionID')) + elif action == 'hangup': + account_name, separator, uuid = str(sval(message, 'Channel')).rpartition('-uuid-') + if account_name != '': + self.event_socket.hangup(uuid) + self.ami.send_hangup_ack() + elif action == 'originate': + self.message_originate(message) + elif action == 'extensionstate': + self.ami.send_extension_state(sval(message, 'ActionID'), sval(message, 'Exten'), sval(message, 'Context')) + else: + ldebug('unknown asterisk message received: %s' % message, self) + self.ami.send_message_unknown(message['Action']) + + + def to_unique_channel_name(self, uuid, channel_name): + + # strip anything left of sip_account_name + path, separator, contact_part = channel_name.rpartition('/sip:') + if path == '': + path, separator, contact_part = channel_name.rpartition('/') + + # if failed return name unchanged + if path == '': + return channel_name + + + # strip domain part + account_name = contact_part.partition('@')[0] + + # if failed return name unchanged + if account_name == '': + return channel_name + + # create unique channel name + return 'SIP/%s-uuid-%s' % (account_name, uuid) + + def message_originate(self, message): + destination_number = str(sval(message, 'Exten')) + action_id = sval(message, 'ActionID') + self.ami.send_originate_ack(action_id) + uuid = self.event_socket.originate(self.account_name, destination_number, action_id) + + + def handle_fs_event(self, event): + event_type = event['Event-Name'] + #ldebug('event type received: %s' % event_type, self) + + event_types = { + 'CHANNEL_CREATE': self.event_channel_create, + 'CHANNEL_DESTROY': self.event_channel_destroy, + 'CHANNEL_STATE': self.event_channel_state, + 'CHANNEL_ANSWER': self.event_channel_answer, + 'CHANNEL_BRIDGE': self.event_channel_bridge, + } + + uuid = event_types[event_type](event) + + if not uuid: + return False + + channel = sval(self.channels, uuid); + + if not channel: + return False + + o_uuid = channel['o_uuid'] + o_channel = sval(self.channels, o_uuid); + + if sval(channel, 'origination_action') or sval(o_channel, 'origination_action'): + if not sval(channel, 'ami_start') and not sval(o_channel, 'ami_start'): + if sval(channel, 'owned') and sval(channel, 'origination_action'): + ldebug('sending AMI events for origitate call start (on this channel): %s' % uuid, self) + self.ami_send_originate_start(channel) + self.channels[uuid]['ami_start'] = True + elif sval(o_channel, 'owned') and sval(o_channel, 'origination_action'): + ldebug('sending AMI events for origitate call start (on other channel): %s' % uuid, self) + self.ami_send_originate_start(o_channel) + self.channels[o_uuid]['ami_start'] = True + elif o_channel: + if sval(channel, 'owned') and sval(channel, 'origination_action'): + ldebug('sending AMI events for origitate call progress (on this channel): %s' % uuid, self) + self.ami_send_originate_outbound(channel) + self.channels[uuid]['origination_action'] = False + elif sval(o_channel, 'owned') and sval(o_channel, 'origination_action'): + ldebug('sending AMI events for origitate call progress (on other channel): %s' % uuid, self) + self.ami_send_originate_outbound(o_channel) + self.channels[o_uuid]['origination_action'] = False + elif o_channel: + if not sval(channel, 'ami_start') and not sval(o_channel, 'ami_start'): + if sval(channel, 'owned') and sval(channel, 'direction') == 'inbound': + ldebug('sending AMI events for outbound call start (on this channel): %s' % uuid, self) + self.ami_send_outbound_start(channel) + self.channels[uuid]['ami_start'] = True + elif sval(o_channel, 'owned') and sval(channel, 'direction') == 'outbound': + ldebug('sending AMI events for outbound call start (on other channel): %s' % uuid, self) + self.ami_send_outbound_start(o_channel) + self.channels[o_uuid]['ami_start'] = True + + if not sval(channel, 'ami_start')and not sval(o_channel, 'ami_start'): + if sval(channel, 'owned') and sval(channel, 'direction') == 'outbound': + ldebug('sending AMI events for inbound call start (on this channel): %s' % uuid, self) + self.ami_send_inbound_start(channel) + self.channels[uuid]['ami_start'] = True + elif sval(o_channel, 'owned') and sval(channel, 'direction') == 'inbound': + ldebug('sending AMI events for inbound call start (on other channel): %s' % uuid, self) + self.ami_send_inbound_start(o_channel) + self.channels[o_uuid]['ami_start'] = True + + + def event_channel_create(self, event): + uuid = sval(event, 'Unique-ID') + o_uuid = sval(event, 'Other-Leg-Unique-ID') + + if uuid in self.channels: + ldebug('channel already listed: %s' % uuid, self) + return false + + channel_name = self.to_unique_channel_name(uuid, unquote(str(sval(event, 'Channel-Name')))) + o_channel_name = self.to_unique_channel_name(o_uuid, unquote(str(sval(event, 'Other-Leg-Channel-Name')))) + + if self.account_name in channel_name: + channel_owned = True + else: + channel_owned = False + + if self.account_name in o_channel_name: + channel_related = True + else: + channel_related = False + + if not channel_owned and not channel_related: + ldebug('channel neither owned nor reladed to account: %s' % uuid, self) + return False + + channel = { + 'uuid': uuid, + 'name': channel_name, + 'direction': sval(event, 'Call-Direction'), + 'channel_state': sval(event, 'Channel-State'), + 'call_state': sval(event, 'Channel-Call-State'), + 'answer_state': sval(event, 'Answer-State'), + 'owned': channel_owned, + 'related': channel_related, + 'caller_id_name': unquote(str(sval(event, 'Caller-Caller-ID-Name'))), + 'caller_id_number': unquote(str(sval(event, 'Caller-Caller-ID-Number'))), + 'callee_id_name': unquote(str(sval(event, 'Caller-Callee-ID-Name'))), + 'callee_id_number': unquote(str(sval(event, 'Caller-Callee-ID-Number'))), + 'destination_number': str(sval(event, 'Caller-Destination-Number')), + 'origination_action': sval(event, 'variable_origination_action'), + 'o_uuid': o_uuid, + 'o_name': o_channel_name, + } + + if channel['answer_state'] == 'ringing': + if channel['direction'] == 'inbound': + asterisk_channel_state = 4 + else: + asterisk_channel_state = 5 + else: + asterisk_channel_state = 0 + + if not o_uuid: + ldebug('one legged call, channel: %s' % uuid, self) + elif o_uuid not in self.channels: + o_channel = { + 'uuid': o_uuid, + 'name': o_channel_name, + 'direction': sval(event, 'Other-Leg-Direction'), + 'channel_state': sval(event, 'Channel-State'), + 'call_state': sval(event, 'Channel-Call-State'), + 'answer_state': sval(event, 'Answer-State'), + 'owned': channel_related, + 'related': channel_owned, + 'caller_id_name': unquote(str(sval(event, 'Caller-Caller-ID-Name'))), + 'caller_id_number': unquote(str(sval(event, 'Caller-Caller-ID-Number'))), + 'callee_id_name': unquote(str(sval(event, 'Caller-Callee-ID-Name'))), + 'callee_id_number': unquote(str(sval(event, 'Caller-Callee-ID-Number'))), + 'destination_number': str(sval(event, 'Other-Leg-Destination-Number')), + 'o_uuid': uuid, + 'o_name': channel_name, + } + + if o_channel['answer_state'] == 'ringing': + if o_channel['direction'] == 'inbound': + asterisk_o_channel_state = 4 + else: + asterisk_o_channel_state = 5 + else: + asterisk_o_channel_state = 0 + + ldebug('create channel list entry for related channel: %s, name: %s' % (o_uuid, o_channel_name), self) + self.channels[o_uuid] = o_channel + else: + ldebug('updating channel: %s, name: %s, o_uuid: %s, o_name %s' % (o_uuid, o_channel_name, uuid, channel_name), self) + self.channels[o_uuid]['o_uuid'] = uuid + self.channels[o_uuid]['o_name'] = channel_name + o_channel = self.channels[o_uuid] + + if channel_owned: + ldebug('create channel list entry for own channel: %s, name: %s' % (uuid, channel_name), self) + elif channel_related: + ldebug('create channel list entry for related channel: %s, name: %s' % (uuid, channel_name), self) + + self.channels[uuid] = channel + + return uuid + + + def event_channel_destroy(self, event): + uuid = sval(event, 'Unique-ID') + hangup_cause_code = int(sval(event, 'variable_hangup_cause_q850')) + channel = sval(self.channels, uuid) + + if channel: + channel['hangup_cause_code'] = hangup_cause_code + if sval(channel, 'ami_start'): + self.ami_send_outbound_end(channel) + del self.channels[uuid] + ldebug('channel removed from list: %s, cause %d' % (uuid, hangup_cause_code), self) + + return uuid + + + def event_channel_state(self, event): + uuid = sval(event, 'Unique-ID') + channel_state = sval(event, 'Channel-State') + call_state = sval(event, 'Channel-Call-State') + answer_state = sval(event, 'Answer-State') + + if sval(self.channels, uuid) and False: + ldebug('updating channel state - channel: %s, channel_state: %s, call_state %s, answer_state: %s' % (uuid, channel_state, call_state, answer_state), self) + self.channels[uuid]['channel_state'] = channel_state + self.channels[uuid]['call_state'] = call_state + self.channels[uuid]['answer_state'] = answer_state + + return uuid + + + def event_channel_answer(self, event): + uuid = sval(event, 'Unique-ID') + o_uuid = sval(event, 'Other-Leg-Unique-ID') + channel = sval(self.channels, uuid) + if not o_uuid: + o_uuid = sval(channel, 'o_uuid') + o_channel = sval(self.channels, o_uuid) + origination_action = sval(channel, 'origination_action') + + if channel: + channel_state = sval(event, 'Channel-State') + call_state = sval(event, 'Channel-Call-State') + answer_state = sval(event, 'Answer-State') + ldebug('channel answered - channel: %s, owned: %s, channel_state: %s, call_state %s, answer_state: %s, other leg: %s' % (uuid, sval(channel, 'owned'), channel_state, call_state, answer_state, o_uuid), self) + self.ami.send_event_newstate(uuid, sval(channel, 'name'), 6, sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name')) + + self.channels[uuid]['channel_state'] = channel_state + self.channels[uuid]['call_state'] = call_state + self.channels[uuid]['answer_state'] = answer_state + + if sval(channel, 'origination_action'): + if sval(channel, 'owned'): + ldebug('sending AMI originate response - success: %s' % uuid, self) + self.ami.send_event_originate_response(sval(channel, 'uuid'), sval(channel, 'name'), sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name'), '101', sval(channel, 'origination_action'), 4) + elif not o_uuid: + ldebug('sending AMI events for outbound call start on one legged call (this channel): %s' % uuid, self) + self.ami_send_outbound_start(channel) + self.ami.send_event_bridge(uuid, sval(channel, 'name'), sval(channel, 'caller_id_number'), o_uuid, sval(o_channel, 'name'), sval(o_channel, 'caller_id_number')) + + self.channels[uuid]['ami_start'] = True + + return uuid + + return False + + + def event_channel_bridge(self, event): + uuid = sval(event, 'Unique-ID') + o_uuid = sval(event, 'Other-Leg-Unique-ID') + + ldebug('bridge channel: %s to %s' % (uuid, o_uuid), self) + channel = sval(self.channels, uuid) + o_channel = sval(self.channels, o_uuid) + + if sval(channel, 'owned') or sval(o_channel, 'owned'): + ldebug('sending AMI bridge response: %s -> %s' % (uuid, o_uuid), self) + self.ami.send_event_bridge(uuid, sval(channel, 'name'), sval(channel, 'caller_id_number'), o_uuid, sval(o_channel, 'name'), sval(o_channel, 'caller_id_number')) + + + def ami_send_outbound_start(self, channel): + self.ami.send_event_newchannel(sval(channel, 'uuid'), sval(channel, 'name'), 0, sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name'), sval(channel, 'destination_number')) + self.ami.send_event_newstate(sval(channel, 'uuid'), sval(channel, 'name'), 4, sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name')) + self.ami.send_event_newchannel(sval(channel, 'o_uuid'), sval(channel, 'o_name'), 0, '', '', '') + self.ami.send_event_dial_begin(sval(channel, 'uuid'), sval(channel, 'name'), sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name'), sval(channel, 'o_name'), sval(channel, 'o_uuid'), sval(channel, 'destination_number')) + self.ami.send_event_newcallerid(sval(channel, 'o_uuid'), sval(channel, 'o_name'), sval(channel, 'destination_number'), '', 0) + self.ami.send_event_newstate(sval(channel, 'o_uuid'), sval(channel, 'o_name'), 5, sval(channel, 'destination_number'), '') + + + def ami_send_outbound_end(self, channel): + self.ami.send_event_hangup(sval(channel, 'o_uuid'), sval(channel, 'o_name'), sval(channel, 'destination_number'), '', sval(channel, 'hangup_cause_code')) + self.ami.send_event_dial_end(sval(channel, 'uuid'), sval(channel, 'name')) + self.ami.send_event_hangup(sval(channel, 'uuid'), sval(channel, 'name'), sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name'), sval(channel, 'hangup_cause_code')) + + if sval(channel, 'origination_action'): + self.ami.send_event_originate_response(sval(channel, 'uuid'), sval(channel, 'name'), sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name'), sval(channel, 'destination_number'), sval(channel, 'origination_action'), 1) + + + def ami_send_inbound_start(self, channel): + self.ami.send_event_newchannel(sval(channel, 'o_uuid'), sval(channel, 'o_name'), 0, sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name'), sval(channel, 'callee_id_number')) + self.ami.send_event_newstate(sval(channel, 'o_uuid'), sval(channel, 'o_name'), 4, sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name')) + self.ami.send_event_newchannel(sval(channel, 'uuid'), sval(channel, 'name'), 0, '', '', '') + self.ami.send_event_dial_begin(sval(channel, 'o_uuid'), sval(channel, 'o_name'), sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name'), sval(channel, 'name'), sval(channel, 'uuid'), sval(channel, 'destination_number')) + self.ami.send_event_newstate(sval(channel, 'uuid'), sval(channel, 'name'), 5, sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name')) + self.ami.send_event_newcallerid(sval(channel, 'uuid'), sval(channel, 'name'), sval(channel, 'destination_number'), '', 0) + + + def ami_send_originate_start(self, channel): + self.ami.send_event_newchannel(sval(channel, 'uuid'), sval(channel, 'name'), 0, '', '', '') + self.ami.send_event_newcallerid(sval(channel, 'uuid'), sval(channel, 'name'), sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name'), 0) + self.ami.send_event_newaccountcode(sval(channel, 'uuid'), sval(channel, 'name')) + self.ami.send_event_newcallerid(sval(channel, 'uuid'), sval(channel, 'name'), sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name'), 0) + self.ami.send_event_newstate(sval(channel, 'uuid'), sval(channel, 'name'), 5, sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name')) + + + def ami_send_originate_outbound(self, channel): + self.ami.send_event_newchannel(sval(channel, 'o_uuid'), sval(channel, 'o_name'), 0, '', '', '') + self.ami.send_event_dial_begin(sval(channel, 'uuid'), sval(channel, 'name'), sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name'), sval(channel, 'o_name'), sval(channel, 'o_uuid'), sval(channel, 'destination_number')) + self.ami.send_event_newcallerid(sval(channel, 'o_uuid'), sval(channel, 'o_name'), sval(channel, 'destination_number'), '', 0) + self.ami.send_event_newstate(sval(channel, 'o_uuid'), sval(channel, 'o_name'), 5, sval(channel, 'destination_number'), '') diff --git a/misc/mon_ami/mon_ami_main.py b/misc/mon_ami/mon_ami_main.py new file mode 100644 index 0000000..13dd4bb --- /dev/null +++ b/misc/mon_ami/mon_ami_main.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- +# MonAMI Asterisk Manger Interface Server +# Main Programm +# (c) AMOOMA GmbH 2012 + +from log import ldebug, linfo, lwarn, lerror, lcritic, setup_log +from time import sleep +from signal import signal, SIGHUP, SIGTERM, SIGINT +from optparse import OptionParser +from freeswitch import FreeswitchEventSocket +from mon_ami_server import MonAMIServer +from sqliter import SQLiteR + +def signal_handler(signal_number, frame): + global event_socket + global mon_ami_server + + ldebug('signal %d received ' % signal_number, frame) + + if (signal_number == SIGTERM): + ldebug('shutdown signal (%d) received ' % signal_number, frame) + event_socket.stop() + mon_ami_server.stop() + elif (signal_number == SIGINT): + ldebug('interrupt signal (%d) received ' % signal_number, frame) + event_socket.stop() + mon_ami_server.stop() + elif (signal_number == SIGHUP): + ldebug('hangup signal (%d) received - ignore' % signal_number, frame) + +def user_password_authentication(user_name, password): + global configuration_options + + if configuration_options.user_ignore_name and configuration_options.user_ignore_password: + ldebug('user-password authentication credentials provided but ignored - user: %s, password: %s' % (user_name, '*' * len(str(password)))) + return True + + if configuration_options.user_override_name != None and configuration_options.user_override_password != None: + if user_name == configuration_options.user_override_name and password == configuration_options.user_override_password: + return True + return False + + db = SQLiteR(configuration_options.user_db_name) + if not db.connect(): + lerror('cound not connect to user database "%s"' % configuration_options.user_db_name) + return False + + user = db.find(configuration_options.user_db_table, {configuration_options.user_db_name_row: user_name, configuration_options.user_db_password_row: password}) + db.disconnect() + + if user: + ldebug('user-password authentication accepted - user: %s, password: %s' % (user_name, '*' * len(str(password)))) + return True + + linfo('user-password authentication failed - user: %s, password: %s' % (user_name, '*' * len(str(password)))) + return False + +def main(): + global event_socket + global mon_ami_server + global configuration_options + + option_parser = OptionParser() + + # Log options + option_parser.add_option("--log-file", action="store", type="string", dest="log_file", default=None) + option_parser.add_option("--log-level", action="store", type="int", dest="log_level", default=5) + + # FreeSWITCH event_socket + option_parser.add_option("--freeswitch-address", action="store", type="string", dest="freeswitch_address", default='127.0.0.1') + option_parser.add_option("--freeswitch-port", action="store", type="int", dest="freeswitch_port", default=8021) + option_parser.add_option("--freeswitch-password", action="store", type="string", dest="freeswitch_password", default='ClueCon') + + # Asterisk Manager Interface + option_parser.add_option("-a", "--address", "--ami-address", action="store", type="string", dest="ami_address", default='0.0.0.0') + option_parser.add_option("-p", "--port", "--ami-port", action="store", type="int", dest="ami_port", default=5038) + + # User database + option_parser.add_option("--user-db-name", action="store", type="string", dest="user_db_name", default='/opt/GS5/db/development.sqlite3') + option_parser.add_option("--user-db-table", action="store", type="string", dest="user_db_table", default='sip_accounts') + option_parser.add_option("--user-db-name-row", action="store", type="string", dest="user_db_name_row", default='auth_name') + option_parser.add_option("--user-db-password-row", action="store", type="string", dest="user_db_password_row", default='password') + + # Define common User/Password options + option_parser.add_option("--user-override-name", action="store", type="string", dest="user_override_name", default=None) + option_parser.add_option("--user-override-password", action="store", type="string", dest="user_override_password", default=None) + option_parser.add_option("--user-ignore-name", action="store_true", dest="user_ignore_name", default=False) + option_parser.add_option("--user-ignore-password", action="store_true", dest="user_ignore_password", default=False) + + (configuration_options, args) = option_parser.parse_args() + + setup_log(configuration_options.log_file, configuration_options.log_level) + ldebug('starting MonAMI main process') + + # Catch signals + signal(SIGHUP, signal_handler) + signal(SIGTERM, signal_handler) + signal(SIGINT, signal_handler) + + # Starting FreeSWITCH event_socket thread + event_socket = FreeswitchEventSocket(configuration_options.freeswitch_address, configuration_options.freeswitch_port, configuration_options.freeswitch_password) + event_socket.start() + + if event_socket.isAlive(): + # Starting Asterisk manager thread + mon_ami_server = MonAMIServer(configuration_options.ami_address, configuration_options.ami_port, event_socket) + mon_ami_server.user_password_authentication = user_password_authentication + mon_ami_server.start() + + while mon_ami_server.isAlive(): + sleep(1) + + if event_socket.isAlive(): + ldebug('killing event_socket thread') + event_socket.stop() + + ldebug('exiting MonAMI main process') diff --git a/misc/mon_ami/mon_ami_server.py b/misc/mon_ami/mon_ami_server.py new file mode 100644 index 0000000..68e72c8 --- /dev/null +++ b/misc/mon_ami/mon_ami_server.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +# MonAMI Asterisk Manger Interface Server +# Asterisk AMI Emulator server thread +# (c) AMOOMA GmbH 2012 + +from threading import Thread +from log import ldebug, linfo, lwarn, lerror, lcritic +from time import sleep +from traceback import format_exc +from tcp_server import TCPServer +from mon_ami_handler import MonAMIHandler +import socket + +class MonAMIServer(Thread): + + def __init__(self, address=None, port=None, event_socket=None): + Thread.__init__(self) + self.runthread = True + self.port = port + self.address = address + self.event_socket = event_socket + self.handler_threads = {} + self.user_password_authentication = None + + def stop(self): + ldebug('thread stop', self) + ldebug('client connections: %s' % len(self.handler_threads), self) + for thread_id, handler_thread in self.handler_threads.items(): + if handler_thread.isAlive(): + handler_thread.stop() + self.runthread = False + + def register_handler_thread(self, handler_thread): + if handler_thread.isAlive(): + ldebug('registering handler thread %d ' % id(handler_thread), self) + self.handler_threads[id(handler_thread)] = handler_thread + else: + lwarn('handler thread passed away: %d' % id(handler_thread), self) + + + def deregister_handler_thread(self, handler_thread): + if id(handler_thread) in self.handler_threads: + ldebug('deregistering handler thread %d ' % id(handler_thread), self) + del self.handler_threads[id(handler_thread)] + else: + lwarn('handler thread %d not registered' % id(handler_thread), self) + + + def run(self): + ldebug('starting MonAMI server thread', self) + serversocket = TCPServer(self.address, self.port).listen() + #serversocket.setblocking(0) + + if not serversocket: + ldebug('server socket could not be bound', self) + return 1 + + while self.runthread: + try: + client_socket, client_address = serversocket.accept() + except socket.timeout as exception: + # Socket timeout occured + continue + except socket.error as exception: + lerror('socket error (%s): %s - ' % (exception, format_exc()), self) + sleep(1) + continue + except: + lerror('general error: %s - ' % format_exc(), self) + sleep(1) + continue + + ldebug('connected to %s:%d' % client_address, self) + + client_thread = MonAMIHandler(client_socket, client_address, self.event_socket) + client_thread.deregister_at_server = self.deregister_handler_thread + client_thread.user_password_authentication = self.user_password_authentication + client_thread.start() + if client_thread.isAlive(): + self.register_handler_thread(client_thread) + + ldebug('registered handler threads: %d' % len(self.handler_threads), self) + + ldebug('exiting MonAMI server thread', self) + diff --git a/misc/mon_ami/sqliter.py b/misc/mon_ami/sqliter.py new file mode 100644 index 0000000..5b03729 --- /dev/null +++ b/misc/mon_ami/sqliter.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -*- +# SQLite library + +import sqlite3 + +class SQLiteR(): + + def __init__(self, database = None): + self.db_name = database + if (self.db_name == None): + self.db_name = ':memory:' + self.db_conn = None + self.db_cursor = None + + def record_factory(self, cursor, row): + record = dict() + for index, column in enumerate(cursor.description): + record[column[0]] = row[index] + return record + + def connect(self, isolation_level = None): + try: + self.db_conn = sqlite3.connect(self.db_name) + self.db_conn.row_factory = self.record_factory + self.db_cursor = self.db_conn.cursor() + except: + return False + + self.db_conn.isolation_level = isolation_level + return True + + def disconnect(self): + try: + self.db_nonn.close() + except: + return False + return True + + def execute(self, query, parameters = []): + try: + return self.db_cursor.execute(query, parameters) + except: + return False + + def fetch_row(self): + return self.db_cursor.fetchone() + + def fetch_rows(self): + return self.db_cursor.fetchall() + + def execute_get_rows(self, query, parameters = []): + if (self.execute(query, parameters)): + return self.fetch_rows() + else: + return False + + def execute_get_row(self, query, parameters = []): + query = "%s LIMIT 1" % query + if (self.execute(query, parameters)): + return self.fetch_row() + else: + return False + + def execute_get_value(self, query, parameters = []): + row = self.execute_get_row(query, parameters) + if (row): + return row[0] + else: + return row + + def create_table(self, table, structure, primary_key = None): + columns = list() + for row in structure: + key, value = row.items()[0] + sql_type = "VARCHAR(255)" + sql_key = '' + if (key == primary_key): + sql_key = 'PRIMARY KEY' + type_r = value.split(':', 1) + type_n = type_r[0] + if (type_n == 'integer'): + sql_type = 'INTEGER' + elif (type_n == 'string'): + try: + sql_type = "VARCHAR(%s)" % type_r[1] + except IndexError, e: + sql_type = "VARCHAR(255)" + + columns.append('"%s" %s %s' % (key, sql_type, sql_key)) + + query = 'CREATE TABLE "%s" (%s)' % (table, ', '.join(columns)) + return self.execute(query) + + def save(self, table, row): + keys = row.keys() + query = 'INSERT OR REPLACE INTO "%s" (%s) VALUES (:%s)' % (table, ', '.join(keys), ', :'.join(keys)) + + return self.execute(query, row) + + def find_sql(self, table, rows = None): + values = list() + if (rows): + if (type(rows) == type(list())): + rows_list = rows + else: + rows_list = list() + rows_list.append(rows) + + query_parts = list() + + for row in rows_list: + statements = list() + for key, value in row.items(): + if (value == None): + statements.append("\"%s\" IS ?" % (key)) + else: + statements.append("\"%s\" = ?" % (key)) + values.append(value) + query_parts.append('(%s)' % ' AND '.join(statements)) + + query = 'SELECT * FROM "%s" WHERE %s' % (table, ' OR '.join(query_parts)) + else: + query = 'SELECT * FROM "%s"' % table + return query, values + + def find(self, table, row = None): + query, value = self.find_sql(table, row) + return self.execute_get_row(query, value) + + def findall(self, table, row = None): + query, values = self.find_sql(table, row) + return self.execute_get_rows(query, values) diff --git a/misc/mon_ami/tcp_server.py b/misc/mon_ami/tcp_server.py new file mode 100644 index 0000000..5536282 --- /dev/null +++ b/misc/mon_ami/tcp_server.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# MonAMI Asterisk Manger Interface Server +# TCP Server +# (c) AMOOMA GmbH 2012 + +import socket +from traceback import format_exc +from log import ldebug, linfo, lwarn, lerror, lcritic + +class TCPServer(): + + def __init__(self, address=None, port=None, timeout=1): + self.SOCKET_BACKLOG = 5 + self.port = port + self.address = address + self.socket_timeout = timeout + + def listen(self): + tcpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + tcpsocket.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 ) + + ldebug('binding server to %s:%d, timeout: %d' % (self.address, self.port, self.socket_timeout), self) + + try: + tcpsocket.bind((self.address, self.port)) + except ValueError as exception: + lerror('server socket address error: %s - %s' % (exception, format_exc()), self) + return False + except socket.error as exception: + lerror('server socket error (%d): %s - %s' % (exception[0], exception[1], format_exc()), self) + return False + except: + lerror('general server socket error: %s' % format_exc(), self) + return False + + tcpsocket.listen(self.SOCKET_BACKLOG) + tcpsocket.settimeout(self.socket_timeout) + + return tcpsocket diff --git a/misc/nginx/nginx.conf b/misc/nginx/nginx.conf new file mode 100644 index 0000000..c1c1a39 --- /dev/null +++ b/misc/nginx/nginx.conf @@ -0,0 +1,81 @@ + +user www-data; +worker_processes 1; + +#error_log logs/error.log; +#error_log logs/error.log notice; +#error_log logs/error.log info; + +#pid logs/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + passenger_root /usr/local/rvm/gems/ruby-1.9.2-p290/gems/passenger-3.0.11; + passenger_ruby /usr/local/rvm/wrappers/ruby-1.9.2-p290/ruby; + + include mime.types; + default_type application/octet-stream; + + #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + # '$status $body_bytes_sent "$http_referer" ' + # '"$http_user_agent" "$http_x_forwarded_for"'; + + #access_log logs/access.log main; + + sendfile on; + #tcp_nopush on; + + #keepalive_timeout 0; + keepalive_timeout 65; + + #gzip on; + + server { + listen 80; + server_name localhost; + root /opt/GS5/public; + passenger_enabled on; + } + + # another virtual host using mix of IP-, name-, and port-based configuration + # + #server { + # listen 8000; + # listen somename:8080; + # server_name somename alias another.alias; + + # location / { + # root html; + # index index.html index.htm; + # } + #} + + + # HTTPS server + # + #server { + # listen 443; + # server_name localhost; + + # ssl on; + # ssl_certificate cert.pem; + # ssl_certificate_key cert.key; + + # ssl_session_timeout 5m; + + # ssl_protocols SSLv2 SSLv3 TLSv1; + # ssl_ciphers HIGH:!aNULL:!MD5; + # ssl_prefer_server_ciphers on; + + # location / { + # root html; + # index index.html index.htm; + # } + #} + +} diff --git a/private_pub.ru b/private_pub.ru new file mode 100644 index 0000000..2db4acf --- /dev/null +++ b/private_pub.ru @@ -0,0 +1,8 @@ +# Run with: rackup private_pub.ru -s thin -E production +require "bundler/setup" +require "yaml" +require "faye" +require "private_pub" + +PrivatePub.load_config(File.expand_path("../config/private_pub.yml", __FILE__), ENV["RAILS_ENV"] || "development") +run PrivatePub.faye_app diff --git a/public/404.html b/public/404.html new file mode 100644 index 0000000..9a48320 --- /dev/null +++ b/public/404.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html> +<head> + <title>The page you were looking for doesn't exist (404)</title> + <style type="text/css"> + body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; } + div.dialog { + width: 25em; + padding: 0 4em; + margin: 4em auto 0 auto; + border: 1px solid #ccc; + border-right-color: #999; + border-bottom-color: #999; + } + h1 { font-size: 100%; color: #f00; line-height: 1.5em; } + </style> +</head> + +<body> + <!-- This file lives in public/404.html --> + <div class="dialog"> + <h1>The page you were looking for doesn't exist.</h1> + <p>You may have mistyped the address or the page may have moved.</p> + </div> +</body> +</html> diff --git a/public/422.html b/public/422.html new file mode 100644 index 0000000..83660ab --- /dev/null +++ b/public/422.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html> +<head> + <title>The change you wanted was rejected (422)</title> + <style type="text/css"> + body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; } + div.dialog { + width: 25em; + padding: 0 4em; + margin: 4em auto 0 auto; + border: 1px solid #ccc; + border-right-color: #999; + border-bottom-color: #999; + } + h1 { font-size: 100%; color: #f00; line-height: 1.5em; } + </style> +</head> + +<body> + <!-- This file lives in public/422.html --> + <div class="dialog"> + <h1>The change you wanted was rejected.</h1> + <p>Maybe you tried to change something you didn't have access to.</p> + </div> +</body> +</html> diff --git a/public/500.html b/public/500.html new file mode 100644 index 0000000..da74faf --- /dev/null +++ b/public/500.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html> +<head> + <title>We're sorry, but something went wrong (500)</title> + <style type="text/css"> + body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; } + div.dialog { + width: 25em; + padding: 0 4em; + margin: 4em auto 0 auto; + border: 1px solid #ccc; + border-right-color: #999; + border-bottom-color: #999; + } + h1 { font-size: 100%; color: #f00; line-height: 1.5em; } + </style> +</head> + +<body> + <!-- This file lives in public/500.html --> + <div class="dialog"> + <h1>We're sorry, but something went wrong.</h1> + <p>Please take a minute and report an issue at <a href="https://github.com/amooma/GS5/issues">https://github.com/amooma/GS5/issues</a> or contact your admin.</p> + </div> +</body> +</html> diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/public/favicon.ico diff --git a/public/images/fallback/mini_default.jpg b/public/images/fallback/mini_default.jpg Binary files differnew file mode 100644 index 0000000..42c9f2d --- /dev/null +++ b/public/images/fallback/mini_default.jpg diff --git a/public/images/fallback/mini_default_female.png b/public/images/fallback/mini_default_female.png Binary files differnew file mode 100644 index 0000000..bc54d26 --- /dev/null +++ b/public/images/fallback/mini_default_female.png diff --git a/public/images/fallback/mini_default_male.png b/public/images/fallback/mini_default_male.png Binary files differnew file mode 100644 index 0000000..b75e36c --- /dev/null +++ b/public/images/fallback/mini_default_male.png diff --git a/public/images/fallback/profile_default.jpg b/public/images/fallback/profile_default.jpg Binary files differnew file mode 100644 index 0000000..d807028 --- /dev/null +++ b/public/images/fallback/profile_default.jpg diff --git a/public/images/fallback/profile_default.psd b/public/images/fallback/profile_default.psd Binary files differnew file mode 100644 index 0000000..89b5b55 --- /dev/null +++ b/public/images/fallback/profile_default.psd diff --git a/public/images/fallback/profile_default_female.png b/public/images/fallback/profile_default_female.png Binary files differnew file mode 100644 index 0000000..614c4bc --- /dev/null +++ b/public/images/fallback/profile_default_female.png diff --git a/public/images/fallback/profile_default_male.png b/public/images/fallback/profile_default_male.png Binary files differnew file mode 100644 index 0000000..f7b44ff --- /dev/null +++ b/public/images/fallback/profile_default_male.png diff --git a/public/images/fallback/small_default.jpg b/public/images/fallback/small_default.jpg Binary files differnew file mode 100644 index 0000000..5a8cbf2 --- /dev/null +++ b/public/images/fallback/small_default.jpg diff --git a/public/images/fallback/small_default_female.png b/public/images/fallback/small_default_female.png Binary files differnew file mode 100644 index 0000000..b9de992 --- /dev/null +++ b/public/images/fallback/small_default_female.png diff --git a/public/images/fallback/small_default_male.png b/public/images/fallback/small_default_male.png Binary files differnew file mode 100644 index 0000000..e5d3434 --- /dev/null +++ b/public/images/fallback/small_default_male.png diff --git a/public/images/fallback/snom_caller_picture_default.jpg b/public/images/fallback/snom_caller_picture_default.jpg Binary files differnew file mode 100644 index 0000000..6bbcc83 --- /dev/null +++ b/public/images/fallback/snom_caller_picture_default.jpg diff --git a/public/images/fallback/snom_caller_picture_default_female.png b/public/images/fallback/snom_caller_picture_default_female.png Binary files differnew file mode 100644 index 0000000..da6a904 --- /dev/null +++ b/public/images/fallback/snom_caller_picture_default_female.png diff --git a/public/images/fallback/snom_caller_picture_default_male.png b/public/images/fallback/snom_caller_picture_default_male.png Binary files differnew file mode 100644 index 0000000..a783f8b --- /dev/null +++ b/public/images/fallback/snom_caller_picture_default_male.png diff --git a/public/images/snom/snom_300.jpg b/public/images/snom/snom_300.jpg Binary files differnew file mode 100644 index 0000000..f115b4b --- /dev/null +++ b/public/images/snom/snom_300.jpg diff --git a/public/images/snom/snom_320.jpg b/public/images/snom/snom_320.jpg Binary files differnew file mode 100644 index 0000000..8d83dd5 --- /dev/null +++ b/public/images/snom/snom_320.jpg diff --git a/public/images/snom/snom_360.jpg b/public/images/snom/snom_360.jpg Binary files differnew file mode 100644 index 0000000..d633d67 --- /dev/null +++ b/public/images/snom/snom_360.jpg diff --git a/public/images/snom/snom_370.jpg b/public/images/snom/snom_370.jpg Binary files differnew file mode 100644 index 0000000..2698586 --- /dev/null +++ b/public/images/snom/snom_370.jpg diff --git a/public/images/snom/snom_820.jpg b/public/images/snom/snom_820.jpg Binary files differnew file mode 100644 index 0000000..8306ea7 --- /dev/null +++ b/public/images/snom/snom_820.jpg diff --git a/public/images/snom/snom_821.jpg b/public/images/snom/snom_821.jpg Binary files differnew file mode 100644 index 0000000..8306ea7 --- /dev/null +++ b/public/images/snom/snom_821.jpg diff --git a/public/images/snom/snom_870.jpg b/public/images/snom/snom_870.jpg Binary files differnew file mode 100644 index 0000000..c0fa148 --- /dev/null +++ b/public/images/snom/snom_870.jpg diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..b9fe2d7 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,6 @@ +# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file +# +# Because it is a PBX we disallow any search engine by default. +# +User-Agent: * +Disallow: / diff --git a/script/agi_server b/script/agi_server new file mode 100755 index 0000000..0a0fd76 --- /dev/null +++ b/script/agi_server @@ -0,0 +1,35 @@ +#! /usr/bin/env ruby + +APP_PATH = File.expand_path('../../config/application', __FILE__) +require File.expand_path('../../config/boot', __FILE__) +require APP_PATH + +begin + Rails.application.require_environment! +rescue ActiveRecord::AdapterNotSpecified => e + $stderr.puts "No such Rails environment: \"#{Rails.env}\"." + exit 1 +end + +begin + require 'agi_server' + server_instance = AGIServer::server() + +rescue SignalException => e + case e.signo + when 1 + $stderr.puts "Signal 1 caught, exiting" + when 2 + $stderr.puts "Interrupt signal caught, exiting" + when 15 + $stderr.puts "Exit signal caught, exiting" + else + $stderr.print "#{e.class.to_s}" + $stderr.print " (Signal #{e.signo.to_s})" if e.respond_to?(:signo) && e.signo + end + $stderr.puts "" + exit (e.signo + 128) +end + +exit 0 + diff --git a/script/delayed_job b/script/delayed_job new file mode 100755 index 0000000..edf1959 --- /dev/null +++ b/script/delayed_job @@ -0,0 +1,5 @@ +#!/usr/bin/env ruby + +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment')) +require 'delayed/command' +Delayed::Command.new(ARGV).daemonize diff --git a/script/erd b/script/erd new file mode 100755 index 0000000..5ba938c --- /dev/null +++ b/script/erd @@ -0,0 +1,38 @@ +#! /bin/sh + +echo "" +echo "### Generating Dot file ..." +bundle exec \ + rake erd filetype=dot \ + attributes=content,foreign_keys \ + indirect=true \ + orientation=horizontal \ + notation=simple + +if which dot; then + echo "" + echo "### Generating PDF file (via dot) ..." + dot -Tpdf:quartz:quartz -o ERD.pdf ERD.dot +else + if which graphviz; then + echo "" + echo "### Generating PDF file (via graphviz) ..." + bundle exec \ + rake erd filetype=pdf \ + attributes=content,foreign_keys \ + indirect=true \ + orientation=horizontal \ + notation=simple + else + echo "No dot no PDF." + fi +fi + +if which dot; then + echo "" + echo "### Generating PNG file (via dot) ..." + dot -Tpng:quartz:quartz -o ERD.png ERD.dot +else + echo "No dot no PNG." +fi + diff --git a/script/fax_new b/script/fax_new new file mode 100755 index 0000000..8015da8 --- /dev/null +++ b/script/fax_new @@ -0,0 +1,111 @@ +#! /usr/bin/env ruby + +begin + +if !ARGV[0] or ARGV[0] == '' or !ARGV[10] or ARGV[10] == '' + $stderr.puts "usage: fax_new <fax_account_id> \ +<result_code> \ +<document_total_pages> \ +<document_transferred_pages> \ +<ecm_requested> \ +<ecm_used> \ +<image_resolution> \ +<remote_station_id> \ +<transfer_rate> \ +<transmission_time> \ +<document> \ +<caller_id_number> \ +<caller_id_name>" + exit 1 +end + +fax_arguments = { + :fax_account_id => ARGV[0], + :result_code => ARGV[1], + :document_total_pages => ARGV[2], + :document_transferred_pages => ARGV[3], + :ecm_requested => ARGV[4], + :ecm_used => ARGV[5], + :image_resolution => ARGV[6], + :remote_station_id => ARGV[7], + :transfer_rate => ARGV[8], + :transmission_time => ARGV[9], + :document => ARGV[10], + :caller_id_number => ARGV[11], + :caller_id_name => ARGV[12], +} + +APP_PATH = File.expand_path('../../config/application', __FILE__) +require File.expand_path('../../config/boot', __FILE__) +require APP_PATH + +begin + Rails.application.require_environment! +rescue ActiveRecord::AdapterNotSpecified => e + error "No such Rails environment: \"#{Rails.env}\"." + exit 1 +end + +TIFF_FUFFIX = ".tiff" +PDF_SUFFIX = ".pdf" + +tiff_file = fax_arguments[:document] + +if !tiff_file or !File.exists?( tiff_file ) + $stderr.puts "File \"#{tiff_file}\" does not exist" + exit 1 +end + +paper_size = "letter" +pdf_file = "#{File.dirname(tiff_file)}/#{File.basename(tiff_file, TIFF_FUFFIX)}#{PDF_SUFFIX}" + +system "tiff2pdf \\ + -o \"#{pdf_file}\" \\ + -p #{paper_size} \\ + -a \"#{fax_arguments[:remote_station_id]}\" \\ + -c \"AMOOMA Gemeinschaft version #{GEMEINSCHAFT_VERSION}\" \\ + -t \"#{fax_arguments[:remote_station_id]}\" \"#{tiff_file}\"" + +if !File.exists?( pdf_file ) + $stderr.puts "File \"#{pdf_file}\" does not exist" + exit 1 +end + +fax_account = FaxAccount.find(fax_arguments[:fax_account_id]) +if !fax_account + $stderr.puts "Fax account \"#{fax_arguments[:fax_account_id]}\" does not exist" + exit 1 +end + +fax_arguments[:document] = nil +fax_arguments[:success] = true +fax_arguments[:inbound] = true +fax_arguments[:sent_at] = Time.now +fax_arguments[:local_station_id] = fax_account.station_id +fax_arguments[:retry_counter] = 0 +fax_arguments[:fax_resolution_id] = FaxResolution.first.id +fax_arguments[:image_size] = File.size(tiff_file) +fax_arguments[:ecm_used] = fax_arguments[:ecm_used] == "on" ? true : false +fax_document = fax_account.fax_documents.build(fax_arguments) +fax_document.document = File.open(pdf_file) + +if ! fax_document.save + $stderr.puts "Error(s) creating fax document:" + $stderr.puts fax_document.errors.inspect + exit 1 +end + +fax_document.mark_as_inbound! + +Notifications.new_fax(fax_document).deliver + +exit 0 + +rescue SignalException => e + $stderr.print "#{e.class.to_s}" + $stderr.print " (Signal #{e.signo.to_s})" if e.respond_to?(:signo) && e.signo + $stderr.puts "" + exit 130 +end + +exit 0 diff --git a/script/fax_new.sh b/script/fax_new.sh new file mode 100755 index 0000000..3c98d41 --- /dev/null +++ b/script/fax_new.sh @@ -0,0 +1,4 @@ +#! /bin/bash +cd /opt/GS5/script/ +source /usr/local/rvm/scripts/rvm +/opt/GS5/script/fax_new "$@" diff --git a/script/logout_phones b/script/logout_phones new file mode 100755 index 0000000..2a1f266 --- /dev/null +++ b/script/logout_phones @@ -0,0 +1,29 @@ +#! /usr/bin/env ruby + +begin + +APP_PATH = File.expand_path('../../config/application', __FILE__) +require File.expand_path('../../config/boot', __FILE__) +require APP_PATH + +begin + Rails.application.require_environment! +rescue ActiveRecord::AdapterNotSpecified => e + error "No such Rails environment: \"#{Rails.env}\"." + exit 1 +end + +phones = Phone.where(:nightly_reboot => true).each do |phone| + if ! phone.user_logout + $stderr.puts "#{phone.id} #{phone.mac_address} - #{phone.errors.messages.inspect}" + end +end + +rescue SignalException => e + $stderr.print "#{e.class.to_s}" + $stderr.print " (Signal #{e.signo.to_s})" if e.respond_to?(:signo) && e.signo + $stderr.puts "" + exit 130 +end + +exit 0 diff --git a/script/logout_phones.sh b/script/logout_phones.sh new file mode 100755 index 0000000..2a1a54e --- /dev/null +++ b/script/logout_phones.sh @@ -0,0 +1,4 @@ +#! /bin/bash +cd /opt/GS5/script/ +source /usr/local/rvm/scripts/rvm +/opt/GS5/script/logout_phones $@ diff --git a/script/notes b/script/notes new file mode 100755 index 0000000..1285884 --- /dev/null +++ b/script/notes @@ -0,0 +1,25 @@ +#! /bin/sh + +# `rake notes` doesn't find everything and is too slow. + +#find . -type f \ +# | grep -vEe '/(\.git|\.DS_Store|\.idea/|\.project|doc/app/|script/notes|log/|debian/visual-inspection/|public/javascripts/controls\.js)' \ +# | xargs grep --color -IEe '# *(FIXME|TODO|OPTIMIZE)' +#exit 0 + +find . -not \( \ + -name '.svn' -prune -o \ + -name '.git' -prune -o \ + -name '.DS_Store' -prune -o \ + -name '.idea' -prune -o \ + -name '.project' -prune -o \ + -path '*/script/notes' -prune -o \ + -path '*/doc/app/*' -prune -o \ + -path '*/doc/html-onefile/*.html' -prune -o \ + -path '*/doc/docbook-xsl-*' -prune -o \ + -path '*/public/javascripts/controls.js' -prune -o \ + -path '*/log/*.log' -prune -o \ + -false \ + \) -type f -print0 \ + | xargs -0 grep --color -IEe '#[^#]*(FIXME|TODO|OPTIMIZE)\b.*' + diff --git a/script/rails b/script/rails new file mode 100755 index 0000000..5f16f40 --- /dev/null +++ b/script/rails @@ -0,0 +1,68 @@ +#!/usr/bin/env ruby +# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. + +APP_PATH = File.expand_path('../../config/application', __FILE__) +require File.expand_path('../../config/boot', __FILE__) + + +# # ---[ Custom stuff ]--------------------------------------{ +# # Start web server with SSL enabled. +# # http://www.nearinfinity.com/blogs/chris_rohr/configuring_webrick_to_use_ssl.html +# +# require 'rubygems' +# require 'rails/commands/server' +# require 'rack' +# +# # like WEBrick::Utils::getservername() +# def getservername() +# return 'abc' +# require 'socket' +# host = Socket::gethostname +# begin +# return Socket::gethostbyname(host)[0] +# rescue +# return host +# end +# end +# +# require 'webrick' +# require 'webrick/https' +# +# module Rails +# class Server < ::Rack::Server +# # http://api.rubyonrails.org/classes/Rails/Server.html#method-i-default_options +# def default_options +# super.merge({ +# :Port => 3001, +# # https://github.com/raesene/dradisframework/commit/705d067eb9eef9ac98de29439757a0b1102c15cc +# :DoNotReverseLookup => true, +# :environment => (ENV['RAILS_ENV'] || 'development').dup, +# :daemonize => false, +# :debugger => false, +# :pid => File.expand_path( 'tmp/pids/server.pid' ), +# :config => File.expand_path( 'config.ru' ), +# :SSLEnable => true, +# :SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE, +# :SSLPrivateKey => OpenSSL::PKey::RSA.new( +# File.open( 'misc/etc/ssl/amooma/server_key.pem' ).read), +# :SSLCertificate => OpenSSL::X509::Certificate.new( +# File.open( 'misc/etc/ssl/amooma/server_cert.pem' ).read), +# :SSLCertName => [[ 'CN', getservername() ]], +# }) +# end +# end +# end +# +# # require 'rack/handler' +# # Rack::Handler.class_eval do +# # def self.default( options = {} ) +# # Rack::Handler::Thin +# # end +# # end +# +# # ---------------------------------------------------------} +# + + +require 'rails/commands' + diff --git a/script/voicemail_new b/script/voicemail_new new file mode 100755 index 0000000..bfdf39f --- /dev/null +++ b/script/voicemail_new @@ -0,0 +1,61 @@ +#! /usr/bin/env ruby + +begin + +if !ARGV[0] or ARGV[0] == '' or !ARGV[1] or ARGV[1] == '' + $stderr.puts "usage: voicemail_new <account_name> <voicemail_uuid>" + exit 1 +end + + +APP_PATH = File.expand_path('../../config/application', __FILE__) +require File.expand_path('../../config/boot', __FILE__) +require APP_PATH + +begin + Rails.application.require_environment! +rescue ActiveRecord::AdapterNotSpecified => e + error "No such Rails environment: \"#{Rails.env}\"." + exit 1 +end + +voicemail_account_name = ARGV[0] +uuid = ARGV[1] + +message = FreeswitchVoicemailMsg.where(:username => voicemail_account_name, :uuid => uuid).first + +if ! message + $stderr.puts "Message \"#{uuid}\" does not exist" + exit 1 +end + +if !File.exists?( message.file_path ) + $stderr.puts "File \"#{message.file_path}\" does not exist" + exit 1 +end + +owner_account = SipAccount.where(:auth_name => voicemail_account_name).first +if ! owner_account + $stderr.puts "SipAccount \"#{voicemail_account_name}\" does not exist" + exit 1 +end + +sip_account = SipAccount.find_by_auth_name(message.username) +if sip_account && sip_account.sip_accountable.class == User + user = sip_account.sip_accountable + + if user.send_voicemail_as_email_attachment == true + if Notifications.new_voicemail(message).deliver + message.delete + end + end +end + +rescue SignalException => e + $stderr.print "#{e.class.to_s}" + $stderr.print " (Signal #{e.signo.to_s})" if e.respond_to?(:signo) && e.signo + $stderr.puts "" + exit 130 +end + +exit 0 diff --git a/script/voicemail_new.sh b/script/voicemail_new.sh new file mode 100755 index 0000000..dcb29f5 --- /dev/null +++ b/script/voicemail_new.sh @@ -0,0 +1,4 @@ +#! /bin/bash + +source /usr/local/rvm/scripts/rvm +/opt/GS5/script/voicemail_new $@ diff --git a/test/factories/api_rows.rb b/test/factories/api_rows.rb new file mode 100644 index 0000000..42428ae --- /dev/null +++ b/test/factories/api_rows.rb @@ -0,0 +1,17 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :row do + user_name "MyString" + last_name "MyString" + middle_name "MyString" + first_name "MyString" + office_phone_number "MyString" + internal_extension "MyString" + mobile_phone_number "MyString" + fax_phone_number "MyString" + email "MyString" + pin "MyString" + photo_file_name "MyString" + end +end diff --git a/test/factories/area_codes.rb b/test/factories/area_codes.rb new file mode 100644 index 0000000..87b2271 --- /dev/null +++ b/test/factories/area_codes.rb @@ -0,0 +1,7 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :area_code do |f| + f.sequence(:name) { |n| "AreaCode #{n}" } + f.sequence(:area_code) { |n| "#{n}" } + f.association :country +end
\ No newline at end of file diff --git a/test/factories/call_forwards.rb b/test/factories/call_forwards.rb new file mode 100644 index 0000000..d275e9d --- /dev/null +++ b/test/factories/call_forwards.rb @@ -0,0 +1,16 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :call_forward do |f| + f.association :phone_number + #OPTIMIZE Make sure that the phone_number's phone_numberable + # isn't a phone_book_entry but a sip_account. + #f.sequence( :call_forward_case_id ) { |n| CallForwardCase.where(:value => "always").first.id } + #f.association :call_forward_case + f.sequence( :call_forward_case_id ) { |n| 1 } + f.sequence( :destination ) { |n| "20#{n}" } + f.sequence( :to_voicemail ) { |n| false } + f.sequence( :timeout ) { |n| nil } + f.sequence( :source ) { |n| nil } + f.sequence( :depth ) { |n| 5 } + f.sequence( :active ) { |n| false } +end diff --git a/test/factories/conference_invitees.rb b/test/factories/conference_invitees.rb new file mode 100644 index 0000000..4e1eb68 --- /dev/null +++ b/test/factories/conference_invitees.rb @@ -0,0 +1,8 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :conference_invitee do |f| + f.phone_number { Factory.build(:phone_number) } + f.association :conference + f.speaker true + f.moderator false +end
\ No newline at end of file diff --git a/test/factories/conferences.rb b/test/factories/conferences.rb new file mode 100644 index 0000000..3c49a7a --- /dev/null +++ b/test/factories/conferences.rb @@ -0,0 +1,8 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :conference do |f| + f.sequence(:name) { |n| "Conference room #{n}" } + f.open_for_anybody true + f.association :conferenceable, :factory => :tenant + f.max_members 10 +end
\ No newline at end of file diff --git a/test/factories/countries.rb b/test/factories/countries.rb new file mode 100644 index 0000000..cc3fc88 --- /dev/null +++ b/test/factories/countries.rb @@ -0,0 +1,8 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :country do |f| + f.sequence(:name) { |n| "Country #{n}" } + f.sequence(:country_code) { |n| "#{n}" } + f.sequence(:international_call_prefix) { |n| "#{n}" } + f.sequence(:trunk_prefix) { |n| "#{n}" } +end
\ No newline at end of file diff --git a/test/factories/gemeinschaft_setups.rb b/test/factories/gemeinschaft_setups.rb new file mode 100644 index 0000000..ed69bc3 --- /dev/null +++ b/test/factories/gemeinschaft_setups.rb @@ -0,0 +1,8 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :gemeinschaft_setup do |f| + f.association :user + f.association :sip_domain + f.association :country + f.association :language +end
\ No newline at end of file diff --git a/test/factories/gui_function_memberships.rb b/test/factories/gui_function_memberships.rb new file mode 100644 index 0000000..c6f6d4a --- /dev/null +++ b/test/factories/gui_function_memberships.rb @@ -0,0 +1,10 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :gui_function_membership do + gui_function_id 1 + user_group_id 1 + activated false + output "MyString" + end +end diff --git a/test/factories/languages.rb b/test/factories/languages.rb new file mode 100644 index 0000000..29d2ece --- /dev/null +++ b/test/factories/languages.rb @@ -0,0 +1,6 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :language do |f| + f.name 'Deutsch' + f.code 'de' +end
\ No newline at end of file diff --git a/test/factories/manufacturers.rb b/test/factories/manufacturers.rb new file mode 100644 index 0000000..f091dd2 --- /dev/null +++ b/test/factories/manufacturers.rb @@ -0,0 +1,6 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :manufacturer do |f| + f.sequence(:name) { |n| "#{n}. manufacturer" } + f.sequence(:ieee_name) { |n| "#{n}. ieee" } +end
\ No newline at end of file diff --git a/test/factories/ouis.rb b/test/factories/ouis.rb new file mode 100644 index 0000000..3a90fd4 --- /dev/null +++ b/test/factories/ouis.rb @@ -0,0 +1,6 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :oui do |f| + f.association :manufacturer + f.sequence(:value) { |n| (n + 11184810).to_s(16).upcase } +end
\ No newline at end of file diff --git a/test/factories/phone_book_entries.rb b/test/factories/phone_book_entries.rb new file mode 100644 index 0000000..7427886 --- /dev/null +++ b/test/factories/phone_book_entries.rb @@ -0,0 +1,7 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :phone_book_entry do |f| + f.sequence(:last_name) { |n| "Lastname #{n}" } + f.sequence(:is_male) { |n| true } + f.association :phone_book +end diff --git a/test/factories/phone_books.rb b/test/factories/phone_books.rb new file mode 100644 index 0000000..4aa8d07 --- /dev/null +++ b/test/factories/phone_books.rb @@ -0,0 +1,6 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :phone_book do |f| + f.sequence(:name) { |n| "Phone book #{n}" } + f.association :phone_bookable, :factory => :user +end diff --git a/test/factories/phone_models.rb b/test/factories/phone_models.rb new file mode 100644 index 0000000..8e6a50f --- /dev/null +++ b/test/factories/phone_models.rb @@ -0,0 +1,6 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :phone_model do |f| + f.sequence(:name) { |n| "Phone Model #{n}" } + f.association :manufacturer +end diff --git a/test/factories/phone_number_ranges.rb b/test/factories/phone_number_ranges.rb new file mode 100644 index 0000000..cb2f7ee --- /dev/null +++ b/test/factories/phone_number_ranges.rb @@ -0,0 +1,6 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :phone_number_range do |f| + f.name INTERNAL_EXTENSIONS + f.association :phone_number_rangeable, :factory => :tenant +end diff --git a/test/factories/phone_numbers.rb b/test/factories/phone_numbers.rb new file mode 100644 index 0000000..74b43cc --- /dev/null +++ b/test/factories/phone_numbers.rb @@ -0,0 +1,7 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :phone_number do |f| + f.sequence(:name) { |n| "Name #{n}" } + f.sequence(:number) { |n| "(0)30 227 #{n}" } + f.association :phone_numberable, :factory => :phone_book_entry +end
\ No newline at end of file diff --git a/test/factories/phones.rb b/test/factories/phones.rb new file mode 100644 index 0000000..646d548 --- /dev/null +++ b/test/factories/phones.rb @@ -0,0 +1,15 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :phone do |f| + f.sequence(:mac_address) { |n| ('%06d' % n).to_s + ('%06d' % n).to_s } + f.association :phone_model + f.association :phoneable, :factory => :tenant + + # We have to make sure that the OUI is created as well. + f.after_build do |instance| + Factory.create(:oui, + :manufacturer => instance.phone_model.manufacturer, + :value => instance.mac_address.slice(0, 6) + ) + end +end
\ No newline at end of file diff --git a/test/factories/sip_accounts.rb b/test/factories/sip_accounts.rb new file mode 100644 index 0000000..2f91717 --- /dev/null +++ b/test/factories/sip_accounts.rb @@ -0,0 +1,17 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :sip_account do |f| + f.association :sip_accountable, :factory => :user + f.sequence(:auth_name) {|n| "auth_name#{n}" } + f.sequence(:caller_name) {|n| "Foo Account #{n}" } + f.sequence(:password) {|n| "12345678" } + + f.after_build do |sip_account| + if sip_account.tenant_id.blank? + tenant = sip_account.create_tenant(FactoryGirl.build(:tenant).attributes) + sip_domain = tenant.create_sip_domain(FactoryGirl.build(:sip_domain).attributes) + sip_account.tenant.tenant_memberships.create(:user_id => sip_account.sip_accountable.id) + sip_account.tenant_id = tenant.id + end + end +end diff --git a/test/factories/sip_domains.rb b/test/factories/sip_domains.rb new file mode 100644 index 0000000..347b6a6 --- /dev/null +++ b/test/factories/sip_domains.rb @@ -0,0 +1,6 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :sip_domain do |f| + f.sequence(:host ) {|n| "host#{n}.localdomain" } + f.sequence(:realm ) {|n| "host#{n}.localdomain" } +end diff --git a/test/factories/softkey_functions.rb b/test/factories/softkey_functions.rb new file mode 100644 index 0000000..6af7cac --- /dev/null +++ b/test/factories/softkey_functions.rb @@ -0,0 +1,7 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :softkey_function do + name "MyString" + end +end diff --git a/test/factories/tenant_memberships.rb b/test/factories/tenant_memberships.rb new file mode 100644 index 0000000..ee52367 --- /dev/null +++ b/test/factories/tenant_memberships.rb @@ -0,0 +1,6 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :tenant_membership do |f| + f.association :user + f.association :tenant +end
\ No newline at end of file diff --git a/test/factories/tenants.rb b/test/factories/tenants.rb new file mode 100644 index 0000000..9a62e93 --- /dev/null +++ b/test/factories/tenants.rb @@ -0,0 +1,8 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :tenant do |f| + f.sequence(:name) { |n| "Tenant #{n}" } + f.association :country + f.association :language +# f.association :sip_domain +end diff --git a/test/factories/user_group_memberships.rb b/test/factories/user_group_memberships.rb new file mode 100644 index 0000000..7904f2f --- /dev/null +++ b/test/factories/user_group_memberships.rb @@ -0,0 +1,14 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :user_group_membership do |f| + f.association :user_group + f.association :user + + # Make sure that the User is a member of the Tenant. + f.after_build do |instance| + Factory.create(:tenant_membership, + :tenant_id => instance.user_group.tenant.id, + :user_id => instance.user.id + ) + end +end
\ No newline at end of file diff --git a/test/factories/user_groups.rb b/test/factories/user_groups.rb new file mode 100644 index 0000000..9a448ec --- /dev/null +++ b/test/factories/user_groups.rb @@ -0,0 +1,6 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :user_group do |f| + f.sequence(:name) { |n| "UserGroup #{n}" } + f.association :tenant +end
\ No newline at end of file diff --git a/test/factories/users.rb b/test/factories/users.rb new file mode 100644 index 0000000..3d53141 --- /dev/null +++ b/test/factories/users.rb @@ -0,0 +1,11 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +Factory.define :user do |f| + f.sequence(:user_name) { |n| "User #{n}" } + f.sequence(:first_name) { |n| "John #{n}" } + f.sequence(:last_name) { |n| "Smith #{n}" } + f.sequence(:email) { |n| "john.smith#{n}@company.com" } + f.sequence(:password) { |n| "Testpassword#{n}" } + f.sequence(:password_confirmation) { |n| "Testpassword#{n}" } + f.association :language +end
\ No newline at end of file diff --git a/test/fixtures/.gitkeep b/test/fixtures/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixtures/.gitkeep diff --git a/test/functional/.gitkeep b/test/functional/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/functional/.gitkeep diff --git a/test/functional/access_authorizations_controller_test.rb b/test/functional/access_authorizations_controller_test.rb new file mode 100644 index 0000000..98761fd --- /dev/null +++ b/test/functional/access_authorizations_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class AccessAuthorizationsControllerTest < ActionController::TestCase + setup do + @access_authorization = access_authorizations(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:access_authorizations) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create access_authorization" do + assert_difference('AccessAuthorization.count') do + post :create, access_authorization: @access_authorization.attributes + end + + assert_redirected_to access_authorization_path(assigns(:access_authorization)) + end + + test "should show access_authorization" do + get :show, id: @access_authorization.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @access_authorization.to_param + assert_response :success + end + + test "should update access_authorization" do + put :update, id: @access_authorization.to_param, access_authorization: @access_authorization.attributes + assert_redirected_to access_authorization_path(assigns(:access_authorization)) + end + + test "should destroy access_authorization" do + assert_difference('AccessAuthorization.count', -1) do + delete :destroy, id: @access_authorization.to_param + end + + assert_redirected_to access_authorizations_path + end +end diff --git a/test/functional/acd_agents_controller_test.rb b/test/functional/acd_agents_controller_test.rb new file mode 100644 index 0000000..11355eb --- /dev/null +++ b/test/functional/acd_agents_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class AcdAgentsControllerTest < ActionController::TestCase + setup do + @acd_agent = acd_agents(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:acd_agents) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create acd_agent" do + assert_difference('AcdAgent.count') do + post :create, acd_agent: @acd_agent.attributes + end + + assert_redirected_to acd_agent_path(assigns(:acd_agent)) + end + + test "should show acd_agent" do + get :show, id: @acd_agent.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @acd_agent.to_param + assert_response :success + end + + test "should update acd_agent" do + put :update, id: @acd_agent.to_param, acd_agent: @acd_agent.attributes + assert_redirected_to acd_agent_path(assigns(:acd_agent)) + end + + test "should destroy acd_agent" do + assert_difference('AcdAgent.count', -1) do + delete :destroy, id: @acd_agent.to_param + end + + assert_redirected_to acd_agents_path + end +end diff --git a/test/functional/acd_callers_controller_test.rb b/test/functional/acd_callers_controller_test.rb new file mode 100644 index 0000000..960de7d --- /dev/null +++ b/test/functional/acd_callers_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class AcdCallersControllerTest < ActionController::TestCase + setup do + @acd_caller = acd_callers(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:acd_callers) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create acd_caller" do + assert_difference('AcdCaller.count') do + post :create, acd_caller: @acd_caller.attributes + end + + assert_redirected_to acd_caller_path(assigns(:acd_caller)) + end + + test "should show acd_caller" do + get :show, id: @acd_caller.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @acd_caller.to_param + assert_response :success + end + + test "should update acd_caller" do + put :update, id: @acd_caller.to_param, acd_caller: @acd_caller.attributes + assert_redirected_to acd_caller_path(assigns(:acd_caller)) + end + + test "should destroy acd_caller" do + assert_difference('AcdCaller.count', -1) do + delete :destroy, id: @acd_caller.to_param + end + + assert_redirected_to acd_callers_path + end +end diff --git a/test/functional/addresses_controller_test.rb b/test/functional/addresses_controller_test.rb new file mode 100644 index 0000000..b1f5c83 --- /dev/null +++ b/test/functional/addresses_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class AddressesControllerTest < ActionController::TestCase +# setup do +# @address = addresses(:one) +# end +# +# test "should get index" do +# get :index +# assert_response :success +# assert_not_nil assigns(:addresses) +# end +# +# test "should get new" do +# get :new +# assert_response :success +# end +# +# test "should create address" do +# assert_difference('Address.count') do +# post :create, address: @address.attributes +# end +# +# assert_redirected_to address_path(assigns(:address)) +# end +# +# test "should show address" do +# get :show, id: @address.to_param +# assert_response :success +# end +# +# test "should get edit" do +# get :edit, id: @address.to_param +# assert_response :success +# end +# +# test "should update address" do +# put :update, id: @address.to_param, address: @address.attributes +# assert_redirected_to address_path(assigns(:address)) +# end +# +# test "should destroy address" do +# assert_difference('Address.count', -1) do +# delete :destroy, id: @address.to_param +# end +# +# assert_redirected_to addresses_path +# end +end diff --git a/test/functional/api/rows_controller_test.rb b/test/functional/api/rows_controller_test.rb new file mode 100644 index 0000000..ca1926c --- /dev/null +++ b/test/functional/api/rows_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class Api::RowsControllerTest < ActionController::TestCase + setup do + @api_row = api_rows(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:api_rows) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create api_row" do + assert_difference('Api::Row.count') do + post :create, api_row: @api_row.attributes + end + + assert_redirected_to api_row_path(assigns(:api_row)) + end + + test "should show api_row" do + get :show, id: @api_row + assert_response :success + end + + test "should get edit" do + get :edit, id: @api_row + assert_response :success + end + + test "should update api_row" do + put :update, id: @api_row, api_row: @api_row.attributes + assert_redirected_to api_row_path(assigns(:api_row)) + end + + test "should destroy api_row" do + assert_difference('Api::Row.count', -1) do + delete :destroy, id: @api_row + end + + assert_redirected_to api_rows_path + end +end diff --git a/test/functional/automatic_call_distributors_controller_test.rb b/test/functional/automatic_call_distributors_controller_test.rb new file mode 100644 index 0000000..e8914fd --- /dev/null +++ b/test/functional/automatic_call_distributors_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class AutomaticCallDistributorsControllerTest < ActionController::TestCase + setup do + @automatic_call_distributor = automatic_call_distributors(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:automatic_call_distributors) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create automatic_call_distributor" do + assert_difference('AutomaticCallDistributor.count') do + post :create, automatic_call_distributor: @automatic_call_distributor.attributes + end + + assert_redirected_to automatic_call_distributor_path(assigns(:automatic_call_distributor)) + end + + test "should show automatic_call_distributor" do + get :show, id: @automatic_call_distributor.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @automatic_call_distributor.to_param + assert_response :success + end + + test "should update automatic_call_distributor" do + put :update, id: @automatic_call_distributor.to_param, automatic_call_distributor: @automatic_call_distributor.attributes + assert_redirected_to automatic_call_distributor_path(assigns(:automatic_call_distributor)) + end + + test "should destroy automatic_call_distributor" do + assert_difference('AutomaticCallDistributor.count', -1) do + delete :destroy, id: @automatic_call_distributor.to_param + end + + assert_redirected_to automatic_call_distributors_path + end +end diff --git a/test/functional/call_forwards_controller_test.rb b/test/functional/call_forwards_controller_test.rb new file mode 100644 index 0000000..0993623 --- /dev/null +++ b/test/functional/call_forwards_controller_test.rb @@ -0,0 +1,91 @@ +require 'test_helper' + +class CallForwardsControllerTest < ActionController::TestCase + + setup do + @user = Factory.create(:user) + + #@tenant = Factory.create(:tenant) + #@tenant.tenant_memberships.create(:user_id => @user.id) + #@user.update_attributes!(:current_tenant_id => @tenant.id) + + @sip_account = Factory.create( + :sip_account, + :sip_accountable => @user, + ) + @user.sip_accounts << @sip_account + @sip_account = @user.sip_accounts.last + + @phone_number = Factory.create( + :phone_number, + :phone_numberable => @sip_account, + ) + @sip_account.phone_numbers << @phone_number + @phone_number = @sip_account.phone_numbers.last + + @call_forward = Factory.create( + :call_forward, + :phone_number => @phone_number, + ) + @phone_number.call_forwards << @call_forward + @call_forward = @phone_number.call_forwards.last + end + + test "should get index" do + session[:user_id] = @user.id + get :index, + :phone_number_id => @phone_number.to_param + assert_response :success + assert_not_nil assigns(:call_forwards) + end + + test "should get new" do + get :new, + :phone_number_id => @phone_number.to_param + assert_response :success + end + + #TODO +# test "should create call_forward" do +# assert_difference('CallForward.count') do +# post :create, +# :phone_number_id => @phone_number.to_param, +# :call_forward => Factory.attributes_for( +# :call_forward +# ) +# end +# assert_redirected_to( phone_number_call_forward_path( @phone_number, @call_forward ) ) +# end + + test "should show call_forward" do + session[:user_id] = @user.id + get :show, + :phone_number_id => @phone_number.to_param, + :id => @call_forward.to_param + assert_response :success + end + + test "should get edit" do + get :edit, + :phone_number_id => @phone_number.to_param, + :id => @call_forward.to_param + assert_response :success + end + + test "should update call_forward" do + put :update, + :phone_number_id => @phone_number.to_param, + :id => @call_forward.to_param, call_forward: @call_forward.attributes + assert_redirected_to( phone_number_call_forward_path( @phone_number, @call_forward ) ) + end + + test "should destroy call_forward" do + assert_difference('CallForward.count', -1) do + delete :destroy, + :phone_number_id => @phone_number.to_param, + :id => @call_forward.to_param + end + assert_redirected_to( phone_number_call_forwards_path( @phone_number ) ) + end + +end diff --git a/test/functional/callthroughs_controller_test.rb b/test/functional/callthroughs_controller_test.rb new file mode 100644 index 0000000..330629c --- /dev/null +++ b/test/functional/callthroughs_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class CallthroughsControllerTest < ActionController::TestCase + setup do + @callthrough = callthroughs(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:callthroughs) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create callthrough" do + assert_difference('Callthrough.count') do + post :create, callthrough: @callthrough.attributes + end + + assert_redirected_to callthrough_path(assigns(:callthrough)) + end + + test "should show callthrough" do + get :show, id: @callthrough.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @callthrough.to_param + assert_response :success + end + + test "should update callthrough" do + put :update, id: @callthrough.to_param, callthrough: @callthrough.attributes + assert_redirected_to callthrough_path(assigns(:callthrough)) + end + + test "should destroy callthrough" do + assert_difference('Callthrough.count', -1) do + delete :destroy, id: @callthrough.to_param + end + + assert_redirected_to callthroughs_path + end +end diff --git a/test/functional/conference_invitees_controller_test.rb b/test/functional/conference_invitees_controller_test.rb new file mode 100644 index 0000000..72d2e2c --- /dev/null +++ b/test/functional/conference_invitees_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class ConferenceInviteesControllerTest < ActionController::TestCase + + setup do + @conference_invitee = Factory.create(:conference_invitee) + end + +# test "should get index" do +# get :index +# assert_response :success +# assert_not_nil assigns(:conference_invitees) +# end +# +# test "should get new" do +# get :new +# assert_response :success +# end +# +# test "should create conference_invitee" do +# assert_difference('ConferenceInvitee.count') do +# post :create, conference_invitee: @conference_invitee.attributes +# end +# assert_redirected_to conference_invitee_path(assigns(:conference_invitee)) +# end +# +# test "should show conference_invitee" do +# get :show, id: @conference_invitee.to_param +# assert_response :success +# end +# +# test "should get edit" do +# get :edit, id: @conference_invitee.to_param +# assert_response :success +# end +# +# test "should update conference_invitee" do +# put :update, id: @conference_invitee.to_param, conference_invitee: @conference_invitee.attributes +# assert_redirected_to conference_invitee_path(assigns(:conference_invitee)) +# end +# +# test "should destroy conference_invitee" do +# assert_difference('ConferenceInvitee.count', -1) do +# delete :destroy, id: @conference_invitee.to_param +# end +# assert_redirected_to conference_invitees_path +# end + +end diff --git a/test/functional/conferences_controller_test.rb b/test/functional/conferences_controller_test.rb new file mode 100644 index 0000000..954838b --- /dev/null +++ b/test/functional/conferences_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class ConferencesControllerTest < ActionController::TestCase + + setup do + @conference = Factory.create(:conference) + end + +# test "should get index" do +# get :index +# assert_response :success +# assert_not_nil assigns(:conferences) +# end +# +# test "should get new" do +# get :new +# assert_response :success +# end +# +# test "should create conference" do +# assert_difference('Conference.count') do +# post :create, conference: @conference.attributes +# end +# assert_redirected_to conference_path(assigns(:conference)) +# end +# +# test "should show conference" do +# get :show, id: @conference.to_param +# assert_response :success +# end +# +# test "should get edit" do +# get :edit, id: @conference.to_param +# assert_response :success +# end +# +# test "should update conference" do +# put :update, id: @conference.to_param, conference: @conference.attributes +# assert_redirected_to conference_path(assigns(:conference)) +# end +# +# test "should destroy conference" do +# assert_difference('Conference.count', -1) do +# delete :destroy, id: @conference.to_param +# end +# assert_redirected_to conferences_path +# end + +end diff --git a/test/functional/config_siemens_controller_test.rb b/test/functional/config_siemens_controller_test.rb new file mode 100644 index 0000000..949f776 --- /dev/null +++ b/test/functional/config_siemens_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class ConfigSiemensControllerTest < ActionController::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/functional/fax_accounts_controller_test.rb b/test/functional/fax_accounts_controller_test.rb new file mode 100644 index 0000000..64b781d --- /dev/null +++ b/test/functional/fax_accounts_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class FaxAccountsControllerTest < ActionController::TestCase + setup do + @fax_account = fax_accounts(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:fax_accounts) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create fax_account" do + assert_difference('FaxAccount.count') do + post :create, fax_account: @fax_account.attributes + end + + assert_redirected_to fax_account_path(assigns(:fax_account)) + end + + test "should show fax_account" do + get :show, id: @fax_account.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @fax_account.to_param + assert_response :success + end + + test "should update fax_account" do + put :update, id: @fax_account.to_param, fax_account: @fax_account.attributes + assert_redirected_to fax_account_path(assigns(:fax_account)) + end + + test "should destroy fax_account" do + assert_difference('FaxAccount.count', -1) do + delete :destroy, id: @fax_account.to_param + end + + assert_redirected_to fax_accounts_path + end +end diff --git a/test/functional/fax_documents_controller_test.rb b/test/functional/fax_documents_controller_test.rb new file mode 100644 index 0000000..0322c61 --- /dev/null +++ b/test/functional/fax_documents_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class FaxDocumentsControllerTest < ActionController::TestCase + setup do + @fax_document = fax_documents(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:fax_documents) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create fax_document" do + assert_difference('FaxDocument.count') do + post :create, fax_document: @fax_document.attributes + end + + assert_redirected_to fax_document_path(assigns(:fax_document)) + end + + test "should show fax_document" do + get :show, id: @fax_document.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @fax_document.to_param + assert_response :success + end + + test "should update fax_document" do + put :update, id: @fax_document.to_param, fax_document: @fax_document.attributes + assert_redirected_to fax_document_path(assigns(:fax_document)) + end + + test "should destroy fax_document" do + assert_difference('FaxDocument.count', -1) do + delete :destroy, id: @fax_document.to_param + end + + assert_redirected_to fax_documents_path + end +end diff --git a/test/functional/gemeinschaft_setups_controller_test.rb b/test/functional/gemeinschaft_setups_controller_test.rb new file mode 100644 index 0000000..b23a878 --- /dev/null +++ b/test/functional/gemeinschaft_setups_controller_test.rb @@ -0,0 +1,50 @@ +require 'test_helper' + +class GemeinschaftSetupsControllerTest < ActionController::TestCase + + setup do + @gemeinschaft_setup = Factory.build(:gemeinschaft_setup) + end + +# test "should get index" do +# get :index +# assert_response :success +# assert_not_nil assigns(:gemeinschaft_setups) +# end + + test "should get new" do + get :new + assert_response :success + end + +# test "should create gemeinschaft_setup" do +# assert_difference('GemeinschaftSetup.count') do +# post :create, +# gemeinschaft_setup: Factory.attributes_for(:gemeinschaft_setup) +# end +# assert_redirected_to gemeinschaft_setup_path(assigns(:gemeinschaft_setup)) +# end + +# test "should show gemeinschaft_setup" do +# get :show, id: @gemeinschaft_setup.to_param +# assert_response :success +# end + +# test "should get edit" do +# get :edit, id: @gemeinschaft_setup.to_param +# assert_response :success +# end + +# test "should update gemeinschaft_setup" do +# put :update, id: @gemeinschaft_setup.to_param, gemeinschaft_setup: @gemeinschaft_setup.attributes +# assert_redirected_to gemeinschaft_setup_path(assigns(:gemeinschaft_setup)) +# end + +# test "should destroy gemeinschaft_setup" do +# assert_difference('GemeinschaftSetup.count', -1) do +# delete :destroy, id: @gemeinschaft_setup.to_param +# end +# assert_redirected_to gemeinschaft_setups_path +# end + +end diff --git a/test/functional/gs_cluster_sync_log_entries_controller_test.rb b/test/functional/gs_cluster_sync_log_entries_controller_test.rb new file mode 100644 index 0000000..f8fa336 --- /dev/null +++ b/test/functional/gs_cluster_sync_log_entries_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class GsClusterSyncLogEntriesControllerTest < ActionController::TestCase + setup do + @gs_cluster_sync_log_entry = gs_cluster_sync_log_entries(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:gs_cluster_sync_log_entries) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create gs_cluster_sync_log_entry" do + assert_difference('GsClusterSyncLogEntry.count') do + post :create, gs_cluster_sync_log_entry: @gs_cluster_sync_log_entry.attributes + end + + assert_redirected_to gs_cluster_sync_log_entry_path(assigns(:gs_cluster_sync_log_entry)) + end + + test "should show gs_cluster_sync_log_entry" do + get :show, id: @gs_cluster_sync_log_entry.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @gs_cluster_sync_log_entry.to_param + assert_response :success + end + + test "should update gs_cluster_sync_log_entry" do + put :update, id: @gs_cluster_sync_log_entry.to_param, gs_cluster_sync_log_entry: @gs_cluster_sync_log_entry.attributes + assert_redirected_to gs_cluster_sync_log_entry_path(assigns(:gs_cluster_sync_log_entry)) + end + + test "should destroy gs_cluster_sync_log_entry" do + assert_difference('GsClusterSyncLogEntry.count', -1) do + delete :destroy, id: @gs_cluster_sync_log_entry.to_param + end + + assert_redirected_to gs_cluster_sync_log_entries_path + end +end diff --git a/test/functional/gs_nodes_controller_test.rb b/test/functional/gs_nodes_controller_test.rb new file mode 100644 index 0000000..f5349ed --- /dev/null +++ b/test/functional/gs_nodes_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class GsNodesControllerTest < ActionController::TestCase + setup do + @gs_node = gs_nodes(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:gs_nodes) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create gs_node" do + assert_difference('GsNode.count') do + post :create, gs_node: @gs_node.attributes + end + + assert_redirected_to gs_node_path(assigns(:gs_node)) + end + + test "should show gs_node" do + get :show, id: @gs_node.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @gs_node.to_param + assert_response :success + end + + test "should update gs_node" do + put :update, id: @gs_node.to_param, gs_node: @gs_node.attributes + assert_redirected_to gs_node_path(assigns(:gs_node)) + end + + test "should destroy gs_node" do + assert_difference('GsNode.count', -1) do + delete :destroy, id: @gs_node.to_param + end + + assert_redirected_to gs_nodes_path + end +end diff --git a/test/functional/gui_functions_controller_test.rb b/test/functional/gui_functions_controller_test.rb new file mode 100644 index 0000000..9a6326f --- /dev/null +++ b/test/functional/gui_functions_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class GuiFunctionsControllerTest < ActionController::TestCase + setup do + @gui_function = gui_functions(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:gui_functions) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create gui_function" do + assert_difference('GuiFunction.count') do + post :create, gui_function: @gui_function.attributes + end + + assert_redirected_to gui_function_path(assigns(:gui_function)) + end + + test "should show gui_function" do + get :show, id: @gui_function.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @gui_function.to_param + assert_response :success + end + + test "should update gui_function" do + put :update, id: @gui_function.to_param, gui_function: @gui_function.attributes + assert_redirected_to gui_function_path(assigns(:gui_function)) + end + + test "should destroy gui_function" do + assert_difference('GuiFunction.count', -1) do + delete :destroy, id: @gui_function.to_param + end + + assert_redirected_to gui_functions_path + end +end diff --git a/test/functional/hunt_group_members_controller_test.rb b/test/functional/hunt_group_members_controller_test.rb new file mode 100644 index 0000000..9830c99 --- /dev/null +++ b/test/functional/hunt_group_members_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class HuntGroupMembersControllerTest < ActionController::TestCase + setup do + @hunt_group_member = hunt_group_members(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:hunt_group_members) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create hunt_group_member" do + assert_difference('HuntGroupMember.count') do + post :create, hunt_group_member: @hunt_group_member.attributes + end + + assert_redirected_to hunt_group_member_path(assigns(:hunt_group_member)) + end + + test "should show hunt_group_member" do + get :show, id: @hunt_group_member.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @hunt_group_member.to_param + assert_response :success + end + + test "should update hunt_group_member" do + put :update, id: @hunt_group_member.to_param, hunt_group_member: @hunt_group_member.attributes + assert_redirected_to hunt_group_member_path(assigns(:hunt_group_member)) + end + + test "should destroy hunt_group_member" do + assert_difference('HuntGroupMember.count', -1) do + delete :destroy, id: @hunt_group_member.to_param + end + + assert_redirected_to hunt_group_members_path + end +end diff --git a/test/functional/hunt_groups_controller_test.rb b/test/functional/hunt_groups_controller_test.rb new file mode 100644 index 0000000..0234e12 --- /dev/null +++ b/test/functional/hunt_groups_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class HuntGroupsControllerTest < ActionController::TestCase + setup do + @hunt_group = hunt_groups(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:hunt_groups) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create hunt_group" do + assert_difference('HuntGroup.count') do + post :create, hunt_group: @hunt_group.attributes + end + + assert_redirected_to hunt_group_path(assigns(:hunt_group)) + end + + test "should show hunt_group" do + get :show, id: @hunt_group.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @hunt_group.to_param + assert_response :success + end + + test "should update hunt_group" do + put :update, id: @hunt_group.to_param, hunt_group: @hunt_group.attributes + assert_redirected_to hunt_group_path(assigns(:hunt_group)) + end + + test "should destroy hunt_group" do + assert_difference('HuntGroup.count', -1) do + delete :destroy, id: @hunt_group.to_param + end + + assert_redirected_to hunt_groups_path + end +end diff --git a/test/functional/manufacturers_controller_test.rb b/test/functional/manufacturers_controller_test.rb new file mode 100644 index 0000000..dad38e5 --- /dev/null +++ b/test/functional/manufacturers_controller_test.rb @@ -0,0 +1,77 @@ +require 'test_helper' + +class ManufacturersControllerTest < ActionController::TestCase + setup do + @tenant = Factory.create(:tenant) + @user = Factory.create(:user) + + @tenant.tenant_memberships.create(:user_id => @user.id) + + @user.update_attributes!(:current_tenant_id => @tenant.id) + + @manufacturer = Factory.create(:manufacturer) + + @expected_status_if_not_authorized = :redirect + end + + test "should not do anything for a normal user" do + session[:user_id] = @user.id + get :index + assert_response :redirect + + get :new + assert_response :redirect + + get :show, id: @manufacturer.to_param + assert_response :redirect + + get :edit, id: @manufacturer.to_param + assert_response :redirect + end + + # Maybe some sort of SuperUser Group should have access. + # Needs some more thinking. + # + # test "should get index" do + # session[:user_id] = @user.id + # get :index + # assert_response :success + # assert_not_nil assigns(:manufacturers) + # end + # + # test "should get new" do + # get :new + # assert_response :success + # end + # + # test "should create manufacturer" do + # assert_difference('Manufacturer.count') do + # post :create, manufacturer: Factory.build(:manufacturer).attributes + # end + # + # assert_redirected_to manufacturer_path(assigns(:manufacturer)) + # end + # + # test "should show manufacturer" do + # get :show, id: @manufacturer.to_param + # assert_response :success + # end + # + # test "should get edit" do + # get :edit, id: @manufacturer.to_param + # assert_response :success + # end + # + # test "should update manufacturer" do + # put :update, id: @manufacturer.to_param, manufacturer: @manufacturer.attributes + # assert_redirected_to manufacturer_path(assigns(:manufacturer)) + # end + # + # test "should destroy manufacturer" do + # assert_difference('Manufacturer.count', -1) do + # delete :destroy, id: @manufacturer.to_param + # end + # + # assert_redirected_to manufacturers_path + # end +end diff --git a/test/functional/notifications_test.rb b/test/functional/notifications_test.rb new file mode 100644 index 0000000..d84bda2 --- /dev/null +++ b/test/functional/notifications_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +class NotificationsTest < ActionMailer::TestCase + test "new_pin" do + mail = Notifications.new_pin + assert_equal "New pin", mail.subject + assert_equal ["to@example.org"], mail.to + assert_equal ["from@example.com"], mail.from + assert_match "Hi", mail.body.encoded + end + +end diff --git a/test/functional/page_controller_test.rb b/test/functional/page_controller_test.rb new file mode 100644 index 0000000..a5ae5ad --- /dev/null +++ b/test/functional/page_controller_test.rb @@ -0,0 +1,35 @@ +require 'test_helper' + +class PageControllerTest < ActionController::TestCase + + #test "on a fresh system you should not get index but be redirected to the setup wizard" do + test "should be redirected to setup wizard on a fresh system" do + session[:user_id] = nil + get :index + assert_redirected_to wizards_new_initial_setup_path + end + + + test "a logged in user should get index" do + @tenant = Factory.create(:tenant) + @user = Factory.create(:user) + + @tenant.users << @user + + session[:user_id] = @user.id + get :index + assert_response :success + end + + test "a logged out user should be redirected to the login" do + @tenant = Factory.create(:tenant) + @user = Factory.create(:user) + + @tenant.users << @user + + session[:user_id] = nil + get :index + assert_redirected_to log_in_path + end + +end diff --git a/test/functional/phone_book_entries_controller_test.rb b/test/functional/phone_book_entries_controller_test.rb new file mode 100644 index 0000000..81c5b61 --- /dev/null +++ b/test/functional/phone_book_entries_controller_test.rb @@ -0,0 +1,103 @@ +require 'test_helper' + +class PhoneBookEntriesControllerTest < ActionController::TestCase + + setup do + @user1 = Factory.create(:user) + pb = @user1.phone_books.first + @user1_phone_book_entry = Factory.create( + :phone_book_entry, + :phone_book_id => pb.id + ) + + @expected_status_if_not_authorized = :redirect + end + + test "should not get index (not logged in)" do + get :index + assert_response @expected_status_if_not_authorized + end + + test "should get index" do + session[:user_id] = @user1.id + get :index + assert_response :success + assert_not_nil assigns(:phone_book_entries) + end + + +# test "should get new" do +# get :new +# assert_response :success +# end + + test "should not have a route for new" do + assert_raises(ActionController::RoutingError) { + get :new + } + end + +# +# test "should create phone_book_entry" do +# assert_difference('PhoneBookEntry.count') do +# post :create, phone_book_entry: @user1_phone_book_entry.attributes +# end +# +# assert_redirected_to phone_book_entry_path(assigns(:phone_book_entry)) +# end +# + test "should not show phone_book_entry (not logged in)" do + get :show, id: @user1_phone_book_entry.to_param + assert_response @expected_status_if_not_authorized + end + + test "should show phone_book_entry without nesting" do + session[:user_id] = @user1.id + get :show, id: @user1_phone_book_entry.to_param + assert_response :success + end + + test "should show phone_book_entry nested in phone_book" do + session[:user_id] = @user1.id + get :show, phone_book_id: @user1_phone_book_entry.phone_book.id, id: @user1_phone_book_entry.to_param + assert_response :success + end +# +# test "should get edit" do +# get :edit, id: @user1_phone_book_entry.to_param +# assert_response :success +# end + + test "should not have a route for edit" do + assert_raises(ActionController::RoutingError) { + get :edit, id: @user1_phone_book_entry.to_param + } + end + +# +# test "should update phone_book_entry" do +# put :update, id: @user1_phone_book_entry.to_param, phone_book_entry: @user1_phone_book_entry.attributes +# assert_redirected_to phone_book_entry_path(assigns(:phone_book_entry)) +# end + + test "should not have a route for update" do + assert_raises(ActionController::RoutingError) { + put :update, id: @user1_phone_book_entry.to_param, phone_book_entry: @user1_phone_book_entry.attributes + } + end + +# +# test "should destroy phone_book_entry" do +# assert_difference('PhoneBookEntry.count', -1) do +# delete :destroy, id: @user1_phone_book_entry.to_param +# end +# assert_redirected_to phone_book_entries_path +# end + + test "should not have a route for destroy" do + assert_raises(ActionController::RoutingError) { + delete :destroy, id: @user1_phone_book_entry.to_param + } + end + +end diff --git a/test/functional/phone_books_controller_test.rb b/test/functional/phone_books_controller_test.rb new file mode 100644 index 0000000..a00f597 --- /dev/null +++ b/test/functional/phone_books_controller_test.rb @@ -0,0 +1,71 @@ +require 'test_helper' + +class PhoneBooksControllerTest < ActionController::TestCase + setup do + @tenant = Factory.create(:tenant) + @admins = @tenant.user_groups.find_or_create_by_name('Admins') + @users = @tenant.user_groups.find_or_create_by_name('Users') + @user = Factory.create(:user) + + @tenant.users << @user + @users.users << @user + + @personal_phone_book = Factory.create(:phone_book, + :phone_bookable_type => @user.class.to_s, + :phone_bookable_id => @user.id + ) + phone_book_entry = Factory.create(:phone_book_entry) + @personal_phone_book.phone_book_entries << phone_book_entry + + @expected_status_if_not_authorized = :redirect + + session[:user_id] = @user.id + end + + test "should get index" do + get :index, user_id: @user.id + assert_response :success + assert_not_nil assigns(:phone_books) + end + + test "should get new" do + get :new, user_id: @user.id + assert_response :success + end + + test "should create phone_book" do + phone_book2 = Factory.build(:phone_book, + :phone_bookable_type => @user.class.to_s, + :phone_bookable_id => @user.id + ) + assert_difference('PhoneBook.count') do + post :create, phone_book: phone_book2.attributes, user_id: @user.id + end + assert_redirected_to( user_phone_book_path( @user, assigns(:phone_book))) + end + + test "should show phone_book" do + get :show, id: @personal_phone_book.to_param, user_id: @user.id + assert_response :success + end + + test "should get edit" do + get :edit, id: @personal_phone_book.to_param, user_id: @user.id + assert_response :success + end + + test "should update phone_book" do + put :update, id: @personal_phone_book.to_param, phone_book: @personal_phone_book.attributes, user_id: @user.id + assert_redirected_to( user_phone_book_path(@user, assigns(:phone_book))) + end + + # TODO: Fix this test + # + # test "should destroy phone_book" do + # assert_difference('PhoneBook.count', -1) do + # delete :destroy, id: @personal_phone_book.to_param, user_id: @user.id + # end + # assert_redirected_to( user_phone_books_path( @user )) + # end + +end diff --git a/test/functional/phone_models_controller_test.rb b/test/functional/phone_models_controller_test.rb new file mode 100644 index 0000000..2d1a87a --- /dev/null +++ b/test/functional/phone_models_controller_test.rb @@ -0,0 +1,143 @@ +require 'test_helper' + +class PhoneModelsControllerTest < ActionController::TestCase + + setup do + # Create a tenant: + @tenant = Factory.create(:tenant) + + # Create a User who is member of the Tenant but has no special rights: + @user = Factory.create(:user) + @tenant.tenant_memberships.create(:user_id => @user.id) + @user.update_attributes!(:current_tenant_id => @tenant.id) + + # Create a User who is member of the Tenant and has super admin rights: + @super_admin = Factory.create(:user) + @tenant.tenant_memberships.create(:user_id => @super_admin.id) + @super_admin.update_attributes!(:current_tenant_id => @tenant.id) + + # Create a PhoneModel + # + @phone_model = Factory.create(:phone_model) + end + + [ '@user.id', '' ].each do |user_id_code| + # Note: Do *not* actually create the user outside of tests. + + explanation = user_id_code.blank? ? + "if not logged in" : + "if logged in as an ordinary user" + + test "should not get index #{explanation}" do + session[:user_id] = eval( user_id_code ) + get :index, manufacturer_id: @phone_model.manufacturer_id + assert_response :redirect + end + + test "should not get new #{explanation}" do + session[:user_id] = eval( user_id_code ) + get :new, manufacturer_id: @phone_model.manufacturer_id + assert_response :redirect + end + + test "should not create phone_model #{explanation}" do + session[:user_id] = eval( user_id_code ) + + assert_no_difference('PhoneModel.count') do + post :create, manufacturer_id: @phone_model.manufacturer_id, phone_model: Factory.build(:phone_model, + :manufacturer_id => @phone_model.manufacturer_id).attributes + end + end + + test "should not show phone_model #{explanation}" do + session[:user_id] = eval( user_id_code ) + get :show, manufacturer_id: @phone_model.manufacturer_id, id: @phone_model.to_param + assert_response :redirect + end + + test "should not get edit #{explanation}" do + session[:user_id] = eval( user_id_code ) + get :edit, manufacturer_id: @phone_model.manufacturer_id, id: @phone_model.to_param + assert_response :redirect + end + + test "should not update phone_model #{explanation}" do + session[:user_id] = eval( user_id_code ) + + # save the old name: + old_name = PhoneModel.find(@phone_model.id).name + # try to make an update: + put :update, manufacturer_id: @phone_model.manufacturer_id, id: @phone_model.to_param, phone_model: @phone_model.attributes.merge({ + 'name' => @phone_model.name.reverse + }) + # check that the update didn't work: + assert_equal old_name, PhoneModel.find(@phone_model.id).name + + assert_response :redirect + end + + test "should not destroy phone_model #{explanation}" do + session[:user_id] = eval( user_id_code ) + + assert_no_difference('PhoneModel.count') do + delete :destroy, manufacturer_id: @phone_model.manufacturer_id, id: @phone_model.to_param + end + assert_response :redirect + end + + end + + + test "should get index as super admin" do + session[:user_id] = @super_admin.id + get :index, manufacturer_id: @phone_model.manufacturer_id + assert_response :success + assert_not_nil assigns(:phone_models) + end + + test "should get new as super admin" do + session[:user_id] = @super_admin.id + get :new, manufacturer_id: @phone_model.manufacturer_id + assert_response :success + end + + + # # We don't have access to manufacturer_id. We'll need to + # # add routes first. + # test "should create phone_model as super admin" do + # assert_difference('PhoneModel.count') do + # post :create, phone_model: Factory.build(:phone_model, + # :manufacturer_id => @phone_model.manufacturer_id).attributes + # end + # + # assert_redirected_to manufacturer_phone_model_path( @phone_model.manufacturer_id, assigns(:phone_model)) + # end + + test "should show phone_model as super admin" do + session[:user_id] = @super_admin.id + get :show, manufacturer_id: @phone_model.manufacturer_id, id: @phone_model.to_param + assert_response :success + end + + test "should get edit as super admin" do + session[:user_id] = @super_admin.id + get :edit, manufacturer_id: @phone_model.manufacturer_id, id: @phone_model.to_param + assert_response :success + end + + test "should update phone_model as super admin" do + session[:user_id] = @super_admin.id + put :update, manufacturer_id: @phone_model.manufacturer_id, id: @phone_model.to_param, phone_model: @phone_model.attributes + assert_redirected_to manufacturer_phone_model_path( @phone_model.manufacturer_id, assigns(:phone_model)) + end + + test "should destroy phone_model as super admin" do + session[:user_id] = @super_admin.id + assert_difference('PhoneModel.count', -1) do + delete :destroy, manufacturer_id: @phone_model.manufacturer_id, id: @phone_model.to_param + end + + assert_redirected_to manufacturer_phone_models_path( @phone_model.manufacturer_id ) + end + +end diff --git a/test/functional/phone_number_ranges_controller_test.rb b/test/functional/phone_number_ranges_controller_test.rb new file mode 100644 index 0000000..39f28c4 --- /dev/null +++ b/test/functional/phone_number_ranges_controller_test.rb @@ -0,0 +1,78 @@ +require 'test_helper' + +class PhoneNumberRangesControllerTest < ActionController::TestCase + + #TODO Uncomment tests once the views are implemented. + + setup do + @phone_number_range = Factory.create(:phone_number_range) + end + + test "should get index" do + get :index, + :"#{@phone_number_range.phone_number_rangeable_type.underscore}_id" => @phone_number_range.phone_number_rangeable.try(:to_param) + assert_response :success + assert_not_nil assigns(:phone_number_ranges) + end + +# test "should get new" do +# get :new, +# :"#{@phone_number_range.phone_number_rangeable_type.underscore}_id" => @phone_number_range.phone_number_rangeable.try(:to_param) +# assert_response :success +# end + +# test "should create phone_number_range" do +# assert_difference('PhoneNumberRange.count') do +# post :create, +# :"#{@phone_number_range.phone_number_rangeable_type.underscore}_id" => @phone_number_range.phone_number_rangeable.try(:to_param), +# :phone_number_range => @phone_number_range.attributes +# end +# assert_redirected_to( +# method( :"#{@phone_number_range.phone_number_rangeable_type.underscore}_phone_number_range_path" ).( +# @phone_number_range.phone_number_rangeable, +# @phone_number_range +# ) +# ) +# end + +# test "should show phone_number_range" do +# get :show, +# :"#{@phone_number_range.phone_number_rangeable_type.underscore}_id" => @phone_number_range.phone_number_rangeable.try(:to_param), +# :id => @phone_number_range.to_param +# assert_response :success +# end + +# test "should get edit" do +# get :edit, +# :"#{@phone_number_range.phone_number_rangeable_type.underscore}_id" => @phone_number_range.phone_number_rangeable.try(:to_param), +# :id => @phone_number_range.to_param +# assert_response :success +# end + + test "should update phone_number_range" do + put :update, + :"#{@phone_number_range.phone_number_rangeable_type.underscore}_id" => @phone_number_range.phone_number_rangeable.try(:to_param), + :id => @phone_number_range.to_param, + :phone_number_range => @phone_number_range.attributes +# assert_redirected_to( +# method( :"#{@phone_number_range.phone_number_rangeable_type.underscore}_phone_number_range_path" ).( +# @phone_number_range.phone_number_rangeable, +# @phone_number_range +# ) +# ) + end + +# test "should destroy phone_number_range" do +# assert_difference('PhoneNumberRange.count', -1) do +# delete :destroy, +# :"#{@phone_number_range.phone_number_rangeable_type.underscore}_id" => @phone_number_range.phone_number_rangeable.try(:to_param), +# :id => @phone_number_range.to_param +# end +# assert_redirected_to( +# method( :"#{@phone_number_range.phone_number_rangeable_type.underscore}_phone_number_ranges_path" ).( +# @phone_number_range.phone_number_rangeable +# ) +# ) +# end + +end diff --git a/test/functional/phone_numbers_controller_test.rb b/test/functional/phone_numbers_controller_test.rb new file mode 100644 index 0000000..ab0a4b2 --- /dev/null +++ b/test/functional/phone_numbers_controller_test.rb @@ -0,0 +1,115 @@ +require 'test_helper' + +class PhoneNumbersControllerTest < ActionController::TestCase + + setup do + @tenant = Factory.create(:tenant) + @user = Factory.create(:user) + + @tenant.tenant_memberships.create(:user_id => @user.id) + + @user.update_attributes!(:current_tenant_id => @tenant.id) + + @private_phone_book = @user.phone_books.first + + @private_phone_book_entry = Factory.create( + :phone_book_entry, + :phone_book => @private_phone_book + ) + @phone_number = Factory.create( + :phone_number, + :phone_numberable => @private_phone_book_entry + ) + + @expected_status_if_not_authorized = :redirect + end + + + test "should get index" do + session[:user_id] = @user.id + get :index, phone_book_entry_id: @private_phone_book_entry.id + assert_response :success + assert_not_nil assigns(:phone_numbers) + end + + test "should not get index (not logged in)" do + get :index, phone_book_entry_id: @private_phone_book_entry.id + assert_response @expected_status_if_not_authorized + end + + test "should get new" do + session[:user_id] = @user.id + get :new, phone_book_entry_id: @private_phone_book_entry.id + assert_response :success + end + + # test "should not get new (not logged in)" do + # get :new, phone_book_entry_id: @private_phone_book_entry.id + # assert_response @expected_status_if_not_authorized + # end + + # test "should create phone_number" do + # session[:user_id] = @user.id + # assert_difference('PhoneNumber.count') do + # post :create, phone_book_entry_id: @private_phone_book_entry.id, phone_number: @phone_number.attributes + # end + # assert_redirected_to( phone_book_entry_phone_number_path( assigns(:phone_number))) + # end + # + # test "should not create phone_number (not logged in)" do + # assert_no_difference('PhoneNumber.count') do + # post :create, phone_book_entry_id: @private_phone_book_entry.id, phone_number: @phone_number.attributes + # end + # assert_response @expected_status_if_not_authorized + # end + + test "should show phone_number" do + session[:user_id] = @user.id + get :show, phone_book_entry_id: @private_phone_book_entry.id, id: @phone_number.to_param + assert_response :success + end + + test "should not show phone_number (not logged in)" do + get :show, phone_book_entry_id: @private_phone_book_entry.id, id: @phone_number.to_param + assert_response @expected_status_if_not_authorized + end + + + test "should get edit" do + session[:user_id] = @user.id + get :edit, phone_book_entry_id: @private_phone_book_entry.id, id: @phone_number.to_param + assert_response :success + end + + test "should not get edit (not logged in)" do + get :edit, phone_book_entry_id: @private_phone_book_entry.id, id: @phone_number.to_param + assert_response @expected_status_if_not_authorized + end + + test "should update phone_number" do + session[:user_id] = @user.id + put :update, phone_book_entry_id: @private_phone_book_entry.id, id: @phone_number.to_param, phone_number: @phone_number.attributes + assert_redirected_to( phone_book_entry_phone_number_path( assigns(:phone_number))) + end + + test "should not update phone_number (not logged in)" do + put :update, phone_book_entry_id: @private_phone_book_entry.id, id: @phone_number.to_param, phone_number: @phone_number.attributes + assert_response @expected_status_if_not_authorized + end + + test "should destroy phone_number" do + session[:user_id] = @user.id + assert_difference('PhoneNumber.count', -1) do + delete :destroy, phone_book_entry_id: @private_phone_book_entry.id, id: @phone_number.to_param + end + assert_redirected_to( phone_book_entry_phone_numbers_path() ) + end + + test "should not destroy phone_number (not logged in)" do + assert_no_difference('PhoneNumber.count') do + delete :destroy, phone_book_entry_id: @private_phone_book_entry.id, id: @phone_number.to_param + end + assert_response @expected_status_if_not_authorized + end + +end diff --git a/test/functional/phone_sip_accounts_controller_test.rb b/test/functional/phone_sip_accounts_controller_test.rb new file mode 100644 index 0000000..37a3219 --- /dev/null +++ b/test/functional/phone_sip_accounts_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class PhoneSipAccountsControllerTest < ActionController::TestCase + setup do + @phone_sip_account = phone_sip_accounts(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:phone_sip_accounts) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create phone_sip_account" do + assert_difference('PhoneSipAccount.count') do + post :create, phone_sip_account: @phone_sip_account.attributes + end + + assert_redirected_to phone_sip_account_path(assigns(:phone_sip_account)) + end + + test "should show phone_sip_account" do + get :show, id: @phone_sip_account.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @phone_sip_account.to_param + assert_response :success + end + + test "should update phone_sip_account" do + put :update, id: @phone_sip_account.to_param, phone_sip_account: @phone_sip_account.attributes + assert_redirected_to phone_sip_account_path(assigns(:phone_sip_account)) + end + + test "should destroy phone_sip_account" do + assert_difference('PhoneSipAccount.count', -1) do + delete :destroy, id: @phone_sip_account.to_param + end + + assert_redirected_to phone_sip_accounts_path + end +end diff --git a/test/functional/phones_controller_test.rb b/test/functional/phones_controller_test.rb new file mode 100644 index 0000000..1b0aa55 --- /dev/null +++ b/test/functional/phones_controller_test.rb @@ -0,0 +1,55 @@ +require 'test_helper' + +class PhonesControllerTest < ActionController::TestCase + # TODO nil und normaler User duerfen nichts. + + #TODO Uncomment tests once the route has been implemented. + +# setup do +# @phone = Factory.create(:phone) +# end +# +# test "should get index" do +# get :index +# assert_response :success +# assert_not_nil assigns(:phones) +# end +# +# test "should get new" do +# get :new +# assert_response :success +# end +# +# # Would have to be a nested route to work. +# # +# # test "should create phone" do +# # assert_difference('Phone.count') do +# # post :create, phone: Factory.build(:phone, :phone_model_id => @phone.phone_model_id).attributes +# # end +# # +# # assert_redirected_to phone_path(assigns(:phone)) +# # end +# +# test "should show phone" do +# get :show, id: @phone.to_param +# assert_response :success +# end +# +# test "should get edit" do +# get :edit, id: @phone.to_param +# assert_response :success +# end +# +# test "should update phone" do +# put :update, id: @phone.to_param, phone: @phone.attributes +# assert_redirected_to phone_path(assigns(:phone)) +# end +# +# test "should destroy phone" do +# assert_difference('Phone.count', -1) do +# delete :destroy, id: @phone.to_param +# end +# +# assert_redirected_to phones_path +# end +end diff --git a/test/functional/phones_sip_accounts_controller_test.rb b/test/functional/phones_sip_accounts_controller_test.rb new file mode 100644 index 0000000..967b8fe --- /dev/null +++ b/test/functional/phones_sip_accounts_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class PhonesSipAccountsControllerTest < ActionController::TestCase + + setup do + @phones_sip_account = Factory.create(:phones_sip_account) + end + +# test "should get index" do +# get :index +# assert_response :success +# assert_not_nil assigns(:phones_sip_accounts) +# end +# +# test "should get new" do +# get :new +# assert_response :success +# end +# +# test "should create phones_sip_account" do +# assert_difference('PhonesSipAccount.count') do +# post :create, phones_sip_account: @phones_sip_account.attributes +# end +# assert_redirected_to phones_sip_account_path(assigns(:phones_sip_account)) +# end +# +# test "should show phones_sip_account" do +# get :show, id: @phones_sip_account.to_param +# assert_response :success +# end +# +# test "should get edit" do +# get :edit, id: @phones_sip_account.to_param +# assert_response :success +# end +# +# test "should update phones_sip_account" do +# put :update, id: @phones_sip_account.to_param, phones_sip_account: @phones_sip_account.attributes +# assert_redirected_to phones_sip_account_path(assigns(:phones_sip_account)) +# end +# +# test "should destroy phones_sip_account" do +# assert_difference('PhonesSipAccount.count', -1) do +# delete :destroy, id: @phones_sip_account.to_param +# end +# assert_redirected_to phones_sip_accounts_path +# end + +end diff --git a/test/functional/ringtones_controller_test.rb b/test/functional/ringtones_controller_test.rb new file mode 100644 index 0000000..f0f04f6 --- /dev/null +++ b/test/functional/ringtones_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class RingtonesControllerTest < ActionController::TestCase + setup do + @ringtone = ringtones(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:ringtones) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create ringtone" do + assert_difference('Ringtone.count') do + post :create, ringtone: @ringtone.attributes + end + + assert_redirected_to ringtone_path(assigns(:ringtone)) + end + + test "should show ringtone" do + get :show, id: @ringtone.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @ringtone.to_param + assert_response :success + end + + test "should update ringtone" do + put :update, id: @ringtone.to_param, ringtone: @ringtone.attributes + assert_redirected_to ringtone_path(assigns(:ringtone)) + end + + test "should destroy ringtone" do + assert_difference('Ringtone.count', -1) do + delete :destroy, id: @ringtone.to_param + end + + assert_redirected_to ringtones_path + end +end diff --git a/test/functional/sessions_controller_test.rb b/test/functional/sessions_controller_test.rb new file mode 100644 index 0000000..d30ebc3 --- /dev/null +++ b/test/functional/sessions_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class SessionsControllerTest < ActionController::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/functional/sip_accounts_controller_test.rb b/test/functional/sip_accounts_controller_test.rb new file mode 100644 index 0000000..5c97dd2 --- /dev/null +++ b/test/functional/sip_accounts_controller_test.rb @@ -0,0 +1,76 @@ +require 'test_helper' + +class SipAccountsControllerTest < ActionController::TestCase + + setup do + @tenant = Factory.create(:tenant) + @user = Factory.create(:user) + @tenant.tenant_memberships.create(:user_id => @user.id) + @sip_account = @user.sip_accounts.create( Factory.build(:sip_account).attributes ) + + @parent_param = @sip_account.sip_accountable_type.foreign_key.to_sym + @parent_id = @sip_account.sip_accountable.id + end + + test "should get index" do + session[:user_id] = @user.id + get :index, + @parent_param => @parent_id + assert_response :success + assert_not_nil assigns(:sip_accounts) + end + + test "should get new" do + session[:user_id] = @user.id + get :new, + @parent_param => @parent_id + assert_response :success + end + + test "should create sip_account" do + session[:user_id] = @user.id + assert_difference('SipAccount.count') do + post :create, + @parent_param => @parent_id, + sip_account: Factory.attributes_for(:sip_account) + end + end + + test "should show sip_account" do + session[:user_id] = @user.id + get :show, + @parent_param => @parent_id, + id: @sip_account.to_param + assert_response :success + end + + test "should get edit" do + session[:user_id] = @user.id + get :edit, + @parent_param => @parent_id, + id: @sip_account.to_param + assert_response :success + end + + test "should update sip_account" do + session[:user_id] = @user.id + put :update, + @parent_param => @parent_id, + id: @sip_account.to_param, + sip_account: @sip_account.attributes + # TODO Find the right redirect/answer. + #assert_redirected_to method( :"#{@sip_account.sip_accountable_type.underscore}_sip_account_path" ).( @sip_account.sip_accountable, @sip_account ) + end + + test "should destroy sip_account" do + session[:user_id] = @user.id + assert_difference('SipAccount.count', -1) do + delete :destroy, + @parent_param => @parent_id, + id: @sip_account.to_param + end + # TODO Find the right redirect/answer. + #assert_redirected_to method( :"#{@sip_account.sip_accountable_type.underscore}_sip_accounts_path" ).( @sip_account.sip_accountable ) + end + +end diff --git a/test/functional/sip_domains_controller_test.rb b/test/functional/sip_domains_controller_test.rb new file mode 100644 index 0000000..f26ea02 --- /dev/null +++ b/test/functional/sip_domains_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class SipDomainsControllerTest < ActionController::TestCase + setup do + @sip_domain = Factory.create(:sip_domain) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:sip_domains) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create sip_domain" do + assert_difference('SipDomain.count') do + post :create, sip_domain: Factory.build(:sip_domain).attributes + end + + assert_redirected_to sip_domain_path(assigns(:sip_domain)) + end + + test "should show sip_domain" do + get :show, id: @sip_domain.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @sip_domain.to_param + assert_response :success + end + + test "should update sip_domain" do + put :update, id: @sip_domain.to_param, sip_domain: @sip_domain.attributes + assert_redirected_to sip_domain_path(assigns(:sip_domain)) + end + + test "should destroy sip_domain" do + assert_difference('SipDomain.count', -1) do + delete :destroy, id: @sip_domain.to_param + end + + assert_redirected_to sip_domains_path + end +end diff --git a/test/functional/softkeys_controller_test.rb b/test/functional/softkeys_controller_test.rb new file mode 100644 index 0000000..6eed165 --- /dev/null +++ b/test/functional/softkeys_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class SoftkeysControllerTest < ActionController::TestCase + setup do + @softkey = softkeys(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:softkeys) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create softkey" do + assert_difference('Softkey.count') do + post :create, softkey: @softkey.attributes + end + + assert_redirected_to softkey_path(assigns(:softkey)) + end + + test "should show softkey" do + get :show, id: @softkey.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @softkey.to_param + assert_response :success + end + + test "should update softkey" do + put :update, id: @softkey.to_param, softkey: @softkey.attributes + assert_redirected_to softkey_path(assigns(:softkey)) + end + + test "should destroy softkey" do + assert_difference('Softkey.count', -1) do + delete :destroy, id: @softkey.to_param + end + + assert_redirected_to softkeys_path + end +end diff --git a/test/functional/system_messages_controller_test.rb b/test/functional/system_messages_controller_test.rb new file mode 100644 index 0000000..2894cd3 --- /dev/null +++ b/test/functional/system_messages_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class SystemMessagesControllerTest < ActionController::TestCase + setup do + @system_message = system_messages(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:system_messages) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create system_message" do + assert_difference('SystemMessage.count') do + post :create, system_message: @system_message.attributes + end + + assert_redirected_to system_message_path(assigns(:system_message)) + end + + test "should show system_message" do + get :show, id: @system_message.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @system_message.to_param + assert_response :success + end + + test "should update system_message" do + put :update, id: @system_message.to_param, system_message: @system_message.attributes + assert_redirected_to system_message_path(assigns(:system_message)) + end + + test "should destroy system_message" do + assert_difference('SystemMessage.count', -1) do + delete :destroy, id: @system_message.to_param + end + + assert_redirected_to system_messages_path + end +end diff --git a/test/functional/tenants_controller_test.rb b/test/functional/tenants_controller_test.rb new file mode 100644 index 0000000..b4e2df4 --- /dev/null +++ b/test/functional/tenants_controller_test.rb @@ -0,0 +1,51 @@ +require 'test_helper' + +class TenantsControllerTest < ActionController::TestCase + # TODO Create tests which test that a login user has specific rights. + + # setup do + # @tenant = Factory.create(:tenant) + # end + # + # test "should get index" do + # get :index + # assert_response :success + # assert_not_nil assigns(:tenants) + # end + # + # test "should get new" do + # get :new + # assert_response :success + # end + # + # test "should create tenant" do + # assert_difference('Tenant.count') do + # post :create, tenant: Factory.build(:tenant).attributes + # end + # + # assert_redirected_to tenant_path(assigns(:tenant)) + # end + # + # test "should show tenant" do + # get :show, id: @tenant.to_param + # assert_response :success + # end + # + # test "should get edit" do + # get :edit, id: @tenant.to_param + # assert_response :success + # end + # + # test "should update tenant" do + # put :update, id: @tenant.to_param, tenant: @tenant.attributes + # assert_redirected_to tenant_path(assigns(:tenant)) + # end + # + # test "should destroy tenant" do + # assert_difference('Tenant.count', -1) do + # delete :destroy, id: @tenant.to_param + # end + # + # assert_redirected_to tenants_path + # end +end diff --git a/test/functional/user_groups_controller_test.rb b/test/functional/user_groups_controller_test.rb new file mode 100644 index 0000000..0e39048 --- /dev/null +++ b/test/functional/user_groups_controller_test.rb @@ -0,0 +1,51 @@ +require 'test_helper' + +class UserGroupsControllerTest < ActionController::TestCase + # TODO Create tests which test that a login user has specific rights. + + # setup do + # @user_group = Factory.create(:user_group) + # end + # + # test "should get index" do + # get :index + # assert_response :success + # assert_not_nil assigns(:user_groups) + # end + # + # test "should get new" do + # get :new + # assert_response :success + # end + # + # test "should create user_group" do + # assert_difference('UserGroup.count') do + # post :create, user_group: Factory.build(:user_group).attributes + # end + # + # assert_redirected_to user_group_path(assigns(:user_group)) + # end + # + # test "should show user_group" do + # get :show, id: @user_group.to_param + # assert_response :success + # end + # + # test "should get edit" do + # get :edit, id: @user_group.to_param + # assert_response :success + # end + # + # test "should update user_group" do + # put :update, id: @user_group.to_param, user_group: @user_group.attributes + # assert_redirected_to user_group_path(assigns(:user_group)) + # end + # + # test "should destroy user_group" do + # assert_difference('UserGroup.count', -1) do + # delete :destroy, id: @user_group.to_param + # end + # + # assert_redirected_to user_groups_path + # end +end diff --git a/test/functional/users_controller_test.rb b/test/functional/users_controller_test.rb new file mode 100644 index 0000000..d1898f7 --- /dev/null +++ b/test/functional/users_controller_test.rb @@ -0,0 +1,65 @@ +require 'test_helper' + +class UsersControllerTest < ActionController::TestCase + setup do + @tenant = Factory.create(:tenant) + @user = Factory.create(:user) + + @tenant.tenant_memberships.create(:user_id => @user.id) + + @user.update_attributes!(:current_tenant_id => @tenant.id) + + @expected_status_if_not_authorized = :redirect + end + + test "should get index" do + session[:user_id] = @user.id + get :index + assert_response :success + assert_not_nil assigns(:users) + end + + test "should get new" do + session[:user_id] = nil + get :new + assert_response :success + end + + # TODO Fix Test + # + # test "should create user" do + # session[:user_id] = nil + # assert_difference('User.count') do + # post :create, user: Factory.build(:user).attributes + # end + # + # # assert_redirected_to user_path(assigns(:user)) + # end + + test "should show user" do + session[:user_id] = @user.id + get :show, id: @user.to_param + assert_response :success + end + + + test "should get edit" do + session[:user_id] = @user.id + get :edit, id: @user.to_param + assert_response :success + end + + test "should update user" do + session[:user_id] = @user.id + put :update, id: @user.to_param, user: @user.attributes + assert_redirected_to user_path(assigns(:user)) + end + + test "should not destroy itself" do + assert_no_difference('User.count') do + delete :destroy, id: @user.to_param + end + +# assert_redirected_to users_path + end +end diff --git a/test/functional/whitelists_controller_test.rb b/test/functional/whitelists_controller_test.rb new file mode 100644 index 0000000..842069f --- /dev/null +++ b/test/functional/whitelists_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class WhitelistsControllerTest < ActionController::TestCase + setup do + @whitelist = whitelists(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:whitelists) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create whitelist" do + assert_difference('Whitelist.count') do + post :create, whitelist: @whitelist.attributes + end + + assert_redirected_to whitelist_path(assigns(:whitelist)) + end + + test "should show whitelist" do + get :show, id: @whitelist.to_param + assert_response :success + end + + test "should get edit" do + get :edit, id: @whitelist.to_param + assert_response :success + end + + test "should update whitelist" do + put :update, id: @whitelist.to_param, whitelist: @whitelist.attributes + assert_redirected_to whitelist_path(assigns(:whitelist)) + end + + test "should destroy whitelist" do + assert_difference('Whitelist.count', -1) do + delete :destroy, id: @whitelist.to_param + end + + assert_redirected_to whitelists_path + end +end diff --git a/test/integration/.gitkeep b/test/integration/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/integration/.gitkeep diff --git a/test/performance/browsing_test.rb b/test/performance/browsing_test.rb new file mode 100644 index 0000000..3fea27b --- /dev/null +++ b/test/performance/browsing_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' +require 'rails/performance_test_help' + +class BrowsingTest < ActionDispatch::PerformanceTest + # Refer to the documentation for all available options + # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory] + # :output => 'tmp/performance', :formats => [:flat] } + + def test_homepage + get '/' + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000..b6836c0 --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,13 @@ +ENV["RAILS_ENV"] = "test" +require File.expand_path('../../config/environment', __FILE__) +require 'rails/test_help' + +class ActiveSupport::TestCase + # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order. + # + # Note: You'll currently still have to declare fixtures explicitly in integration tests + # -- they do not yet inherit this setting + # fixtures :all + + # Add more helper methods to be used by all tests here... +end diff --git a/test/unit/.gitkeep b/test/unit/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/unit/.gitkeep diff --git a/test/unit/access_authorization_test.rb b/test/unit/access_authorization_test.rb new file mode 100644 index 0000000..ebd9f0b --- /dev/null +++ b/test/unit/access_authorization_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class AccessAuthorizationTest < ActiveSupport::TestCase + # def test_should_be_valid + # assert AccessAuthorization.new.valid? + # end +end diff --git a/test/unit/acd_agent_test.rb b/test/unit/acd_agent_test.rb new file mode 100644 index 0000000..9e1a116 --- /dev/null +++ b/test/unit/acd_agent_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class AcdAgentTest < ActiveSupport::TestCase + def test_should_be_valid + assert AcdAgent.new.valid? + end +end diff --git a/test/unit/acd_caller_test.rb b/test/unit/acd_caller_test.rb new file mode 100644 index 0000000..68d5801 --- /dev/null +++ b/test/unit/acd_caller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class AcdCallerTest < ActiveSupport::TestCase + def test_should_be_valid + assert AcdCaller.new.valid? + end +end diff --git a/test/unit/address_test.rb b/test/unit/address_test.rb new file mode 100644 index 0000000..6d676ba --- /dev/null +++ b/test/unit/address_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class AddressTest < ActiveSupport::TestCase + def test_should_be_valid + assert Address.new.valid? + end +end diff --git a/test/unit/api/row_test.rb b/test/unit/api/row_test.rb new file mode 100644 index 0000000..dc21050 --- /dev/null +++ b/test/unit/api/row_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class Api::RowTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/unit/area_code_test.rb b/test/unit/area_code_test.rb new file mode 100644 index 0000000..10c7c08 --- /dev/null +++ b/test/unit/area_code_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class AreaCodeTest < ActiveSupport::TestCase + def test_should_have_a_valid_factory + assert Factory.build(:area_code).valid? + end +end diff --git a/test/unit/automatic_call_distributor_test.rb b/test/unit/automatic_call_distributor_test.rb new file mode 100644 index 0000000..b025df8 --- /dev/null +++ b/test/unit/automatic_call_distributor_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class AutomaticCallDistributorTest < ActiveSupport::TestCase + def test_should_be_valid + assert AutomaticCallDistributor.new.valid? + end +end diff --git a/test/unit/call_forward_case_test.rb b/test/unit/call_forward_case_test.rb new file mode 100644 index 0000000..0d1d238 --- /dev/null +++ b/test/unit/call_forward_case_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class CallForwardCaseTest < ActiveSupport::TestCase + + # Cannot be factory'd. CallForwardCases are seeded. + +end diff --git a/test/unit/call_forward_test.rb b/test/unit/call_forward_test.rb new file mode 100644 index 0000000..95716c5 --- /dev/null +++ b/test/unit/call_forward_test.rb @@ -0,0 +1,9 @@ +require 'test_helper' + +class CallForwardTest < ActiveSupport::TestCase + + test "should have a valid factory" do + assert Factory.build(:call_forward).valid? + end + +end diff --git a/test/unit/callthrough_test.rb b/test/unit/callthrough_test.rb new file mode 100644 index 0000000..c1c6ab3 --- /dev/null +++ b/test/unit/callthrough_test.rb @@ -0,0 +1,134 @@ +require 'test_helper' + +class CallthroughTest < ActiveSupport::TestCase + def setup + # Basic setup of a new system + # + germany = Country.create(:name => "Germany", :country_code => "49", :international_call_prefix => "00", :trunk_prefix => "0" ) + Language.create(:name => 'Deutsch', :code => 'de') + AreaCode.create(:country => germany, :name => "Bendorf", :area_code => "2622") + + @gemeinschaft_setup = GemeinschaftSetup.new + @gemeinschaft_setup.country = Country.first + @gemeinschaft_setup.language = Language.first + + @current_user = @gemeinschaft_setup.build_user( + :user_name => I18n.t('gemeinschaft_setups.initial_setup.admin_name'), + :male => true, + :email => 'admin@localhost', + :first_name => 'Max', + :last_name => 'Mustermann', + :password => 'xxxxxxxxxx', + :password_confirmation => 'xxxxxxxxxx', + :language_id => Language.first.id, + ) + @sip_domain = @gemeinschaft_setup.build_sip_domain( + :host => '10.0.0.1', + :realm => '10.0.0.1', + ) + + @gemeinschaft_setup.save + + super_tenant = Tenant.create( + :name => SUPER_TENANT_NAME, + :country_id => @gemeinschaft_setup.country.id, + :language_id => @gemeinschaft_setup.language_id, + :description => I18n.t('gemeinschaft_setups.initial_setup.super_tenant_description'), + ) + + # Admin + super_tenant.tenant_memberships.create(:user_id => @gemeinschaft_setup.user.id) + + # Create the Super-Tenant's group: + super_tenant_super_admin_group = super_tenant.user_groups.create(:name => I18n.t('gemeinschaft_setups.initial_setup.super_admin_group_name')) + super_tenant_super_admin_group.user_group_memberships.create(:user_id => @gemeinschaft_setup.user.id) + + # Create the tenant. + # + @tenant = @sip_domain.tenants.build(:name => 'AMOOMA GmbH') + @tenant.country = Country.first + @tenant.language = Language.first + @tenant.internal_extension_ranges = '10-20' + @tenant.did_list = '02622-70648-x, 02622-706480' + @tenant.save + + @tenant.tenant_memberships.create(:user_id => @current_user.id) + @current_user.update_attributes!(:current_tenant_id => @tenant.id) + + # The first user becomes a member of the 'admin' UserGroup + # + admin_group = @tenant.user_groups.create(:name => I18n.t('gemeinschaft_setups.initial_setup.admin_group_name')) + admin_group.users << @current_user + + # User group + # + user_group = @tenant.user_groups.create(:name => I18n.t('gemeinschaft_setups.initial_setup.user_group_name')) + user_group.users << @current_user + + # Generate the internal_extensions + # + @tenant.generate_internal_extensions + + # Generate the external numbers (DIDs) + # + @tenant.generate_dids + end + + test 'the setup should create a valid system' do + # Basics + # + assert_equal 1, Country.count + assert_equal 1, Language.count + + # Testing the installation + # + assert @gemeinschaft_setup.valid? + assert @sip_domain.valid? + assert @current_user.valid? + + assert @tenant.valid? + + assert_equal 0, SipAccount.count + assert_equal 2, Tenant.count + assert_equal 1, User.count + + # Check the amount of phone_numbers + # + assert_equal 11, @tenant.phone_number_ranges.find_by_name(INTERNAL_EXTENSIONS).phone_numbers.count + assert_equal 12, @tenant.phone_number_ranges.find_by_name(DIRECT_INWARD_DIALING_NUMBERS).phone_numbers.count + end + + test 'that a callthrough can only be created with at least one DID' do + assert_equal 0, Callthrough.count + + did = @tenant.phone_number_ranges.find_by_name(DIRECT_INWARD_DIALING_NUMBERS).phone_numbers.first + + callthrough = @tenant.callthroughs.build + + assert !callthrough.valid? + + callthrough.phone_numbers.build(:number => did.number) + + assert callthrough.save + assert_equal 1, Callthrough.count + end + + # TODO Activate this after fixing unique phone_number + # + # test 'that one DID can not be used by two different callthroughs' do + # assert_equal 0, Callthrough.count + + # did = @tenant.phone_number_ranges.find_by_name(DIRECT_INWARD_DIALING_NUMBERS).phone_numbers.first + + # callthroughs = Array.new + # (1..2).each do |i| + # callthroughs[i] = @tenant.callthroughs.build + # callthroughs[i].phone_numbers.build(:number => did.number) + # callthroughs[i].save + # end + + # assert callthroughs[1].valid?, '1st Callthrough is not valid' + # assert !callthroughs[2].valid?, '2nd Callthrough is not valid' + # end + +end diff --git a/test/unit/conference_invitee_test.rb b/test/unit/conference_invitee_test.rb new file mode 100644 index 0000000..bcc4c9b --- /dev/null +++ b/test/unit/conference_invitee_test.rb @@ -0,0 +1,17 @@ +require 'test_helper' + +class ConferenceInviteeTest < ActiveSupport::TestCase + + def test_should_have_a_valid_factory + assert Factory.build(:conference_invitee).valid? + end + + test "parent conference should not have a phone number twice" do + invitee = Factory.create(:conference_invitee) + conference = invitee.conference + phone_number = PhoneNumber.new(:number => invitee.phone_number.number) + invitee_bad = conference.conference_invitees.build(:phone_number => phone_number) + assert !invitee_bad.valid? + end + +end diff --git a/test/unit/conference_test.rb b/test/unit/conference_test.rb new file mode 100644 index 0000000..b7cce28 --- /dev/null +++ b/test/unit/conference_test.rb @@ -0,0 +1,48 @@ +require 'test_helper' + +class ConferenceTest < ActiveSupport::TestCase + + def test_should_have_a_valid_factory + assert Factory.build(:conference).valid? + end + + def test_dates_must_make_sense + # We can't create a conference in the past. + assert !Factory.build(:conference, :start => Time.now - 1.day, :end => Time.now + 1.day).valid? + + # But we can create a conference which started 2 minutes ago. + assert Factory.build(:conference, :start => Time.now - 2.minutes, :end => Time.now + 1.hour).valid? + + # The end must be before the start. + assert !Factory.build(:conference, :start => Time.now + 2.day, :end => Time.now + 1.day).valid? + + # No date at all is fine. + assert Factory.build(:conference, :start => nil, :end => nil).valid? + + # Just start or just end is not ok. + assert !Factory.build(:conference, :start => nil, :end => Time.now + 1.day).valid? + assert !Factory.build(:conference, :start => Time.now + 1.day, :end => nil).valid? + end + + def test_pin_must_be_nil_or_more_than_6_digits + conference = Factory.build(:conference, :pin => nil) + + assert conference.valid? + + (MINIMUM_PIN_LENGTH - 1).times do |i| + pin = "#{10**i}" + conference.pin = pin + assert !conference.valid? + end + + conference.pin = "#{10**(MINIMUM_PIN_LENGTH - 1)}" + assert conference.valid? + + conference.pin = "-#{10**(MINIMUM_PIN_LENGTH - 1)}" + assert !conference.valid? + + conference.pin = 'Teststring' + assert !conference.valid? + end + +end diff --git a/test/unit/country_test.rb b/test/unit/country_test.rb new file mode 100644 index 0000000..4933843 --- /dev/null +++ b/test/unit/country_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class CountryTest < ActiveSupport::TestCase + def test_should_have_a_valid_factory + assert Factory.build(:country).valid? + end +end diff --git a/test/unit/dial_in_number_store_test.rb b/test/unit/dial_in_number_store_test.rb new file mode 100644 index 0000000..d1341f9 --- /dev/null +++ b/test/unit/dial_in_number_store_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class DialInNumberStoreTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/unit/fax_account_test.rb b/test/unit/fax_account_test.rb new file mode 100644 index 0000000..f4048cb --- /dev/null +++ b/test/unit/fax_account_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class FaxAccountTest < ActiveSupport::TestCase + # def test_should_be_valid + # assert FaxAccount.new.valid? + # end +end diff --git a/test/unit/fax_document_test.rb b/test/unit/fax_document_test.rb new file mode 100644 index 0000000..190a953 --- /dev/null +++ b/test/unit/fax_document_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class FaxDocumentTest < ActiveSupport::TestCase + # def test_should_be_valid + # assert FaxDocument.new.valid? + # end +end diff --git a/test/unit/fax_page_test.rb b/test/unit/fax_page_test.rb new file mode 100644 index 0000000..522ffd5 --- /dev/null +++ b/test/unit/fax_page_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class FaxPageTest < ActiveSupport::TestCase + # def test_should_be_valid + # assert FaxPage.new.valid? + # end +end diff --git a/test/unit/fax_resolution_test.rb b/test/unit/fax_resolution_test.rb new file mode 100644 index 0000000..93676af --- /dev/null +++ b/test/unit/fax_resolution_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class FaxResolutionTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/unit/fax_thumbnail_test.rb b/test/unit/fax_thumbnail_test.rb new file mode 100644 index 0000000..4fc5bc8 --- /dev/null +++ b/test/unit/fax_thumbnail_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class FaxThumbnailTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/unit/gemeinschaft_setup_test.rb b/test/unit/gemeinschaft_setup_test.rb new file mode 100644 index 0000000..5ea6523 --- /dev/null +++ b/test/unit/gemeinschaft_setup_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class GemeinschaftSetupTest < ActiveSupport::TestCase + def test_should_have_a_valid_factory + assert Factory.build(:gemeinschaft_setup).valid? + end +end diff --git a/test/unit/gs_cluster_sync_log_entry_test.rb b/test/unit/gs_cluster_sync_log_entry_test.rb new file mode 100644 index 0000000..bff49e9 --- /dev/null +++ b/test/unit/gs_cluster_sync_log_entry_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class GsClusterSyncLogEntryTest < ActiveSupport::TestCase + def test_should_be_valid + assert GsClusterSyncLogEntry.new.valid? + end +end diff --git a/test/unit/gs_node_test.rb b/test/unit/gs_node_test.rb new file mode 100644 index 0000000..1155cab --- /dev/null +++ b/test/unit/gs_node_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class GsNodeTest < ActiveSupport::TestCase + def test_should_be_valid + assert GsNode.new.valid? + end +end diff --git a/test/unit/gui_function_membership_test.rb b/test/unit/gui_function_membership_test.rb new file mode 100644 index 0000000..c85ec68 --- /dev/null +++ b/test/unit/gui_function_membership_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class GuiFunctionMembershipTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/unit/gui_function_test.rb b/test/unit/gui_function_test.rb new file mode 100644 index 0000000..60bce1d --- /dev/null +++ b/test/unit/gui_function_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class GuiFunctionTest < ActiveSupport::TestCase + def test_should_be_valid + assert GuiFunction.new.valid? + end +end diff --git a/test/unit/helpers/api/rows_helper_test.rb b/test/unit/helpers/api/rows_helper_test.rb new file mode 100644 index 0000000..16c8fe7 --- /dev/null +++ b/test/unit/helpers/api/rows_helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class Api::RowsHelperTest < ActionView::TestCase +end diff --git a/test/unit/helpers/config_siemens_helper_test.rb b/test/unit/helpers/config_siemens_helper_test.rb new file mode 100644 index 0000000..1afae0d --- /dev/null +++ b/test/unit/helpers/config_siemens_helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class ConfigSiemensHelperTest < ActionView::TestCase +end diff --git a/test/unit/helpers/page_helper_test.rb b/test/unit/helpers/page_helper_test.rb new file mode 100644 index 0000000..6009662 --- /dev/null +++ b/test/unit/helpers/page_helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class PageHelperTest < ActionView::TestCase +end diff --git a/test/unit/helpers/sessions_helper_test.rb b/test/unit/helpers/sessions_helper_test.rb new file mode 100644 index 0000000..7d44e09 --- /dev/null +++ b/test/unit/helpers/sessions_helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class SessionsHelperTest < ActionView::TestCase +end diff --git a/test/unit/hunt_group_member_test.rb b/test/unit/hunt_group_member_test.rb new file mode 100644 index 0000000..4472cc2 --- /dev/null +++ b/test/unit/hunt_group_member_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class HuntGroupMemberTest < ActiveSupport::TestCase + # def test_should_be_valid + # assert HuntGroupMember.new.valid? + # end +end diff --git a/test/unit/hunt_group_test.rb b/test/unit/hunt_group_test.rb new file mode 100644 index 0000000..5ef843b --- /dev/null +++ b/test/unit/hunt_group_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class HuntGroupTest < ActiveSupport::TestCase + # def test_should_be_valid + # assert HuntGroup.new.valid? + # end +end diff --git a/test/unit/language_test.rb b/test/unit/language_test.rb new file mode 100644 index 0000000..3308735 --- /dev/null +++ b/test/unit/language_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class LanguageTest < ActiveSupport::TestCase + test "has a valid factory" do + assert Factory.build(:language).valid? + end +end diff --git a/test/unit/manufacturer_test.rb b/test/unit/manufacturer_test.rb new file mode 100644 index 0000000..635a977 --- /dev/null +++ b/test/unit/manufacturer_test.rb @@ -0,0 +1,27 @@ +require 'test_helper' + +class ManufacturerTest < ActiveSupport::TestCase + def test_should_have_a_valid_factory + assert Factory.build(:manufacturer).valid? + end + + # StateMachine Tests: + def test_that_the_initial_state_should_be_active + @manufacturer = Factory.create(:manufacturer) + assert_equal 'active', @manufacturer.state + assert @manufacturer.active? + end + + def test_not_active_state_will_not_be_displayed + @manufacturer = Factory.create(:manufacturer) + assert_equal 1, Manufacturer.count + + @manufacturer.deactivate! + assert_equal 0, Manufacturer.count + assert_equal 1, Manufacturer.unscoped.count + + @manufacturer.activate! + assert_equal 1, Manufacturer.count + assert_equal Manufacturer.count, Manufacturer.unscoped.count + end +end diff --git a/test/unit/oui_test.rb b/test/unit/oui_test.rb new file mode 100644 index 0000000..de48a97 --- /dev/null +++ b/test/unit/oui_test.rb @@ -0,0 +1,14 @@ +require 'test_helper' + +class OuiTest < ActiveSupport::TestCase + def test_should_have_a_valid_factory + assert Factory.build(:oui).valid? + end + + def test_that_the_initial_state_should_be_active + @oui = Factory.create(:oui) + assert_equal 'active', @oui.state + assert @oui.active? + end + +end diff --git a/test/unit/phone_book_entry_test.rb b/test/unit/phone_book_entry_test.rb new file mode 100644 index 0000000..c8d639b --- /dev/null +++ b/test/unit/phone_book_entry_test.rb @@ -0,0 +1,51 @@ +require 'test_helper' + +class PhoneBookEntryTest < ActiveSupport::TestCase + def test_should_be_valid + assert Factory.build(:phone_book_entry).valid? + end + + # TODO Fix this test. + + # test "only user can read entries in private phone books" do + # user = Factory.create(:user) + # phone_book = Factory.create(:phone_book, :phone_bookable_type => 'User', :phone_bookable_id => user.id) + # phone_book_entry = Factory.create(:phone_book_entry, :phone_book_id => phone_book.id) + + # evil_user = Factory.create(:user) + + # user_ability = Ability.new( user ) + # evil_user_ability = Ability.new( evil_user ) + + # [ :show, :index ].each { |action| + # assert user_ability.can? action, phone_book_entry + # assert evil_user_ability.cannot? action, phone_book_entry + # } + # end + + def test_that_the_initial_state_should_be_active + @phone_book_entry = Factory.create(:phone_book_entry) + assert_equal 'active', @phone_book_entry.state + assert @phone_book_entry.active? + end + + test "a destroyed phone_book will destroy all phone_book_entries" do + phone_book = Factory.create(:phone_book) + 10.times { Factory.create(:phone_book_entry, :phone_book_id => phone_book.id) } + + phone_book2 = Factory.create(:phone_book) + 5.times { Factory.create(:phone_book_entry, :phone_book_id => phone_book2.id) } + + assert_equal 15, PhoneBookEntry.all.count + + phone_book.destroy + + assert_equal 5, PhoneBookEntry.all.count + end + + test "that the value_of_to_s field is filled" do + phone_book_entry = Factory.create(:phone_book_entry) + assert_equal phone_book_entry.value_of_to_s, phone_book_entry.to_s + end + +end diff --git a/test/unit/phone_book_test.rb b/test/unit/phone_book_test.rb new file mode 100644 index 0000000..7db48f8 --- /dev/null +++ b/test/unit/phone_book_test.rb @@ -0,0 +1,119 @@ +require 'test_helper' + +class PhoneBookTest < ActiveSupport::TestCase + def test_should_have_a_valid_factory + assert Factory.build(:phone_book).valid? + end + + def test_should_have_unique_name_depending_on_type + user1 = Factory.create(:user) + user2 = Factory.create(:user) + tenant = Factory.create(:tenant) + + phonebook = Factory.create(:phone_book, :phone_bookable => user1) + assert !user1.phone_books.build(:name => phonebook.name).valid? + assert user2.phone_books.build(:name => phonebook.name).valid? + assert tenant.phone_books.build(:name => phonebook.name).valid? + end + + # TODO Create a real system for the phone_book tests and than test again. + + + # test "User gets a private phone book with rw rights" do + # user = Factory.create(:user) + # assert_equal 1, user.phone_books.count + + # phone_book = user.phone_books.first + + # user_ability = Ability.new( user ) + + # [ :show, :destroy, :edit ].each { |action| + # assert user_ability.can?( action, phone_book ), "should be able to #{action}" + # } + + # # Lets test some stuff about the phone_book_entries + # assert_equal 0, phone_book.phone_book_entries.count + + # entry1 = Factory.create(:phone_book_entry, :phone_book_id => phone_book.id) + # entry2 = Factory.create(:phone_book_entry, :phone_book_id => phone_book.id) + # entry3 = Factory.create(:phone_book_entry, :phone_book_id => phone_book.id) + # assert_equal 3, phone_book.phone_book_entries.count + + # assert_equal 1, PhoneBookEntry.where(:id => entry1.id).count + # assert_equal 1, PhoneBookEntry.where(:id => entry2.id).count + # assert_equal 1, PhoneBookEntry.where(:id => entry3.id).count + # user.phone_books.first.destroy + # assert_equal 0, user.phone_books.count + # assert_equal 0, PhoneBookEntry.where(:id => entry1.id).count + # assert_equal 0, PhoneBookEntry.where(:id => entry2.id).count + # assert_equal 0, PhoneBookEntry.where(:id => entry3.id).count + # end + + test "Tenant gets automatically one phone book and can destroy it" do + tenant = Factory.create(:tenant) + assert_equal 1, tenant.phone_books.count + tenant.phone_books.first.destroy + assert_equal 0, tenant.phone_books.count + end + + # test "only tenant members can read a tenant phone book" do + # tenant = Factory.create(:tenant) + # user = Factory.create(:user) + # tenant.users << user + # tenant.save + # user.current_tenant = tenant + # user.save + # phone_book = Factory.create(:phone_book, :phone_bookable_type => 'Tenant', :phone_bookable_id => tenant.id) + + # evil_user = Factory.create(:user) + + # user_ability = Ability.new( user ) + # evil_user_ability = Ability.new( evil_user ) + + # [ :show ].each { |action| + # assert user_ability.can?( action, phone_book ), "should be able to #{action}" + # assert evil_user_ability.cannot?( action, phone_book ), "should not be able to #{action}" + # } + # end + + + + # test "tenant's phone book can not be edited by tenant members" do + # tenant = Factory.create(:tenant) + # user = Factory.create(:user) + # tenant.users << user + # phone_book = Factory.create(:phone_book, :phone_bookable_type => 'Tenant', :phone_bookable_id => tenant.id) + + # evil_user = Factory.create(:user) + + # user_ability = Ability.new( user ) + # evil_user_ability = Ability.new( evil_user ) + + # [ :edit, :destroy ].each { |action| + # assert user_ability.cannot?( action, phone_book ), "should not be able to #{action}" + # assert evil_user_ability.cannot?( action, phone_book ), "should not be able to #{action}" + # } + # end + + # test "only user can manage his private phone book after creating it" do + # user = Factory.create(:user) + # phone_book = Factory.create(:phone_book, :phone_bookable_type => 'User', :phone_bookable_id => user.id) + + # evil_user = Factory.create(:user) + + # user_ability = Ability.new( user ) + # evil_user_ability = Ability.new( evil_user ) + + # [ :show, :destroy, :edit ].each { |action| + # assert user_ability.can?( action, phone_book ), "should be able to #{action}" + # assert evil_user_ability.cannot?( action, phone_book ), "should not be able to #{action}" + # } + # end + + def test_that_the_initial_state_should_be_active + @phone_book = Factory.create(:phone_book) + assert_equal 'active', @phone_book.state + assert @phone_book.active? + end + +end diff --git a/test/unit/phone_model_test.rb b/test/unit/phone_model_test.rb new file mode 100644 index 0000000..e358cf9 --- /dev/null +++ b/test/unit/phone_model_test.rb @@ -0,0 +1,9 @@ +require 'test_helper' + +class PhoneModelTest < ActiveSupport::TestCase + + def test_should_have_a_valid_factory + assert Factory.build(:phone_model).valid? + end + +end diff --git a/test/unit/phone_number_range_test.rb b/test/unit/phone_number_range_test.rb new file mode 100644 index 0000000..1770299 --- /dev/null +++ b/test/unit/phone_number_range_test.rb @@ -0,0 +1,9 @@ +require 'test_helper' + +class PhoneNumberRangeTest < ActiveSupport::TestCase + + def test_should_have_a_valid_factory + assert Factory.build(:phone_number_range).valid? + end + +end diff --git a/test/unit/phone_number_test.rb b/test/unit/phone_number_test.rb new file mode 100644 index 0000000..e10b20c --- /dev/null +++ b/test/unit/phone_number_test.rb @@ -0,0 +1,260 @@ +# ruby coding: utf-8 + +require 'test_helper' + +class PhoneNumberTest < ActiveSupport::TestCase + + test "should have valid factory" do + assert Factory.build(:phone_number).valid? + end + + def test_that_the_initial_state_should_be_active + @phone_number = Factory.create(:phone_number) + assert_equal 'active', @phone_number.state + assert @phone_number.active? + end + + test "that the value_of_to_s field is filled" do + phone_number = Factory.create(:phone_number) + assert_equal phone_number.value_of_to_s, phone_number.to_s + end + + { + '+492612000' => { + :country_code => "49", + :area_code => "261", + :central_office_code => nil, + :subscriber_number => "2000", + :extension => nil, + }, + '+49 261 2000' => { + :country_code => "49", + :area_code => "261", + :central_office_code => nil, + :subscriber_number => "2000", + :extension => nil, + }, + '+49-261-2000' => { + :country_code => "49", + :area_code => "261", + :central_office_code => nil, + :subscriber_number => "2000", + :extension => nil, + }, + '492612000' => { + :country_code => "49", + :area_code => "261", + :central_office_code => nil, + :subscriber_number => "2000", + :extension => nil, + }, + '49888888882000' => nil, # unknown area code + '552612000' => nil, # unknown country code + '15551234567' => { + :country_code => "1", + :area_code => "555", + :central_office_code => "123", + :subscriber_number => "4567", + :extension => nil, + }, + '2612000' => nil, # not an international number + '02612000' => nil, # not an international number + '00492612000' => nil, # invalid format + '2000' => nil, + '' => nil, + nil => nil, + '++++' => nil, + '###' => nil, + 'äöü' => nil, + false => nil, + true => nil, # true.to_s == "true" # invalid number + }.each_pair do |number, expected| + test "should parse number #{number.inspect} correctly" do + # load some country codes: + usa = Country.create(:name => "United States of America", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + germany = Country.create(:name => "Germany", :country_code => "49", :international_call_prefix => "00", :trunk_prefix => "0" ) + cuba = Country.create(:name => "Cuba", :country_code => "53", :international_call_prefix => "119", :trunk_prefix => "" ) + # load some area codes: + AreaCode.create(:country => germany, :name => "Koblenz am Rhein", :area_code => "261") + AreaCode.create(:country => germany, :name => "Neuwied", :area_code => "2631") + AreaCode.create(:country => germany, :name => "Berlin", :area_code => "30") + AreaCode.create(:country => germany, :name => "Hamburg", :area_code => "40") + AreaCode.create(:country => germany, :name => "Hohenmocker", :area_code => "39993") + + assert_equal expected, PhoneNumber.parse_international_number( number ) + end + end + + { + '+492612000' => { + :country_code => "49", + :area_code => "261", + :central_office_code => nil, + :subscriber_number => "2000", + :extension => nil, + }, + '+49 261 2000' => { + :country_code => "49", + :area_code => "261", + :central_office_code => nil, + :subscriber_number => "2000", + :extension => nil, + }, + '+49-261-2000' => { + :country_code => "49", + :area_code => "261", + :central_office_code => nil, + :subscriber_number => "2000", + :extension => nil, + }, + '110' => { + :country_code => nil, + :area_code => nil, + :central_office_code => nil, + :subscriber_number => "110", + :extension => nil, + }, + '11833' => { + :country_code => nil, + :area_code => nil, + :central_office_code => nil, + :subscriber_number => "11833", + :extension => nil, + }, + '15551234567' => { + :country_code => nil, + :area_code => nil, + :central_office_code => nil, + :subscriber_number => "15551234567", + :extension => nil, + }, + '0015551234567' => { + :country_code => "1", + :area_code => "555", + :central_office_code => "123", + :subscriber_number => "4567", + :extension => nil, + }, + '+15551234567' => { + :country_code => "1", + :area_code => "555", + :central_office_code => "123", + :subscriber_number => "4567", + :extension => nil, + }, + '02612000' => { + :country_code => "49", + :area_code => "261", + :central_office_code => nil, + :subscriber_number => "2000", + :extension => nil, + }, + '00492612000' => { + :country_code => "49", + :area_code => "261", + :central_office_code => nil, + :subscriber_number => "2000", + :extension => nil, + }, + '2000' => { + :country_code => nil, + :area_code => nil, + :central_office_code => nil, + :subscriber_number => nil, + :extension => "2000", + }, + '99' => { + :country_code => nil, + :area_code => nil, + :central_office_code => nil, + :subscriber_number => nil, + :extension => "99", + }, + '5' => { + :country_code => nil, + :area_code => nil, + :central_office_code => nil, + :subscriber_number => nil, + :extension => "5", + }, + '' => nil, + nil => nil, + '++++' => nil, + '###' => nil, + 'äöü' => nil, + false => nil, + true => nil, # true.to_s == "true" # invalid number + }.each_pair do |number, expected| + test "should parse number #{number.inspect} correctly for a specific tenant" do + # load some country codes: + usa = Country.create(:name => "United States of America", :country_code => "1", :international_call_prefix => "011", :trunk_prefix => "1" ) + germany = Country.create(:name => "Germany", :country_code => "49", :international_call_prefix => "00", :trunk_prefix => "0" ) + cuba = Country.create(:name => "Cuba", :country_code => "53", :international_call_prefix => "119", :trunk_prefix => "" ) + # load some area codes: + AreaCode.create(:country => germany, :name => "Koblenz am Rhein", :area_code => "261") + AreaCode.create(:country => germany, :name => "Neuwied", :area_code => "2631") + AreaCode.create(:country => germany, :name => "Berlin", :area_code => "30") + AreaCode.create(:country => germany, :name => "Hamburg", :area_code => "40") + AreaCode.create(:country => germany, :name => "Hohenmocker", :area_code => "39993") + + # create a tenant + tenant = Factory.create(:tenant, :country_id => germany.id) + # create some extensions + internal_extension_range = tenant.phone_number_ranges.create(:name => INTERNAL_EXTENSIONS) + ['2000', '2001', '2003', '2004', '2005', '2006', '2007', '2008', '2009', '2010', '5', '99'].each do |extension| + internal_extension_range.phone_numbers.create(:name => "Extension #{extension}", :number => extension) + end + + assert_equal expected, PhoneNumber.parse( number, tenant ) + end + end + + # TODO: Test uniqueness of a phone_number when creating it. + + # test "has to be unique per sip_account" do + # germany = Country.create(:name => "Germany", :country_code => "49", :international_call_prefix => "00", :trunk_prefix => "0" ) + # Language.create(:name => 'Deutsch', :code => 'de') + # AreaCode.create(:country => germany, :name => "Bendorf", :area_code => "2622") + + # @sip_domain = Factory.create(:sip_domain) + + # @tenant = @sip_domain.tenants.build(:name => 'AMOOMA GmbH') + # @tenant.country = Country.first + # @tenant.language = Language.first + # @tenant.internal_extension_ranges = '10-20' + # @tenant.save + + # @tenant.generate_internal_extensions + + # sip_account = @tenant.sip_accounts.build(Factory.create(:sip_account).attributes) + # phone_number = sip_account.phone_numbers.create(Factory.build(:phone_number, :number => '10').attributes) + # phone_number_evil = sip_account.phone_numbers.build(phone_number.attributes) + + # assert phone_number.valid? + # assert !phone_number_evil.valid? + # end + + # test "has to be unique per SIP domain even for different tenants" do + # provider_sip_domain = Factory.create(:sip_domain) + # tenants = [] + # sip_accounts = [] + # 2.times { |i| + # tenants[i] = provider_sip_domain.tenants.create(Factory.build(:tenant, :internal_extension_ranges => '10-20').attributes) + # tenants[i].generate_internal_extensions + + # sip_accounts[i] = tenants[i].sip_accounts.build(Factory.build(:sip_account, :tenant_id => tenants[i].id).attributes) + # sip_accounts[i].phone_numbers.build(:number => '10') + # } + # sip_accounts[0].save + + # assert sip_accounts[0].valid?, 'Should be valid.' + # assert ! sip_accounts[1].valid?, + # "Shouldn't be possible to use the same phone number more than once per SIP domain." + + # # Lets change the second phone_number for a positiv test: + # # + # sip_accounts[1].phone_numbers.first.number = '11' + # assert sip_accounts[1].valid? + # end + +end diff --git a/test/unit/phone_sip_account_test.rb b/test/unit/phone_sip_account_test.rb new file mode 100644 index 0000000..735799e --- /dev/null +++ b/test/unit/phone_sip_account_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class PhoneSipAccountTest < ActiveSupport::TestCase + # def test_should_be_valid + # assert PhoneSipAccount.new.valid? + # end +end diff --git a/test/unit/phone_test.rb b/test/unit/phone_test.rb new file mode 100644 index 0000000..4ad21df --- /dev/null +++ b/test/unit/phone_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class PhoneTest < ActiveSupport::TestCase + + def test_should_have_a_valid_factory + assert Factory.build(:phone).valid? + end + + # test "should destroy_all phones_sip_accounts if the phoneable changed" do + # sip_domain = Factory.create(:sip_domain) + # tenant = sip_domain.tenants.create(Factory.build(:tenant).attributes) + # + # user1 = Factory.create(:user) + # user2 = Factory.create(:user) + # tenant.tenant_memberships.create(:user_id => user1.id) + # tenant.tenant_memberships.create(:user_id => user2.id) + # + # phone = Factory.create(:phone, :phoneable => tenant) + # + # # Nothing there + # # + # assert_equal 0, phone.sip_accounts.count + # + # # move the phone to user1 + # # + # phone.phoneable = user1 + # phone.save + # + # # create some sip_accounts associated to phone + # # + # 3.times { Factory.create(:sip_account, :sip_accountable => user1, :tenant_id => tenant.id) } + # SipAccount.all.each do |sip_account| + # phone.phones_sip_accounts.create(:sip_account_id => sip_account.id) + # end + # + # # Should have 3 sip_accounts + # # + # assert_equal 3, phone.sip_accounts.count + # + # # Move to user2 + # phone.phoneable = user2 + # phone.save + # + # # Should have 0 sip_accounts + # # + # assert_equal 0, phone.sip_accounts.count + # end + +end diff --git a/test/unit/ringtone_test.rb b/test/unit/ringtone_test.rb new file mode 100644 index 0000000..dee6002 --- /dev/null +++ b/test/unit/ringtone_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class RingtoneTest < ActiveSupport::TestCase + # def test_should_be_valid + # assert Ringtone.new.valid? + # end +end diff --git a/test/unit/sip_account_test.rb b/test/unit/sip_account_test.rb new file mode 100644 index 0000000..6595ccc --- /dev/null +++ b/test/unit/sip_account_test.rb @@ -0,0 +1,34 @@ +require 'test_helper' + +class SipAccountTest < ActiveSupport::TestCase + + def test_should_have_a_valid_factory + assert Factory.build(:sip_account).valid? + end + + test "that the value_of_to_s field is filled" do + sip_account = Factory.create(:sip_account) + assert_equal sip_account.value_of_to_s, sip_account.to_s + end + + test "should have a unique auth_name per sip_domain" do + provider_sip_domain = Factory.create(:sip_domain) + tenants = [] + sip_accounts = [] + 2.times { |i| + tenants[i] = provider_sip_domain.tenants.create(Factory.build(:tenant).attributes) + sip_accounts[i] = Factory.build( + :sip_account, + :sip_accountable => tenants[i], + :auth_name => "somerandomauthname", + :tenant_id => tenants[i].id + ) + } + sip_accounts[0].save! + + assert sip_accounts[0].valid? + assert ! sip_accounts[1].valid?, + "Shouldn't be possible to use the same phone number more than once per SIP realm." + end + +end diff --git a/test/unit/sip_domain_test.rb b/test/unit/sip_domain_test.rb new file mode 100644 index 0000000..5d31f69 --- /dev/null +++ b/test/unit/sip_domain_test.rb @@ -0,0 +1,9 @@ +require 'test_helper' + +class SipDomainTest < ActiveSupport::TestCase + + def test_should_have_a_valid_factory + assert Factory.build(:sip_domain).valid? + end + +end diff --git a/test/unit/softkey_function_test.rb b/test/unit/softkey_function_test.rb new file mode 100644 index 0000000..ed63836 --- /dev/null +++ b/test/unit/softkey_function_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class SoftkeyFunctionTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/unit/softkey_test.rb b/test/unit/softkey_test.rb new file mode 100644 index 0000000..ca173f9 --- /dev/null +++ b/test/unit/softkey_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class SoftkeyTest < ActiveSupport::TestCase + # def test_should_be_valid + # assert Softkey.new.valid? + # end +end diff --git a/test/unit/system_message_test.rb b/test/unit/system_message_test.rb new file mode 100644 index 0000000..0476ac8 --- /dev/null +++ b/test/unit/system_message_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class SystemMessageTest < ActiveSupport::TestCase + # def test_should_be_valid + # assert SystemMessage.new.valid? + # end +end diff --git a/test/unit/tenant_membership_test.rb b/test/unit/tenant_membership_test.rb new file mode 100644 index 0000000..8e7a191 --- /dev/null +++ b/test/unit/tenant_membership_test.rb @@ -0,0 +1,14 @@ +require 'test_helper' + +class TenantMembershipTest < ActiveSupport::TestCase + def test_should_have_a_valid_factory + assert Factory.build(:tenant_membership).valid? + end + + def test_that_the_initial_state_should_be_active + @tenant_membership = Factory.create(:tenant_membership) + assert_equal 'active', @tenant_membership.state + assert @tenant_membership.active? + end + +end diff --git a/test/unit/tenant_test.rb b/test/unit/tenant_test.rb new file mode 100644 index 0000000..4d4abce --- /dev/null +++ b/test/unit/tenant_test.rb @@ -0,0 +1,33 @@ +require 'test_helper' + +class TenantTest < ActiveSupport::TestCase + def test_should_have_a_valid_factory + assert Factory.build(:tenant).valid? + end + + def test_should_have_unique_name + tenant = Factory.create(:tenant) + assert !Factory.build(:tenant, :name => tenant.name).valid? + assert Factory.build(:tenant, :name => "different_#{tenant.name}").valid? + end + + def test_that_the_initial_state_should_be_active + @tenant = Factory.create(:tenant) + assert_equal 'active', @tenant.state + assert @tenant.active? + end + + def test_not_active_state_will_not_be_displayed + @tenant = Factory.create(:tenant) + assert_equal 1, Tenant.count + + @tenant.deactivate! + assert_equal 0, Tenant.count + assert_equal 1, Tenant.unscoped.count + + @tenant.activate! + assert_equal 1, Tenant.count + assert_equal Tenant.count, Tenant.unscoped.count + end + +end diff --git a/test/unit/user_group_membership_test.rb b/test/unit/user_group_membership_test.rb new file mode 100644 index 0000000..251229f --- /dev/null +++ b/test/unit/user_group_membership_test.rb @@ -0,0 +1,39 @@ +require 'test_helper' + +class UserGroupMembershipTest < ActiveSupport::TestCase + def test_should_have_a_valid_factory + assert Factory.build(:user_group_membership).valid? + end + def test_should_have_unique_members_in_each_group + group1 = Factory.create(:user_group) + group2 = Factory.create(:user_group) + user1 = Factory.create(:user) + user2 = Factory.create(:user) + member = Factory.create( + :user_group_membership, + :user_id => user1.id, + :user_group_id => group1.id + ) + assert !Factory.build( + :user_group_membership, + :user_id => user1.id, + :user_group_id => group1.id + ).valid? + assert Factory.build( + :user_group_membership, + :user_id => user1.id, + :user_group_id => group2.id + ).valid? + assert Factory.build( + :user_group_membership, + :user_id => user2.id, + :user_group_id => group1.id + ).valid? + end + + def test_that_the_initial_state_should_be_active + @user_group_membership = Factory.create(:user_group_membership) + assert_equal 'active', @user_group_membership.state + assert @user_group_membership.active? + end +end diff --git a/test/unit/user_group_test.rb b/test/unit/user_group_test.rb new file mode 100644 index 0000000..5e815a8 --- /dev/null +++ b/test/unit/user_group_test.rb @@ -0,0 +1,38 @@ +require 'test_helper' + +class UserGroupTest < ActiveSupport::TestCase + def test_should_have_a_valid_factory + assert Factory.build(:user_group).valid? + end + + def test_should_have_unique_name_on_same_tenant + tenant1 = Factory.create(:tenant) + tenant2 = Factory.create(:tenant) + group = Factory.create(:user_group, :tenant_id => tenant1.id) + assert !Factory.build(:user_group, :name => group.name, :tenant_id => tenant1.id).valid? + assert Factory.build(:user_group, :name => group.name, :tenant_id => tenant2.id).valid? + assert Factory.build(:user_group, :name => "different_#{group.name}", :tenant_id => tenant1.id).valid? + end + + test "user_group_membership only available for tenant_memberships" do + good_tenant = Factory.create(:tenant) + evil_tenant = Factory.create(:tenant) + + user = Factory.create(:user) + good_tenant.tenant_memberships.create(:user_id => user.id) + + good_user_group = good_tenant.user_groups.create(:name => 'Example') + evil_user_group = evil_tenant.user_groups.create(:name => 'Example') + + # Check the basics + assert_equal 1, good_tenant.user_groups.count + assert_equal 1, evil_tenant.user_groups.count + assert_equal 1, good_tenant.users.count + assert_equal 0, evil_tenant.users.count + + # Check if the user can become a member + assert good_user_group.user_group_memberships.build(:user_id => user.id).valid? + assert !evil_user_group.user_group_memberships.build(:user_id => user.id).valid? + end + +end diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb new file mode 100644 index 0000000..bb89e77 --- /dev/null +++ b/test/unit/user_test.rb @@ -0,0 +1,82 @@ +require 'test_helper' + +class UserTest < ActiveSupport::TestCase + + def test_should_have_a_valid_factory + assert Factory.build(:user).valid? + end + + def test_should_have_a_unique_email_address + user = Factory.create(:user) + assert !Factory.build(:user, :email => user.email).valid? + assert Factory.build(:user, :email => "different_#{user.email}").valid? + end + + def test_can_not_move_to_a_current_tenant_without_a_membership_relation + super_tenant = Factory.create(:tenant) + good_tenant = Factory.create(:tenant) + evil_tenant = Factory.create(:tenant) + + user = Factory.create(:user) + super_tenant.tenant_memberships.create(:user_id => user.id) + good_tenant.tenant_memberships.create(:user_id => user.id) + + assert user.update_attributes(:current_tenant_id => super_tenant.id) + assert !user.update_attributes(:current_tenant_id => evil_tenant.id) + assert user.update_attributes(:current_tenant_id => good_tenant.id) + end + + test "should be possible to modify the user without changing the PIN" do + user = Factory.create(:user) + pin_salt = user.pin_salt + pin_hash = user.pin_hash + user.middle_name = "#{user.middle_name} Foo" + assert user.save, "Should be possible to save the user." + user = User.where(:id => user.id).first + assert user + assert_equal pin_salt, user.pin_salt, "PIN salt should not change." + assert_equal pin_hash, user.pin_hash, "PIN hash should not change." + end + + test "should be possible to change the PIN" do + user = Factory.create(:user) + pin_salt = user.pin_salt + pin_hash = user.pin_hash + new_pin = '453267' + user.new_pin = new_pin + user.new_pin_confirmation = new_pin + assert user.save, "Should be possible to save the user." + user = User.where(:id => user.id).first + assert_not_equal "#{pin_salt}#{pin_hash}", "#{user.pin_salt}#{user.pin_hash}", + "PIN salt/hash should have changed." + end + + test "should not be possible to change the PIN if the confirmation does not match" do + user = Factory.create(:user) + pin_salt = user.pin_salt + pin_hash = user.pin_hash + user.new_pin = '123001' + user.new_pin_confirmation = '123002' + assert ! user.save, "Should not be possible to save the user." + assert ! user.valid?, "Should not be valid." + assert user.errors && user.errors.messages + assert ( + (user.errors.messages[:new_pin] && user.errors.messages[:new_pin].length > 0) || + (user.errors.messages[:new_pin_confirmation] && user.errors.messages[:new_pin_confirmation].length > 0) + ), "There should be an error message because new_pin != new_pin_confirmation." + end + + test "PIN must be numeric" do + user = Factory.create(:user) + new_pin = 'xxxx' + user.new_pin = new_pin + user.new_pin_confirmation = new_pin + assert ! user.save, "Should not be possible to save the user." + assert ! user.valid?, "Should not be valid." + assert ( + (user.errors.messages[:new_pin] && user.errors.messages[:new_pin].length > 0) || + (user.errors.messages[:new_pin_confirmation] && user.errors.messages[:new_pin_confirmation].length > 0) + ), "There should be an error message because PIN isn't numeric." + end + +end diff --git a/test/unit/whitelist_test.rb b/test/unit/whitelist_test.rb new file mode 100644 index 0000000..5678bbb --- /dev/null +++ b/test/unit/whitelist_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class WhitelistTest < ActiveSupport::TestCase + def test_should_be_valid + assert Whitelist.new.valid? + end +end diff --git a/vendor/assets/stylesheets/.gitkeep b/vendor/assets/stylesheets/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/vendor/assets/stylesheets/.gitkeep diff --git a/vendor/plugins/.gitkeep b/vendor/plugins/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/vendor/plugins/.gitkeep |