summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Wintermeyer <stefan.wintermeyer@amooma.de>2013-03-25 10:27:27 +0100
committerStefan Wintermeyer <stefan.wintermeyer@amooma.de>2013-03-25 10:27:27 +0100
commitdf6e17e48995f25e72509986f30700d778b179b6 (patch)
treef432c24b8e4ad81009188650dabfd99194883265
parent11f186a118285fbc87a536af26730780a9ad01f5 (diff)
parentcce94a74aa5c9691f9b37cd9be5a6831f8063812 (diff)
Merge branch 'develop'5.1.2
-rw-r--r--Gemfile9
-rw-r--r--Gemfile.lock143
-rw-r--r--app/assets/javascripts/application.js1
-rw-r--r--app/assets/javascripts/switchboard_entry.js.coffee6
-rw-r--r--app/assets/stylesheets/application.css1
-rw-r--r--app/assets/stylesheets/switchboard_entries.css.scss5
-rw-r--r--app/controllers/acd_agents_controller.rb32
-rw-r--r--app/controllers/application_controller.rb4
-rw-r--r--app/controllers/call_routes_controller.rb22
-rw-r--r--app/controllers/calls_controller.rb6
-rw-r--r--app/controllers/conference_invitees_controller.rb7
-rw-r--r--app/controllers/config_polycom_controller.rb4
-rw-r--r--app/controllers/config_siemens_controller.rb2
-rw-r--r--app/controllers/config_snom_controller.rb29
-rw-r--r--app/controllers/gemeinschaft_setups_controller.rb5
-rw-r--r--app/controllers/gs_nodes_controller.rb2
-rw-r--r--app/controllers/ringtones_controller.rb35
-rw-r--r--app/controllers/sip_accounts_controller.rb7
-rw-r--r--app/controllers/softkeys_controller.rb19
-rw-r--r--app/controllers/switchboard_entries_controller.rb74
-rw-r--r--app/controllers/switchboards_controller.rb66
-rw-r--r--app/controllers/trigger_controller.rb73
-rw-r--r--app/helpers/switchboard_entries_helper.rb2
-rw-r--r--app/helpers/switchboards_helper.rb2
-rw-r--r--app/models/ability.rb10
-rw-r--r--app/models/acd_agent.rb9
-rw-r--r--app/models/automatic_call_distributor.rb2
-rw-r--r--app/models/backup_job.rb2
-rw-r--r--app/models/call.rb24
-rw-r--r--app/models/call_route.rb18
-rw-r--r--app/models/conference.rb49
-rw-r--r--app/models/conference_invitee.rb19
-rw-r--r--app/models/gateway.rb27
-rw-r--r--app/models/gateway_setting.rb5
-rw-r--r--app/models/hunt_group.rb3
-rw-r--r--app/models/intruder.rb24
-rw-r--r--app/models/ringtone.rb2
-rw-r--r--app/models/sip_account.rb45
-rw-r--r--app/models/softkey.rb23
-rw-r--r--app/models/switchboard.rb16
-rw-r--r--app/models/switchboard_entry.rb31
-rw-r--r--app/models/user.rb4
-rw-r--r--app/views/acd_agents/_index_core.html.haml13
-rw-r--r--app/views/automatic_call_distributors/show.html.haml7
-rw-r--r--app/views/call_routes/test.html.haml55
-rw-r--r--app/views/conferences/_index_members.html.haml40
-rw-r--r--app/views/conferences/show.html.haml23
-rw-r--r--app/views/fax_documents/_fax_document.html.haml66
-rw-r--r--app/views/fax_documents/show.html.haml67
-rw-r--r--app/views/gateways/show.html.haml76
-rw-r--r--app/views/hunt_groups/show.html.haml7
-rw-r--r--app/views/layouts/_navbar.html.haml10
-rw-r--r--app/views/layouts/application.html.haml1
-rw-r--r--app/views/sip_accounts/show.html.haml21
-rw-r--r--app/views/softkeys/_form_core.html.haml6
-rw-r--r--app/views/softkeys/show.html.haml3
-rw-r--r--app/views/switchboard_entries/_form.html.haml7
-rw-r--r--app/views/switchboard_entries/_form_core.html.haml3
-rw-r--r--app/views/switchboard_entries/_index_core.html.haml17
-rw-r--r--app/views/switchboard_entries/_switchboard_entry.html.haml45
-rw-r--r--app/views/switchboard_entries/edit.html.haml3
-rw-r--r--app/views/switchboard_entries/index.html.haml6
-rw-r--r--app/views/switchboard_entries/new.html.haml3
-rw-r--r--app/views/switchboard_entries/show.html.haml22
-rw-r--r--app/views/switchboards/_current_user_dashboard.html.haml33
-rw-r--r--app/views/switchboards/_form.html.haml7
-rw-r--r--app/views/switchboards/_form_core.html.haml2
-rw-r--r--app/views/switchboards/_index_core.html.haml10
-rw-r--r--app/views/switchboards/edit.html.haml12
-rw-r--r--app/views/switchboards/index.html.haml8
-rw-r--r--app/views/switchboards/new.html.haml3
-rw-r--r--app/views/switchboards/show.html.haml21
-rw-r--r--app/views/tenants/_table_of_sip_accounts.html.haml45
-rw-r--r--app/views/trigger/voicemail.html.erb4
-rw-r--r--app/views/users/_switchboards.html.haml7
-rw-r--r--app/views/users/show.html.haml3
-rw-r--r--config/locales/views/conferences/de.yml13
-rw-r--r--config/locales/views/conferences/en.yml13
-rw-r--r--config/locales/views/sip_accounts/de.yml3
-rw-r--r--config/locales/views/sip_accounts/en.yml3
-rw-r--r--config/locales/views/switchboard_entries/de.yml55
-rw-r--r--config/locales/views/switchboard_entries/en.yml55
-rw-r--r--config/locales/views/switchboards/de.yml45
-rw-r--r--config/locales/views/switchboards/en.yml45
-rw-r--r--config/private_pub.yml2
-rw-r--r--config/routes.rb20
-rw-r--r--db/migrate/20130307104654_create_switchboards.rb13
-rw-r--r--db/migrate/20130307122344_create_switchboard_entries.rb15
-rw-r--r--db/migrate/20130309055700_change_perimeter_gs_parameters.rb18
-rw-r--r--db/migrate/20130314031100_add_event_socket_gs_parameters.rb11
-rw-r--r--db/migrate/20130314104000_change_calls_active.rb84
-rw-r--r--db/migrate/20130314134600_change_conference_parameters.rb37
-rw-r--r--db/migrate/20130315074600_change_calls_active2.rb86
-rw-r--r--lib/freeswitch_event.rb45
-rw-r--r--lib/tasks/backup.rake16
-rw-r--r--misc/freeswitch/conf/freeswitch.xml61
-rw-r--r--misc/freeswitch/scripts/common/array.lua97
-rw-r--r--misc/freeswitch/scripts/common/conference.lua364
-rw-r--r--misc/freeswitch/scripts/common/database.lua23
-rw-r--r--misc/freeswitch/scripts/common/gateway.lua79
-rw-r--r--misc/freeswitch/scripts/common/intruder.lua43
-rw-r--r--misc/freeswitch/scripts/common/log.lua25
-rw-r--r--misc/freeswitch/scripts/common/object.lua12
-rw-r--r--misc/freeswitch/scripts/common/perimeter.lua44
-rw-r--r--misc/freeswitch/scripts/common/sip_account.lua6
-rw-r--r--misc/freeswitch/scripts/common/str.lua51
-rw-r--r--misc/freeswitch/scripts/configuration.lua186
-rw-r--r--misc/freeswitch/scripts/dialplan/callback.lua85
-rw-r--r--misc/freeswitch/scripts/dialplan/dialplan.lua102
-rw-r--r--misc/freeswitch/scripts/dialplan/fax.lua4
-rw-r--r--misc/freeswitch/scripts/dialplan/functions.lua52
-rw-r--r--misc/freeswitch/scripts/dialplan/hunt_group.lua27
-rw-r--r--misc/freeswitch/scripts/dialplan/ivr.lua119
-rw-r--r--misc/freeswitch/scripts/dialplan/router.lua52
-rw-r--r--misc/freeswitch/scripts/dialplan/session.lua18
-rw-r--r--misc/freeswitch/scripts/dialplan/sip_call.lua11
-rw-r--r--misc/freeswitch/scripts/dialplan/voicemail.lua9
-rw-r--r--misc/freeswitch/scripts/event/perimeter_defense.lua24
-rw-r--r--misc/freeswitch/scripts/event/presence_update.lua142
-rw-r--r--misc/freeswitch/scripts/send_fax.lua6
-rw-r--r--misc/freeswitch/scripts/test_route.lua84
-rw-r--r--private_pub.ru10
-rw-r--r--test/functional/switchboard_entries_controller_test.rb49
-rw-r--r--test/functional/switchboards_controller_test.rb49
-rw-r--r--test/unit/switchboard_entry_test.rb7
-rw-r--r--test/unit/switchboard_test.rb7
126 files changed, 3208 insertions, 744 deletions
diff --git a/Gemfile b/Gemfile
index e4e839f..0084033 100644
--- a/Gemfile
+++ b/Gemfile
@@ -5,7 +5,7 @@ end
source 'http://rubygems.org'
-gem 'rails', '3.2.12'
+gem 'rails', '3.2.13'
gem 'bcrypt-ruby'
gem 'sqlite3'
gem 'mysql2'
@@ -54,8 +54,7 @@ group :test do
end
gem 'haml'
-# gem 'simple_form', '~> 2.0.1'
-gem 'simple_form', github: 'plataformatec/simple_form', branch: 'v2.1'
+gem 'simple_form', '~> 2.1.0'
# Image Upload
gem 'carrierwave'
@@ -80,7 +79,9 @@ gem 'uuid'
# Application server
gem 'unicorn'
+# http://railscasts.com/episodes/316-private-pub
gem 'thin'
+gem 'private_pub'
# Backup https://github.com/meskyanichi/backup
gem 'backup'
@@ -88,6 +89,8 @@ gem 'backup'
# Cronjobs
gem 'whenever'
+gem 'whois'
+
# Local Variables:
# mode: ruby
# End:
diff --git a/Gemfile.lock b/Gemfile.lock
index d8531cd..1a120fe 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,21 +1,12 @@
-GIT
- remote: git://github.com/plataformatec/simple_form.git
- revision: e3da7301dcf6feb9a1db275ff5cb3f85afce0e80
- branch: v2.1
- specs:
- simple_form (2.1.0.dev)
- actionpack (~> 3.0)
- activemodel (~> 3.0)
-
GEM
remote: http://rubygems.org/
specs:
- actionmailer (3.2.12)
- actionpack (= 3.2.12)
- mail (~> 2.4.4)
- actionpack (3.2.12)
- activemodel (= 3.2.12)
- activesupport (= 3.2.12)
+ actionmailer (3.2.13)
+ actionpack (= 3.2.13)
+ mail (~> 2.5.3)
+ actionpack (3.2.13)
+ activemodel (= 3.2.13)
+ activesupport (= 3.2.13)
builder (~> 3.0.0)
erubis (~> 2.7.0)
journey (~> 1.0.4)
@@ -23,30 +14,33 @@ GEM
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.2.1)
- activemodel (3.2.12)
- activesupport (= 3.2.12)
+ activemodel (3.2.13)
+ activesupport (= 3.2.13)
builder (~> 3.0.0)
- activerecord (3.2.12)
- activemodel (= 3.2.12)
- activesupport (= 3.2.12)
+ activerecord (3.2.13)
+ activemodel (= 3.2.13)
+ activesupport (= 3.2.13)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
- activeresource (3.2.12)
- activemodel (= 3.2.12)
- activesupport (= 3.2.12)
- activesupport (3.2.12)
- i18n (~> 0.6)
+ activeresource (3.2.13)
+ activemodel (= 3.2.13)
+ activesupport (= 3.2.13)
+ activesupport (3.2.13)
+ i18n (= 0.6.1)
multi_json (~> 1.0)
- acts_as_list (0.1.9)
+ acts_as_list (0.2.0)
+ activerecord (>= 3.0)
+ addressable (2.3.3)
arel (3.0.2)
- backup (3.0.27)
+ backup (3.1.3)
open4 (~> 1.3.0)
thor (>= 0.15.4, < 2)
bcrypt-ruby (3.0.1)
- better_errors (0.5.0)
+ better_errors (0.7.2)
coderay (>= 1.0.0)
erubis (>= 2.6.6)
- binding_of_caller (0.6.8)
+ binding_of_caller (0.7.1)
+ debug_inspector (>= 0.0.1)
breadcrumbs_on_rails (2.3.0)
builder (3.0.4)
cache_digests (0.2.0)
@@ -55,31 +49,41 @@ GEM
carrierwave (0.8.0)
activemodel (>= 3.2.0)
activesupport (>= 3.2.0)
- chronic (0.9.0)
+ chronic (0.9.1)
chunky_png (1.2.7)
- coderay (1.0.8)
+ coderay (1.0.9)
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.4.0)
+ coffee-script-source (1.6.1)
compass (0.12.2)
chunky_png (~> 1.2)
fssm (>= 0.2.7)
sass (~> 3.1)
compass-rails (1.0.3)
compass (>= 0.12.2, < 0.14)
+ cookiejar (0.3.0)
daemons (1.1.9)
dalli (2.6.2)
+ debug_inspector (0.0.2)
delayed_job (3.0.5)
activesupport (~> 3.0)
- delayed_job_active_record (0.4.1)
+ delayed_job_active_record (0.4.3)
activerecord (>= 2.1.0, < 4)
delayed_job (~> 3.0)
+ em-http-request (1.0.3)
+ addressable (>= 2.2.3)
+ cookiejar
+ em-socksify
+ eventmachine (>= 1.0.0.beta.4)
+ http_parser.rb (>= 0.5.3)
+ em-socksify (0.2.1)
+ eventmachine (>= 1.0.0.beta.4)
erubis (2.7.0)
- eventmachine (1.0.0)
+ eventmachine (1.0.3)
execjs (1.4.0)
multi_json (~> 1.0)
factory_girl (4.2.0)
@@ -87,12 +91,22 @@ GEM
factory_girl_rails (4.2.1)
factory_girl (~> 4.2.0)
railties (>= 3.0.0)
+ faye (0.8.9)
+ 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.7)
+ eventmachine (>= 0.12.0)
fssm (0.2.10)
haml (4.0.0)
tilt
hike (1.2.1)
hirb (0.7.1)
http_accept_language (1.0.2)
+ http_parser.rb (0.5.3)
i18n (0.6.1)
inifile (2.0.2)
journey (1.0.4)
@@ -103,20 +117,22 @@ GEM
kgio (2.8.0)
macaddr (1.6.1)
systemu (~> 2.5.0)
- mail (2.4.4)
+ mail (2.5.3)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
mime-types (1.21)
- mini_magick (3.4)
+ mini_magick (3.5.0)
subexec (~> 0.2.1)
- multi_json (1.6.0)
+ multi_json (1.7.1)
mysql2 (0.3.11)
- nokogiri (1.5.6)
+ nokogiri (1.5.7)
open4 (1.3.0)
polyglot (0.3.3)
- quiet_assets (1.0.1)
- railties (~> 3.1)
+ private_pub (1.0.3)
+ faye
+ quiet_assets (1.0.2)
+ railties (>= 3.1, < 5.0)
rack (1.4.5)
rack-cache (1.2)
rack (>= 0.4)
@@ -124,26 +140,26 @@ GEM
rack
rack-test (0.6.2)
rack (>= 1.0)
- rails (3.2.12)
- actionmailer (= 3.2.12)
- actionpack (= 3.2.12)
- activerecord (= 3.2.12)
- activeresource (= 3.2.12)
- activesupport (= 3.2.12)
+ rails (3.2.13)
+ actionmailer (= 3.2.13)
+ actionpack (= 3.2.13)
+ activerecord (= 3.2.13)
+ activeresource (= 3.2.13)
+ activesupport (= 3.2.13)
bundler (~> 1.0)
- railties (= 3.2.12)
- railties (3.2.12)
- actionpack (= 3.2.12)
- activesupport (= 3.2.12)
+ railties (= 3.2.13)
+ railties (3.2.13)
+ actionpack (= 3.2.13)
+ activesupport (= 3.2.13)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (>= 0.14.6, < 2.0)
raindrops (0.10.0)
rake (10.0.3)
- rdoc (3.12.1)
+ rdoc (3.12.2)
json (~> 1.4)
- sass (3.2.5)
+ sass (3.2.7)
sass-rails (3.2.6)
railties (~> 3.2.0)
sass (>= 3.1.10)
@@ -151,6 +167,9 @@ GEM
sextant (0.2.3)
activesupport (>= 3.2)
rails (>= 3.2)
+ simple_form (2.1.0)
+ actionpack (~> 3.0)
+ activemodel (~> 3.0)
sprockets (2.2.2)
hike (~> 1.2)
multi_json (~> 1.0)
@@ -158,35 +177,37 @@ GEM
tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.7)
state_machine (1.1.2)
- strong_parameters (0.1.6)
+ strong_parameters (0.2.0)
actionpack (~> 3.0)
activemodel (~> 3.0)
railties (~> 3.0)
subexec (0.2.2)
systemu (2.5.2)
- thin (1.5.0)
+ thin (1.5.1)
daemons (>= 1.0.9)
eventmachine (>= 0.12.6)
rack (>= 1.0.0)
thor (0.17.0)
- tilt (1.3.3)
+ tilt (1.3.6)
treetop (1.4.12)
polyglot
polyglot (>= 0.3.1)
- tzinfo (0.3.35)
+ tzinfo (0.3.37)
uglifier (1.3.0)
execjs (>= 0.3.0)
multi_json (~> 1.0, >= 1.0.2)
- unicorn (4.6.0)
+ unicorn (4.6.2)
kgio (~> 2.6)
rack
raindrops (~> 0.7)
- uuid (2.3.6)
+ uuid (2.3.7)
macaddr (~> 1.0)
whenever (0.8.2)
activesupport (>= 2.3.4)
chronic (>= 0.6.3)
+ whois (2.7.0)
will_paginate (3.0.4)
+ yajl-ruby (1.1.0)
PLATFORMS
ruby
@@ -217,11 +238,12 @@ DEPENDENCIES
mini_magick
mysql2
nokogiri
+ private_pub
quiet_assets
- rails (= 3.2.12)
+ rails (= 3.2.13)
sass-rails
sextant
- simple_form!
+ simple_form (~> 2.1.0)
sqlite3
state_machine
strong_parameters
@@ -230,4 +252,5 @@ DEPENDENCIES
unicorn
uuid
whenever
+ whois
will_paginate
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index c6f4107..68a6e22 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -13,4 +13,5 @@
//= require jquery
//= require jquery-ui
//= require jquery_ujs
+//= require private_pub
//= require_tree .
diff --git a/app/assets/javascripts/switchboard_entry.js.coffee b/app/assets/javascripts/switchboard_entry.js.coffee
new file mode 100644
index 0000000..d14825e
--- /dev/null
+++ b/app/assets/javascripts/switchboard_entry.js.coffee
@@ -0,0 +1,6 @@
+jQuery ->
+ $('#switchboard_entries').sortable
+ axis: 'y'
+ handle: '.handle'
+ update: ->
+ $.post($(this).data('update-url'), $(this).sortable('serialize')) \ No newline at end of file
diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
index 44868e4..557bbda 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.css
@@ -16,4 +16,5 @@
*= require softkeys
*= require phone_numbers
*= require route_elements
+ *= require switchboard_entries
*/
diff --git a/app/assets/stylesheets/switchboard_entries.css.scss b/app/assets/stylesheets/switchboard_entries.css.scss
new file mode 100644
index 0000000..12b67ee
--- /dev/null
+++ b/app/assets/stylesheets/switchboard_entries.css.scss
@@ -0,0 +1,5 @@
+#switchboard_entries .handle {
+ font-size: 12px;
+ color: #777;
+ cursor: move;
+} \ No newline at end of file
diff --git a/app/controllers/acd_agents_controller.rb b/app/controllers/acd_agents_controller.rb
index 4c08f68..e2aabdf 100644
--- a/app/controllers/acd_agents_controller.rb
+++ b/app/controllers/acd_agents_controller.rb
@@ -1,7 +1,7 @@
class AcdAgentsController < ApplicationController
- load_and_authorize_resource :automatic_call_distributor
- load_and_authorize_resource :acd_agent, :through => [:automatic_call_distributor]
-
+ load_and_authorize_resource :automatic_call_distributor, :except => [:toggle]
+ load_and_authorize_resource :acd_agent, :through => [:automatic_call_distributor], :except => [:toggle]
+ load_and_authorize_resource :acd_agent, :only => [:toggle]
before_filter :spread_breadcrumbs
def index
@@ -62,16 +62,24 @@ class AcdAgentsController < ApplicationController
redirect_to automatic_call_distributor_acd_agents_path(@automatic_call_distributor), :notice => t('acd_agents.controller.successfuly_destroyed')
end
+ def toggle
+ @acd_agent.toggle_status
+ redirect_to request.referer
+ end
+
+ private
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)
+ if @automatic_call_distributor
+ 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
end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index d1d918e..12bea54 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -110,6 +110,10 @@ class ApplicationController < ActionController::Base
redirect_to log_in_path, :alert => 'Access denied! You need to login first.'
end
end
+
+ def request_remote_ip
+ request.env['HTTP_X_FORWARDED_FOR'] || request.remote_ip
+ end
private
diff --git a/app/controllers/call_routes_controller.rb b/app/controllers/call_routes_controller.rb
index 2dcd648..e5cf56a 100644
--- a/app/controllers/call_routes_controller.rb
+++ b/app/controllers/call_routes_controller.rb
@@ -87,6 +87,28 @@ class CallRoutesController < ApplicationController
end
end
+ def test
+ if !params[:sip_account_id].blank?
+ account = SipAccount.where(:id => params[:sip_account_id]).first
+ elsif !params[:hunt_group_id].blank?
+ account = HuntGroup.where(:id => params[:hunt_group_id]).first
+ end
+
+ if account
+ destination_number = params[:destination_number]
+ routing_table = params[:routing_table]
+ @route_test = CallRoute.test_route(routing_table, {
+ 'caller.destination_number' => destination_number,
+ 'caller.auth_account_type' => account.class.name,
+ 'caller.auth_account_id' => account.id,
+ 'caller.auth_account_uuid' => account.try(:uuid),
+ 'caller.account_type' => account.class.name,
+ 'caller.account_id' => account.id,
+ 'caller.account_uuid' => account.try(:uuid),
+ })
+ end
+ end
+
private
def call_route_parameter_params
params.require(:call_route).permit(:routing_table, :name, :endpoint_type, :endpoint_id, :position)
diff --git a/app/controllers/calls_controller.rb b/app/controllers/calls_controller.rb
index 9d85a10..ae0093c 100644
--- a/app/controllers/calls_controller.rb
+++ b/app/controllers/calls_controller.rb
@@ -17,11 +17,11 @@ class CallsController < ApplicationController
if !params[:url].blank?
protocol, separator, phone_number = params[:url].partition(':')
if ! phone_number.blank?
- @call = @parent.calls.new()
+ @call = @parent.call_legs.new()
@call.destination = phone_number
end
elsif !params[:number].blank?
- @call = @parent.calls.new()
+ @call = @parent.call_legs.new()
@call.destination = params[:number]
end
end
@@ -31,7 +31,7 @@ class CallsController < ApplicationController
end
def create
- @call = @sip_account.calls.create(params[:call])
+ @call = @sip_account.call_legs.build(params[:call])
if @call && @call.call
m = method( :"#{@parent.class.name.underscore}_calls_url" )
diff --git a/app/controllers/conference_invitees_controller.rb b/app/controllers/conference_invitees_controller.rb
index e891ebc..cbe4790 100644
--- a/app/controllers/conference_invitees_controller.rb
+++ b/app/controllers/conference_invitees_controller.rb
@@ -35,10 +35,7 @@ class ConferenceInviteesController < ApplicationController
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)
+ redirect_to conference_conference_invitees_url(@conference), :notice => t('conference_invitees.controller.successfuly_created')
else
render :new
end
@@ -50,7 +47,7 @@ class ConferenceInviteesController < ApplicationController
def update
if @conference_invitee.update_attributes(params[:conference_invitee])
- redirect_to @conference_invitee, :notice => t('conference_invitees.controller.successfuly_updated')
+ redirect_to conference_conference_invitees_url(@conference), :notice => t('conference_invitees.controller.successfuly_updated')
else
render :edit
end
diff --git a/app/controllers/config_polycom_controller.rb b/app/controllers/config_polycom_controller.rb
index fd730e3..ca314c2 100644
--- a/app/controllers/config_polycom_controller.rb
+++ b/app/controllers/config_polycom_controller.rb
@@ -67,8 +67,8 @@ class ConfigPolycomController < ApplicationController
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 })
+ 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"
diff --git a/app/controllers/config_siemens_controller.rb b/app/controllers/config_siemens_controller.rb
index 1966d49..7d5eb3f 100644
--- a/app/controllers/config_siemens_controller.rb
+++ b/app/controllers/config_siemens_controller.rb
@@ -146,7 +146,7 @@ class ConfigSiemensController < ApplicationController
country = 'US'
language = 'en'
if ! @phone.nil?
- @phone.update_attributes(:ip_address => request.remote_ip)
+ @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
diff --git a/app/controllers/config_snom_controller.rb b/app/controllers/config_snom_controller.rb
index eb94038..8db1d57 100644
--- a/app/controllers/config_snom_controller.rb
+++ b/app/controllers/config_snom_controller.rb
@@ -233,8 +233,8 @@ class ConfigSnomController < ApplicationController
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 })
+ 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()
@@ -281,7 +281,30 @@ class ConfigSnomController < ApplicationController
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-"})
+ conference = softkey.softkeyable
+ if conference.class == Conference
+ @softkeys.push({
+ :context => sip_account_index,
+ :function => softkey.softkey_function.name,
+ :label => softkey.label,
+ :softkey => softkey,
+ :general_type => t("softkeys.functions.#{softkey.softkey_function.name}"),
+ :subscription => {
+ :to => "sip:conference#{conference.id}@#{sip_account.host}",
+ :for => "sip:conference#{conference.id}@#{sip_account.host}",
+ },
+ :actions => [{
+ :type => :dial,
+ :target => "f-ta-#{softkey.number}",
+ :when => 'on press',
+ :states => 'connected,holding',
+ },{
+ :type => :dial,
+ :target => softkey.number,
+ :when => 'on press',
+ }],
+ })
+ end
when 'parking_stall'
parking_stall = softkey.softkeyable
if parking_stall.class == ParkingStall
diff --git a/app/controllers/gemeinschaft_setups_controller.rb b/app/controllers/gemeinschaft_setups_controller.rb
index a62df99..4949fa7 100644
--- a/app/controllers/gemeinschaft_setups_controller.rb
+++ b/app/controllers/gemeinschaft_setups_controller.rb
@@ -62,6 +62,11 @@ class GemeinschaftSetupsController < ApplicationController
GsParameter.where(:name => 'ringtone_url').first.update_attributes(:value => "http://#{@gemeinschaft_setup.sip_domain.host}")
GsParameter.where(:name => 'user_image_url').first.update_attributes(:value => "http://#{@gemeinschaft_setup.sip_domain.host}/uploads/user/image")
+ # Set ringback_tone
+ if @gemeinschaft_setup.country.country_code.to_s == '49'
+ GsParameter.where(:entity => 'dialplan', :section => 'variables', :name => 'ringback').first.update_attributes(:value => '%(1000,4000,425.0)')
+ end
+
# Restart FreeSWITCH
if Rails.env.production?
require 'freeswitch_event'
diff --git a/app/controllers/gs_nodes_controller.rb b/app/controllers/gs_nodes_controller.rb
index 17c9e8b..6a5e0a6 100644
--- a/app/controllers/gs_nodes_controller.rb
+++ b/app/controllers/gs_nodes_controller.rb
@@ -48,7 +48,7 @@ class GsNodesController < ApplicationController
end
def sync
- if !GsNode.where(:ip_address => request.remote_ip).first
+ if !GsNode.where(:ip_address => request_remote_ip).first
render(
:status => 404,
:layout => false,
diff --git a/app/controllers/ringtones_controller.rb b/app/controllers/ringtones_controller.rb
index e5a4f64..96e9c6d 100644
--- a/app/controllers/ringtones_controller.rb
+++ b/app/controllers/ringtones_controller.rb
@@ -49,19 +49,42 @@ class RingtonesController < ApplicationController
end
def spread_breadcrumbs
- if @parent.class == PhoneNumber && @parent.phone_numberable.class == SipAccount
- @sip_account = @parent.phone_numberable
+ 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)
- 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)
+ add_breadcrumb t("ringtones.index.page_title"), sip_account_ringtones_path(@sip_account)
if @ringtone && !@ringtone.new_record?
- add_breadcrumb @ringtone, phone_number_ringtone_path(@phone_number, @ringtone)
+ add_breadcrumb @ringtone
+ end
+ elsif @parent.class == PhoneNumber
+ if @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
+ end
+ elsif @parent.phone_numberable.class == HuntGroup
+ @hunt_group = @parent.phone_numberable
+ add_breadcrumb t("hunt_groups.index.page_title"), method( :"#{@hunt_group.tenant.class.name.underscore}_hunt_groups_path" ).(@hunt_group.tenant)
+ add_breadcrumb @hunt_group, method( :"#{@hunt_group.tenant.class.name.underscore}_hunt_group_path" ).(@hunt_group.tenant, @hunt_group)
+ add_breadcrumb t("phone_numbers.index.page_title"), hunt_group_phone_numbers_path(@hunt_group)
+ add_breadcrumb @phone_number, hunt_group_phone_number_path(@hunt_group, @phone_number)
+ add_breadcrumb t("ringtones.index.page_title"), phone_number_ringtones_path(@phone_number)
+ if @ringtone && !@ringtone.new_record?
+ add_breadcrumb @ringtone
+ end
end
end
end
diff --git a/app/controllers/sip_accounts_controller.rb b/app/controllers/sip_accounts_controller.rb
index b34172d..0d34109 100644
--- a/app/controllers/sip_accounts_controller.rb
+++ b/app/controllers/sip_accounts_controller.rb
@@ -10,7 +10,10 @@ class SipAccountsController < ApplicationController
end
def show
- @register_tel_protocol = "#{request.protocol}#{request.host_with_port}/sip_accounts/#{@sip_account.try(:id)}/calls/new?url=%s"
+ @register_protocols = {
+ :tel => "#{request.protocol}#{request.host_with_port}/sip_accounts/#{@sip_account.try(:id)}/calls/new?url=%s",
+ :callto => "#{request.protocol}#{request.host_with_port}/sip_accounts/#{@sip_account.try(:id)}/calls/new?url=%s",
+ }
end
def new
@@ -72,7 +75,7 @@ class SipAccountsController < ApplicationController
def destroy
@sip_account.destroy
m = method( :"#{@parent.class.name.underscore}_sip_accounts_url" )
- redirect_to :root, :notice => t('sip_accounts.controller.successfuly_destroyed')
+ redirect_to m.(@parent), :notice => t('sip_accounts.controller.successfuly_destroyed')
end
private
diff --git a/app/controllers/softkeys_controller.rb b/app/controllers/softkeys_controller.rb
index 9179d8c..adb0e10 100644
--- a/app/controllers/softkeys_controller.rb
+++ b/app/controllers/softkeys_controller.rb
@@ -2,7 +2,7 @@ class SoftkeysController < ApplicationController
load_and_authorize_resource :sip_account, :except => [:sort]
load_and_authorize_resource :softkey, :through => [:sip_account], :except => [:sort]
- before_filter :set_available_softkey_functions, :only => [ :new, :edit, :update ]
+ before_filter :set_available_softkey_functions, :only => [ :new, :edit, :update, :create ]
before_filter :spread_breadcrumbs, :except => [:sort]
def index
@@ -13,8 +13,6 @@ class SoftkeysController < ApplicationController
def new
@softkey = @sip_account.softkeys.build
-
- delete_call_forward_softkey_if_no_callforward_is_available
end
def create
@@ -27,14 +25,12 @@ class SoftkeysController < ApplicationController
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
@@ -56,10 +52,13 @@ class SoftkeysController < ApplicationController
private
def set_available_softkey_functions
+ @possible_call_forwards = @softkey.possible_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
+ if softkey_function.name != 'call_forwarding' or @possible_call_forwards.count > 0
+ @softkey_functions << softkey_function
+ end
end
end
end
@@ -77,12 +76,4 @@ class SoftkeysController < ApplicationController
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/switchboard_entries_controller.rb b/app/controllers/switchboard_entries_controller.rb
new file mode 100644
index 0000000..ef6c72e
--- /dev/null
+++ b/app/controllers/switchboard_entries_controller.rb
@@ -0,0 +1,74 @@
+class SwitchboardEntriesController < ApplicationController
+ load_and_authorize_resource :switchboard
+ authorize_resource :switchboard_entry, :through => :switchboard, :except => [:sort]
+
+ def index
+ @switchboard_entries = @switchboard.switchboard_entries
+ spread_breadcrumbs
+ end
+
+ def show
+ @switchboard_entry = @switchboard.switchboard_entries.find(params[:id])
+ spread_breadcrumbs
+ end
+
+ def new
+ @switchboard_entry = @switchboard.switchboard_entries.build
+ @sip_accounts = SipAccount.all - @switchboard.sip_accounts
+ spread_breadcrumbs
+ end
+
+ def create
+ @switchboard_entry = @switchboard.switchboard_entries.build(switchboard_entry_params)
+ spread_breadcrumbs
+ if @switchboard_entry.save
+ redirect_to switchboard_switchboard_entries_path(@switchboard), :notice => t('switchboard_entries.controller.successfuly_created')
+ else
+ render :new
+ end
+ end
+
+ def edit
+ @switchboard_entry = @switchboard.switchboard_entries.find(params[:id])
+ @sip_accounts = SipAccount.all - @switchboard.sip_accounts + [@switchboard_entry.sip_account]
+ spread_breadcrumbs
+ end
+
+ def update
+ @switchboard_entry = @switchboard.switchboard_entries.find(params[:id])
+ if @switchboard_entry.update_attributes(switchboard_entry_params)
+ redirect_to [@switchboard, @switchboard_entry], :notice => t('switchboard_entries.controller.successfuly_updated')
+ else
+ render :edit
+ end
+ end
+
+ def destroy
+ @switchboard_entry = @switchboard.switchboard_entries.find(params[:id])
+ @switchboard_entry.destroy
+ redirect_to switchboard_switchboard_entries_path(@switchboard), :notice => t('switchboard_entries.controller.successfuly_destroyed')
+ end
+
+ def sort
+ params[:switchboard_entry].reverse.each do |id|
+ @switchboard.switchboard_entries.find(id).move_to_top
+ end
+ render nothing: true
+ end
+
+ private
+ def switchboard_entry_params
+ params.require(:switchboard_entry).permit(:name, :sip_account_id)
+ end
+
+ def spread_breadcrumbs
+ add_breadcrumb t("users.index.page_title"), tenant_users_path(@switchboard.user.current_tenant)
+ add_breadcrumb @switchboard.user, tenant_user_path(@switchboard.user.current_tenant, @switchboard.user)
+ add_breadcrumb t("switchboards.index.page_title"), user_switchboards_path(@switchboard.user)
+ add_breadcrumb @switchboard, user_switchboard_path(@switchboard.user, @switchboard)
+ add_breadcrumb t("switchboard_entries.index.page_title"), switchboard_switchboard_entries_path(@switchboard)
+ if @switchboard_entry && !@switchboard_entry.new_record?
+ add_breadcrumb @switchboard_entry, switchboard_switchboard_entries_path(@switchboard, @switchboard_entry)
+ end
+ end
+end
diff --git a/app/controllers/switchboards_controller.rb b/app/controllers/switchboards_controller.rb
new file mode 100644
index 0000000..98008c1
--- /dev/null
+++ b/app/controllers/switchboards_controller.rb
@@ -0,0 +1,66 @@
+class SwitchboardsController < ApplicationController
+ load_and_authorize_resource :user
+ authorize_resource :switchboard, :through => :user
+
+ def index
+ @switchboards = @user.switchboards
+ spread_breadcrumbs
+ end
+
+ def show
+ @switchboard = @user.switchboards.find(params[:id])
+ @switchboard_entries = @switchboard.switchboard_entries
+ spread_breadcrumbs
+ end
+
+ def new
+ @switchboard = @user.switchboards.build
+ spread_breadcrumbs
+ end
+
+ def create
+ @switchboard = @user.switchboards.build(switchboard_params)
+ spread_breadcrumbs
+ if @switchboard.save
+ redirect_to user_switchboards_path(@user), :notice => t('switchboards.controller.successfuly_created')
+ else
+ render :new
+ end
+ end
+
+ def edit
+ @switchboard = @user.switchboards.find(params[:id])
+ spread_breadcrumbs
+ end
+
+ def update
+ @switchboard = @user.switchboards.find(params[:id])
+ spread_breadcrumbs
+ if @switchboard.update_attributes(switchboard_params)
+ redirect_to [@user, @switchboard], :notice => t('switchboards.controller.successfuly_updated')
+ else
+ render :edit
+ end
+ end
+
+ def destroy
+ @switchboard = @user.switchboards.find(params[:id])
+ @switchboard.destroy
+ spread_breadcrumbs
+ redirect_to user_switchboards_path(@user), :notice => t('switchboards.controller.successfuly_destroyed')
+ end
+
+ private
+ def switchboard_params
+ params.require(:switchboard).permit(:name)
+ end
+
+ def spread_breadcrumbs
+ 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("switchboards.index.page_title"), user_switchboards_path(@user)
+ if @switchboard && !@switchboard.new_record?
+ add_breadcrumb @switchboard, user_switchboard_path(@user, @switchboard)
+ end
+ end
+end
diff --git a/app/controllers/trigger_controller.rb b/app/controllers/trigger_controller.rb
index 5e836c4..6b58d6a 100644
--- a/app/controllers/trigger_controller.rb
+++ b/app/controllers/trigger_controller.rb
@@ -16,6 +16,11 @@ class TriggerController < ApplicationController
next
end
+ # Indicate a new voicemail in the navigation bar.
+ #
+ PrivatePub.publish_to("/users/#{user.id}/messages/new", "$('#new_voicemail_or_fax_indicator').hide('fast').show('slow');")
+ PrivatePub.publish_to("/users/#{user.id}/messages/new", "document.title = '* ' + document.title.replace( '* ' , '');")
+
if user.email.blank?
next
end
@@ -59,6 +64,64 @@ class TriggerController < ApplicationController
end
end
+ def fax_has_been_sent
+ fax_document = FaxDocument.find(params[:id])
+
+ if fax_document
+ # push the partial to the webbrowser
+ #
+ new_html = ActionController::Base.helpers.escape_javascript(render_to_string("fax_documents/_fax_document", :layout => false, :locals => {:fax_document => fax_document}))
+ PrivatePub.publish_to("/fax_documents/#{fax_document.id}", "$('#" + fax_document.id.to_s + ".fax_document').replaceWith('#{new_html}');")
+
+ render(
+ :status => 200,
+ :layout => false,
+ :content_type => 'text/plain',
+ :text => "<!-- OK -->",
+ )
+ else
+ render(
+ :status => 501,
+ :layout => false,
+ :content_type => 'text/plain',
+ :text => "<!-- ERRORS: #{errors.join(', ')} -->",
+ )
+ end
+ end
+
+ def sip_account_update
+ sip_account = SipAccount.find(params[:id])
+
+ if sip_account.updated_at < Time.now
+
+ # Push an update to sip_account.switchboard_entries
+ #
+ sip_account.switchboard_entries.each do |switchboard_entry|
+ escaped_switchboard_entry_partial = ActionController::Base.helpers.escape_javascript(render_to_string("switchboard_entries/_switchboard_entry", :layout => false, :locals => {:switchboard_entry => switchboard_entry}))
+ PrivatePub.publish_to("/switchboards/#{switchboard_entry.switchboard.id}", "$('#switchboard_entry_id_" + switchboard_entry.id.to_s + "').replaceWith('#{escaped_switchboard_entry_partial}');")
+ end
+
+ # Push an update to the needed switchboards
+ #
+ Switchboard.where(:user_id => sip_account.sip_accountable.id).each do |switchboard|
+ if sip_account.call_legs.where(:sip_account_id => switchboard.user.sip_account_ids).any? ||
+ sip_account.b_call_legs.where(:sip_account_id => switchboard.user.sip_account_ids).any?
+ escaped_switchboard_partial = ActionController::Base.helpers.escape_javascript(render_to_string("switchboards/_current_user_dashboard", :layout => false, :locals => {:current_user => switchboard.user}))
+ PrivatePub.publish_to("/switchboards/#{switchboard.id}", "$('.dashboard').replaceWith('#{escaped_switchboard_partial}');")
+ end
+ end
+
+ sip_account.touch
+ end
+
+ render(
+ :status => 200,
+ :layout => false,
+ :content_type => 'text/plain',
+ :text => "<!-- OK -->",
+ )
+ end
+
def fax
if !params[:fax_account_id].blank?
fax_account = FaxAccount.where(:id => params[:fax_account_id].to_i).first
@@ -87,6 +150,8 @@ class TriggerController < ApplicationController
if fax_document.save
Notifications.new_fax(fax_document).deliver
+ @last_fax_document = fax_document
+
begin
File.delete(pdf_file)
rescue => e
@@ -106,6 +171,14 @@ class TriggerController < ApplicationController
end
if errors.count == 0
+ # Indicate a new fax in the navigation bar.
+ #
+ if @last_fax_document.fax_account.fax_accountable.class == User
+ user = @last_fax_document.fax_account.fax_accountable
+ PrivatePub.publish_to("/users/#{user.id}/messages/new", "$('#new_voicemail_or_fax_indicator').hide('fast').show('slow');")
+ PrivatePub.publish_to("/users/#{user.id}/messages/new", "document.title = '* ' + document.title.replace( '* ' , '');")
+ end
+
render(
:status => 200,
:layout => false,
diff --git a/app/helpers/switchboard_entries_helper.rb b/app/helpers/switchboard_entries_helper.rb
new file mode 100644
index 0000000..ca9b1f9
--- /dev/null
+++ b/app/helpers/switchboard_entries_helper.rb
@@ -0,0 +1,2 @@
+module SwitchboardEntriesHelper
+end
diff --git a/app/helpers/switchboards_helper.rb b/app/helpers/switchboards_helper.rb
new file mode 100644
index 0000000..6fbea8a
--- /dev/null
+++ b/app/helpers/switchboards_helper.rb
@@ -0,0 +1,2 @@
+module SwitchboardsHelper
+end
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 3cd1d4d..2dd96b8 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -170,6 +170,11 @@ class Ability
can :manage, Ringtone, :ringtoneable_type => 'SipAccount', :ringtoneable_id => user.sip_account_ids
can :create, Ringtone
+ # User can read and toggle status of ACD agents
+ #
+ can :read, AcdAgent, :destination_type => 'SipAccount', :destination_id => user.sip_account_ids
+ can :toggle, AcdAgent, :destination_type => 'SipAccount', :destination_id => user.sip_account_ids
+
# SoftkeyFunctions
#
can :read, SoftkeyFunction
@@ -178,6 +183,11 @@ class Ability
#
can :manage, VoicemailMessage
can :manage, VoicemailSetting
+
+ # Switchboard
+ #
+ can :read, Switchboard, :id => user.switchboard_ids
+ can :read, SwitchboardEntry, :switchboard_id => user.switchboard_ids
end
end
else
diff --git a/app/models/acd_agent.rb b/app/models/acd_agent.rb
index 4be4700..61899f8 100644
--- a/app/models/acd_agent.rb
+++ b/app/models/acd_agent.rb
@@ -20,6 +20,15 @@ class AcdAgent < ActiveRecord::Base
self.name || I18n.t('acd_agents.name') + ' ID ' + self.id.to_s
end
+ def toggle_status
+ if self.status == 'active'
+ self.status = 'inactive'
+ else
+ self.status = 'active'
+ end
+ return self.save
+ end
+
private
def set_presence
dialplan_function = nil
diff --git a/app/models/automatic_call_distributor.rb b/app/models/automatic_call_distributor.rb
index 5807757..b9d7c51 100644
--- a/app/models/automatic_call_distributor.rb
+++ b/app/models/automatic_call_distributor.rb
@@ -5,6 +5,8 @@ class AutomaticCallDistributor < ActiveRecord::Base
has_many :acd_agents, :dependent => :destroy
has_many :phone_numbers, :as => :phone_numberable, :dependent => :destroy
+ has_many :call_forwards, :as => :call_forwardable, :dependent => :destroy
+
accepts_nested_attributes_for :phone_numbers,
:reject_if => lambda { |phone_number| phone_number[:number].blank? },
:allow_destroy => true
diff --git a/app/models/backup_job.rb b/app/models/backup_job.rb
index 48dd27e..9cb4f53 100644
--- a/app/models/backup_job.rb
+++ b/app/models/backup_job.rb
@@ -37,7 +37,7 @@ class BackupJob < ActiveRecord::Base
if tmp_backup_directory.blank?
self.state = 'failed'
else
- system "cd #{backup_directory} && sudo /bin/tar czf #{backup_name_prefix}#{File.basename(tmp_backup_directory)}.tar.gz #{File.basename(tmp_backup_directory)}"
+ system "cd #{backup_directory} && nice -n 19 sudo /bin/tar czf #{backup_name_prefix}#{File.basename(tmp_backup_directory)}.tar.gz #{File.basename(tmp_backup_directory)}"
require 'fileutils'
FileUtils.rm_rf tmp_backup_directory
file = File::Stat.new("#{backup_directory}/#{backup_name_prefix}#{File.basename(tmp_backup_directory)}.tar.gz")
diff --git a/app/models/call.rb b/app/models/call.rb
index 8f657fa..a8a0d7f 100644
--- a/app/models/call.rb
+++ b/app/models/call.rb
@@ -39,6 +39,30 @@ class Call < ActiveRecord::Base
return FreeswitchAPI.execute('uuid_kill', self.uuid, true);
end
+ def transfer_blind(destination, call_leg=:aleg, auth_account=nil)
+ if destination.blank?
+ return nil
+ end
+
+ if call_leg == :bleg
+ channel_uuid = self.b_uuid
+ else
+ channel_uuid = self.uuid
+ end
+
+ if channel_uuid.blank?
+ return nil
+ end
+
+ require 'freeswitch_event'
+ if auth_account
+ FreeswitchAPI.api('uuid_setvar', channel_uuid, 'gs_auth_account_type', auth_account.class.name)
+ FreeswitchAPI.api('uuid_setvar', channel_uuid, 'gs_auth_account_uuid', auth_account.uuid)
+ end
+
+ return FreeswitchAPI.api_result(FreeswitchAPI.api('uuid_transfer', channel_uuid, destination))
+ end
+
def get_variable_from_uuid(channel_uuid, variable_name)
if channel_uuid.blank?
return nil
diff --git a/app/models/call_route.rb b/app/models/call_route.rb
index 8bc811a..590d49b 100644
--- a/app/models/call_route.rb
+++ b/app/models/call_route.rb
@@ -7,7 +7,7 @@ class CallRoute < ActiveRecord::Base
has_many :route_elements, :dependent => :destroy, :order => :position
validates :name,
- :presence => true
+ :presence => true
validates :routing_table,
:presence => true,
@@ -253,4 +253,20 @@ class CallRoute < ActiveRecord::Base
end
end
+
+ def self.test_route(table, caller)
+ arguments = ["'#{table}' table"]
+ caller.each do |key, value|
+ arguments << "'#{value}' '#{key}'"
+ end
+
+ require 'freeswitch_event'
+ result = FreeswitchAPI.api_result(FreeswitchAPI.api('lua', 'test_route.lua', arguments.join(' ')))
+ if result.blank? then
+ return
+ end
+
+ return JSON.parse(result)
+ end
+
end
diff --git a/app/models/conference.rb b/app/models/conference.rb
index aee75d5..a9d6708 100644
--- a/app/models/conference.rb
+++ b/app/models/conference.rb
@@ -7,6 +7,12 @@ class Conference < ActiveRecord::Base
has_many :conference_invitees, :dependent => :destroy
has_many :phone_numbers, :as => :phone_numberable, :dependent => :destroy
+ before_validation {
+ if !self.pin.blank?
+ self.pin = self.pin.to_s.gsub(/[^0-9]/, '')
+ end
+ }
+
validates_presence_of :conferenceable_type, :conferenceable_id
validates_presence_of :conferenceable
validates_presence_of :name
@@ -21,15 +27,12 @@ class Conference < ActiveRecord::Base
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 => (GsParameter.get('MINIMUM_PIN_LENGTH').nil? ? 4 : GsParameter.get('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? }
+
before_save :send_pin_email_when_pin_has_changed
@@ -46,11 +49,45 @@ class Conference < ActiveRecord::Base
def to_s
name
end
+
+ def list_conference
+ require 'freeswitch_event'
+ result = FreeswitchAPI.api_result(FreeswitchAPI.api('conference', "conference#{self.id}", 'xml_list'))
+ if result =~ /^\<\?xml/
+ data = Hash.from_xml(result)
+ if data
+ return data.fetch('conferences',{}).fetch('conference',{})
+ end
+ end
+ return nil
+ end
+
+ def list_members(data=self.list_conference())
+ if data.blank?
+ return {}
+ end
+
+ members = data.fetch('members',{}).fetch('member',{})
+ if members.class != Array
+ members = [members]
+ end
+
+ members.each_with_index do |member, index|
+ members[index][:call] = Call.where(:uuid => member['uuid']).first
+ if !members[index][:call]
+ members[index][:call] = Call.where(:b_uuid => member['uuid']).first
+ if members[index][:call]
+ members[index][:call_bleg] = true;
+ end
+ end
+ end
+
+ return members;
+ 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
diff --git a/app/models/conference_invitee.rb b/app/models/conference_invitee.rb
index d6e3bac..5b99ca4 100644
--- a/app/models/conference_invitee.rb
+++ b/app/models/conference_invitee.rb
@@ -1,27 +1,28 @@
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
+ before_validation {
+ if !self.pin.blank?
+ self.pin = self.pin.to_s.gsub(/[^0-9]/, '')
+ end
+ }
+
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 => (GsParameter.get('MINIMUM_PIN_LENGTH').nil? ? 4 : GsParameter.get('MINIMUM_PIN_LENGTH')),
- :allow_nil => true,
- :allow_blank => true
+ validates_length_of :pin, :minimum => (GsParameter.get('MINIMUM_PIN_LENGTH').nil? ? 4 : GsParameter.get('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}"
diff --git a/app/models/gateway.rb b/app/models/gateway.rb
index 2f17b57..bf391fb 100644
--- a/app/models/gateway.rb
+++ b/app/models/gateway.rb
@@ -27,7 +27,34 @@ class Gateway < ActiveRecord::Base
"#{GATEWAY_PREFIX}#{self.id}"
end
+ def status
+ if self.technology == 'sip' then
+ return status_sip
+ end
+ end
+
+ def inbound_register
+ username = self.gateway_settings.where(:name => 'inbound_username').first.try(:value)
+ if username.blank?
+ return
+ end
+
+ return SipRegistration.where(:sip_user => username).first
+ end
+
private
+ def status_sip
+ require 'freeswitch_event'
+ result = FreeswitchAPI.api_result(FreeswitchAPI.api('sofia', 'xmlstatus', 'gateway', "gateway#{self.id}"))
+ if result =~ /^\<\?xml/
+ data = Hash.from_xml(result)
+ if data
+ return data.fetch('gateway', nil)
+ end
+ end
+ return nil
+ end
+
def downcase_technology
self.technology = self.technology.downcase if !self.technology.blank?
end
diff --git a/app/models/gateway_setting.rb b/app/models/gateway_setting.rb
index 381dde5..b412bc6 100644
--- a/app/models/gateway_setting.rb
+++ b/app/models/gateway_setting.rb
@@ -5,11 +5,14 @@ class GatewaySetting < ActiveRecord::Base
'domain' => 'String',
'username' => 'String',
'password' => 'String',
- 'contact' => 'String',
'register' => 'Boolean',
'auth_source' => 'String',
'auth_pattern' => 'String',
+ 'inbound_username' => 'String',
+ 'inbound_password' => 'String',
'number_source' => 'String',
+ 'contact' => 'String',
+ 'dial_string' => 'String',
'profile' => 'String',
},
'xmpp' => {
diff --git a/app/models/hunt_group.rb b/app/models/hunt_group.rb
index 7338606..93279ae 100644
--- a/app/models/hunt_group.rb
+++ b/app/models/hunt_group.rb
@@ -2,7 +2,8 @@ class HuntGroup < ActiveRecord::Base
attr_accessible :name, :strategy, :seconds_between_jumps, :phone_numbers_attributes
belongs_to :tenant, :touch => true
- has_many :call_forwards, :as => :destinationable, :dependent => :destroy
+ has_many :destrination_call_forwards, :as => :destinationable, :dependent => :destroy
+ has_many :call_forwards, :as => :call_forwardable, :dependent => :destroy
validates_uniqueness_of :name, :scope => :tenant_id,
:allow_nil => true, :allow_blank => true
diff --git a/app/models/intruder.rb b/app/models/intruder.rb
index 9a1c39a..9c01634 100644
--- a/app/models/intruder.rb
+++ b/app/models/intruder.rb
@@ -50,6 +50,27 @@ class Intruder < ActiveRecord::Base
}
end
+ def perimeter_db_rescan
+ Intruder.perimeter_control(:db_rescan, :key => self.key)
+ end
+
+ def self.perimeter_db_rescan(key=nil)
+ Intruder.perimeter_control(:db_rescan, :key => key)
+ end
+
+ def self.perimeter_control(action, attributes={})
+ require 'freeswitch_event'
+ event = FreeswitchEvent.new('CUSTOM')
+ event.add_header('Event-Subclass', 'perimeter::control')
+ event.add_header('action', action)
+ attributes.each do |name, value|
+ if !name.blank? && value then
+ event.add_header(name, value)
+ end
+ end
+ return event.fire()
+ end
+
private
def set_key_if_empty
if self.key.blank?
@@ -98,6 +119,7 @@ class Intruder < ActiveRecord::Base
write_firewall_list
restart_firewall
end
+ self.perimeter_db_rescan
end
end
@@ -108,6 +130,7 @@ class Intruder < ActiveRecord::Base
restart_firewall
end
end
+ self.perimeter_db_rescan
end
def check_if_delete_relevant
@@ -117,5 +140,6 @@ class Intruder < ActiveRecord::Base
restart_firewall
end
end
+ self.perimeter_db_rescan
end
end
diff --git a/app/models/ringtone.rb b/app/models/ringtone.rb
index 45ecd93..2b08c9f 100644
--- a/app/models/ringtone.rb
+++ b/app/models/ringtone.rb
@@ -24,6 +24,6 @@ class Ringtone < ActiveRecord::Base
belongs_to :ringtoneable, :polymorphic => true
def to_s
- self.bellcore_id.to_s
+ CORE_RINGTONES_AVAILABLE.index(self.bellcore_id)
end
end
diff --git a/app/models/sip_account.rb b/app/models/sip_account.rb
index cdb609d..0c923be 100644
--- a/app/models/sip_account.rb
+++ b/app/models/sip_account.rb
@@ -41,6 +41,9 @@ class SipAccount < ActiveRecord::Base
has_many :call_legs, :class_name => 'Call'
has_many :b_call_legs, :class_name => 'Call', :foreign_key => 'b_sip_account_id'
+ has_many :acd_agents, :as => :destination, :dependent => :destroy
+ has_many :switchboard_entries, :dependent => :destroy
+
# Delegations:
#
delegate :host, :to => :sip_domain, :allow_nil => true
@@ -160,12 +163,16 @@ class SipAccount < ActiveRecord::Base
);
end
-
- def target_sip_accounts_by_permission(permission)
+ def target_group_ids_by_permission(permission)
target_groups = Group.union(self.groups.collect{|g| g.permission_targets(permission)})
target_groups = target_groups + Group.union(self.sip_accountable.groups.collect{|g| g.permission_targets(permission)})
+
+ return target_groups
+ end
+
+ def target_sip_accounts_by_permission(permission)
sip_accounts = []
- GroupMembership.where(:group_id => target_groups).each do |group_membership|
+ GroupMembership.where(:group_id => target_group_ids_by_permission(permission)).each do |group_membership|
if group_membership.item.class == User || group_membership.item.class == Tenant
sip_accounts = sip_accounts + group_membership.item.sip_accounts
elsif group_membership.item.class == SipAccount
@@ -178,6 +185,38 @@ class SipAccount < ActiveRecord::Base
return sip_accounts
end
+ def status
+ states = Array.new
+
+ self.call_legs.each do |call_leg|
+ states << {
+ :status => call_leg.b_callstate || call_leg.callstate,
+ :status_channel => call_leg.callstate,
+ :caller => true,
+ :endpoint_name => call_leg.callee_name,
+ :endpoint_number => call_leg.destination,
+ :endpoint_sip_account_id => call_leg.b_sip_account_id,
+ :start_stamp => call_leg.start_stamp,
+ :secure => call_leg.secure,
+ }
+ end
+
+ self.b_call_legs.each do |call_leg|
+ call_status =
+ states << {
+ :status => call_leg.b_callstate,
+ :status_channel => call_leg.b_callstate,
+ :caller => false,
+ :endpoint_name => call_leg.caller_id_name,
+ :endpoint_number => call_leg.caller_id_number,
+ :endpoint_sip_account_id => call_leg.sip_account_id,
+ :start_stamp => call_leg.start_stamp,
+ :secure => call_leg.b_secure,
+ }
+ end
+
+ return states
+ end
private
diff --git a/app/models/softkey.rb b/app/models/softkey.rb
index 6063017..27527f9 100644
--- a/app/models/softkey.rb
+++ b/app/models/softkey.rb
@@ -1,17 +1,17 @@
class Softkey < ActiveRecord::Base
- attr_accessible :softkey_function_id, :number, :label, :uuid, :softkeyable_type, :softkeyable_id
+ attr_accessible :softkey_function_id, :number, :label, :uuid, :softkeyable_type, :softkeyable_id, :call_forward, :blf
belongs_to :sip_account
belongs_to :softkey_function
belongs_to :softkeyable, :polymorphic => true
- validates_presence_of :softkeyable_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) }
+ ['blf', 'speed_dial','dtmf','conference'].include?(softkey.softkey_function.name) }
+
+ validates_presence_of :softkeyable_id, :if => Proc.new{ |softkey| self.softkey_function_id != nil &&
+ ['call_forwarding'].include?(softkey.softkey_function.name) }
validates_presence_of :uuid
validates_uniqueness_of :uuid
@@ -28,7 +28,6 @@ class Softkey < ActiveRecord::Base
call_forwards = call_forwards + phone_number.call_forwards
end
-
phone_numbers_ids = self.sip_account.phone_number_ids
phone_numbers = PhoneNumber.where(:id => phone_numbers_ids).pluck(:number)
@@ -43,7 +42,16 @@ class Softkey < ActiveRecord::Base
end
def possible_blf_sip_accounts
- self.sip_account.target_sip_accounts_by_permission('presence')
+ self.sip_account.target_sip_accounts_by_permission(:presence)
+ end
+
+ def possible_pickup_groups
+ Group.where(:id => self.sip_account.target_group_ids_by_permission(:presence))
+ end
+
+ def possible_blf
+ blf = possible_pickup_groups.collect{ |g| ["#{g.class.name}: #{g.to_s}", "#{g.id}:#{g.class.name}"] }
+ blf + possible_blf_sip_accounts.collect{ |g| ["#{g.class.name}: #{g.to_s}", "#{g.id}:#{g.class.name}"] }
end
def to_s
@@ -73,6 +81,7 @@ class Softkey < ActiveRecord::Base
return self.position.to_i < Softkey.where(:sip_account_id => self.sip_account_id ).order(:position).last.position.to_i
end
+
private
# 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.
diff --git a/app/models/switchboard.rb b/app/models/switchboard.rb
new file mode 100644
index 0000000..74e2767
--- /dev/null
+++ b/app/models/switchboard.rb
@@ -0,0 +1,16 @@
+class Switchboard < ActiveRecord::Base
+ # https://github.com/rails/strong_parameters
+ include ActiveModel::ForbiddenAttributesProtection
+
+ validates :name,
+ :presence => true,
+ :uniqueness => {:scope => :user_id}
+
+ belongs_to :user, :touch => true
+ has_many :switchboard_entries, :dependent => :destroy
+ has_many :sip_accounts, :through => :switchboard_entries
+
+ def to_s
+ self.name.to_s
+ end
+end
diff --git a/app/models/switchboard_entry.rb b/app/models/switchboard_entry.rb
new file mode 100644
index 0000000..76d002f
--- /dev/null
+++ b/app/models/switchboard_entry.rb
@@ -0,0 +1,31 @@
+class SwitchboardEntry < ActiveRecord::Base
+ # https://github.com/rails/strong_parameters
+ include ActiveModel::ForbiddenAttributesProtection
+
+ belongs_to :switchboard, :touch => true
+ belongs_to :sip_account, :touch => true
+
+ validates :switchboard,
+ :presence => true
+
+ validates :sip_account,
+ :presence => true
+
+ validates :name,
+ :length => { :maximum => 10 },
+ :uniqueness => {:scope => :switchboard_id},
+ :allow_blank => true,
+ :allow_nil => true
+
+ acts_as_list :scope => [ :switchboard_id ]
+
+ default_scope order(:position)
+
+ def to_s
+ if self.name.blank? && !self.sip_account.to_s.blank?
+ self.sip_account.to_s
+ else
+ self.name.to_s
+ end
+ end
+end
diff --git a/app/models/user.rb b/app/models/user.rb
index 913d75f..b74fc06 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -74,8 +74,6 @@ class User < ActiveRecord::Base
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'
@@ -94,6 +92,8 @@ class User < ActiveRecord::Base
has_many :group_memberships, :as => :item, :dependent => :destroy, :uniq => true
has_many :groups, :through => :group_memberships
+ has_many :switchboards, :dependent => :destroy
+
# Avatar like photo
mount_uploader :image, ImageUploader
diff --git a/app/views/acd_agents/_index_core.html.haml b/app/views/acd_agents/_index_core.html.haml
index c8967cd..f1172f2 100644
--- a/app/views/acd_agents/_index_core.html.haml
+++ b/app/views/acd_agents/_index_core.html.haml
@@ -1,8 +1,8 @@
%table.table.table-striped
%thead
%tr
+ %th
%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')
@@ -10,8 +10,17 @@
%tbody
- for acd_agent in acd_agents
%tr
+ %td
+ - if acd_agent.status == 'active'
+ %a.btn.btn-small.btn-success{ :href => toggle_acd_agent_path(acd_agent) }
+ %i.icon-ok.icon-white
+ - elsif acd_agent.status == 'inactive'
+ %a.btn.btn-small.btn-danger{ :href => toggle_acd_agent_path(acd_agent) }
+ %i.icon-ban-circle.icon-white
+ - else
+ %a.btn.btn-small.btn-warning{ :href => toggle_acd_agent_path(acd_agent) }
+ = acd_agent.status
%td= acd_agent.name
- %td= acd_agent.status
%td= acd_agent.last_call
%td= acd_agent.calls_answered
%td= acd_agent.destination
diff --git a/app/views/automatic_call_distributors/show.html.haml b/app/views/automatic_call_distributors/show.html.haml
index a461652..18fd8b8 100644
--- a/app/views/automatic_call_distributors/show.html.haml
+++ b/app/views/automatic_call_distributors/show.html.haml
@@ -63,6 +63,13 @@
= 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 @automatic_call_distributor.call_forwards.count > 0 || can?(:create, @automatic_call_distributor.call_forwards.build)
+ %h3= t('call_forwards.index.page_title')
+ - if @automatic_call_distributor.call_forwards.count > 0
+ = render "call_forwards/index_core", :call_forwards => @automatic_call_distributor.call_forwards
+ %br
+ = render :partial => 'shared/create_link', :locals => { :parent => @automatic_call_distributor, :child_class => CallForward }
+
- 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
diff --git a/app/views/call_routes/test.html.haml b/app/views/call_routes/test.html.haml
new file mode 100644
index 0000000..0b0fba7
--- /dev/null
+++ b/app/views/call_routes/test.html.haml
@@ -0,0 +1,55 @@
+%h2= 'Routing Test'
+
+- if @route_test
+ - if @route_test['destination']
+ %h3= 'Destination'
+ %table.table.table-striped
+ %thead
+ %tr
+ %td= 'number'
+ %td= 'type'
+ %td= 'ID'
+
+ %tbody{ :id => "destination" }
+ %tr
+ %td= @route_test['destination']['number']
+ %td= @route_test['destination']['type']
+ %td= @route_test['destination']['id']
+ -if @route_test['routes'] && @route_test['routes'].count > 0
+ %h3= 'Routes'
+ %table.table.table-striped
+ %thead
+ %tr
+ %th
+ %th= 'ID'
+ %th= 'destination_number'
+ %th= 'endpoint'
+
+ %tbody{ :id => "route_test" }
+ - @route_test['routes'].each do |index, route_entry|
+ %tr
+ %td= index
+ %td= route_entry['route_id']
+ %td= route_entry['destination_number']
+ %td= "#{route_entry['type']}=#{route_entry['id']}"
+ -if @route_test['log'] && @route_test['log'].count > 0
+ %h3= 'Log'
+ %table.table.table-striped
+ %thead
+ %tr
+ %th
+ %tbody{ :id => "log" }
+ - @route_test['log'].each do |index, log_line|
+ - if log_line =~ /^ROUTE_NO_MATCH/
+ - entry_class = 'table error'
+ - elsif log_line =~ /^ELEMENT_NO_MATCH/
+ - entry_class = 'table warning'
+ - elsif log_line =~ /^ROUTE \d+ /
+ - entry_class = 'table success'
+ - elsif log_line =~ /^[A-Z_]+_MATCH/
+ - entry_class = 'table info'
+ - else
+ - entry_class = nil
+ %tr{:class => entry_class}
+ %td= log_line
+
diff --git a/app/views/conferences/_index_members.html.haml b/app/views/conferences/_index_members.html.haml
new file mode 100644
index 0000000..3837f25
--- /dev/null
+++ b/app/views/conferences/_index_members.html.haml
@@ -0,0 +1,40 @@
+%table.table.table-striped
+ %thead
+ %tr
+ %th= t('conferences.index_members.caller')
+ %th= t('conferences.index_members.join_time')
+ %th= t('conferences.index_members.talking')
+ %th= t('conferences.index_members.flags')
+
+
+ %tbody
+ - for member in members
+ %tr
+ %td
+ -if member[:call]
+ - if member[:call_bleg]
+ = member[:call].b_caller_id_name
+ = member[:call].b_caller_id_number
+ - else
+ = member[:call].caller_id_name
+ = member[:call].caller_id_number
+ %td
+ = member['join_time']
+ %td
+ = member['flags']['talking']
+ %td
+ - if member['flags']['can_hear'] == 'true'
+ %span.label
+ = t('conferences.index_members.can_hear')
+ - if member['flags']['can_speak'] == 'true'
+ %span.label
+ = t('conferences.index_members.can_speak')
+ - if member['flags']['is_moderator'] == 'true'
+ %span.label
+ = t('conferences.index_members.moderator')
+ - if member['flags']['has_floor'] == 'true'
+ %span.label
+ = t('conferences.index_members.has_floor')
+ - if member['flags']['has_video'] == 'true'
+ %span.label
+ = t('conferences.index_members.video')
diff --git a/app/views/conferences/show.html.haml b/app/views/conferences/show.html.haml
index 302f294..fcebb88 100644
--- a/app/views/conferences/show.html.haml
+++ b/app/views/conferences/show.html.haml
@@ -45,6 +45,24 @@
%strong= t('conferences.show.announce_left_member_by_name') + ":"
%td
= @conference.announce_left_member_by_name
+ - conference_data = @conference.list_conference
+ - if conference_data
+ %tr
+ %td
+ %strong= t('conferences.show.identifier') + ":"
+ %td
+ = conference_data['name']
+ %tr
+ %td
+ %strong= t('conferences.show.member_count') + ":"
+ %td
+ = conference_data['member_count']
+ %tr
+ %td
+ %strong= t('conferences.show.run_time') + ":"
+ %td
+ = conference_data['run_time']
+
= render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @parent, :child => @conference }
@@ -54,6 +72,11 @@
= render :partial => 'shared/create_link', :locals => {:parent => @conference, :child_class => PhoneNumber}
+- members = @conference.list_members(conference_data)
+- if members && members.count > 0
+ %h2= t("conferences.show.members")
+ = render "index_members", :members => members
+
%h2= t("conference_invitees.index.page_title")
- if @conference.conference_invitees.count > 0
= render "conference_invitees/index_core", :conference_invitees => @conference.conference_invitees
diff --git a/app/views/fax_documents/_fax_document.html.haml b/app/views/fax_documents/_fax_document.html.haml
new file mode 100644
index 0000000..930a0c3
--- /dev/null
+++ b/app/views/fax_documents/_fax_document.html.haml
@@ -0,0 +1,66 @@
+.fax_document{:id => fax_document.id}
+ .row
+ .span12
+ %table.table.table-striped
+ - case fax_document.state
+ - when 'unsuccessful'
+ - current_status = 'error'
+ - when 'sending'
+ - current_status = 'success'
+ - when 'queued_for_sending'
+ - current_status = 'warning'
+ - else
+ - current_status = ''
+
+ %tr{:class => current_status}
+ %td
+ %strong= t('fax_documents.index.state') + ":"
+ %td
+ = t("fax_documents.states.#{fax_document.state}")
+ %tr
+ %td
+ %strong= t('fax_documents.index.result_code') + ":"
+ %td
+ = fax_document.result_code
+ %tr
+ %td
+ %strong= t('fax_documents.index.result_text') + ":"
+ %td
+ = t("fax_documents.result_codes.code_#{fax_document.result_code}")
+ %tr
+ %td
+ %strong= t('fax_documents.show.document_transferred_pages') + ":"
+ %td
+ = fax_document.document_transferred_pages
+ %tr
+ %td
+ %strong= t('fax_documents.show.remote_station_id') + ":"
+ %td
+ = fax_document.remote_station_id
+ %tr
+ %td
+ %strong= t('fax_documents.show.fax_resolution') + ":"
+ %td
+ = fax_document.fax_resolution
+
+ - if fax_document.document?
+ %p
+ %a{:href => fax_account_fax_document_path(fax_document.fax_account, fax_document, :format => :pdf), :method => :get}
+ %i{:class => 'icon-download'}
+ = t("fax_documents.index.actions.download_pdf") + " (#{number_to_human_size(fax_document.document.size, :precision => 2)})"
+
+ .row
+ .span12
+ - if fax_document.fax_thumbnails.any?
+ %ul.thumbnails
+ - fax_document.fax_thumbnails.limit(50).each do |fax_thumbnail|
+ %li.span4
+ %div.thumbnail
+ %a.thumbnail{:href => fax_thumbnail.thumbnail.url}
+ =image_tag(fax_thumbnail.thumbnail.url, :alt => "Page #{fax_thumbnail.position}")
+ %p
+ = "#{fax_thumbnail.position}/#{fax_document.fax_thumbnails.count}"
+
+ .row
+ .span12
+ = render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => fax_document.fax_account, :child => fax_document } \ No newline at end of file
diff --git a/app/views/fax_documents/show.html.haml b/app/views/fax_documents/show.html.haml
index b8f3e9e..6ae0ee1 100644
--- a/app/views/fax_documents/show.html.haml
+++ b/app/views/fax_documents/show.html.haml
@@ -1,67 +1,6 @@
- content_for :title, t("fax_documents.show.page_title")
-.row
- .span12
- %table.table.table-striped
- - case @fax_document.state
- - when 'unsuccessful'
- - current_status = 'error'
- - when 'sending'
- - current_status = 'success'
- - when 'queued_for_sending'
- - current_status = 'warning'
- - else
- - current_status = ''
+= render :partial => 'fax_document', :locals => {:fax_document => @fax_document}
- %tr{:class => current_status}
- %td
- %strong= t('fax_documents.index.state') + ":"
- %td
- = t("fax_documents.states.#{@fax_document.state}")
- %tr
- %td
- %strong= t('fax_documents.index.result_code') + ":"
- %td
- = @fax_document.result_code
- %tr
- %td
- %strong= t('fax_documents.index.result_text') + ":"
- %td
- = t("fax_documents.result_codes.code_#{@fax_document.result_code}")
- %tr
- %td
- %strong= t('fax_documents.show.document_transferred_pages') + ":"
- %td
- = @fax_document.document_transferred_pages
- %tr
- %td
- %strong= t('fax_documents.show.remote_station_id') + ":"
- %td
- = @fax_document.remote_station_id
- %tr
- %td
- %strong= t('fax_documents.show.fax_resolution') + ":"
- %td
- = @fax_document.fax_resolution
-
- - if @fax_document.document?
- %p
- %a{:href => fax_account_fax_document_path(@fax_account, @fax_document, :format => :pdf), :method => :get}
- %i{:class => 'icon-download'}
- = t("fax_documents.index.actions.download_pdf") + " (#{number_to_human_size(@fax_document.document.size, :precision => 2)})"
-
-.row
- .span12
- - if @fax_document.fax_thumbnails.any?
- %ul.thumbnails
- - @fax_document.fax_thumbnails.limit(50).each do |fax_thumbnail|
- %li.span4
- %div.thumbnail
- %a.thumbnail{:href => fax_thumbnail.thumbnail.url}
- =image_tag(fax_thumbnail.thumbnail.url, :alt => "Page #{fax_thumbnail.position}")
- %p
- = "#{fax_thumbnail.position}/#{@fax_document.fax_thumbnails.count}"
-
-.row
- .span12
- = render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @fax_document.fax_account, :child => @fax_document }
+- if ['sending', 'queued_for_sending'].include? @fax_document.state
+ = subscribe_to "/fax_documents/#{@fax_document.id}" \ No newline at end of file
diff --git a/app/views/gateways/show.html.haml b/app/views/gateways/show.html.haml
index 6cf09ec..19b1304 100644
--- a/app/views/gateways/show.html.haml
+++ b/app/views/gateways/show.html.haml
@@ -33,6 +33,82 @@
%td
= @gateway.description
+ - if @gateway.technology.to_s == 'sip'
+ - status = @gateway.status
+ - if !@gateway.status.blank?
+ %tr
+ %td
+ %strong= t('gateways.show.status') + ":"
+ %td
+ = "#{status['status']} (#{status['state']})"
+ %tr
+ %td
+ %strong= t('gateways.show.identifier') + ":"
+ %td
+ = status['name']
+ %tr
+ %td
+ %strong= t('gateways.show.profile') + ":"
+ %td
+ = status['profile']
+ %tr
+ %td
+ %strong= t('gateways.show.username') + ":"
+ %td
+ = status['username']
+ %tr
+ %td
+ %strong= t('gateways.show.password') + ":"
+ %td
+ = status['password']
+ %tr
+ %td
+ %strong= t('gateways.show.realm') + ":"
+ %td
+ = status['realm']
+ %tr
+ %td
+ %strong= t('gateways.show.contact') + ":"
+ %td
+ = status['contact']
+ %tr
+ %td
+ %strong= t('gateways.show.from') + ":"
+ %td
+ = status['from']
+ %tr
+ %td
+ %strong= t('gateways.show.to') + ":"
+ %td
+ = status['to']
+ %tr
+ %td
+ %strong= t('gateways.show.proxy') + ":"
+ %td
+ = status['proxy']
+ - registration = @gateway.inbound_register
+ - if !registration.blank?
+ %tr
+ %td
+ %strong= t('gateways.show.inbound_register_status') + ":"
+ %td
+ = registration.status
+ %tr
+ %td
+ %strong= t('gateways.show.inbound_register_contact') + ":"
+ %td
+ = registration.contact
+ %tr
+ %td
+ %strong= t('gateways.show.inbound_register_user_agent') + ":"
+ %td
+ = registration.user_agent
+ %tr
+ %td
+ %strong= t('gateways.show.inbound_register_network') + ":"
+ %td
+ = "#{registration.network_ip}:#{registration.network_port}"
+
= render :partial => 'shared/show_edit_destroy_part', :locals => { :child => @gateway }
diff --git a/app/views/hunt_groups/show.html.haml b/app/views/hunt_groups/show.html.haml
index 3ffe4f3..99eb3e0 100644
--- a/app/views/hunt_groups/show.html.haml
+++ b/app/views/hunt_groups/show.html.haml
@@ -19,6 +19,13 @@
%br
= render :partial => 'shared/create_link', :locals => {:parent => @hunt_group, :child_class => PhoneNumber}
+- if @hunt_group.call_forwards.count > 0 || can?(:create, @hunt_group.call_forwards.build)
+ %h2= t('call_forwards.index.page_title')
+ - if @hunt_group.call_forwards.count > 0
+ = render "call_forwards/index_core", :call_forwards => @hunt_group.call_forwards
+ %br
+ = render :partial => 'shared/create_link', :locals => { :parent => @hunt_group, :child_class => CallForward }
+
%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
diff --git a/app/views/layouts/_navbar.html.haml b/app/views/layouts/_navbar.html.haml
index 8004c0e..3e1da1c 100644
--- a/app/views/layouts/_navbar.html.haml
+++ b/app/views/layouts/_navbar.html.haml
@@ -23,7 +23,7 @@
%li
%a{:href => sip_account_voicemail_messages_path(current_user.sip_accounts.first)}
=t("voicemail_messages.index.page_title")
-
+
- if current_user
%ul.nav.pull-right
%li.display
@@ -39,10 +39,18 @@
- if current_page?(tenant_user_path(current_user.current_tenant, current_user))
%li.active
%a.navbar-link{:href => tenant_user_path(current_user.current_tenant, current_user)}
+ %i.icon-star.icon-white{:id => 'new_voicemail_or_fax_indicator'}
+ :javascript
+ $("#new_voicemail_or_fax_indicator").hide()
+ = subscribe_to "/users/#{current_user.id}/messages/new"
= current_user
- else
%li
%a.navbar-link{:href => tenant_user_path(current_user.current_tenant, current_user)}
+ %i.icon-star.icon-white{:id => 'new_voicemail_or_fax_indicator'}
+ :javascript
+ $("#new_voicemail_or_fax_indicator").hide()
+ = subscribe_to "/users/#{current_user.id}/messages/new"
= current_user
- if single_sign_on_system? == false
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index eab6096..027f837 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -36,4 +36,5 @@
.span12
%hr/
= render 'layouts/footer'
+
/ /container
diff --git a/app/views/sip_accounts/show.html.haml b/app/views/sip_accounts/show.html.haml
index 365aea8..e79907f 100644
--- a/app/views/sip_accounts/show.html.haml
+++ b/app/views/sip_accounts/show.html.haml
@@ -60,12 +60,14 @@
= render :partial => 'shared/show_edit_destroy_part', :locals => { :parent => @sip_account.sip_accountable, :child => @sip_account }
-%p
- %strong= t('sip_accounts.show.tel_protocol') + ':'
-%p
- %a.btn.btn-small.btn-default{ :href => '', :onclick => "navigator.registerProtocolHandler(\"tel\", \"#{@register_tel_protocol}\", \"#{@sip_account.to_s}\");" }
- %i.icon-plus
- = t('sip_accounts.show.register_tel_protocol')
+- if @register_protocols.count > 0
+ %p
+ %strong= t('sip_accounts.show.call_protocols') + ':'
+ %p
+ - @register_protocols.each do |protocol, url|
+ %a.btn.btn-small.btn-default{ :href => '', :onclick => "navigator.registerProtocolHandler(\"#{protocol}\", \"#{url}\", \"#{@sip_account.to_s}\");" }
+ %i.icon-plus
+ = t("sip_accounts.show.register_#{protocol}_protocol")
%p
%strong= t('ringtones.name') + ':'
@@ -99,3 +101,10 @@
- if @sip_account.calls.count > 0
%h2= t("calls.index.page_title")
= render "calls/index_core", :calls => @sip_account.calls, :parent => @sip_account
+
+- if (can?(:read, AcdAgent) && @sip_account.acd_agents.count > 0) || can?(:create, @sip_account.acd_agents.build)
+ %h2= t('acd_agents.index.page_title')
+ - if @sip_account.acd_agents.count > 0
+ = render "acd_agents/index_core", :acd_agents => @sip_account.acd_agents
+ %br
+ = render :partial => 'shared/create_link', :locals => { :parent => @sip_account, :child_class => AcdAgent }
diff --git a/app/views/softkeys/_form_core.html.haml b/app/views/softkeys/_form_core.html.haml
index f447aa6..7bed1a5 100644
--- a/app/views/softkeys/_form_core.html.haml
+++ b/app/views/softkeys/_form_core.html.haml
@@ -4,10 +4,10 @@
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
- - call_forwards = @softkey.possible_call_forwards
- - if call_forwards && call_forwards.count > 0
- = f.association :softkeyable, :collection => call_forwards, :label => t('softkeys.form.call_forward.label'), :hint => conditional_hint('softkeys.form.call_forward.hint'), :include_blank => false
+ - if @possible_call_forwards && @possible_call_forwards.count > 0
+ = f.input :softkeyable_id, :collection => @possible_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/show.html.haml b/app/views/softkeys/show.html.haml
index 7c068ae..64adbe6 100644
--- a/app/views/softkeys/show.html.haml
+++ b/app/views/softkeys/show.html.haml
@@ -1,6 +1,9 @@
- content_for :title, t("softkeys.show.page_title")
%p
+ %strong= t("softkeys.show.label") + ":"
+ =@softkey.label
+%p
%strong= t("softkeys.functions.#{@softkey.softkey_function}") + ":"
=@softkey.to_s
diff --git a/app/views/switchboard_entries/_form.html.haml b/app/views/switchboard_entries/_form.html.haml
new file mode 100644
index 0000000..b3d56ec
--- /dev/null
+++ b/app/views/switchboard_entries/_form.html.haml
@@ -0,0 +1,7 @@
+= simple_form_for([@switchboard, @switchboard_entry]) do |f|
+ = f.error_notification
+
+ = render "form_core", :f => f
+
+ .form-actions
+ = f.button :submit, conditional_t('switchboard_entries.form.submit')
diff --git a/app/views/switchboard_entries/_form_core.html.haml b/app/views/switchboard_entries/_form_core.html.haml
new file mode 100644
index 0000000..6f340c2
--- /dev/null
+++ b/app/views/switchboard_entries/_form_core.html.haml
@@ -0,0 +1,3 @@
+.inputs
+ = f.association :sip_account, :collection => @sip_accounts, :label => t('switchboard_entries.form.sip_account_id.label'), :hint => conditional_hint('switchboard_entries.form.sip_account_id.hint'), :autofocus => true, :include_blank => false
+ = f.input :name, :label => t('switchboard_entries.form.name.label'), :hint => conditional_hint('switchboard_entries.form.name.hint')
diff --git a/app/views/switchboard_entries/_index_core.html.haml b/app/views/switchboard_entries/_index_core.html.haml
new file mode 100644
index 0000000..d647626
--- /dev/null
+++ b/app/views/switchboard_entries/_index_core.html.haml
@@ -0,0 +1,17 @@
+%table.table.table-striped
+ %tr
+ %th
+ %th= t('switchboard_entries.index.sip_account_id')
+ %th= t('switchboard_entries.index.name')
+ %th
+
+ - if switchboard_entries.any?
+ %tbody{ :id => "switchboard_entries", :'data-update-url' => sort_switchboard_switchboard_entries_path(switchboard_entries.first.switchboard) }
+ - for switchboard_entry in switchboard_entries
+ = content_tag_for :tr, switchboard_entry do
+ %td
+ %span.handle
+ %i.icon-resize-vertical
+ %td= switchboard_entry.sip_account
+ %td= switchboard_entry.name
+ =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => switchboard_entry.switchboard, :child => switchboard_entry} \ No newline at end of file
diff --git a/app/views/switchboard_entries/_switchboard_entry.html.haml b/app/views/switchboard_entries/_switchboard_entry.html.haml
new file mode 100644
index 0000000..8c44153
--- /dev/null
+++ b/app/views/switchboard_entries/_switchboard_entry.html.haml
@@ -0,0 +1,45 @@
+%li.span2{:id => "switchboard_entry_id_#{switchboard_entry.id}"}
+ %div.thumbnail
+ %a.thumbnail{:href => tenant_user_path(switchboard_entry.sip_account.sip_accountable.current_tenant, switchboard_entry.sip_account.sip_accountable)}
+ - if switchboard_entry.sip_account.sip_accountable.image?
+ = image_tag(switchboard_entry.sip_account.sip_accountable.image_url(:profile).to_s, :class => 'img-rounded', :style => 'width: 100px;')
+ - else
+ - if switchboard_entry.sip_account.sip_accountable.male?
+ = image_tag('icons/user-male-16x.png', :class => 'img-rounded', :style => 'width: 100px;')
+ - else
+ = image_tag('icons/user-female-16x.png', :class => 'img-rounded', :style => 'width: 100px;')
+ %p
+ %small
+ = truncate(switchboard_entry.to_s, :length => 23)
+ %br
+ - if switchboard_entry.sip_account.phone_numbers.any? && !switchboard_entry.sip_account.call_legs.where(callstate: 'ACTIVE').any? && !switchboard_entry.sip_account.b_call_legs.where(b_callstate: 'ACTIVE').any?
+ %span.label
+ = switchboard_entry.sip_account.phone_numbers.first.number
+
+ %br
+
+ - if switchboard_entry.sip_account.registration
+ - switchboard_entry.sip_account.call_legs.where(callstate: 'RINGING').each do |call_leg|
+ %span.label.label-warning
+ %i.icon-bell.icon-white
+ = "#{call_leg.caller_id_number}"
+
+ - switchboard_entry.sip_account.call_legs.where(callstate: 'EARLY').each do |call_leg|
+ %span.label.label-info
+ = "calls #{call_leg.destination}"
+
+ - switchboard_entry.sip_account.call_legs.where(callstate: 'ACTIVE').each do |call_leg|
+ %span.label
+ = "#{switchboard_entry.sip_account.phone_numbers.first.number} => "
+ %span.label.label-info
+ = "#{call_leg.callee_number}"
+
+ - switchboard_entry.sip_account.b_call_legs.where(b_callstate: 'ACTIVE').each do |b_call_leg|
+ %span.label.label-info
+ = "#{b_call_leg.b_caller_id_number} =>"
+ %span.label
+ = switchboard_entry.sip_account.phone_numbers.first.number
+
+ - else
+ %span.label.label-inverse
+ %i.icon-ban-circle.icon-white
diff --git a/app/views/switchboard_entries/edit.html.haml b/app/views/switchboard_entries/edit.html.haml
new file mode 100644
index 0000000..8885e25
--- /dev/null
+++ b/app/views/switchboard_entries/edit.html.haml
@@ -0,0 +1,3 @@
+- content_for :title, t("switchboard_entries.edit.page_title")
+
+= render "form" \ No newline at end of file
diff --git a/app/views/switchboard_entries/index.html.haml b/app/views/switchboard_entries/index.html.haml
new file mode 100644
index 0000000..302b778
--- /dev/null
+++ b/app/views/switchboard_entries/index.html.haml
@@ -0,0 +1,6 @@
+- content_for :title, t("switchboard_entries.index.page_title")
+
+- if @switchboard_entries && @switchboard_entries.count > 0
+ = render "index_core", :switchboard_entries => @switchboard_entries
+
+= render :partial => 'shared/create_link', :locals => {:parent => @switchboard, :child_class => SwitchboardEntry} \ No newline at end of file
diff --git a/app/views/switchboard_entries/new.html.haml b/app/views/switchboard_entries/new.html.haml
new file mode 100644
index 0000000..0801a65
--- /dev/null
+++ b/app/views/switchboard_entries/new.html.haml
@@ -0,0 +1,3 @@
+- content_for :title, t("switchboard_entries.new.page_title")
+
+= render "form" \ No newline at end of file
diff --git a/app/views/switchboard_entries/show.html.haml b/app/views/switchboard_entries/show.html.haml
new file mode 100644
index 0000000..b519781
--- /dev/null
+++ b/app/views/switchboard_entries/show.html.haml
@@ -0,0 +1,22 @@
+- content_for :title, t("switchboard_entries.show.page_title")
+
+.row
+ .span6
+ %table.table.table-striped
+ %tr
+ %td
+ %strong= t('switchboard_entries.show.sip_account_id') + ":"
+ %td
+ = @switchboard_entry.sip_account
+ %tr
+ %td
+ %strong= t('switchboard_entries.show.name') + ":"
+ %td
+ = @switchboard_entry.name
+ %tr
+ %td
+ %strong= t('switchboard_entries.show.position') + ":"
+ %td
+ = @switchboard_entry.position
+
+ = render :partial => 'shared/show_edit_destroy_part', :locals => {:parent => @switchboard, :child => @switchboard_entry } \ No newline at end of file
diff --git a/app/views/switchboards/_current_user_dashboard.html.haml b/app/views/switchboards/_current_user_dashboard.html.haml
new file mode 100644
index 0000000..8dd75b9
--- /dev/null
+++ b/app/views/switchboards/_current_user_dashboard.html.haml
@@ -0,0 +1,33 @@
+.dashboard
+ - current_user.sip_accounts.each do |sip_account|
+ %table.table.table-striped
+ %thead
+ %tr
+ %td
+ %td
+ Destination
+ %td
+ Start
+ %td{:span => '2'}
+ Caller
+ %tbody
+ - sip_account.call_legs.where(callstate: 'RINGING').each do |call_leg|
+ %tr.warning
+ %td
+ %i.icon-bell
+ %td
+ %span.label.label-info
+ =sip_account.phone_numbers.first.to_s
+ %td=l Time.at(call_leg.start_stamp)
+ %td=call_leg.callee_name
+ %td=call_leg.callee_number
+
+ - sip_account.b_call_legs.where(direction: 'inbound').each do |call_leg|
+ %tr
+ %td
+ %td
+ %span.label.label-info
+ =sip_account.phone_numbers.first.to_s
+ %td=l Time.at(call_leg.start_stamp)
+ %td=call_leg.b_caller_id_name
+ %td=call_leg.b_caller_id_number
diff --git a/app/views/switchboards/_form.html.haml b/app/views/switchboards/_form.html.haml
new file mode 100644
index 0000000..bb09713
--- /dev/null
+++ b/app/views/switchboards/_form.html.haml
@@ -0,0 +1,7 @@
+= simple_form_for([@user, @switchboard]) do |f|
+ = f.error_notification
+
+ = render "form_core", :f => f
+
+ .form-actions
+ = f.button :submit, conditional_t('switchboards.form.submit')
diff --git a/app/views/switchboards/_form_core.html.haml b/app/views/switchboards/_form_core.html.haml
new file mode 100644
index 0000000..61b5934
--- /dev/null
+++ b/app/views/switchboards/_form_core.html.haml
@@ -0,0 +1,2 @@
+.inputs
+ = f.input :name, :label => t('switchboards.form.name.label'), :hint => conditional_hint('switchboards.form.name.hint'), :autofocus => true
diff --git a/app/views/switchboards/_index_core.html.haml b/app/views/switchboards/_index_core.html.haml
new file mode 100644
index 0000000..858f624
--- /dev/null
+++ b/app/views/switchboards/_index_core.html.haml
@@ -0,0 +1,10 @@
+%table.table.table-striped
+ %tr
+ %th= t('switchboards.index.name')
+ %th
+
+
+ - for switchboard in switchboards
+ %tr
+ %td= switchboard.name
+ =render :partial => 'shared/index_view_edit_destroy_part', :locals => {:parent => user, :child => switchboard} \ No newline at end of file
diff --git a/app/views/switchboards/edit.html.haml b/app/views/switchboards/edit.html.haml
new file mode 100644
index 0000000..f2e69f4
--- /dev/null
+++ b/app/views/switchboards/edit.html.haml
@@ -0,0 +1,12 @@
+- content_for :title, t("switchboards.edit.page_title")
+
+.row
+ .span12
+ = render "form"
+
+.row
+ .span12
+ - if @switchboard.switchboard_entries && @switchboard.switchboard_entries.count > 0
+ = render "switchboard_entries/index_core", :switchboard_entries => @switchboard.switchboard_entries
+
+ = render :partial => 'shared/create_link', :locals => {:parent => @switchboard, :child_class => SwitchboardEntry} \ No newline at end of file
diff --git a/app/views/switchboards/index.html.haml b/app/views/switchboards/index.html.haml
new file mode 100644
index 0000000..4f29d7d
--- /dev/null
+++ b/app/views/switchboards/index.html.haml
@@ -0,0 +1,8 @@
+- content_for :title, t("switchboards.index.page_title")
+
+.row
+ .span6
+ - if @switchboards && @switchboards.count > 0
+ = render :partial => "index_core", :locals => {:switchboards => @switchboards, :user => @user}
+
+ = render :partial => 'shared/create_link', :locals => {:parent => @user, :child_class => Switchboard} \ No newline at end of file
diff --git a/app/views/switchboards/new.html.haml b/app/views/switchboards/new.html.haml
new file mode 100644
index 0000000..9f5918f
--- /dev/null
+++ b/app/views/switchboards/new.html.haml
@@ -0,0 +1,3 @@
+- content_for :title, t("switchboards.new.page_title")
+
+= render "form" \ No newline at end of file
diff --git a/app/views/switchboards/show.html.haml b/app/views/switchboards/show.html.haml
new file mode 100644
index 0000000..a825806
--- /dev/null
+++ b/app/views/switchboards/show.html.haml
@@ -0,0 +1,21 @@
+- content_for :title, "Switchboard #{@switchboard.name}"
+
+.row
+ .span12
+ = render :partial => "current_user_dashboard", :current_user => current_user
+
+ %ul.thumbnails
+ = render :partial => "switchboard_entries/switchboard_entry", :collection => @switchboard_entries
+
+ - if can? :edit, @switchboard
+ .row
+ .span12
+ %a.btn.btn-small.btn-warning{:href => switchboard_switchboard_entries_path(@switchboard) }
+ %i.icon-edit.icon-white
+ %span.hidden-phone
+ =t("switchboard_entries.index.page_title")
+
+ .span6
+
+
+= subscribe_to "/switchboards/#{@switchboard.id}" \ No newline at end of file
diff --git a/app/views/tenants/_table_of_sip_accounts.html.haml b/app/views/tenants/_table_of_sip_accounts.html.haml
index 34eeb14..b2f2612 100644
--- a/app/views/tenants/_table_of_sip_accounts.html.haml
+++ b/app/views/tenants/_table_of_sip_accounts.html.haml
@@ -1,25 +1,24 @@
-- cache(['tenant_show_table_of_sip_accounts', I18n.locale, tenant, tenant.sip_accounts.count, SipAccount.count, tenant.sip_accounts.reorder(:updated_at).last]) do
- .row
- - if GsParameter.get('AUTO_ADMIN_ONLINE_HELP') == true && !SipAccount.any?
- .span4
- -# SIP accounts
- -#
- %h2= t('sip_accounts.index.page_title')
- - if tenant.sip_accounts.any?
- = render "sip_accounts/index_core", :sip_accounts => tenant.sip_accounts
- = render :partial => 'shared/create_link', :locals => {:parent => tenant, :child_class => SipAccount}
+.row
+ - if GsParameter.get('AUTO_ADMIN_ONLINE_HELP') == true && !SipAccount.any?
+ .span4
+ -# SIP accounts
+ -#
+ %h2= t('sip_accounts.index.page_title')
+ - if tenant.sip_accounts.any?
+ = render "sip_accounts/index_core", :sip_accounts => tenant.sip_accounts
+ = render :partial => 'shared/create_link', :locals => {:parent => tenant, :child_class => SipAccount}
- .span8
- .well
- %p
- In der #{link_to 'Admin-Doku', page_help_path} finden Sie die Beschreibung wie ein neues Telefon mit einem SIP-Account eingerichtet werden kann. Dazu gibt es auch einen Screencast:
- = render :partial => 'page/docu/screencast_list', :locals => {:screencast_name => 'firmen_sip_account_und_telefon_anlegen'}
+ .span8
+ .well
+ %p
+ In der #{link_to 'Admin-Doku', page_help_path} finden Sie die Beschreibung wie ein neues Telefon mit einem SIP-Account eingerichtet werden kann. Dazu gibt es auch einen Screencast:
+ = render :partial => 'page/docu/screencast_list', :locals => {:screencast_name => 'firmen_sip_account_und_telefon_anlegen'}
- - else
- .span12
- -# SIP accounts
- -#
- %h2= t('sip_accounts.index.page_title')
- - if tenant.sip_accounts.any?
- = render "sip_accounts/index_core", :sip_accounts => tenant.sip_accounts
- = render :partial => 'shared/create_link', :locals => {:parent => tenant, :child_class => SipAccount}
+ - else
+ .span12
+ -# SIP accounts
+ -#
+ %h2= t('sip_accounts.index.page_title')
+ - if tenant.sip_accounts.any?
+ = render "sip_accounts/index_core", :sip_accounts => tenant.sip_accounts
+ = render :partial => 'shared/create_link', :locals => {:parent => tenant, :child_class => SipAccount}
diff --git a/app/views/trigger/voicemail.html.erb b/app/views/trigger/voicemail.html.erb
deleted file mode 100644
index 9bafe17..0000000
--- a/app/views/trigger/voicemail.html.erb
+++ /dev/null
@@ -1,4 +0,0 @@
-<h1>Trigger#voicemail</h1>
-<p>Find me in app/views/trigger/voicemail.html.erb</p>
-
-<%= debug(params) %>
diff --git a/app/views/users/_switchboards.html.haml b/app/views/users/_switchboards.html.haml
new file mode 100644
index 0000000..183b6ae
--- /dev/null
+++ b/app/views/users/_switchboards.html.haml
@@ -0,0 +1,7 @@
+-# Switchboards
+-#
+- if SipAccount.any? && (can?( :index, Switchboard ) && user.switchboards.any? ) || can?( :create, Switchboard )
+ %h2= t('switchboards.index.page_title')
+ - if can?( :index, Switchboard ) && user.switchboards.count > 0
+ = render :partial => "switchboards/index_core", :locals => {:switchboards => user.switchboards, :user => user}
+ = render :partial => 'shared/create_link', :locals => {:parent => user, :child_class => Switchboard} \ No newline at end of file
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index ea90ab4..98f7cc6 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -87,3 +87,6 @@
- cache(['user_show_conferences_overview', I18n.locale, @user, @user.conferences]) do
= render :partial => 'conferences', :locals => {:user => @user}
+
+ - cache(['user_switchboards_overview', I18n.locale, @user, @user.switchboards]) do
+ = render :partial => 'switchboards', :locals => {:user => @user} \ No newline at end of file
diff --git a/config/locales/views/conferences/de.yml b/config/locales/views/conferences/de.yml
index ba0869c..080047f 100644
--- a/config/locales/views/conferences/de.yml
+++ b/config/locales/views/conferences/de.yml
@@ -27,6 +27,16 @@ de:
show: 'Anzeigen'
create: 'Neu anlegen'
create_for: 'Neue Telefonkonferenz für %{resource} anlegen'
+ index_members:
+ caller: 'Anrufer'
+ join_time: 'Laufzeit'
+ talking: 'Spricht'
+ flags: 'Flags'
+ can_hear: 'hören'
+ can_speak: 'sprechen'
+ moderator: 'moderator'
+ has_floor: 'saal'
+ video: 'video'
show:
page_title: 'Telefonkonferenz anzeigen'
name: 'Name'
@@ -38,6 +48,9 @@ de:
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'
+ identifier: 'Identifier'
+ member_count: 'Mitglieder'
+ run_time: 'Laufzeit'
actions:
confirm_destroy: 'Sind Sie sicher, dass Sie diese Telefonkonferenz löschen möchten?'
destroy: 'Löschen'
diff --git a/config/locales/views/conferences/en.yml b/config/locales/views/conferences/en.yml
index e11e9cb..9866122 100644
--- a/config/locales/views/conferences/en.yml
+++ b/config/locales/views/conferences/en.yml
@@ -27,6 +27,16 @@ en:
show: 'View'
create: 'New'
create_for: 'New conference for %{resource}'
+ index_members:
+ caller: 'Caller'
+ join_time: 'Join time'
+ talking: 'Talking'
+ flags: 'Flags'
+ can_hear: 'hear'
+ can_speak: 'speak'
+ moderator: 'moderator'
+ has_floor: 'floor'
+ video: 'video'
show:
page_title: 'Show conference'
name: 'Name'
@@ -38,6 +48,9 @@ en:
open_for_anybody: 'Open for anybody'
announce_new_member_by_name: 'Announce new member'
announce_left_member_by_name: 'Announce left member'
+ identifier: 'Identifier'
+ member_count: 'Members'
+ run_time: 'Run time'
actions:
confirm_destroy: 'Are you sure you want to delete this conference?'
destroy: 'Delete'
diff --git a/config/locales/views/sip_accounts/de.yml b/config/locales/views/sip_accounts/de.yml
index 2b4939b..6accbdb 100644
--- a/config/locales/views/sip_accounts/de.yml
+++ b/config/locales/views/sip_accounts/de.yml
@@ -41,8 +41,9 @@ de:
registration: 'Registrierung'
expires: 'Läuft ab'
user_agent: 'User Agent'
- tel_protocol: 'TEL Protokoll'
+ call_protocols: 'URLs'
register_tel_protocol: 'Für tel: URLs registrieren'
+ register_callto_protocol: 'Für callto: URLs registrieren'
actions:
confirm_destroy: 'Sind Sie sicher, dass Sie diesen SIP-Account löschen möchten?'
destroy: 'Löschen'
diff --git a/config/locales/views/sip_accounts/en.yml b/config/locales/views/sip_accounts/en.yml
index 78d20c2..f160c7d 100644
--- a/config/locales/views/sip_accounts/en.yml
+++ b/config/locales/views/sip_accounts/en.yml
@@ -41,8 +41,9 @@ en:
registration: 'Registration'
expires: 'Expires'
user_agent: 'User Agent'
- tel_protocol: 'TEL Protocol'
+ call_protocols: 'URLs'
register_tel_protocol: 'Register for tel: URLs with browser'
+ register_callto_protocol: 'Register for callto: URLs with browser'
actions:
confirm_destroy: 'Are you sure you want to delete this SIP account?'
destroy: 'Delete'
diff --git a/config/locales/views/switchboard_entries/de.yml b/config/locales/views/switchboard_entries/de.yml
new file mode 100644
index 0000000..41804b5
--- /dev/null
+++ b/config/locales/views/switchboard_entries/de.yml
@@ -0,0 +1,55 @@
+de:
+ switchboard_entries:
+ name: 'Switchboard-Eintrag'
+ controller:
+ successfuly_created: 'Switchboard-Eintrag wurde angelegt.'
+ successfuly_updated: 'Switchboard-Eintrag wurde aktualisiert.'
+ successfuly_destroyed: 'Switchboard-Eintrag wurde gelöscht.'
+ index:
+ page_title: 'Liste aller Switchboard-Einträge'
+ switchboard_id: 'Switchboard'
+ sip_account_id: 'SIP-Account'
+ name: 'Name'
+ position: 'Position'
+ actions:
+ confirm_destroy: 'Sind Sie sicher, dass Sie folgendes löschen möchten: Switchboard-Eintrag'
+ destroy: 'Löschen'
+ edit: 'Bearbeiten'
+ show: 'Anzeigen'
+ create: 'Neu anlegen'
+ create_for: 'Switchboard-Eintrag neu anlegen für %{resource}'
+ show:
+ page_title: 'Switchboard-Eintrag bearbeiten'
+ switchboard_id: 'Switchboard'
+ sip_account_id: 'SIP-Account'
+ name: 'Name'
+ position: 'Position'
+ actions:
+ confirm_destroy: 'Sind Sie sicher, dass die dieses Element löschen möchten?'
+ destroy: 'Löschen'
+ edit: 'Bearbeiten'
+ view_all: 'Alle anzeigen'
+ new:
+ page_title: 'Switchboard-Eintrag neu anlegen'
+ actions:
+ back_to_list: 'Zurück zur Übersicht'
+ edit:
+ page_title: 'Switchboard-Eintrag bearbeiten'
+ actions:
+ back_to_list: 'Zurück zur Übersicht'
+ edit: 'Bearbeiten'
+ view_all: 'Alle anzeigen'
+ form:
+ switchboard_id:
+ label: 'Switchboard'
+ hint: ''
+ sip_account_id:
+ label: 'SIP-Account'
+ hint: ''
+ name:
+ label: 'Name'
+ hint: ''
+ position:
+ label: 'Position'
+ hint: ''
+ submit: 'Absenden' \ No newline at end of file
diff --git a/config/locales/views/switchboard_entries/en.yml b/config/locales/views/switchboard_entries/en.yml
new file mode 100644
index 0000000..71f2fe4
--- /dev/null
+++ b/config/locales/views/switchboard_entries/en.yml
@@ -0,0 +1,55 @@
+en:
+ switchboard_entries:
+ name: 'Switchboardentry'
+ controller:
+ successfuly_created: 'Successfully created Switchboardentry.'
+ successfuly_updated: 'Successfully updated Switchboardentry.'
+ successfuly_destroyed: 'Successfully destroyed Switchboardentry.'
+ index:
+ page_title: 'Listing Switchboardentry'
+ switchboard_id: 'Switchboard'
+ sip_account_id: 'Sip account'
+ name: 'Name'
+ position: 'Position'
+ actions:
+ confirm_destroy: 'Are you sure you want to delete this Switchboardentry?'
+ destroy: 'Delete'
+ edit: 'Edit'
+ show: 'View'
+ create: 'New'
+ create_for: 'New Switchboardentry for %{resource}'
+ show:
+ page_title: 'Show Switchboardentry'
+ switchboard_id: 'Switchboard'
+ sip_account_id: 'Sip account'
+ name: 'Name'
+ position: 'Position'
+ actions:
+ confirm_destroy: 'Are you sure you want to delete this element?'
+ destroy: 'Delete'
+ edit: 'Edit'
+ view_all: 'View All'
+ new:
+ page_title: 'New Switchboardentry'
+ actions:
+ back_to_list: 'Back to Index'
+ edit:
+ page_title: 'Editing Switchboardentry'
+ actions:
+ back_to_list: 'Back to Index'
+ edit: 'Edit'
+ view_all: 'View All'
+ form:
+ switchboard_id:
+ label: 'Switchboard'
+ hint: ''
+ sip_account_id:
+ label: 'Sip account'
+ hint: ''
+ name:
+ label: 'Name'
+ hint: ''
+ position:
+ label: 'Position'
+ hint: ''
+ submit: 'Submit' \ No newline at end of file
diff --git a/config/locales/views/switchboards/de.yml b/config/locales/views/switchboards/de.yml
new file mode 100644
index 0000000..97a47b8
--- /dev/null
+++ b/config/locales/views/switchboards/de.yml
@@ -0,0 +1,45 @@
+de:
+ switchboards:
+ name: 'Switchboard'
+ controller:
+ successfuly_created: 'Switchboard wurde angelegt.'
+ successfuly_updated: 'Switchboard wurde aktualisiert.'
+ successfuly_destroyed: 'Switchboard wurde gelöscht.'
+ index:
+ page_title: 'Switchboards'
+ name: 'Name'
+ user_id: 'User'
+ actions:
+ confirm_destroy: 'Sind Sie sicher, dass Sie folgendes löschen möchten: Switchboard'
+ destroy: 'Löschen'
+ edit: 'Bearbeiten'
+ show: 'Anzeigen'
+ create: 'Neu anlegen'
+ create_for: 'Switchboard neu anlegen für %{resource}'
+ show:
+ page_title: 'Switchboard bearbeiten'
+ name: 'Name'
+ user_id: 'User'
+ actions:
+ confirm_destroy: 'Sind Sie sicher, dass die dieses Element löschen möchten?'
+ destroy: 'Löschen'
+ edit: 'Bearbeiten'
+ view_all: 'Alle anzeigen'
+ new:
+ page_title: 'Switchboard neu anlegen'
+ actions:
+ back_to_list: 'Zurück zur Übersicht'
+ edit:
+ page_title: 'Switchboard bearbeiten'
+ actions:
+ back_to_list: 'Zurück zur Übersicht'
+ edit: 'Bearbeiten'
+ view_all: 'Alle anzeigen'
+ form:
+ name:
+ label: 'Name'
+ hint: ''
+ user_id:
+ label: 'User'
+ hint: ''
+ submit: 'Absenden' \ No newline at end of file
diff --git a/config/locales/views/switchboards/en.yml b/config/locales/views/switchboards/en.yml
new file mode 100644
index 0000000..a860763
--- /dev/null
+++ b/config/locales/views/switchboards/en.yml
@@ -0,0 +1,45 @@
+en:
+ switchboards:
+ name: 'Switchboard'
+ controller:
+ successfuly_created: 'Successfully created Switchboard.'
+ successfuly_updated: 'Successfully updated Switchboard.'
+ successfuly_destroyed: 'Successfully destroyed Switchboard.'
+ index:
+ page_title: 'Switchboards'
+ name: 'Name'
+ user_id: 'User'
+ actions:
+ confirm_destroy: 'Are you sure you want to delete this Switchboard?'
+ destroy: 'Delete'
+ edit: 'Edit'
+ show: 'View'
+ create: 'New'
+ create_for: 'New Switchboard for %{resource}'
+ show:
+ page_title: 'Show Switchboard'
+ name: 'Name'
+ user_id: 'User'
+ actions:
+ confirm_destroy: 'Are you sure you want to delete this element?'
+ destroy: 'Delete'
+ edit: 'Edit'
+ view_all: 'View All'
+ new:
+ page_title: 'New Switchboard'
+ actions:
+ back_to_list: 'Back to Index'
+ edit:
+ page_title: 'Editing Switchboard'
+ actions:
+ back_to_list: 'Back to Index'
+ edit: 'Edit'
+ view_all: 'View All'
+ form:
+ name:
+ label: 'Name'
+ hint: ''
+ user_id:
+ label: 'User'
+ hint: ''
+ submit: 'Submit' \ No newline at end of file
diff --git a/config/private_pub.yml b/config/private_pub.yml
index 19a7e9e..840b2c1 100644
--- a/config/private_pub.yml
+++ b/config/private_pub.yml
@@ -6,5 +6,5 @@ test:
secret_token: "secret"
production:
server: "http://example.com/faye"
- secret_token: "ade2c51226bf26e7fbbce1e0d8848082b750d23516b46dc5bc12e910e0e64558"
+ secret_token: "4a6049c5f60cd74690c094757dd7afc431a64ac83f1c54f7cf4b3f28215b3bec"
signature_expiration: 3600 # one hour
diff --git a/config/routes.rb b/config/routes.rb
index 83ac5c4..69caf95 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,5 +1,11 @@
Gemeinschaft42c::Application.routes.draw do
+ resources :switchboards do
+ resources :switchboard_entries do
+ collection { post :sort }
+ end
+ end
+
resources :restore_jobs
resources :groups do
@@ -18,12 +24,15 @@ Gemeinschaft42c::Application.routes.draw do
scope :constraints => lambda{|req|%w(127.0.0.1).include? req.remote_addr} do
get "trigger/voicemail"
get "trigger/fax"
+ match 'trigger/fax_has_been_sent/:id' => 'trigger#fax_has_been_sent'
+ match 'trigger/sip_account_update/:id' => 'trigger#sip_account_update'
end
resources :call_routes do
collection {
post :sort
get :show_variables
+ get :test
}
resources :route_elements do
collection { post :sort }
@@ -57,13 +66,17 @@ Gemeinschaft42c::Application.routes.draw do
collection { post :sort }
end
- resources :acd_agents, :only => [] do
+ resources :acd_agents do
resources :phone_numbers
+ member do
+ get 'toggle'
+ end
end
resources :automatic_call_distributors, :only => [] do
resources :acd_agents
resources :phone_numbers
+ resources :call_forwards
end
resources :hunt_group_members, :only => [] do
@@ -73,6 +86,7 @@ Gemeinschaft42c::Application.routes.draw do
resources :hunt_groups, :only => [] do
resources :hunt_group_members
resources :phone_numbers
+ resources :call_forwards
end
if GsParameter.get('CALLTHROUGH_HAS_WHITELISTS') == true
@@ -209,6 +223,9 @@ Gemeinschaft42c::Application.routes.draw do
resources :fax_accounts
resources :system_messages, :except => [ :edit, :update, :destroy ]
resources :parking_stalls
+ resources :switchboards do
+ get :display
+ end
end
resources :user_groups do
@@ -258,6 +275,7 @@ Gemeinschaft42c::Application.routes.draw do
resources :call_forwards
resources :ringtones
resources :calls
+ resources :acd_agents
resources :call_histories do
collection do
delete 'destroy_multiple'
diff --git a/db/migrate/20130307104654_create_switchboards.rb b/db/migrate/20130307104654_create_switchboards.rb
new file mode 100644
index 0000000..222b168
--- /dev/null
+++ b/db/migrate/20130307104654_create_switchboards.rb
@@ -0,0 +1,13 @@
+class CreateSwitchboards < ActiveRecord::Migration
+ def self.up
+ create_table :switchboards do |t|
+ t.string :name
+ t.integer :user_id
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :switchboards
+ end
+end
diff --git a/db/migrate/20130307122344_create_switchboard_entries.rb b/db/migrate/20130307122344_create_switchboard_entries.rb
new file mode 100644
index 0000000..1c7521e
--- /dev/null
+++ b/db/migrate/20130307122344_create_switchboard_entries.rb
@@ -0,0 +1,15 @@
+class CreateSwitchboardEntries < ActiveRecord::Migration
+ def self.up
+ create_table :switchboard_entries do |t|
+ t.integer :switchboard_id
+ t.integer :sip_account_id
+ t.string :name
+ t.integer :position
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :switchboard_entries
+ end
+end
diff --git a/db/migrate/20130309055700_change_perimeter_gs_parameters.rb b/db/migrate/20130309055700_change_perimeter_gs_parameters.rb
new file mode 100644
index 0000000..7973ee0
--- /dev/null
+++ b/db/migrate/20130309055700_change_perimeter_gs_parameters.rb
@@ -0,0 +1,18 @@
+class ChangePerimeterGsParameters < ActiveRecord::Migration
+ def up
+ GsParameter.find_or_create_by_entity_and_section_and_name('perimeter', 'bad_headers_register', 'from_user').update_attributes(:value => '^%d+$', :class_type => 'String')
+ GsParameter.where(:entity => 'perimeter', :section => 'bad_headers_register', :name => 'to_user').first.update_attributes(:value => '^%d+$', :class_type => 'String')
+
+ GsParameter.where(:entity => 'perimeter', :section => 'checks_call', :name => 'check_bad_headers').first.update_attributes(:value => '20', :class_type => 'Integer')
+ GsParameter.where(:entity => 'perimeter', :section => 'checks_call', :name => 'check_frequency').first.update_attributes(:value => '100', :class_type => 'Integer')
+
+ GsParameter.where(:entity => 'perimeter', :section => 'checks_register', :name => 'check_bad_headers').first.update_attributes(:value => '20', :class_type => 'Integer')
+ GsParameter.where(:entity => 'perimeter', :section => 'checks_register', :name => 'check_frequency').first.update_attributes(:value => '100', :class_type => 'Integer')
+ GsParameter.where(:entity => 'perimeter', :section => 'checks_register', :name => 'check_username_scan').first.update_attributes(:value => '20', :class_type => 'Integer')
+
+ GsParameter.where(:entity => 'perimeter', :section => 'general', :name => 'ban_threshold').first.update_attributes(:value => '1000', :class_type => 'Integer')
+ end
+
+ def down
+ end
+end
diff --git a/db/migrate/20130314031100_add_event_socket_gs_parameters.rb b/db/migrate/20130314031100_add_event_socket_gs_parameters.rb
new file mode 100644
index 0000000..6c26888
--- /dev/null
+++ b/db/migrate/20130314031100_add_event_socket_gs_parameters.rb
@@ -0,0 +1,11 @@
+class AddEventSocketGsParameters < ActiveRecord::Migration
+ def up
+ GsParameter.create(:entity => 'event_socket', :section => 'settings', :name => 'listen-ip', :value => '127.0.0.1', :class_type => 'String', :description => '')
+ GsParameter.create(:entity => 'event_socket', :section => 'settings', :name => 'listen-port', :value => '8021', :class_type => 'Integer', :description => '')
+ GsParameter.create(:entity => 'event_socket', :section => 'settings', :name => 'password', :value => 'ClueCon', :class_type => 'String', :description => '')
+ end
+
+ def down
+ GsParameter.where(:entity => 'event_socket', :section => 'settings').destroy_all
+ end
+end
diff --git a/db/migrate/20130314104000_change_calls_active.rb b/db/migrate/20130314104000_change_calls_active.rb
new file mode 100644
index 0000000..4054157
--- /dev/null
+++ b/db/migrate/20130314104000_change_calls_active.rb
@@ -0,0 +1,84 @@
+class ChangeCallsActive < ActiveRecord::Migration
+ def self.up
+ execute "DROP VIEW IF EXISTS calls_active"
+ if ActiveRecord::Base.connection_config[:adapter] != 'sqlite3'
+ execute <<-SQL
+ CREATE VIEW calls_active AS SELECT
+ a.uuid AS uuid,
+ a.direction AS direction,
+ a.created_epoch AS start_stamp,
+ a.cid_name AS caller_id_name,
+ a.cid_num AS caller_id_number,
+ a.dest AS destination,
+ d.id AS sip_account_id,
+ d.caller_name AS sip_caller_name,
+ a.callee_name AS callee_name,
+ a.callee_num AS callee_number,
+ a.callstate AS callstate,
+ a.read_codec AS read_codec,
+ a.read_rate AS read_rate,
+ a.read_bit_rate AS read_bit_rate,
+ a.write_codec AS write_codec,
+ a.write_rate AS write_rate,
+ a.write_bit_rate AS write_bit_rate,
+ a.secure AS secure,
+ b.uuid AS b_uuid,
+ b.cid_name AS b_caller_id_name,
+ b.cid_num AS b_caller_id_number,
+ b.callstate AS b_callstate,
+ e.id AS b_sip_account_id,
+ e.caller_name AS b_sip_caller_name,
+ b.callee_name AS b_callee_name,
+ b.callee_num AS b_callee_number,
+ b.secure AS b_secure
+ FROM channels a
+ LEFT JOIN calls c ON a.uuid = c.caller_uuid AND a.hostname = c.hostname
+ LEFT JOIN channels b ON b.uuid = c.callee_uuid AND b.hostname = c.hostname
+ LEFT JOIN sip_accounts d ON a.presence_id LIKE CONCAT(d.auth_name, "@%")
+ LEFT JOIN sip_accounts e ON b.presence_id LIKE CONCAT(e.auth_name, "@%")
+ WHERE a.uuid = c.caller_uuid OR a.uuid NOT IN (select callee_uuid from calls)
+ SQL
+ else
+ execute <<-SQL
+ CREATE VIEW calls_active AS SELECT
+ a.uuid AS uuid,
+ a.direction AS direction,
+ a.created_epoch AS start_stamp,
+ a.cid_name AS caller_id_name,
+ a.cid_num AS caller_id_number,
+ a.dest AS destination,
+ d.id AS sip_account_id,
+ d.caller_name AS sip_caller_name,
+ a.callee_name AS callee_name,
+ a.callee_num AS callee_number,
+ a.callstate AS callstate,
+ a.read_codec AS read_codec,
+ a.read_rate AS read_rate,
+ a.read_bit_rate AS read_bit_rate,
+ a.write_codec AS write_codec,
+ a.write_rate AS write_rate,
+ a.write_bit_rate AS write_bit_rate,
+ a.secure AS secure,
+ b.uuid AS b_uuid,
+ b.cid_name AS b_caller_id_name,
+ b.cid_num AS b_caller_id_number,
+ b.callstate AS b_callstate,
+ e.id AS b_sip_account_id,
+ e.caller_name AS b_sip_caller_name,
+ b.callee_name AS b_callee_name,
+ b.callee_num AS b_callee_number,
+ b.secure AS b_secure
+ FROM channels a
+ LEFT JOIN calls c ON a.uuid = c.caller_uuid AND a.hostname = c.hostname
+ LEFT JOIN channels b ON b.uuid = c.callee_uuid AND b.hostname = c.hostname
+ LEFT JOIN sip_accounts d ON a.presence_id LIKE (d.auth_name || "@%")
+ LEFT JOIN sip_accounts e ON b.presence_id LIKE (e.auth_name || "@%")
+ WHERE a.uuid = c.caller_uuid OR a.uuid NOT IN (select callee_uuid from calls)
+ SQL
+ end
+ end
+
+ def self.down
+
+ end
+end \ No newline at end of file
diff --git a/db/migrate/20130314134600_change_conference_parameters.rb b/db/migrate/20130314134600_change_conference_parameters.rb
new file mode 100644
index 0000000..85116e4
--- /dev/null
+++ b/db/migrate/20130314134600_change_conference_parameters.rb
@@ -0,0 +1,37 @@
+class ChangeConferenceParameters < ActiveRecord::Migration
+ def up
+ GsParameter.where(:entity => 'conferences', :section => 'parameters').destroy_all
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'caller-controls', :value => 'speaker', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'moderator-controls', :value => 'moderator', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'muted-sound', :value => 'conference/conf-muted.wav', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'unmuted-sound', :value => 'conference/conf-unmuted.wav', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'moh-sound', :value => '', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'comfort-noise', :value => 'true', :class_type => 'Boolean')
+ end
+
+ def down
+ GsParameter.where(:entity => 'conferences', :section => 'parameters').destroy_all
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'caller-controls', :value => 'speaker', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'moderator-controls', :value => 'moderator', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'max-members', :value => 100, :class_type => 'Integer')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'rate', :value => 16000, :class_type => 'Integer')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'interval', :value => 20, :class_type => 'Integer')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'energy-level', :value => 300, :class_type => 'Integer')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'sound-prefix', :value => '/opt/freeswitch/sounds/de/tts/google', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'muted-sound', :value => 'conference/conf-muted.wav', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'unmuted-sound', :value => 'conference/conf-unmuted.wav', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'alone-sound', :value => 'conference/conf-alone.wav', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'moh-sound', :value => 'local_stream://moh', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'enter-sound', :value => 'tone_stream://%(200,0,500,600,700)', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'exit-sound', :value => 'tone_stream://%(500,0,300,200,100,50,25)', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'kicked-sound', :value => 'conference/conf-kicked.wav', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'locked-sound', :value => 'conference/conf-locked.wav', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'is-locked-sound', :value => 'conference/conf-is-locked.wav', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'is-unlocked-sound', :value => 'conference/conf-is-unlocked.wav', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'pin-sound', :value => 'conference/conf-pin.wav', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'bad-pin-sound', :value => 'conference/conf-bad-pin.wav', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'caller-id-name', :value => 'Conference', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'caller-id-number', :value => '', :class_type => 'String')
+ GsParameter.create(:entity => 'conferences', :section => 'parameters', :name => 'comfort-noise', :value => 'true', :class_type => 'Boolean')
+ end
+end
diff --git a/db/migrate/20130315074600_change_calls_active2.rb b/db/migrate/20130315074600_change_calls_active2.rb
new file mode 100644
index 0000000..378ffde
--- /dev/null
+++ b/db/migrate/20130315074600_change_calls_active2.rb
@@ -0,0 +1,86 @@
+class ChangeCallsActive2 < ActiveRecord::Migration
+ def self.up
+ execute "DROP VIEW IF EXISTS calls_active"
+ if ActiveRecord::Base.connection_config[:adapter] != 'sqlite3'
+ execute <<-SQL
+ CREATE VIEW calls_active AS SELECT
+ a.uuid AS uuid,
+ a.direction AS direction,
+ a.created_epoch AS start_stamp,
+ a.cid_name AS caller_id_name,
+ a.cid_num AS caller_id_number,
+ a.dest AS destination,
+ c.id AS sip_account_id,
+ c.caller_name AS sip_caller_name,
+ a.callee_name AS callee_name,
+ a.callee_num AS callee_number,
+ a.callstate AS callstate,
+ a.read_codec AS read_codec,
+ a.read_rate AS read_rate,
+ a.read_bit_rate AS read_bit_rate,
+ a.write_codec AS write_codec,
+ a.write_rate AS write_rate,
+ a.write_bit_rate AS write_bit_rate,
+ a.secure AS secure,
+ b.uuid AS b_uuid,
+ b.cid_name AS b_caller_id_name,
+ b.cid_num AS b_caller_id_number,
+ b.callstate AS b_callstate,
+ d.id AS b_sip_account_id,
+ d.caller_name AS b_sip_caller_name,
+ b.callee_name AS b_callee_name,
+ b.callee_num AS b_callee_number,
+ b.secure AS b_secure
+ FROM channels a
+ LEFT JOIN channels b ON (a.uuid = b.call_uuid AND a.uuid != b.uuid)
+ LEFT JOIN sip_accounts c ON a.presence_id LIKE CONCAT(c.auth_name, "@%")
+ LEFT JOIN sip_accounts d ON b.presence_id LIKE CONCAT(d.auth_name, "@%")
+ WHERE (a.uuid = b.call_uuid AND a.uuid != b.uuid)
+ OR a.call_uuid IS NULL
+ OR a.call_uuid = a.uuid
+ SQL
+ else
+ execute <<-SQL
+ CREATE VIEW calls_active AS SELECT
+ a.uuid AS uuid,
+ a.direction AS direction,
+ a.created_epoch AS start_stamp,
+ a.cid_name AS caller_id_name,
+ a.cid_num AS caller_id_number,
+ a.dest AS destination,
+ c.id AS sip_account_id,
+ c.caller_name AS sip_caller_name,
+ a.callee_name AS callee_name,
+ a.callee_num AS callee_number,
+ a.callstate AS callstate,
+ a.read_codec AS read_codec,
+ a.read_rate AS read_rate,
+ a.read_bit_rate AS read_bit_rate,
+ a.write_codec AS write_codec,
+ a.write_rate AS write_rate,
+ a.write_bit_rate AS write_bit_rate,
+ a.secure AS secure,
+ b.uuid AS b_uuid,
+ b.cid_name AS b_caller_id_name,
+ b.cid_num AS b_caller_id_number,
+ b.callstate AS b_callstate,
+ d.id AS b_sip_account_id,
+ d.caller_name AS b_sip_caller_name,
+ b.callee_name AS b_callee_name,
+ b.callee_num AS b_callee_number,
+ b.secure AS b_secure
+ FROM channels a
+ LEFT JOIN channels b ON (a.uuid = b.call_uuid AND a.uuid != b.uuid)
+ LEFT JOIN sip_accounts c ON a.presence_id LIKE (c.auth_name || "@%")
+ LEFT JOIN sip_accounts d ON b.presence_id LIKE (d.auth_name || "@%")
+ WHERE (a.uuid = b.call_uuid AND a.uuid != b.uuid)
+ OR a.call_uuid IS NULL
+ OR a.call_uuid = a.uuid
+ SQL
+ end
+ end
+
+ def self.down
+
+ end
+end \ No newline at end of file
diff --git a/lib/freeswitch_event.rb b/lib/freeswitch_event.rb
index 68d9df2..89d8ea0 100644
--- a/lib/freeswitch_event.rb
+++ b/lib/freeswitch_event.rb
@@ -15,7 +15,11 @@ class FreeswitchEventSocket
return false
end
- def connect(password = DEFAULT_PASSWORD, event_host = DEFAULT_HOST, event_port = DEFAULT_PORT)
+ def connect(password = nil, event_host = nil, event_port = nil)
+ event_host = event_host || GsParameter.get('host', 'event_socket', 'client') || GsParameter.get('listen-ip', 'event_socket', 'settings') || DEFAULT_HOST
+ event_port = event_port || GsParameter.get('port', 'event_socket', 'client') || GsParameter.get('listen-port', 'event_socket', 'settings') || DEFAULT_PORT
+ password = password || GsParameter.get('password', 'event_socket', 'client') || GsParameter.get('password', 'event_socket', 'settings') || DEFAULT_PASSWORD
+
begin
@socket = TCPSocket.open(event_host, event_port)
rescue
@@ -103,8 +107,45 @@ class FreeswitchEvent
end
class FreeswitchAPI
- def self.execute(command, arguments, bgapi = false)
+ def self.api_result(result)
+ if not result
+ return nil
+ end
+
+ if result['Content-Type'] == 'api/response'
+ if result['_BODY'].blank?
+ return nil
+ elsif result['_BODY'] =~ /^\+OK/
+ return true
+ elsif result['_BODY'] =~ /^\-ERR/
+ return false
+ else
+ return result['_BODY']
+ end
+ end
+
+ return nil
+ end
+ def self.api(command, *arguments)
+ event = FreeswitchEventSocket.new()
+ if event && event.connect()
+ event.command("api #{command} #{arguments.join(' ')}")
+ result = event.result()
+ content_length = result['Content-Length'].to_i
+ while content_length > result['_BODY'].to_s.length
+ body = event.read(content_length - result['_BODY'].to_s.length)
+ if body.blank?
+ break
+ end
+ result['_BODY'] = result['_BODY'].to_s + body;
+ end
+ event.close()
+ return result
+ end
+ end
+
+ def self.execute(command, arguments, bgapi = false)
event = FreeswitchEventSocket.new()
if event && event.connect()
api = bgapi ? 'bgapi' : 'api'
diff --git a/lib/tasks/backup.rake b/lib/tasks/backup.rake
index ad4d41d..83bcfd3 100644
--- a/lib/tasks/backup.rake
+++ b/lib/tasks/backup.rake
@@ -25,32 +25,32 @@ namespace :backup do
tmp_dir = "/tmp/gs5_restore_directory"
FileUtils.rm_rf tmp_dir
FileUtils.mkdir_p tmp_dir
- system "cd #{tmp_dir} && sudo /bin/tar xzf #{restore_job.backup_file.path}"
+ system "cd #{tmp_dir} && nice -n 19 sudo /bin/tar xzf #{restore_job.backup_file.path}"
restore_directory = Dir.glob("/tmp/gs5_restore_directory/*").first
- system "cd #{restore_directory} && sudo /bin/tar xf GS5.tar && rm GS5.tar"
+ system "cd #{restore_directory} && nice -n 19 sudo /bin/tar xf GS5.tar && rm GS5.tar"
# Restore faxes
#
if File.exists?("#{restore_directory}/GS5/archives/faxes.tar.gz")
- system "cd / && sudo /bin/tar xzfP #{restore_directory}/GS5/archives/faxes.tar.gz"
+ system "cd / && nice -n 19 sudo /bin/tar xzfP #{restore_directory}/GS5/archives/faxes.tar.gz"
end
# Restore voicemails
#
if File.exists?("#{restore_directory}/GS5/archives/voicemails.tar.gz")
- system "cd / && sudo /bin/tar xzfP #{restore_directory}/GS5/archives/voicemails.tar.gz"
+ system "cd / && nice -n 19 sudo /bin/tar xzfP #{restore_directory}/GS5/archives/voicemails.tar.gz"
end
# Restore recordings
#
if File.exists?("#{restore_directory}/GS5/archives/recordings.tar.gz")
- system "cd / && sudo /bin/tar xzfP #{restore_directory}/GS5/archives/recordings.tar.gz"
+ system "cd / && nice -n 19 sudo /bin/tar xzfP #{restore_directory}/GS5/archives/recordings.tar.gz"
end
# Restore avatars
#
if File.exists?("#{restore_directory}/GS5/archives/avatars.tar.gz")
- system "cd / && sudo /bin/tar xzfP #{restore_directory}/GS5/archives/avatars.tar.gz"
+ system "cd / && nice -n 19 sudo /bin/tar xzfP #{restore_directory}/GS5/archives/avatars.tar.gz"
end
# Delete the archive tar.gz to get more air to breathe
@@ -65,11 +65,11 @@ namespace :backup do
db_user = system_odbc_configuration['gemeinschaft']['USER']
db_password = system_odbc_configuration['gemeinschaft']['PASSWORD']
- system "gunzip < #{restore_directory}/GS5/databases/MySQL/gemeinschaft.sql.gz | mysql -u #{db_user} -p#{db_password} #{database}"
+ system "nice -n 19 gunzip < #{restore_directory}/GS5/databases/MySQL/gemeinschaft.sql.gz | nice -n 19 mysql -u #{db_user} -p#{db_password} #{database}"
FileUtils.rm_rf tmp_dir
- system "cd /opt/gemeinschaft && rake db:migrate"
+ system "cd /opt/gemeinschaft && nice -n 19 rake db:migrate"
# Rebuild the thumbnails
#
diff --git a/misc/freeswitch/conf/freeswitch.xml b/misc/freeswitch/conf/freeswitch.xml
index f72651f..3405bc9 100644
--- a/misc/freeswitch/conf/freeswitch.xml
+++ b/misc/freeswitch/conf/freeswitch.xml
@@ -272,8 +272,10 @@
</input>
<input pattern="^name:(.+)$">
<match>
+ <action function="play-file" data="ivr/ivr-thank_you_for_calling.wav"/>
<action function="play-file" data="$1"/>
<action function="play-file" data="voicemail/vm-not_available.wav"/>
+ <action function="play-file" data="ivr/ivr-please_state_your_name_and_reason_for_calling.wav"/>
</match>
</input>
<input pattern="^greeting:(.+)$">
@@ -470,7 +472,7 @@
</match>
</input>
</macro>
- <macro name="conference_welcome">
+ <macro name="conference_welcome">
<input pattern="^(.*)$">
<match>
<action function="play-file" data="conference/conf-welcome.wav"/>
@@ -515,6 +517,13 @@
</match>
</input>
</macro>
+ <macro name="conference_alone">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="conference/conf-alone.wav"/>
+ </match>
+ </input>
+ </macro>
</macros>
</phrases>
</language>
@@ -789,8 +798,10 @@
</input>
<input pattern="^name:(.+)$">
<match>
+ <action function="play-file" data="ivr/ivr-thank_you_for_calling.wav"/>
<action function="play-file" data="$1"/>
<action function="play-file" data="voicemail/vm-not_available.wav"/>
+ <action function="play-file" data="ivr/ivr-please_state_your_name_and_reason_for_calling.wav"/>
</match>
</input>
<input pattern="^greeting:(.+)$">
@@ -914,7 +925,6 @@
<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>
@@ -923,7 +933,6 @@
<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>
@@ -931,7 +940,6 @@
<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>
@@ -941,7 +949,6 @@
<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>
@@ -949,7 +956,6 @@
<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>
@@ -1032,6 +1038,13 @@
</match>
</input>
</macro>
+ <macro name="conference_alone">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="conference/conf-alone.wav"/>
+ </match>
+ </input>
+ </macro>
</macros>
</phrases>
</language>
@@ -1053,13 +1066,6 @@
<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="local_stream.conf" description="stream files from local dir">
<directory name="default" path="/opt/freeswitch/sounds/music/8000">
<param name="rate" value="8000"/>
@@ -1082,7 +1088,21 @@
<param name="interval" value="20"/>
<param name="timer-name" value="soft"/>
</directory>
- </configuration>
+ <directory name="mohh/shout" path="/var/opt/gemeinschaft/shout">
+ <param name="rate" value="16000"/>
+ <param name="shuffle" value="true"/>
+ <param name="channels" value="1"/>
+ <param name="interval" value="10"/>
+ <param name="timer-name" value="soft"/>
+ </directory>
+ <directory name="moh/shout" path="/var/opt/gemeinschaft/shout">
+ <param name="rate" value="8000"/>
+ <param name="shuffle" value="true"/>
+ <param name="channels" value="1"/>
+ <param name="interval" value="10"/>
+ <param name="timer-name" value="soft"/>
+ </directory>
+ </configuration>
<configuration name="logfile.conf" description="File Logging">
<settings>
<param name="rotate-on-hup" value="true"/>
@@ -1181,6 +1201,14 @@
<param name="file-prefix" value="fax_in_"/>
</settings>
</configuration>
+ <configuration name="shout.conf" description="Shoutcast stream config">
+ <settings>
+ <!-- Don't change these unless you are insane -->
+ <!--<param name="decoder" value="i586"/>-->
+ <param name="volume" value="0.7"/>
+ <!--<param name="outscale" value="8192"/>-->
+ </settings>
+ </configuration>
<configuration name="modules.conf" description="Modules">
<modules>
<load module="mod_console"/>
@@ -1200,16 +1228,17 @@
<load module="mod_native_file"/>
<load module="mod_local_stream"/>
<load module="mod_tone_stream"/>
+ <load module="mod_shout"/>
<load module="mod_say_en"/>
<load module="mod_say_de"/>
<load module="mod_spandsp"/>
<load module="mod_snmp"/>
<load module="mod_dingaling"/>
</modules>
- </configuration>
+ </configuration>
<configuration name="lua.conf" description="LUA Configuration">
<settings>
- <param name="module-directory" value="/usr/lib/i386-linux-gnu/lua/5.1/?.so;"/>
+ <param name="module-directory" value="/usr/lib/local-platform/lua/5.1/?.so;"/>
<param name="script-directory" value="/usr/share/freeswitch/scripts/?.lua;/usr/share/lua/5.1/?.lua;"/>
<param name="xml-handler-script" value="configuration.lua"/>
<param name="xml-handler-bindings" value="directory|configuration"/>
diff --git a/misc/freeswitch/scripts/common/array.lua b/misc/freeswitch/scripts/common/array.lua
new file mode 100644
index 0000000..b1b7a71
--- /dev/null
+++ b/misc/freeswitch/scripts/common/array.lua
@@ -0,0 +1,97 @@
+-- Gemeinschaft 5 module: array functions
+-- (c) AMOOMA GmbH 2013
+--
+
+module(...,package.seeall)
+
+function try(array, arguments)
+ if type(arguments) ~= 'string' or type(array) ~= 'table' then
+ return nil;
+ end
+
+ local result = array;
+
+ arguments:gsub('([^%.]+)', function(entry)
+ local success, result = pcall(function() result = (result[tonumber(entry) or entry]); end);
+ end);
+
+ return result;
+end
+
+
+function set(array, arguments, value)
+ local nop, arguments_count = arguments:gsub('%.', '');
+ local structure = array;
+ arguments:gsub('([^%.]+)', function(entry)
+ if arguments_count <= 0 then
+ structure[entry] = value;
+ elseif type(structure[entry]) == 'table' then
+ structure = structure[entry];
+ else
+ structure[entry] = {};
+ structure = structure[entry];
+ end
+ arguments_count = arguments_count - 1;
+ end);
+end
+
+
+function expand_variable(variable_path, variable_sets)
+ for index=1, #variable_sets do
+ local result = try(variable_sets[index], variable_path);
+ if result then
+ return result;
+ end
+ end
+end
+
+-- replace variables in a string by array values
+function expand_variables(line, ...)
+ local variable_sets = {...};
+ return (line:gsub('{([%a%d%._]+)}', function(captured)
+ return expand_variable(captured, variable_sets);
+ end))
+end
+
+
+-- concatenate array values
+function to_s(array, separator, prefix, suffix)
+ require 'common.str';
+
+ local buffer = '';
+ for key, value in pairs(array) do
+ buffer = common.str.append(buffer, value, separator, prefix, suffix);
+ end
+
+ return buffer;
+end
+
+-- concatenate array keys
+function keys_to_s(array, separator, prefix, suffix)
+ require 'common.str';
+
+ local buffer = '';
+ for key, value in pairs(array) do
+ buffer = common.str.append(buffer, key, separator, prefix, suffix);
+ end
+
+ return buffer;
+end
+
+-- convert to JSON
+function to_json(array)
+ require 'common.str';
+ local buffer = '{';
+ for key, value in pairs(array) do
+ if type(value) == 'table' then
+ buffer = buffer .. '"' .. key .. '":' .. to_json(value) .. ',';
+ else
+ buffer = buffer .. '"' .. key .. '":' .. common.str.to_json(value) .. ',';
+ end
+ end
+ if buffer:sub(-1) == ',' then
+ buffer = buffer:sub(1, -2);
+ end
+ buffer = buffer .. '}';
+ return buffer;
+end
diff --git a/misc/freeswitch/scripts/common/conference.lua b/misc/freeswitch/scripts/common/conference.lua
index b56cba9..5694f62 100644
--- a/misc/freeswitch/scripts/common/conference.lua
+++ b/misc/freeswitch/scripts/common/conference.lua
@@ -1,19 +1,11 @@
-- Gemeinschaft 5 module: conference class
--- (c) AMOOMA GmbH 2012-2013
+-- (c) AMOOMA GmbH 2013
--
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 {}
@@ -24,215 +16,271 @@ function Conference.new(self, arg)
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.settings_get(self)
+ require 'common.array';
+ require 'common.configuration_table';
+
+ local configuration = common.configuration_table.get(self.database, 'conferences');
+ if not configuration then
+ return nil;
+ end
+
+ local parameters = configuration.parameters or {};
+ local settings = configuration.settings or {};
+ local sounds = configuration.sounds or {};
+
+ settings.members_max = settings.members_max or tonumber(parameters['max-members']) or 100;
+ settings.pin_length_max = tonumber(settings.pin_length_max) or 10;
+ settings.pin_length_min = tonumber(settings.pin_length_min) or 2;
+ settings.pin_timeout = tonumber(settings.pin_timeout) or 4000;
+ settings.announcement_max_length = tonumber(settings.announcement_max_length) or 10;
+ settings.announcement_silence_threshold = tonumber(settings.announcement_silence_threshold) or 500;
+ settings.announcement_silence_length = tonumber(settings.announcement_silence_length) or 3;
+ settings.flags = settings.flags or { waste = true };
+ sounds.pin = sounds.pin or 'conference/conf-pin.wav';
+ sounds.has_joined = sounds.has_joined or 'conference/conf-has_joined.wav';
+ sounds.has_left = sounds.has_left or 'conference/conf-has_left.wav';
+ sounds.alone = sounds.has_alone or 'conference/conf-alone.wav';
+
+ settings.key_enter = parameters.key_enter or '#';
+ settings.spool_dir = settings.spool_dir or '/var/spool/freeswitch';
+ return settings, sounds;
+end
+
+
function Conference.find_by_id(self, id)
- local sql_query = 'SELECT * FROM `conferences` WHERE `id`= ' .. tonumber(id) .. ' LIMIT 1';
+ local sql_query = 'SELECT *, (NOW() >= `start` AND NOW() <= `end`) AS `open_now` 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.identifier = 'conference' .. conference.id;
conference.uuid = conference_entry.uuid;
- conference.max_members = tonumber(conference.record.max_members) or MEMBERS_MAX;
- end)
+ conference.pin = conference_entry.pin;
+ conference.open_for_public = common.str.to_b(conference_entry.open_for_anybody);
+ conference.announce_entering = common.str.to_b(conference_entry.announce_new_member_by_name);
+ conference.announce_leaving = common.str.to_b(conference_entry.announce_left_member_by_name);
+ if not common.str.blank(conference_entry.open_now) then
+ conference.open_now = common.str.to_b(conference_entry.open_now);
+ end
+ conference.settings, conference.sounds = self:settings_get();
+ if conference.settings then
+ conference.settings.members_max = tonumber(conference.record.max_members) or conference.settings.members_max;
+ else
+ conference.log:error('CONFERENCE - no basic configuration');
+ end
+ 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
+ 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 sql_query = 'SELECT `a`.* \
+ FROM `conference_invitees` `a` \
+ JOIN `phone_numbers` `b` ON `b`.`phone_numberable_id` = `a`.`id` \
+ WHERE `b`.`phone_numberable_type` = "ConferenceInvitee" \
+ AND `a`.`conference_id` = ' .. self.id .. ' \
+ AND `b`.`number` IN ("' .. table.concat(phone_numbers, "','") .. '") \
+ LIMIT 1';
local invitee = nil;
- self.database:query(sql_query, function(conference_entry)
- invitee = conference_entry;
+ self.database:query(sql_query, function(invitee_entry)
+ invitee = invitee_entry;
end)
- return invitee;
+ return invitee;
end
-function Conference.count(self)
- return tonumber(self.caller:result('conference ' .. self.record.id .. ' list count')) or 0;
+
+function Conference.members_count(self)
+ return tonumber(self.caller:result('conference ' .. self.identifier .. ' 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;
+function Conference.check_pin(self, pin)
+ local digits = '';
+ for i = 1, 3 do
+ if digits == pin then
+ self.caller:send_display('PIN: OK');
+ break
+ elseif digits ~= "" then
+ self.caller:send_display('PIN: wrong');
+ self.caller.session:sayPhrase('conference_bad_pin');
+ end
+ self.caller:send_display('Enter PIN');
+ digits = self.caller.session:read(self.settings.pin_length_min, self.settings.pin_length_max, self.sounds.pin, self.settings.pin_timeout, self.settings.key_enter);
+ end
- 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");
+ if digits ~= pin then
+ return false
+ end
- -- 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 };
+ return true;
+end
- 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));
+function Conference.check_ownership(self, check_caller)
+ local owner = nil;
- 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
-
- require 'common.str'
- -- Check if conference is within time frame
- if not common.str.blank(self.record.start) and not common.str.blank(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
+ if check_caller then
+ owner = common.array.try(self.caller, 'account.owner');
+ else
+ owner = common.array.try(self.caller, 'auth_account.owner');
+ end
+ if not owner then
+ return false;
end
- -- 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 tonumber(self.record.conferenceable_id) == owner.id and self.record.conferenceable_type:lower() == owner.class then
+ return true;
+ end
+end
- 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
+function Conference.account_name_file(self)
+ if not self.caller.account or tostring(self.caller.account.class):lower() ~= 'sipaccount' then
+ return;
end
- if not pin and self.record.pin then
- pin = self.record.pin
+ require 'dialplan.voicemail'
+ local voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_sip_account_id(self.caller.account.id);
+ if voicemail_account and not common.str.blank(voicemail_account.record.name_path) then
+ self.log:debug('CONFERENCE ', self.id, ' - caller_name_file: ', voicemail_account.record.name_path);
+ return voicemail_account.record.name_path;
end
+end
- caller:answer();
- caller:sleep(1000);
- caller.session:sayPhrase('conference_welcome');
- if not common.str.blank(pin) then
- local digits = "";
- for i = 1, 3, 1 do
- if digits == pin then
- break
- elseif digits ~= "" then
- caller.session:sayPhrase('conference_bad_pin');
- end
- digits = caller.session:read(PIN_LENGTH_MIN, PIN_LENGTH_MAX, 'conference/conf-pin.wav', PIN_TIMEOUT, '#');
+function Conference.record_name(self)
+ self.caller:send_display('Record name');
+ local name_file = self.settings.spool_dir .. '/conference_caller_name_' .. self.caller.uuid .. '.wav';
+ self.caller.session:sayPhrase('conference_record_name');
+ self.caller.session:recordFile(name_file, self.settings.announcement_max_length, self.settings.announcement_silence_threshold, self.settings.announcement_max_length);
+ self.caller:send_display('Playback name');
+ self.caller:playback(name_file);
+
+ return name_file;
+end
+
+
+function Conference.playback(self, ...)
+ local sound_files = {...};
+ for index=1, #sound_files do
+ self.caller:execute('set',"result=${conference(" .. self.identifier .. " play ".. tostring(sound_files[index]) .. ")}");
+ end
+end
+
+function Conference.phrase(self, phrase, file_name)
+ self.caller:execute('set',"result=${conference(" .. self.identifier .. " phrase ".. phrase .. ':' .. file_name .. ")}");
+end
+
+function Conference.enter(self, caller, domain)
+ self.caller = caller;
+ local members = self:members_count();
+
+ self.log:info('CONFERENCE ', self.id, ' - open_for_public: ', self.open_for_public, ', open_now: ', self.open_now, ', members: ', members, ', members_max: ', self.settings.members_max);
+
+ if self.open_now == false then
+ self.log:notice('CONFERENCE ', self.id, ' - currently closed, start: ', self.record.start, ', end: ', self.record['end']);
+ return { continue = false, code = 493, phrase = 'Conference closed' };
+ end
+
+ if members >= self.settings.members_max then
+ self.log:notice('CONFERENCE ', self.id, ' - full, members: ', members, ', members_max: ', self.settings.members_max);
+ return { continue = false, code = 493, phrase = 'Conference closed' };
+ end
+
+ local invitee = self:find_invitee_by_numbers(caller.caller_phone_numbers);
+ if invitee then
+ if common.str.to_b(invitee.speaker) then
+ self.settings.flags.mute = nil;
+ end
+ if common.str.to_b(invitee.moderator) then
+ self.settings.flags.moderator = true;
end
- if digits ~= pin then
- caller.session:sayPhrase('conference_goodbye');
- return "CALL_REJECTED";
+ self.log:info('CONFERENCE ', self.id, ' - invitee=', invitee.id, '/', invitee.uuid, ', speaker: ', not self.settings.flags.mute, ', moderator: ', self.settings.flags.moderator);
+ self.pin = invitee.pin;
+ elseif self:check_ownership() then
+ self.pin = nil;
+ local caller_owner = false;
+ if self:check_ownership(true) then
+ self.settings.flags.moderator = true;
+ self.settings.flags.dtmf = true;
+ caller_owner = true;
end
+ self.log:info('CONFERENCE ', self.id, ' - owner authenticated: ', self.caller.auth_account.owner.class,'=', self.caller.auth_account.owner.id, '/', self.caller.auth_account.owner.uuid, ', owner: ', caller_owner, ', speaker: ', not self.settings.flags.mute, ', moderator: ', self.settings.flags.moderator);
+ elseif not self.open_for_public then
+ self.log:notice('CONFERENCE ', self.id, ' - not open for public');
+ return { continue = false, code = 493, phrase = 'Conference closed' };
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();
+ caller:answer();
+ if not common.str.blank(self.pin) and not self:check_pin(self.pin) then
+ self.log:notice('CONFERENCE ', self.id, ' - PIN wrong');
+ caller.session:sayPhrase('conference_goodbye');
+ return { continue = false, code = 493, phrase = 'Not authorized' };
end
- -- Enter the conference
+ self.caller:send_display(tostring(self.record.name) .. ', members: ' .. tostring(members));
+ caller:sleep(1000);
+ caller.session:sayPhrase('conference_welcome');
+
+
local name_file = nil;
+ local name_file_delete = 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 = "/var/spool/freeswitch/conference_caller_name_" .. uid .. ".wav";
- caller.session:sayPhrase('conference_record_name');
- session:recordFile(name_file, ANNOUNCEMENT_MAX_LEN, ANNOUNCEMENT_SILENCE_THRESHOLD, ANNOUNCEMENT_SILENCE_LEN);
- caller.session:streamFile(name_file);
+ if self.announce_entering or self.announce_leaving then
+ name_file = self:account_name_file();
+ if not name_file then
+ name_file = self:record_name(caller);
+ name_file_delete = true;
+ end
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, "#");
+ members = self:members_count();
+ if self.announce_entering and name_file then
+ if members > 0 then
+ self:playback(name_file, self.sounds.has_joined);
+ end
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));
+ if members == 0 then
+ caller.session:sayPhrase('conference_alone');
+ end
+
+ self.caller:send_display(tostring(self.record.name));
+ local result = caller:execute('conference', self.identifier .. "@profile_" .. self.identifier .. "++flags{" .. common.array.keys_to_s(self.settings.flags, '|') .. "}");
+
+ self.caller:send_display('Goodbye');
caller.session:sayPhrase('conference_goodbye');
- -- 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);
+ if self.announce_leaving then
+ members = self:members_count();
+ if members > 0 then
+ self:playback(name_file, self.sounds.has_left);
+ if members == 1 then
+ self:playback(self.sounds.alone);
+ end
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();
+ if name_file_delete then
+ os.remove(name_file);
+ end
end
- cause = "NORMAL_CLEARING";
- caller.session:hangup(cause);
- return cause;
+ return { continue = false, code = 200, phrase = 'OK' }
end
diff --git a/misc/freeswitch/scripts/common/database.lua b/misc/freeswitch/scripts/common/database.lua
index 8aed1ac..be32ad7 100644
--- a/misc/freeswitch/scripts/common/database.lua
+++ b/misc/freeswitch/scripts/common/database.lua
@@ -67,6 +67,29 @@ function Database.query_return_value(self, sql_query)
end
+function Database.query_return_first(self, sql_query)
+ local result = nil;
+
+ self.conn:query(sql_query, function(row)
+ result = row;
+ return result;
+ end);
+
+ return result;
+end
+
+
+function Database.query_return_all(self, sql_query)
+ local result = {};
+
+ self.conn:query(sql_query, function(row)
+ table.insert(result, row);
+ end);
+
+ return result;
+end
+
+
function Database.last_insert_id(self)
return self:query_return_value('SELECT LAST_INSERT_ID()');
end
diff --git a/misc/freeswitch/scripts/common/gateway.lua b/misc/freeswitch/scripts/common/gateway.lua
index c1b50a7..ac38326 100644
--- a/misc/freeswitch/scripts/common/gateway.lua
+++ b/misc/freeswitch/scripts/common/gateway.lua
@@ -34,7 +34,11 @@ end
function Gateway.find_by_id(self, id)
- local sql_query = 'SELECT * FROM `gateways` WHERE `id`= ' .. tonumber(id) .. ' LIMIT 1';
+ local sql_query = 'SELECT `a`.*, `c`.`sip_host` AS `domain`, `c`.`contact` AS `contact_full`, `c`.`network_ip`, `c`.`network_port` \
+ FROM `gateways` `a` \
+ LEFT JOIN `gateway_settings` `b` ON `a`.`id` = `b`.`gateway_id` AND `b`.`name` = "inbound_username" \
+ LEFT JOIN `sip_registrations` `c` ON `b`.`value` = `c`.`sip_user` \
+ WHERE `a`.`id`= ' .. tonumber(id) .. ' LIMIT 1';
local gateway = nil;
self.database:query(sql_query, function(entry)
@@ -46,6 +50,9 @@ function Gateway.find_by_id(self, id)
gateway.technology = entry.technology;
gateway.outbound = common.str.to_b(entry.outbound);
gateway.inbound = common.str.to_b(entry.inbound);
+ gateway.domain = entry.domain;
+ gateway.network_ip = entry.network_ip;
+ gateway.network_port = tonumber(entry.network_port) or 5060;
end)
if gateway then
@@ -59,7 +66,45 @@ end
function Gateway.find_by_name(self, name)
local gateway_name = name:gsub('([^%a%d%._%+])', '');
- local sql_query = 'SELECT * FROM `gateways` WHERE `name`= "' .. gateway_name .. '" LIMIT 1';
+ local sql_query = 'SELECT `a`.*, `c`.`sip_host` `domain`, `c`.`contact` AS `contact_full`, `c`.`network_ip`, `c`.`network_port`\
+ FROM `gateways` `a` \
+ LEFT JOIN `gateway_settings` `b` ON `a`.`id` = `b`.`gateway_id` AND `b`.`name` = "inbound_username" \
+ LEFT JOIN `sip_registrations` `c` ON `b`.`value` = `c`.`sip_user` \
+ WHERE `a`.`name`= ' .. self.database:escape(gateway_name, '"') .. ' LIMIT 1';
+
+ local gateway = nil;
+ self.database:query(sql_query, function(entry)
+ require 'common.str';
+ gateway = Gateway:new(self);
+ gateway.record = entry;
+ gateway.id = tonumber(entry.id);
+ gateway.name = entry.name;
+ gateway.technology = entry.technology;
+ gateway.outbound = common.str.to_b(entry.outbound);
+ gateway.inbound = common.str.to_b(entry.inbound);
+ gateway.domain = entry.domain;
+ gateway.network_ip = entry.network_ip;
+ gateway.network_port = tonumber(entry.network_port) or 5060;
+ end)
+
+ if gateway then
+ gateway.settings = self:config_table_get('gateway_settings', gateway.id);
+ end
+
+ return gateway;
+end
+
+
+function Gateway.find_by_auth_name(self, name)
+ local auth_name = name:gsub('([^%a%d%._%+])', '');
+
+ local sql_query = 'SELECT `c`.*, `a`.`value` `password`, `b`.`value` `username` \
+ FROM `gateway_settings` `a` \
+ INNER JOIN `gateway_settings` `b` \
+ ON (`a`.`gateway_id` = `b`.`gateway_id` AND `a`.`name` = "inbound_password" AND `b`.`name` = "inbound_username" AND `b`.`value` = ' .. self.database:escape(auth_name, '"') .. ') \
+ LEFT JOIN `gateways` `c` \
+ ON (`a`.`gateway_id` = `c`.`id`) \
+ WHERE `c`.`inbound` IS TRUE OR `c`.`outbound` IS TRUE LIMIT 1';
local gateway = nil;
self.database:query(sql_query, function(entry)
@@ -82,14 +127,26 @@ end
function Gateway.call_url(self, destination_number)
- if self.technology == 'sip' then
- return 'sofia/gateway/' .. self.GATEWAY_PREFIX .. self.id .. '/' .. tostring(destination_number);
- elseif self.technology == 'xmpp' then
- local destination_str = tostring(destination_number);
- if self.settings.destination_domain then
- destination_str = destination_str .. '@' .. self.settings.destination_domain;
+ require 'common.str';
+
+ if common.str.blank(self.settings.dial_string) then
+ if self.technology == 'sip' then
+ if self.settings.inbound_username and self.settings.inbound_password and not common.str.blank(self.record.domain) then
+ return 'sofia/' .. (self.settings.profile or 'gemeinschaft') .. '/' .. self.settings.inbound_username .. '%' .. self.record.domain;
+ else
+ return 'sofia/gateway/' .. self.GATEWAY_PREFIX .. self.id .. '/' .. tostring(destination_number);
+ end
+
+ elseif self.technology == 'xmpp' then
+ local destination_str = tostring(destination_number);
+ if self.settings.destination_domain then
+ destination_str = destination_str .. '@' .. self.settings.destination_domain;
+ end
+ return 'dingaling/' .. self.GATEWAY_PREFIX .. self.id .. '/' .. destination_str;
end
- return 'dingaling/' .. self.GATEWAY_PREFIX .. self.id .. '/' .. destination_str;
+ else
+ require 'common.array';
+ return tostring(common.array.expand_variables(self.settings.dial_string, self, { destination_number = destination_number }));
end
return '';
@@ -174,9 +231,7 @@ function Gateway.parameters_build(self, gateway_id, technology)
parameters.register = common.str.to_b(settings.register);
end
- if common.str.blank(settings.password) then
- parameters.password = 'gateway' .. gateway_id;
- else
+ if not common.str.blank(settings.password) then
parameters.password = settings.password;
end
diff --git a/misc/freeswitch/scripts/common/intruder.lua b/misc/freeswitch/scripts/common/intruder.lua
index 083ec37..7d12155 100644
--- a/misc/freeswitch/scripts/common/intruder.lua
+++ b/misc/freeswitch/scripts/common/intruder.lua
@@ -49,3 +49,46 @@ function Intruder.update_blacklist(self, event)
self.database:insert_or_update('intruders', intruder_record, { created_at = false, comment = false });
end
+
+
+function Intruder.sources_list(self, key)
+ local sql_query = nil;
+
+ if key then
+ sql_query = 'SELECT * FROM `intruders` WHERE `key` = ' .. self.database:escape(key, '"') .. ' LIMIT 1';
+ else
+ sql_query = 'SELECT * FROM `intruders`';
+ end
+
+ local sources = {};
+ local sources_count = 0;
+ local blacklist_count = 0;
+ local whitelist_count = 0;
+
+ self.database:query(sql_query, function(record)
+ sources[record.key] = {
+ ignore = (record.list_type == 'whitelist'),
+ contact_first = 0,
+ contact_last = 0,
+ contact_count = tonumber(record.contact_count) or 0,
+ span_contact_count = 0,
+ span_start = 0,
+ points = tonumber(record.points) or 0,
+ banned = tonumber(record.bans) or 0,
+ };
+ sources_count = sources_count + 1;
+ if record.list_type == 'whitelist' then
+ whitelist_count = whitelist_count + 1;
+ elseif record.list_type == 'blacklist' then
+ blacklist_count = blacklist_count + 1;
+ end
+ end);
+
+ self.log:info('[intruder] INTRUDER_LIST - entries loaded: ', sources_count, ', blacklist: ', blacklist_count, ', whitelist: ', whitelist_count);
+
+ if key then
+ return sources[key];
+ end
+
+ return sources;
+end
diff --git a/misc/freeswitch/scripts/common/log.lua b/misc/freeswitch/scripts/common/log.lua
index b7c8d09..b9893ac 100644
--- a/misc/freeswitch/scripts/common/log.lua
+++ b/misc/freeswitch/scripts/common/log.lua
@@ -12,6 +12,8 @@ function Log.new(self, arg)
object = arg.object or {}
setmetatable(object, self);
self.__index = self;
+ self.disabled = arg.disabled or false;
+ self.buffer = arg.buffer;
self.prefix = arg.prefix or '### ';
self.level_console = arg.level_console or 0;
@@ -22,18 +24,31 @@ function Log.new(self, arg)
self.level_notice = arg.level_notice or 5;
self.level_info = arg.level_info or 6;
self.level_debug = arg.level_debug or 7;
+ self.level_devel = arg.level_devel or 4;
return object;
end
function Log.message(self, log_level, message_arguments )
+ if self.disabled then
+ return
+ end
local message = tostring(self.prefix);
for index, value in pairs(message_arguments) do
if type(index) == 'number' then
- message = message .. tostring(value);
+ if type(value) == 'table' then
+ require 'common.array';
+ message = message .. common.array.to_json(value);
+ else
+ message = message .. tostring(value);
+ end
end
end
- freeswitch.consoleLog(log_level, message .. '\n');
+ if self.buffer then
+ table.insert(self.buffer, message);
+ elseif freeswitch then
+ freeswitch.consoleLog(log_level, message .. '\n');
+ end
end
function Log.console(self, ...)
@@ -67,3 +82,9 @@ end
function Log.debug(self, ...)
self:message(self.level_debug, {...});
end
+
+function Log.devel(self, ...)
+ local arguments = {...};
+ table.insert(arguments, 1, '**');
+ self:message(self.level_devel, arguments);
+end
diff --git a/misc/freeswitch/scripts/common/object.lua b/misc/freeswitch/scripts/common/object.lua
index 8c195e5..68e1361 100644
--- a/misc/freeswitch/scripts/common/object.lua
+++ b/misc/freeswitch/scripts/common/object.lua
@@ -95,6 +95,18 @@ function Object.find(self, attributes)
if object then
object.owner = self:find{class = object.record.fax_accountable_type, id = tonumber(object.record.fax_accountable_id)};
end
+ elseif class == 'conference' then
+ require 'common.conference';
+
+ if tonumber(attributes.id) then
+ object = common.conference.Conference:new{ log = self.log, database = self.database }:find_by_id(attributes.id);
+ elseif not common.str.blank(attributes.uuid) then
+ object = common.conference.Conference:new{ log = self.log, database = self.database }:find_by_uuid(attributes.uuid);
+ end
+
+ if object then
+ object.owner = self:find{class = object.record.conferenceable_type, id = tonumber(object.record.conferenceable_id)};
+ end
end
if object then
diff --git a/misc/freeswitch/scripts/common/perimeter.lua b/misc/freeswitch/scripts/common/perimeter.lua
index 0815d33..d3b601c 100644
--- a/misc/freeswitch/scripts/common/perimeter.lua
+++ b/misc/freeswitch/scripts/common/perimeter.lua
@@ -9,6 +9,9 @@ Perimeter = {}
function Perimeter.new(self, arg)
+ require 'common.str';
+ require 'common.array';
+
arg = arg or {}
object = arg.object or {}
setmetatable(object, self);
@@ -94,12 +97,24 @@ end
function Perimeter.check(self, event)
- if not event or not event.key then
- self.log:warning('[perimeter] PERIMETER_CHECK - no event/key');
+ if not type(event) == 'list' then
+ self.log:warning('[perimeter] PERIMETER_CHECK - no event data');
+ return;
+ end
+ if not event.key then
+ self.log:warning('[perimeter] PERIMETER_CHECK - no key');
+ for key, value in pairs(event) do
+ self.log:debug('[perimeter] PERIMETER_CHECK event_data - "', key, '" = "', value, '"');
+ end
return;
end
- event.record = self:record_load(event);
+ event.record = self:record_load(event);
+
+ if event.record.ignore then
+ return
+ end
+
if event.record.banned <= self.ban_tries then
for check_name, check_points in pairs(self.checks[event.action]) do
if self.checks_available[check_name] then
@@ -191,7 +206,7 @@ end
function Perimeter.check_bad_headers(self, event)
local points = nil;
for name, pattern in pairs(self.bad_headers[event.action]) do
- pattern = self:expand_variables(pattern, event);
+ pattern = common.array.expand_variables(pattern, event);
local success, result = pcall(string.find, event[name], pattern);
if success and result then
self.log:debug('[', event.key, '/', event.sequence, '] PERIMETER_BAD_HEADERS - ', name, '=', event[name], ' ~= ', pattern);
@@ -213,29 +228,36 @@ function Perimeter.append_blacklist_file(self, event)
event.date = self:format_date(event.timestamp);
if self.blacklist_file_comment then
- blacklist:write(self:expand_variables(self.blacklist_file_comment, event), '\n');
+ blacklist:write(common.array.expand_variables(self.blacklist_file_comment, event), '\n');
end
self.log:debug('[', event.key, '/', event.sequence, '] PERIMETER_APPEND_BLACKLIST - file: ', self.blacklist_file);
- blacklist:write(self:expand_variables(self.blacklist_file_entry, event), '\n');
+ blacklist:write(common.array.expand_variables(self.blacklist_file_entry, event), '\n');
blacklist:close();
end
function Perimeter.execute_ban(self, event)
- local command = self:expand_variables(self.ban_command, event);
+ local command = common.array.expand_variables(self.ban_command, event);
self.log:debug('[', event.key, '/', event.sequence, '] PERIMETER_EXECUTE_BAN - command: ', command);
local result = os.execute(command);
end
+
function Perimeter.update_intruder(self, event)
require 'common.intruder';
local result = common.intruder.Intruder:new{ log = self.log, database = self.database }:update_blacklist(event);
end
-function Perimeter.expand_variables(self, line, variables)
- return (line:gsub('{([%a%d%._]+)}', function(captured)
- return variables[captured] or '';
- end))
+function Perimeter.action_db_rescan(self, record)
+ require 'common.intruder';
+
+ if common.str.blank(record.key) then
+ self.log:info('[perimeter] PERIMETER rescan entire sources database');
+ self.sources = common.intruder.Intruder:new{ log = self.log, database = self.database }:sources_list();
+ else
+ self.log:info('[perimeter] PERIMETER rescan sources database - key: ', record.key);
+ self.sources[record.key] = common.intruder.Intruder:new{ log = self.log, database = self.database }:sources_list(record.key);
+ end
end
diff --git a/misc/freeswitch/scripts/common/sip_account.lua b/misc/freeswitch/scripts/common/sip_account.lua
index 6cc7d25..e7ee0d7 100644
--- a/misc/freeswitch/scripts/common/sip_account.lua
+++ b/misc/freeswitch/scripts/common/sip_account.lua
@@ -130,9 +130,9 @@ end
function SipAccount.call_state(self)
- local sql_query = 'SELECT `callstate` FROM `detailed_calls` \
- WHERE `presence_id` LIKE "' .. self.record.auth_name .. '@%" \
- OR `b_presence_id` LIKE "' .. self.record.auth_name .. '@%" \
+ local sql_query = 'SELECT `callstate` FROM `calls_active` \
+ WHERE `sip_account_id` = ' .. self.id .. ' \
+ OR `b_sip_account_id` = ' .. self.id .. ' \
LIMIT 1';
return self.database:query_return_value(sql_query);
diff --git a/misc/freeswitch/scripts/common/str.lua b/misc/freeswitch/scripts/common/str.lua
index 541199f..3fd8fde 100644
--- a/misc/freeswitch/scripts/common/str.lua
+++ b/misc/freeswitch/scripts/common/str.lua
@@ -4,37 +4,6 @@
module(...,package.seeall)
-function try(array, arguments)
- if type(arguments) ~= 'string' or type(array) ~= 'table' then
- return nil;
- end
-
- local result = array;
-
- arguments:gsub('([^%.]+)', function(entry)
- local success, result = pcall(function() result = (result[tonumber(entry) or entry]); end);
- end);
-
- return result;
-end
-
-
-function set(array, arguments, value)
- local nop, arguments_count = arguments:gsub('%.', '');
- local structure = array;
- arguments:gsub('([^%.]+)', function(entry)
- if arguments_count <= 0 then
- structure[entry] = value;
- elseif type(structure[entry]) == 'table' then
- structure = structure[entry];
- else
- structure[entry] = {};
- structure = structure[entry];
- end
- arguments_count = arguments_count - 1;
- end);
-end
-
-- to number
function to_n(value)
value = tostring(value):gsub('[^%d%.%+%-]', '');
@@ -160,23 +129,3 @@ function append(buffer, value, separator, prefix, suffix)
return buffer;
end
-
--- concatenate array values to string
-function concat(array, separator, prefix, suffix)
- local buffer = '';
- for key, value in pairs(array) do
- buffer = append(buffer, value, separator, prefix, suffix);
- end
-
- return buffer;
-end
-
--- concatenate array keys to string
-function concat_keys(array, separator, prefix, suffix)
- local buffer = '';
- for key, value in pairs(array) do
- buffer = append(buffer, key, separator, prefix, suffix);
- end
-
- return buffer;
-end
diff --git a/misc/freeswitch/scripts/configuration.lua b/misc/freeswitch/scripts/configuration.lua
index 6660c3d..88ef452 100644
--- a/misc/freeswitch/scripts/configuration.lua
+++ b/misc/freeswitch/scripts/configuration.lua
@@ -196,7 +196,7 @@ function conf_conference(database)
if conf_name then
require 'common.conference'
- conference = common.conference.Conference:new{log=log, database=database}:find_by_id(conf_name);
+ conference = common.conference.Conference:new{log=log, database=database}:find_by_id(common.str.to_i(conf_name));
if conference then
log:debug('CONFIG_CONFERENCE ', conf_name, ' name: ', conference.record.name, ', profile: ', profile_name);
config.parameters['caller-id-name'] = conference.record.name or '';
@@ -248,6 +248,7 @@ function conf_conference(database)
};
end
+
function conf_voicemail(database)
require 'configuration.simple_xml'
local xml = configuration.simple_xml.SimpleXml:new();
@@ -283,6 +284,35 @@ function conf_voicemail(database)
};
end
+
+function conf_event_socket(database)
+ require 'configuration.simple_xml'
+ local xml = configuration.simple_xml.SimpleXml:new();
+
+ require 'common.configuration_table';
+ local settings = common.configuration_table.get(database, 'event_socket', 'settings');
+
+ XML_STRING = xml:element{
+ 'document',
+ ['type'] = 'freeswitch/xml',
+ xml:element{
+ 'section',
+ name = 'configuration',
+ description = 'Gemeinschaft 5 FreeSWITCH configuration',
+ xml:element{
+ 'configuration',
+ name = 'event_socket.conf',
+ description = 'Event socket configuration',
+ xml:element{
+ 'settings',
+ xml:from_hash('param', settings, 'name', 'value'),
+ },
+ },
+ },
+ };
+end
+
+
function conf_post_switch(database)
require 'configuration.simple_xml'
local xml = configuration.simple_xml.SimpleXml:new();
@@ -371,6 +401,7 @@ end
function directory_sip_account(database)
+ require 'common.str';
require 'configuration.simple_xml'
local xml = configuration.simple_xml.SimpleXml:new();
@@ -381,21 +412,82 @@ function directory_sip_account(database)
local user_xml = nil;
- 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');
+ if not common.str.blank(auth_name) then
+ require 'common.sip_account'
+ local sip_account = common.sip_account.SipAccount:new{ log = log, database = database}:find_by_auth_name(auth_name);
+
+ require 'common.configuration_table'
+ local user_parameters = common.configuration_table.get(database, 'sip_accounts', 'parameters');
+
+ if sip_account ~= nil then
+ user_parameters['password'] = sip_account.record.password;
+ user_parameters['vm-password'] = sip_account.record.voicemail_pin;
+
+ local user_variables = {
+ user_context = "default",
+ gs_from_gateway = "false",
+ gs_account_id = sip_account.record.id,
+ gs_account_uuid = sip_account.record.uuid,
+ gs_account_type = "SipAccount",
+ gs_account_state = sip_account.record.state,
+ gs_account_caller_name = sip_account.record.caller_name,
+ gs_account_owner_type = sip_account.record.sip_accountable_type,
+ gs_account_owner_id = sip_account.record.sip_accountable_id
+ }
+
+ 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);
+ user_xml = xml:element{
+ 'groups',
+ xml:element{
+ 'group',
+ name = 'default',
+ xml:element{
+ 'users',
+ xml:element{
+ 'user',
+ id = sip_account.record.auth_name,
+ xml:element{
+ 'params',
+ xml:from_hash('param', user_parameters, 'name', 'value'),
+ },
+ xml:element{
+ 'variables',
+ xml:from_hash('variable', user_variables, 'name', 'value'),
+ },
+ },
+ },
+ },
+ };
+ else
+ log:debug('DIRECTORY_SIP_ACCOUNT - auth_name: ', sip_account.record.auth_name, ', caller_name: ', sip_account.record.caller_name, ', domain: ', domain);
+
+ user_xml = xml:element{
+ 'user',
+ id = sip_account.record.auth_name,
+ xml:element{
+ 'params',
+ xml:from_hash('param', user_parameters, 'name', 'value'),
+ },
+ xml:element{
+ 'variables',
+ xml:from_hash('variable', user_variables, 'name', 'value'),
+ },
+ };
+ end
+ else
require 'common.gateway'
- local sip_gateway = common.gateway.Gateway:new{ log = self.log, database = self.database }:find_by_name(gateway_name);
+ local sip_gateway = common.gateway.Gateway:new{ log = log, database = database }:find_by_auth_name(auth_name);
+
if sip_gateway then
- log:debug('DIRECTORY_GATEWAY - name: ', gateway_name, ', auth_name: ', auth_name);
+ log:debug('DIRECTORY_GATEWAY - name: ', sip_gateway.name, ', auth_name: ', auth_name);
local user_variables = {
- user_context = "default",
- gs_from_gateway = "true",
- gs_gateway_name = gateway_name,
- gs_gateway_id = sip_gateway.id,
+ user_context = 'default',
+ gs_from_gateway = 'true',
+ gs_gateway_name = sip_gateway.name,
+ gs_gateway_id = sip_gateway.id,
+ gs_gateway_domain = domain,
}
user_xml = xml:element{
@@ -414,73 +506,7 @@ function directory_sip_account(database)
},
};
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);
-
- require 'common.configuration_table'
- local user_parameters = common.configuration_table.get(database, 'sip_accounts', 'parameters');
-
- if sip_account ~= nil then
- user_parameters['password'] = sip_account.record.password;
- user_parameters['vm-password'] = sip_account.record.voicemail_pin;
-
- local user_variables = {
- user_context = "default",
- gs_from_gateway = "false",
- gs_account_id = sip_account.record.id,
- gs_account_uuid = sip_account.record.uuid,
- gs_account_type = "SipAccount",
- gs_account_state = sip_account.record.state,
- gs_account_caller_name = sip_account.record.caller_name,
- gs_account_owner_type = sip_account.record.sip_accountable_type,
- gs_account_owner_id = sip_account.record.sip_accountable_id
- }
-
- 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);
- user_xml = xml:element{
- 'groups',
- xml:element{
- 'group',
- name = 'default',
- xml:element{
- 'users',
- xml:element{
- 'user',
- id = sip_account.record.auth_name,
- xml:element{
- 'params',
- xml:from_hash('param', user_parameters, 'name', 'value'),
- },
- xml:element{
- 'variables',
- xml:from_hash('variable', user_variables, 'name', 'value'),
- },
- },
- },
- },
- };
- else
- log:debug('DIRECTORY_SIP_ACCOUNT - auth_name: ', sip_account.record.auth_name, ', caller_name: ', sip_account.record.caller_name, ', domain: ', domain);
-
- user_xml = xml:element{
- 'user',
- id = sip_account.record.auth_name,
- xml:element{
- 'params',
- xml:from_hash('param', user_parameters, 'name', 'value'),
- },
- xml:element{
- 'variables',
- xml:from_hash('variable', user_variables, 'name', 'value'),
- },
- };
- end
- else
- log:debug('DIRECTORY_SIP_ACCOUNT - sip account not found - auth_name: ', auth_name, ', domain: ', domain);
+ log:debug('DIRECTORY_SIP_ACCOUNT - neither a sip account nor a gateway found by SIP user name: ', auth_name, ', domain: ', domain);
-- fake sip_account configuration
user_parameters['password'] = tostring(math.random(0, 65534));
user_parameters['vm-password'] = '';
@@ -562,6 +588,8 @@ if XML_REQUEST.section == 'configuration' and XML_REQUEST.tag_name == 'configura
conf_voicemail(database);
elseif XML_REQUEST.key_value == "post_load_switch.conf" then
conf_post_switch(database);
+ elseif XML_REQUEST.key_value == "event_socket.conf" then
+ conf_event_socket(database);
end
elseif XML_REQUEST.section == 'directory' and XML_REQUEST.tag_name == '' then
log:debug('SIP_ACCOUNT_DIRECTORY - initialization phase');
diff --git a/misc/freeswitch/scripts/dialplan/callback.lua b/misc/freeswitch/scripts/dialplan/callback.lua
new file mode 100644
index 0000000..f6fd48e
--- /dev/null
+++ b/misc/freeswitch/scripts/dialplan/callback.lua
@@ -0,0 +1,85 @@
+-- Gemeinschaft 5 module: callback class
+-- (c) AMOOMA GmbH 2013
+--
+
+module(...,package.seeall)
+
+Callback = {}
+
+-- create callback callback ;)
+function Callback.new(self, arg)
+ arg = arg or {}
+ callback = arg.callback or {}
+ setmetatable(callback, self);
+ self.__index = self;
+ self.class = 'callback';
+ self.log = arg.log;
+ self.session = arg.session;
+ self.sessions = {};
+ local id = common.str.to_i(self.session);
+ self.sessions[id] = { id = id, session = self.session, dtmf = {} };
+ return callback;
+end
+
+
+function Callback.callback(self, class, identifier, callback_function, callback_instance, callback_session)
+ local id = common.str.to_i(callback_session or self.session);
+ local session_record = self.sessions[id];
+
+ if not session_record and callback_session then
+ self.sessions[id] = { id = id, session = callback_session, dtmf = {} };
+ session_record = self.sessions[id];
+ end
+
+ if not session_record then
+ return false;
+ end
+
+
+ _G['global_callback_record_' .. id] = session_record;
+
+ session_record.session:setInputCallback('global_callback_handler', 'global_callback_record_' .. id);
+ session_record[class][identifier] = { method = callback_function, instance = callback_instance };
+
+ return true;
+end
+
+
+function Callback.callback_unset(self, class, identifier, callback_session)
+ local session_record = self.sessions[tostring(callback_session or self.session)];
+ if not session_record then
+ return false;
+ end
+
+ -- session_record.session:unsetInputCallback();
+ session_record[class][identifier] = nil;
+
+ return true;
+end
+
+
+function Callback.run(self, arg)
+ local identifier = arg[2];
+ local data = arg[3];
+ local session_record = arg[4];
+ local callbacks = session_record[identifier];
+
+ local return_value = nil;
+ if callbacks then
+ for identifier, callback in pairs(callbacks) do
+ if callback.method then
+ local result = nil;
+ if callback.instance then
+ result = callback.method(callback.instance, data);
+ else
+ result = callback.method(data.digit, data);
+ end
+ if result == false then
+ return_value = 'break';
+ end
+ end
+ end
+ end
+
+ return return_value;
+end
diff --git a/misc/freeswitch/scripts/dialplan/dialplan.lua b/misc/freeswitch/scripts/dialplan/dialplan.lua
index ffad4da..5335328 100644
--- a/misc/freeswitch/scripts/dialplan/dialplan.lua
+++ b/misc/freeswitch/scripts/dialplan/dialplan.lua
@@ -22,6 +22,9 @@ local CALL_FORWARDING_SERVICES = {
-- create dialplan object
function Dialplan.new(self, arg)
+ require 'common.str';
+ require 'common.array';
+
arg = arg or {}
object = arg.object or {}
setmetatable(object, self);
@@ -35,7 +38,6 @@ 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
@@ -74,8 +76,7 @@ end
function Dialplan.configuration_read(self)
- require 'common.str'
- require 'common.configuration_table'
+ require 'common.configuration_table';
-- dialplan configuration
self.config = common.configuration_table.get(self.database, 'dialplan');
@@ -124,7 +125,6 @@ end
function Dialplan.auth_sip_account(self)
- require 'common.str'
if not common.str.blank(self.caller.auth_account_type) then
self.log:info('AUTH_SIP_ACCOUNT - ', self.caller.auth_account_type, '=', self.caller.account_id, '/', self.caller.account_uuid);
return true;
@@ -135,10 +135,22 @@ end
function Dialplan.auth_gateway(self)
require 'common.gateway'
local gateway_class = common.gateway.Gateway:new{ log = self.log, database = self.database};
- local gateway = gateway_class:authenticate(self.caller);
+
+ local gateway = false;
+
+ if self.caller:to_b('gs_from_gateway') then
+ gateway = {
+ name = self.caller:to_s('gs_gateway_name'),
+ id = self.caller:to_i('gs_gateway_id'),
+ }
+ log:info('AUTH_GATEWAY - authenticaded by password and username: ', self.caller:to_s('username'), ', gateway=', gateway.id, '|', gateway.name, ', ip: ', self.caller.sip_contact_host);
+ return gateway_class:find_by_id(gateway.id);
+ else
+ gateway = gateway_class:authenticate(self.caller);
+ end
if gateway then
- log:info('AUTH_GATEWAY - ', gateway.auth_source, ' ~ ', gateway.auth_pattern, ', gateway=', gateway.id, ', name: ', gateway.name, ', ip: ', self.caller.sip_contact_host);
+ log:info('AUTH_GATEWAY - ', gateway.auth_source, ' ~ ', gateway.auth_pattern, ', gateway=', gateway.id, '|', gateway.name, ', ip: ', self.caller.sip_contact_host);
return gateway_class:find_by_id(gateway.id);
end
end
@@ -151,20 +163,15 @@ end
function Dialplan.retrieve_caller_data(self)
- require 'common.str'
self.caller.caller_phone_numbers_hash = {};
- -- TODO: Set auth_account on transfer initiated by calling party
- if not common.str.blank(self.caller.dialed_sip_user) then
- self.caller.auth_account = self:object_find{class = 'sipaccount', domain = self.caller.dialed_domain, auth_account = self.caller.dialed_sip_user};
- if self.caller.set_auth_account then
- self.caller:set_auth_account(self.caller.auth_account);
- end
+ if not common.str.blank(self.caller.previous_destination_type) and not common.str.blank(self.caller.previous_destination_uuid) then
+ self.log:debug('CALLER_DATA - authenticate by previous destination: ', self.caller.previous_destination_type, '=', self.caller.previous_destination_id, '/', self.caller.previous_destination_uuid);
+ self.caller.auth_account = self:object_find{class = self.caller.previous_destination_type, uuid = self.caller.previous_destination_uuid};
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{class = self.caller.auth_account_type, uuid = self.caller.auth_account_uuid};
- if self.caller.set_auth_account then
- self.caller:set_auth_account(self.caller.auth_account);
- end
+ elseif not common.str.blank(self.caller.dialed_sip_user) then
+ self.caller.auth_account = self:object_find{class = 'sipaccount', domain = self.caller.dialed_domain, auth_account = self.caller.dialed_sip_user};
end
if self.caller.auth_account then
@@ -174,13 +181,19 @@ function Dialplan.retrieve_caller_data(self)
else
self.log:error('CALLER_DATA - auth owner not found');
end
+ if self.caller.set_auth_account then
+ self.caller:set_auth_account(self.caller.auth_account);
+ end
else
- self.log:info('CALLER_DATA - no data - unauthenticated call: ', self.caller.auth_account_type, '/', self.caller.auth_account_uuid);
+ self.log:info('CALLER_DATA - no data - unauthenticated call: ', self.caller.auth_account_type, '=', self.caller.auth_account_id, '/', 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{class = self.caller.account_type, uuid = self.caller.account_uuid};
if self.caller.account then
+ self.caller.clir = common.str.to_b(common.array.try(self.caller, 'account.record.clir'));
+ self.caller.clip = common.str.to_b(common.array.try(self.caller, 'account.record.clip'));
+
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
@@ -207,8 +220,6 @@ end
function Dialplan.destination_new(self, arg)
- require 'common.str'
-
local destination = {
number = arg.number or '',
type = arg.type or 'unknown',
@@ -288,7 +299,6 @@ function Dialplan.dial(self, destination)
local user_id = nil;
local tenant_id = nil;
- require 'common.str'
destination.caller_id_number = destination.caller_id_number or self.caller.caller_phone_numbers[1];
if destination.node_local and destination.type == 'sipaccount' then
@@ -296,6 +306,7 @@ function Dialplan.dial(self, destination)
destination.account = self:object_find{class = destination.type, id = destination.id};
if destination.account then
+ destination.uuid = destination.account.uuid;
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);
@@ -306,7 +317,7 @@ function Dialplan.dial(self, destination)
self.log:debug('DESTINATION_GROUPS - pickup_groups: ', table.concat(group_names, ','));
for index=1, #group_ids do
table.insert(destination.pickup_groups, 'g' .. group_ids[index]);
- end
+ end
end
if destination.account and destination.account.owner then
@@ -317,12 +328,14 @@ function Dialplan.dial(self, destination)
elseif destination.account.owner.class == 'tenant' then
tenant_id = destination.account.owner.id;
end
+ self.caller:set_variable('gs_destination_owner_type', destination.account.owner.class);
+ self.caller:set_variable('gs_destination_owner_id', destination.account.owner.id);
+ self.caller:set_variable('gs_destination_owner_uuid', destination.account.owner.uuid);
end
end
if not self.caller.clir then
if user_id or tenant_id then
- require 'common.str'
if self.phonebook_number_lookup then
require 'dialplan.phone_book'
@@ -455,16 +468,14 @@ 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);
+ require 'common.conference';
+ local 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 }
+ return conference:enter(self.caller, self.domain);
end
@@ -579,13 +590,16 @@ 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);
+
+ local voicemail_account = nil;
+
+ local sip_account_id
+ if not common.str.blank(destination.number) and false then
+ voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_number(destination.number);
+ elseif self.caller.auth_account and self.caller.auth_account.class == 'sipaccount' then
+ voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_sip_account_id(self.caller.auth_account.id);
+ end
if not voicemail_account then
self.log:error('VOICEMAIL - no mailbox');
@@ -609,7 +623,6 @@ end
function Dialplan.switch(self, destination)
- require 'common.str'
local result = nil;
self.dial_timeout_active = self.dial_timeout;
@@ -703,7 +716,7 @@ function Dialplan.switch(self, destination)
elseif not common.str.blank(destination.number) then
local result = { continue = false, code = 404, phrase = 'No route' }
- local clip_no_screening = common.str.try(self.caller, 'account.record.clip_no_screening');
+ local clip_no_screening = common.array.try(self.caller, 'account.record.clip_no_screening');
self.caller.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
@@ -713,8 +726,8 @@ function Dialplan.switch(self, destination)
for index, number in ipairs(self.caller.caller_phone_numbers) do
table.insert(self.caller.caller_id_numbers, number);
end
- self.log:info('CALLER_ID_NUMBERS - clir: ', self.caller.clir, ', numbers: ', table.concat(self.caller.caller_id_numbers, ','));
+ self.log:info('SWITCH - clir: ', self.caller.clir, ', caller_id_numbers: ', table.concat(self.caller.caller_id_numbers, ','));
destination.callee_id_number = destination.number;
destination.callee_id_name = nil;
@@ -727,9 +740,8 @@ function Dialplan.switch(self, destination)
end
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');
+ local user_id = common.array.try(self.caller, 'account.owner.id');
+ local tenant_id = common.array.try(self.caller, 'account.owner.record.current_tenant_id');
if user_id or tenant_id then
require 'dialplan.phone_book'
@@ -745,7 +757,6 @@ function Dialplan.switch(self, destination)
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);
@@ -796,7 +807,6 @@ end
function Dialplan.run(self, destination)
- require 'common.str';
require 'dialplan.router';
self.caller:set_variable('hangup_after_bridge', false);
@@ -881,14 +891,21 @@ function Dialplan.run(self, destination)
end
self.caller:set_variable('default_language', self.caller.language);
- self.caller:set_variable('sound_prefix', common.str.try(self.config, 'sounds.' .. tostring(self.caller.language)));
+ self.caller:set_variable('sound_prefix', common.array.try(self.config, 'sounds.' .. tostring(self.caller.language)));
self.log:info('DIALPLAN start - caller_id: ',self.caller.caller_id_number, ' "', self.caller.caller_id_name, '" , number: ', destination.number, ', language: ', self.caller.language);
+
+ self.caller.static_caller_id_number = self.caller.caller_id_number;
+ self.caller.static_caller_id_name = self.caller.caller_id_name;
+
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.caller.caller_id_number = self.caller.static_caller_id_number;
+ self.caller.caller_id_name = self.caller.static_caller_id_name;
self.log:info('LOOP ', loop,
' - destination: ', destination.type, '=', destination.id, '/', destination.uuid,'@', destination.node_id,
@@ -900,6 +917,7 @@ function Dialplan.run(self, destination)
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);
+ self.caller:set_variable('gs_destination_node_id, ', destination.node_id);
result = self:switch(destination);
result = result or { continue = false, code = 502, cause = 'DESTINATION_OUT_OF_ORDER', phrase = 'Destination out of order' }
diff --git a/misc/freeswitch/scripts/dialplan/fax.lua b/misc/freeswitch/scripts/dialplan/fax.lua
index 6dce0a9..dc52d16 100644
--- a/misc/freeswitch/scripts/dialplan/fax.lua
+++ b/misc/freeswitch/scripts/dialplan/fax.lua
@@ -231,8 +231,8 @@ function Fax.insert_document(self, record)
return self.database:query(sql_query);
end
-function Fax.trigger_notification(self, fax_document_id, uuid)
- local command = 'http_request.lua ' .. uuid .. ' http://127.0.0.1/trigger/fax?fax_account_id=' .. tostring(fax_document_id);
+function Fax.trigger_notification(self, fax_account_id, uuid)
+ local command = 'http_request.lua ' .. uuid .. ' http://127.0.0.1/trigger/fax?fax_account_id=' .. tostring(fax_account_id);
require 'common.fapi'
return common.fapi.FApi:new():execute('luarun', command);
diff --git a/misc/freeswitch/scripts/dialplan/functions.lua b/misc/freeswitch/scripts/dialplan/functions.lua
index 780e3be..a47d9d3 100644
--- a/misc/freeswitch/scripts/dialplan/functions.lua
+++ b/misc/freeswitch/scripts/dialplan/functions.lua
@@ -23,7 +23,6 @@ function Functions.ensure_caller_sip_account(self, caller)
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 };
@@ -120,35 +119,32 @@ function Functions.dialplan_function(self, caller, dialed_number)
return result or { continue = false, code = 505, phrase = 'Error executing function', no_cdr = true };
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.log:info('TRANSFER_ALL - initiator: ', caller.account.class, '=', caller.account.id, '/', caller.account.uuid, ', number: ', destination_number);
+ local sql_query = 'SELECT `uuid`, `b_uuid`, `callee_number`, `caller_id_number`, `sip_account_id`, `b_sip_account_id` \
+ FROM `calls_active` WHERE `sip_account_id` = '.. caller.account.id .. ' OR `b_sip_account_id` = '.. caller.account.id;
+ local index = 1;
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);
+ if not common.str.blank(call_entry.uuid) and tostring(caller.account.id) ~= tostring(call_entry.sip_account_id) then
+ self.log:info('TRANSFER_ALEG ', index, ' - channel/', call_entry.uuid, '|', call_entry.caller_id_number);
+ freeswitch.API():execute("uuid_transfer", call_entry.uuid .. " " .. destination_number);
+ end
+ if not common.str.blank(call_entry.b_uuid) and tostring(caller.account.id) ~= tostring(call_entry.b_sip_account_id) then
+ self.log:info('TRANSFER_BLEG ', index, ' - channel/', call_entry.b_uuid, '|', call_entry.callee_number);
+ freeswitch.API():execute("uuid_transfer", call_entry.b_uuid .. " " .. destination_number);
end
- freeswitch.API():execute("uuid_transfer", uid .. " " .. destination_number);
+ index = index + 1;
end)
- return destination_number;
+ return { continue = true, number = destination_number }
end
@@ -165,19 +161,18 @@ function Functions.intercept_any_number(self, caller, destination_number)
local phone_numberable = common.object.Object:new{ log = self.log, database = self.database}:find{class = phone_number.record.phone_numberable_type, id = phone_number.record.phone_numberable_id};
if not phone_numberable then
- self.log:notice('FUNCTION_INTERCEPT_ANY_NUMBER - numberable not found: ', dphone_number.record.phone_numberable_type, '=', phone_number.record.phone_numberable_id);
+ self.log:notice('FUNCTION_INTERCEPT_ANY_NUMBER - numberable not found: ', phone_number.record.phone_numberable_type, '=', phone_number.record.phone_numberable_id);
return { continue = false, code = 404, phrase = 'Destination not found', no_cdr = true };
end
- require 'common.str';
require 'common.group';
local group_class = common.group.Group:new{ log = self.log, database = self.database };
- local group_ids = group_class:union(common.str.try(caller, 'auth_account.group_ids'), common.str.try(caller, 'auth_account.owner.group_ids'));
+ local group_ids = group_class:union(common.array.try(caller, 'auth_account.group_ids'), common.array.try(caller, 'auth_account.owner.group_ids'));
local target_groups, target_group_ids = group_class:permission_targets(group_ids, 'pickup');
- local destination_group_ids = group_class:union(common.str.try(phone_numberable, 'group_ids'), common.str.try(phone_numberable, 'owner.group_ids'));
+ local destination_group_ids = group_class:union(common.array.try(phone_numberable, 'group_ids'), common.array.try(phone_numberable, 'owner.group_ids'));
if #group_class:intersection(destination_group_ids, target_group_ids) == 0 then
- self.log:notice('FUNCTION_INTERCEPT_ANY_NUMBER - Groups not found or insufficient permissions');
+ self.log:notice('FUNCTION_INTERCEPT_ANY_NUMBER - Groups not found or insufficient permissions, destination:', destination_group_ids, ', target: ', target_group_ids);
return { continue = false, code = 402, phrase = '"Insufficient permissions', no_cdr = true };
end
@@ -195,10 +190,9 @@ function Functions.group_pickup(self, caller, group_id)
return { continue = false, code = 505, phrase = 'Incompatible destination', no_cdr = true };
end
- require 'common.str';
require 'common.group';
local group_class = common.group.Group:new{ log = self.log, database = self.database };
- local group_ids = group_class:union(common.str.try(caller, 'auth_account.group_ids'), common.str.try(caller, 'auth_account.owner.group_ids'));
+ local group_ids = group_class:union(common.array.try(caller, 'auth_account.group_ids'), common.array.try(caller, 'auth_account.owner.group_ids'));
local target_group = group_class:is_target(group_id, 'pickup');
if not target_group then
@@ -250,8 +244,6 @@ 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;
@@ -367,7 +359,6 @@ 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
@@ -845,8 +836,6 @@ end
function Functions.hangup(self, caller, code, phrase)
- require 'common.str'
-
if not tonumber(code) then
code = 403;
phrase = 'Forbidden';
@@ -885,8 +874,7 @@ function Functions.call_parking_inout_index(self, caller, stall_index)
return { continue = false, code = 404, phrase = 'No parkings stall specified', no_cdr = true }
end
- require 'common.str';
- local owner = common.str.try(caller, 'auth_account.owner');
+ local owner = common.array.try(caller, 'auth_account.owner');
if not owner then
self.log:notice('FUNCTION_CALL_PARKING_INOUT_INDEX - stall owner not specified');
diff --git a/misc/freeswitch/scripts/dialplan/hunt_group.lua b/misc/freeswitch/scripts/dialplan/hunt_group.lua
index fa3c05b..b1728c3 100644
--- a/misc/freeswitch/scripts/dialplan/hunt_group.lua
+++ b/misc/freeswitch/scripts/dialplan/hunt_group.lua
@@ -38,7 +38,7 @@ end
function HuntGroup.find_by_uuid(self, uuid)
- local sql_query = 'SELECT * FROM `hunt_groups` WHERE `id`= "'.. uuid .. '" LIMIT 1';
+ local sql_query = 'SELECT * FROM `hunt_groups` WHERE `uuid`= "'.. uuid .. '" LIMIT 1';
local hunt_group = nil;
self.database:query(sql_query, function(entry)
@@ -98,25 +98,26 @@ function HuntGroup.run(self, dialplan_object, caller, destination)
self.log:info('HUNTGROUP ', self.record.id, ' - name: ', self.record.name, ', strategy: ', self.record.strategy,', members: ', #hunt_group_members);
- local clip_no_screening = common.str.try(caller, 'account.record.clip_no_screening');
caller.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.caller_id_numbers, number);
- end
- end
for index, number in ipairs(caller.caller_phone_numbers) do
table.insert(caller.caller_id_numbers, number);
end
- self.log:info('CALLER_ID_NUMBERS - clir: ', caller.clir, ', numbers: ', table.concat(caller.caller_id_numbers, ','));
- local save_destination = caller.destination;
+ local phone_numbers = common.phone_number.PhoneNumber:new{ log = self.log, database = self.database }:list_by_owner(self.id, self.class);
+ for index, number in ipairs(phone_numbers) do
+ table.insert(caller.caller_id_numbers, number);
+ end
+
+ self.log:debug('HUNTGROUP ', self.record.id, ' - auth: ', caller.auth_account.class, '=', caller.auth_account.id, '/', caller.auth_account.uuid, ', caller: ', caller.account.class, '=', caller.account.id, '/', caller.account.uuid);
+ self.log:info('HUNTGROUP ', self.record.id, ' - clir: ', caller.clir, ', caller_id_numbers: ', table.concat(caller.caller_id_numbers, ','));
+
+ local hunt_group_destination = caller.destination;
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
-
+ caller.destination = destination;
caller.destination_number = destination.number;
require 'dialplan.router'
@@ -144,8 +145,8 @@ function HuntGroup.run(self, dialplan_object, caller, destination)
end
end
- caller.destination = save_destination;
- caller.destination_number = save_destination.number;
+ caller.destination = hunt_group_destination;
+ caller.destination_number = hunt_group_destination.number;
local forwarding_destination = nil;
if caller.forwarding_service == 'assistant' and caller.auth_account then
@@ -165,7 +166,7 @@ function HuntGroup.run(self, dialplan_object, caller, destination)
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 });
+ result = dialplan.sip_call.SipCall:new{ log = self.log, database = self.database, caller = caller }:fork( recursive_destinations, { callee_id_number = hunt_group_destination.number, timeout = member_timeout });
if result.disposition == 'SUCCESS' then
if result.fork_index then
result.destination = recursive_destinations[result.fork_index];
diff --git a/misc/freeswitch/scripts/dialplan/ivr.lua b/misc/freeswitch/scripts/dialplan/ivr.lua
new file mode 100644
index 0000000..f8b8a2d
--- /dev/null
+++ b/misc/freeswitch/scripts/dialplan/ivr.lua
@@ -0,0 +1,119 @@
+-- Gemeinschaft 5 module: ivr class
+-- (c) AMOOMA GmbH 2013
+--
+
+module(...,package.seeall)
+
+Ivr = {}
+
+-- create ivr ivr ;)
+function Ivr.new(self, arg)
+ arg = arg or {}
+ ivr = arg.ivr or {}
+ setmetatable(ivr, self);
+ self.__index = self;
+ self.class = 'ivr';
+ self.log = arg.log;
+ self.caller = arg.caller;
+ self.dtmf_threshold = arg.dtmf_threshold or 500;
+
+ return ivr;
+end
+
+
+function Ivr.ivr_phrase(self, phrase, keys, timeout, ivr_repeat)
+ ivr_repeat = ivr_repeat or 3;
+ timeout = timeout or 30;
+ self.digit = '';
+ self.exit = false;
+
+ self.break_keys = {};
+ for index=1, #keys do
+ self.break_keys[keys[index]] = true;
+ end
+
+ global_callback:callback('dtmf', 'ivr_ivr_phrase', self.ivr_phrase_dtmf, self);
+
+ for index=0, ivr_repeat do
+ self.caller.session:sayPhrase(phrase, table.concat(keys, ':'));
+ self.caller:sleep(timeout * 1000);
+ if self.exit then
+ break;
+ end
+ end
+
+ global_callback:callback_unset('dtmf', 'ivr_ivr_phrase');
+
+ return self.digit;
+end
+
+
+function Ivr.ivr_phrase_dtmf(self, dtmf)
+ if self.break_keys[dtmf.digit] then
+ self.digit = dtmf.digit;
+ self.exit = true;
+ return false;
+ end
+end
+
+
+function Ivr.read_phrase(self, phrase, phrase_data, max_keys, min_keys, timeout, enter_key)
+ self.max_keys = max_keys or 64;
+ self.min_keys = min_keys or 1;
+ self.enter_key = enter_key or '#';
+ self.digits = '';
+ self.exit = false;
+ timeout = timeout or 30;
+
+ global_callback:callback('dtmf', 'ivr_read_phrase', self.read_phrase_dtmf, self);
+ self.caller.session:sayPhrase(phrase, phrase_data or enter_key or '');
+ self.caller:sleep(timeout * 1000);
+ global_callback:callback_unset('dtmf', 'ivr_read_phrase');
+
+ return self.digits;
+end
+
+
+function Ivr.read_phrase_dtmf(self, dtmf)
+ if dtmf.duration < self.dtmf_threshold then
+ return nil;
+ end
+
+ if self.enter_key == dtmf.digit then
+ self.exit = true;
+ return false;
+ end
+
+ self.digits = self.digits .. dtmf.digit;
+end
+
+
+function Ivr.check_pin(self, phrase, pin, pin_timeout, pin_repeat, key_enter)
+ if not pin then
+ return nil;
+ end
+
+ pin_timeout = pin_timeout or 30;
+ pin_repeat = pin_repeat or 3;
+ key_enter = key_enter or '#';
+
+ local digits = '';
+ for i = 1, pin_repeat do
+ if digits == pin then
+ self.caller:send_display('PIN: OK');
+ break
+ elseif digits ~= "" then
+ self.caller:send_display('PIN: wrong');
+ end
+ self.caller:send_display('Enter PIN');
+ digits = ivr:read_phrase(phrase, nil, 0, pin:len() + 1, pin_timeout, key_enter);
+ end
+
+ if digits ~= pin then
+ self.caller:send_display('PIN: wrong');
+ return false
+ end
+ self.caller:send_display('PIN: OK');
+
+ return true;
+end
diff --git a/misc/freeswitch/scripts/dialplan/router.lua b/misc/freeswitch/scripts/dialplan/router.lua
index 8473c2b..322c748 100644
--- a/misc/freeswitch/scripts/dialplan/router.lua
+++ b/misc/freeswitch/scripts/dialplan/router.lua
@@ -9,6 +9,7 @@ Router = {}
-- create route object
function Router.new(self, arg)
require 'common.str';
+ require 'common.array';
arg = arg or {}
object = arg.object or {}
setmetatable(object, self);
@@ -19,6 +20,7 @@ function Router.new(self, arg)
self.routes = arg.routes or {};
self.caller = arg.caller;
self.variables = arg.variables or {};
+ self.log_details = arg.log_details;
self.routing_tables = {};
return object;
end
@@ -60,31 +62,37 @@ function Router.read_table(self, table_name, force_reload)
end
-function Router.expand_variables(self, line, variables, variables2)
- return (line:gsub('{([%a%d%._]+)}', function(captured)
- return common.str.try(variables, captured) or common.str.try(variables2, captured) or '';
- end))
-end
-
-
function Router.element_match(self, pattern, search_string, replacement, route_variables)
local success, result = pcall(string.find, search_string, pattern);
if not success then
- self.log:error('ELEMENT_MATCH - table error - pattern: ', pattern, ', search_string: ', search_string);
+ self.log:error('ELEMENT_ERROR - table error - pattern: ', pattern, ', search_string: ', search_string);
elseif result then
- return true, search_string:gsub(pattern, self:expand_variables(replacement, route_variables, self.variables));
+ local replace_by = common.array.expand_variables(replacement, route_variables, self.variables)
+ result = search_string:gsub(pattern, replace_by);
+ if self.log_details then
+ self.log:debug('ELEMENT_MATCH - ', search_string, ' ~= ', pattern, ' => ', replacement, ' => ', replace_by);
+ end
+ return true, result;
end
+ if self.log_details then
+ self.log:debug('ELEMENT_NO_MATCH - ', search_string, ' != ', pattern);
+ end
return false;
end
-function Router.element_match_group(self, pattern, groups, replacement, use_key, route_variables)
+function Router.element_match_group(self, pattern, groups, replacement, use_key, route_variables, variable_name)
if type(groups) ~= 'table' then
+ self.log:debug('ELEMENT_FIND_IN_ARRAY - no such array: ', variable_name, ', use_keys: ', tostring(use_key));
return false;
end
+ if self.log_details then
+ self.log:debug('ELEMENT_FIND_IN_ARRAY - array: ', variable_name, ', use_keys: ', tostring(use_key));
+ end
+
for key, value in pairs(groups) do
if use_key then
value = key;
@@ -102,8 +110,9 @@ function Router.route_match(self, route)
gateway = 'gateway' .. route.endpoint_id,
['type'] = route.endpoint_type,
id = route.endpoint_id,
- destination_number = common.str.try(self, 'caller.destination_number'),
+ destination_number = common.array.try(self, 'caller.destination_number'),
channel_variables = {},
+ route_id = route.id,
};
local route_matches = false;
@@ -114,22 +123,26 @@ function Router.route_match(self, route)
local element = route.elements[index];
+ if self.log_details then
+ self.log:debug('ROUTE_ELEMENT ', element.id, ' - var_in: ', element.var_in, ', var_out: ', element.var_out, ', action: ', element.action, ', mandatory: ', element.mandatory);
+ end
+
if element.action ~= 'none' then
if common.str.blank(element.var_in) or common.str.blank(element.pattern) and element.action == 'set' then
result = true;
- replacement = self:expand_variables(element.replacement, destination, self.variables);
+ replacement = common.array.expand_variables(element.replacement, destination, self.variables);
else
local command, variable_name = common.str.partition(element.var_in, ':');
if not command or not variable_name then
- local search_string = tostring(common.str.try(destination, element.var_in) or common.str.try(self.caller, element.var_in));
+ local search_string = tostring(common.array.try(destination, element.var_in) or common.array.try(self.caller, element.var_in));
result, replacement = self:element_match(tostring(element.pattern), search_string, tostring(element.replacement), destination);
elseif command == 'var' then
- local search_string = tostring(common.str.try(self.caller, element.var_in));
+ local search_string = tostring(common.array.try(self.caller, variable_name));
result, replacement = self:element_match(tostring(element.pattern), search_string, tostring(element.replacement));
elseif command == 'key' or command == 'val' then
- local groups = common.str.try(self.caller, variable_name);
- result, replacement = self:element_match_group(tostring(element.pattern), groups, tostring(element.replacement), command == 'key');
+ local groups = common.array.try(destination, variable_name) or common.array.try(self.caller, variable_name);
+ result, replacement = self:element_match_group(tostring(element.pattern), groups, tostring(element.replacement), command == 'key', destination, variable_name);
elseif command == 'chv' then
local search_string = self.caller:to_s(variable_name);
result, replacement = self:element_match(tostring(element.pattern), search_string, tostring(element.replacement));
@@ -151,7 +164,7 @@ function Router.route_match(self, route)
if not common.str.blank(element.var_out) then
local command, variable_name = common.str.partition(element.var_out, ':');
if not command or not variable_name or command == 'var' then
- common.str.set(destination, element.var_out, replacement);
+ common.array.set(destination, element.var_out, replacement);
elseif command == 'chv' then
destination.channel_variables[variable_name] = replacement;
elseif command == 'hdr' then
@@ -180,7 +193,10 @@ function Router.route_run(self, table_name, find_first)
local routes = {};
if type(routing_table) == 'table' then
- for index=1, #routing_table do
+ for index=1, #routing_table do
+ if self.log_details then
+ self.log:info('ROUTE_',table_name:upper(),' ', index,' - ', table_name,'=', routing_table[index].id, '/', routing_table[index].name);
+ end
local route = self:route_match(routing_table[index]);
if route then
table.insert(routes, route);
diff --git a/misc/freeswitch/scripts/dialplan/session.lua b/misc/freeswitch/scripts/dialplan/session.lua
index 7de85ca..9c43e74 100644
--- a/misc/freeswitch/scripts/dialplan/session.lua
+++ b/misc/freeswitch/scripts/dialplan/session.lua
@@ -58,6 +58,13 @@ function Session.init_channel_variables(self)
self.node_id = self:to_i('sip_h_X-GS_node_id');
self.loop_count = self:to_i('sip_h_X-GS_loop_count');
+ self.previous_destination_type = self:to_s('gs_destination_type');
+ self.previous_destination_id = self:to_i('gs_destination_id');
+ self.previous_destination_uuid = self:to_s('gs_destination_uuid');
+ self.previous_destination_owner_type = self:to_s('gs_destination_owner_type');
+ self.previous_destination_owner_id = self:to_i('gs_destination_owner_id');
+ self.previous_destination_owner_uuid = self:to_s('gs_destination_owner_uuid');
+
if self.node_id > 0 and self.node_id ~= self.local_node_id then
self.from_node = true;
else
@@ -166,6 +173,12 @@ function Session.answer(self)
return self.session:answer();
end
+-- Is answered?
+function Session.answered(self)
+ return self.session:answered();
+end
+
+
function Session.intercept(self, uid)
self.session:execute("intercept", uid);
end
@@ -226,3 +239,8 @@ function Session.expand_variables(self, line)
return self.session:getVariable(captured) or '';
end))
end
+
+
+function Session.playback(self, file)
+ self.session:streamFile(file);
+end
diff --git a/misc/freeswitch/scripts/dialplan/sip_call.lua b/misc/freeswitch/scripts/dialplan/sip_call.lua
index 5c98792..0cde601 100644
--- a/misc/freeswitch/scripts/dialplan/sip_call.lua
+++ b/misc/freeswitch/scripts/dialplan/sip_call.lua
@@ -135,6 +135,17 @@ function SipCall.fork(self, destinations, arg )
if destination.alert_info then
table.insert(origination_variables, "alert_info='" .. destination.alert_info .. "'");
end
+ if destination.account then
+ table.insert(origination_variables, "gs_account_type='" .. common.str.to_s(destination.account.class) .. "'");
+ table.insert(origination_variables, "gs_account_id='" .. common.str.to_i(destination.account.id) .. "'");
+ table.insert(origination_variables, "gs_account_uuid='" .. common.str.to_s(destination.account.uuid) .. "'");
+ end
+ if self.caller.auth_account then
+ table.insert(origination_variables, "gs_auth_account_type='" .. common.str.to_s(self.caller.auth_account.class) .. "'");
+ table.insert(origination_variables, "gs_auth_account_id='" .. common.str.to_i(self.caller.auth_account.id) .. "'");
+ table.insert(origination_variables, "gs_auth_account_uuid='" .. common.str.to_s(self.caller.auth_account.uuid) .. "'");
+ end
+
table.insert(dial_strings, '[' .. table.concat(origination_variables , ',') .. ']sofia/' .. sip_account.record.profile_name .. '/' .. sip_account.record.auth_name .. '%' .. sip_account.record.sip_host);
if destination.pickup_groups and #destination.pickup_groups > 0 then
for key=1, #destination.pickup_groups do
diff --git a/misc/freeswitch/scripts/dialplan/voicemail.lua b/misc/freeswitch/scripts/dialplan/voicemail.lua
index ae7d0a1..caeeb48 100644
--- a/misc/freeswitch/scripts/dialplan/voicemail.lua
+++ b/misc/freeswitch/scripts/dialplan/voicemail.lua
@@ -66,15 +66,14 @@ function Voicemail.find_by_name(self, account_name)
return voicemail_account
end
--- Find Voicemail account by name
+-- Find Voicemail account by number
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);
+ local destination_number_object = common.phone_number.PhoneNumber:new{ log = self.log, database = self.database }:find_by_number(phone_number);
+ if destination_number_object and destination_number_object.record.phone_numberable_type:lower() == "sipaccount" then
+ return self:find_by_sip_account_id(destination_number_object.record.phone_numberable_id);
end
return false;
diff --git a/misc/freeswitch/scripts/event/perimeter_defense.lua b/misc/freeswitch/scripts/event/perimeter_defense.lua
index acdfa8d..5bd124b 100644
--- a/misc/freeswitch/scripts/event/perimeter_defense.lua
+++ b/misc/freeswitch/scripts/event/perimeter_defense.lua
@@ -31,9 +31,10 @@ end
function PerimeterDefense.event_handlers(self)
return {
CUSTOM = {
- ['sofia::pre_register'] = self.sofia_pre_register,
- ['sofia::register_attempt'] = self.sofia_register_attempt,
- ['sofia::register_failure'] = self.sofia_register_failure,
+ ['sofia::pre_register'] = self.sofia_pre_register,
+ ['sofia::register_attempt'] = self.sofia_register_attempt,
+ ['sofia::register_failure'] = self.sofia_register_failure,
+ ['perimeter::control'] = self.perimeter_control,
},
CHANNEL_HANGUP = { [true] = self.channel_hangup },
};
@@ -112,3 +113,20 @@ function PerimeterDefense.channel_hangup(self, event)
local record = self:to_call_record(event, 'channel_hangup');
self.perimeter:check(record);
end
+
+
+function PerimeterDefense.control_to_action(self, event)
+ return {
+ key = event:getHeader('key'),
+ };
+end
+
+
+function PerimeterDefense.perimeter_control(self, event)
+ local action = event:getHeader('action');
+ if self.perimeter['action_' .. action] then
+ self.perimeter['action_' .. action](self.perimeter, self:control_to_action(event))
+ else
+ self.log:error('[perimeter_defense] PERIMETER_DEFENSE - could not execute action: ', action);
+ end
+end
diff --git a/misc/freeswitch/scripts/event/presence_update.lua b/misc/freeswitch/scripts/event/presence_update.lua
index 01ec17b..f9d8ee7 100644
--- a/misc/freeswitch/scripts/event/presence_update.lua
+++ b/misc/freeswitch/scripts/event/presence_update.lua
@@ -10,6 +10,13 @@ ACCOUNT_RECORD_TIMEOUT = 120;
PresenceUpdate = {}
function PresenceUpdate.new(self, arg)
+ require 'common.sip_account';
+ require 'dialplan.presence';
+ require 'common.str';
+ require 'common.phone_number';
+ require 'common.fapi';
+ require 'common.configuration_table';
+
arg = arg or {}
object = arg.object or {}
setmetatable(object, self);
@@ -21,6 +28,13 @@ function PresenceUpdate.new(self, arg)
self.presence_accounts = {}
self.account_record = {}
+ self.config = common.configuration_table.get(self.database, 'presence_update');
+ self.config = self.config or {};
+ self.config.trigger = self.config.trigger or {
+ sip_account_presence = true,
+ sip_account_register = true,
+ sip_account_unregister = true,
+ };
return object;
end
@@ -34,10 +48,29 @@ function PresenceUpdate.event_handlers(self)
end
+function PresenceUpdate.retrieve_sip_account(self, account, uuid)
+ uuid = uuid or 'presence_update';
+
+ 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);
+
+ 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
+
+ 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
+
+ return self.account_record[account];
+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');
@@ -59,6 +92,13 @@ end
function PresenceUpdate.sofia_register(self, event)
local account = event:getHeader('from-user');
+ local timestamp = event:getHeader('Event-Date-Timestamp');
+
+ local sip_account = self:retrieve_sip_account(account, account);
+ if sip_account and common.str.to_b(self.config.trigger.sip_account_register) then
+ self:trigger_rails(sip_account, 'register', timestamp, account)
+ end
+
self.log:debug('[', account, '] PRESENCE_UPDATE - flushing account cache on register');
self.presence_accounts[account] = nil;
end
@@ -66,28 +106,48 @@ end
function PresenceUpdate.sofia_ungerister(self, event)
local account = event:getHeader('from-user');
+ local timestamp = event:getHeader('Event-Date-Timestamp');
+
+ local sip_account = self:retrieve_sip_account(account, account);
+ if sip_account and common.str.to_b(self.config.trigger.sip_account_unregister) then
+ self:trigger_rails(sip_account, 'unregister', timestamp, account)
+ end
+
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 call_direction = tostring(event:getHeader('presence-call-direction') or event:getHeader('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');
+ local protocol = tostring(event:getHeader('proto'));
+ local timestamp = event:getHeader('Event-Date-Timestamp');
+ local direction = nil;
+
+ if call_direction == 'inbound' then
+ direction = true;
+ elseif call_direction == 'outbound' then
+ direction = false;
+ end
- 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);
+ if protocol == 'conf' then
+ state = event:getHeader('answer-state');
+ local login = tostring(event:getHeader('proto'));
+ self.log:info('[', uuid,'] PRESENCE_CONFERENCE_', call_direction:upper(), ' ', common.str.to_i(account), ' - identifier: ', account, ', state: ', state);
+ self:conference(direction, account, domain, state, uuid);
+ elseif protocol == 'sip' or protocol == 'any' then
+ if protocol == 'sip' and common.str.blank(state) then
+ self.log:debug('[', uuid,'] PRESENCE_', call_direction:upper(),' no state - protocol: ', protocol, ', account: ', account);
+ return;
+ end
+ self.log:info('[', uuid,'] PRESENCE_', call_direction:upper(),' - protocol: ', protocol, ', account: ', account, ', state: ', state);
+ self:sip_account(direction, account, domain, state, uuid, caller_id, timestamp);
+ else
+ self.log:info('[', uuid,'] PRESENCE_', call_direction:upper(),' unhandled protocol: ', protocol, ', account: ', account, ', state: ', state);
end
end
@@ -114,10 +174,9 @@ 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'
+ 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);
if call_forwarding and common.str.to_b(call_forwarding.record.active) then
local destination_type = tostring(call_forwarding.record.call_forwardable_type):lower()
@@ -138,7 +197,6 @@ function PresenceUpdate.hunt_group_membership(self, account, domain, member_id)
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,
@@ -156,7 +214,6 @@ function PresenceUpdate.acd_membership(self, account, domain, member_id)
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,
@@ -168,32 +225,45 @@ function PresenceUpdate.acd_membership(self, account, domain, member_id)
end
-function PresenceUpdate.sip_account(self, inbound, account, domain, status, uuid, caller_id)
+function PresenceUpdate.sip_account(self, inbound, account, domain, status, uuid, caller_id, timestamp)
local status_map = { progressing = 'early', alerting = 'confirmed', active = 'confirmed' }
+
+ local sip_account = self:retrieve_sip_account(account, uuid);
+ if not sip_account then
+ return;
+ end
- 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);
+ dialplan.presence.Presence:new{
+ log = self.log,
+ database = self.database,
+ inbound = inbound,
+ domain = domain,
+ accounts = sip_account.phone_numbers,
+ uuid = uuid
+ }:set(status_map[status] or 'terminated', caller_id);
- self.account_record[account] = { id = sip_account.id, class = sip_account.class, phone_numbers = phone_numbers, created_at = os.time() }
+ if common.str.to_b(self.config.trigger.sip_account_presence) then
+ self:trigger_rails(sip_account, status_map[status] or 'terminated', timestamp, uuid);
end
+end
- require 'dialplan.presence'
- local result = dialplan.presence.Presence:new{
+
+function PresenceUpdate.conference(self, inbound, account, domain, status, uuid)
+
+ dialplan.presence.Presence:new{
log = self.log,
database = self.database,
inbound = inbound,
domain = domain,
- accounts = self.account_record[account].phone_numbers,
+ accounts = { account },
uuid = uuid
- }:set(status_map[status] or 'terminated', caller_id);
+ }:set(status or 'terminated');
+end
+
+
+function PresenceUpdate.trigger_rails(self, account, status, timestamp, uuid)
+ if account.class == 'sipaccount' then
+ local command = 'http_request.lua ' .. tostring(uuid) .. ' http://127.0.0.1/trigger/sip_account_update/' .. tostring(account.id) .. '?timestamp=' .. timestamp .. '&status=' .. tostring(status);
+ common.fapi.FApi:new():execute('luarun', command);
+ end
end
diff --git a/misc/freeswitch/scripts/send_fax.lua b/misc/freeswitch/scripts/send_fax.lua
index d717f93..ded8c65 100644
--- a/misc/freeswitch/scripts/send_fax.lua
+++ b/misc/freeswitch/scripts/send_fax.lua
@@ -173,6 +173,12 @@ if session and session:answered() then
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'));
+
+ local command = 'http_request.lua sendfax http://127.0.0.1/trigger/fax_has_been_sent/' .. tostring(fax_document.id);
+
+ require 'common.fapi'
+ common.fapi.FApi:new():execute('luarun', command);
+
else
if session then
cause = session:hangupCause();
diff --git a/misc/freeswitch/scripts/test_route.lua b/misc/freeswitch/scripts/test_route.lua
new file mode 100644
index 0000000..98dfda9
--- /dev/null
+++ b/misc/freeswitch/scripts/test_route.lua
@@ -0,0 +1,84 @@
+-- Gemeinschaft 5 routing test module
+-- (c) AMOOMA GmbH 2013
+--
+
+require 'common.array';
+
+local arguments = {};
+local value = nil;
+
+for index=1, #argv do
+ if math.mod(index, 2) == 0 then
+ common.array.set(arguments, argv[index], value);
+ else
+ value = argv[index];
+ end
+end
+
+local caller = arguments.caller or {};
+local channel_variables = arguments.chv or {};
+
+function caller.to_s(variable)
+ return common.str.to_s(arguments[variable])
+end
+
+local log_buffer = {};
+-- initialize logging
+require 'common.log';
+log = common.log.Log:new{ buffer = log_buffer, prefix = '' };
+
+-- connect to database
+require 'common.database';
+local database = common.database.Database:new{ log = log }:connect();
+if not database:connected() then
+ log:critical('TEST_ROUTE - database connection failed');
+ return;
+end
+
+-- dialplan object
+require 'dialplan.dialplan'
+local dialplan_object = dialplan.dialplan.Dialplan:new{ log = log, caller = caller, database = database };
+dialplan_object:configuration_read();
+caller.dialplan = dialplan_object;
+caller.local_node_id = dialplan_object.node_id;
+
+dialplan_object:retrieve_caller_data();
+local destination = arguments.destination or dialplan_object:destination_new{ number = caller.destination_number };
+local routes = {};
+
+if destination and destination.type == 'unknown' then
+ local clip_no_screening = common.array.try(caller, 'account.record.clip_no_screening');
+ caller.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.caller_id_numbers, number);
+ end
+ end
+ if caller.caller_phone_numbers then
+ for index, number in ipairs(caller.caller_phone_numbers) do
+ table.insert(caller.caller_id_numbers, number);
+ end
+ end
+ log:info('CALLER_ID_NUMBERS - clir: ', caller.clir, ', numbers: ', table.concat(caller.caller_id_numbers, ','));
+
+ destination.callee_id_number = destination.number;
+ destination.callee_id_name = nil;
+end
+
+caller.destination = destination;
+
+require 'dialplan.router';
+routes = dialplan.router.Router:new{ log = log, database = database, caller = caller, variables = caller, log_details = true }:route_run(arguments.table or 'outbound');
+
+local result = {
+ routes = routes,
+ destination = destination,
+ log = log_buffer
+}
+
+stream:write(common.array.to_json(result));
+
+-- release database handle
+if database then
+ database:release();
+end
diff --git a/private_pub.ru b/private_pub.ru
new file mode 100644
index 0000000..4892af4
--- /dev/null
+++ b/private_pub.ru
@@ -0,0 +1,10 @@
+# Run with: rackup private_pub.ru -s thin -E production
+require "bundler/setup"
+require "yaml"
+require "faye"
+require "private_pub"
+
+Faye::WebSocket.load_adapter('thin')
+
+PrivatePub.load_config(File.expand_path("../config/private_pub.yml", __FILE__), ENV["RAILS_ENV"] || "development")
+run PrivatePub.faye_app
diff --git a/test/functional/switchboard_entries_controller_test.rb b/test/functional/switchboard_entries_controller_test.rb
new file mode 100644
index 0000000..a20f39e
--- /dev/null
+++ b/test/functional/switchboard_entries_controller_test.rb
@@ -0,0 +1,49 @@
+require 'test_helper'
+
+class SwitchboardEntriesControllerTest < ActionController::TestCase
+ setup do
+ @switchboard_entry = switchboard_entries(:one)
+ end
+
+ test "should get index" do
+ get :index
+ assert_response :success
+ assert_not_nil assigns(:switchboard_entries)
+ end
+
+ test "should get new" do
+ get :new
+ assert_response :success
+ end
+
+ test "should create switchboard_entry" do
+ assert_difference('SwitchboardEntry.count') do
+ post :create, switchboard_entry: @switchboard_entry.attributes
+ end
+
+ assert_redirected_to switchboard_entry_path(assigns(:switchboard_entry))
+ end
+
+ test "should show switchboard_entry" do
+ get :show, id: @switchboard_entry.to_param
+ assert_response :success
+ end
+
+ test "should get edit" do
+ get :edit, id: @switchboard_entry.to_param
+ assert_response :success
+ end
+
+ test "should update switchboard_entry" do
+ put :update, id: @switchboard_entry.to_param, switchboard_entry: @switchboard_entry.attributes
+ assert_redirected_to switchboard_entry_path(assigns(:switchboard_entry))
+ end
+
+ test "should destroy switchboard_entry" do
+ assert_difference('SwitchboardEntry.count', -1) do
+ delete :destroy, id: @switchboard_entry.to_param
+ end
+
+ assert_redirected_to switchboard_entries_path
+ end
+end
diff --git a/test/functional/switchboards_controller_test.rb b/test/functional/switchboards_controller_test.rb
new file mode 100644
index 0000000..6831fc2
--- /dev/null
+++ b/test/functional/switchboards_controller_test.rb
@@ -0,0 +1,49 @@
+require 'test_helper'
+
+class SwitchboardsControllerTest < ActionController::TestCase
+ setup do
+ @switchboard = switchboards(:one)
+ end
+
+ test "should get index" do
+ get :index
+ assert_response :success
+ assert_not_nil assigns(:switchboards)
+ end
+
+ test "should get new" do
+ get :new
+ assert_response :success
+ end
+
+ test "should create switchboard" do
+ assert_difference('Switchboard.count') do
+ post :create, switchboard: @switchboard.attributes
+ end
+
+ assert_redirected_to switchboard_path(assigns(:switchboard))
+ end
+
+ test "should show switchboard" do
+ get :show, id: @switchboard.to_param
+ assert_response :success
+ end
+
+ test "should get edit" do
+ get :edit, id: @switchboard.to_param
+ assert_response :success
+ end
+
+ test "should update switchboard" do
+ put :update, id: @switchboard.to_param, switchboard: @switchboard.attributes
+ assert_redirected_to switchboard_path(assigns(:switchboard))
+ end
+
+ test "should destroy switchboard" do
+ assert_difference('Switchboard.count', -1) do
+ delete :destroy, id: @switchboard.to_param
+ end
+
+ assert_redirected_to switchboards_path
+ end
+end
diff --git a/test/unit/switchboard_entry_test.rb b/test/unit/switchboard_entry_test.rb
new file mode 100644
index 0000000..a86c39a
--- /dev/null
+++ b/test/unit/switchboard_entry_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class SwitchboardEntryTest < ActiveSupport::TestCase
+ def test_should_be_valid
+ assert SwitchboardEntry.new.valid?
+ end
+end
diff --git a/test/unit/switchboard_test.rb b/test/unit/switchboard_test.rb
new file mode 100644
index 0000000..97534fc
--- /dev/null
+++ b/test/unit/switchboard_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class SwitchboardTest < ActiveSupport::TestCase
+ def test_should_be_valid
+ assert Switchboard.new.valid?
+ end
+end