adhearsion 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +339 -0
- data/Rakefile +108 -0
- data/ahn +195 -0
- data/lib/adhearsion.rb +402 -0
- data/lib/constants.rb +20 -0
- data/lib/core_extensions.rb +157 -0
- data/lib/database_functions.rb +76 -0
- data/lib/rami.rb +822 -0
- data/lib/servlet_container.rb +146 -0
- data/new_projects/Rakefile +100 -0
- data/new_projects/config/adhearsion.sqlite3 +0 -0
- data/new_projects/config/adhearsion.yml +11 -0
- data/new_projects/config/database.rb +50 -0
- data/new_projects/config/database.yml +10 -0
- data/new_projects/config/helpers/drb_server.yml +43 -0
- data/new_projects/config/helpers/factorial.alien.c.yml +1 -0
- data/new_projects/config/helpers/manager_proxy.yml +7 -0
- data/new_projects/config/helpers/micromenus.yml +1 -0
- data/new_projects/config/helpers/micromenus/collab.rb +55 -0
- data/new_projects/config/helpers/micromenus/images/tux.bmp +0 -0
- data/new_projects/config/helpers/micromenus/javascripts/builder.js +131 -0
- data/new_projects/config/helpers/micromenus/javascripts/controls.js +834 -0
- data/new_projects/config/helpers/micromenus/javascripts/dragdrop.js +944 -0
- data/new_projects/config/helpers/micromenus/javascripts/effects.js +956 -0
- data/new_projects/config/helpers/micromenus/javascripts/prototype.js +2319 -0
- data/new_projects/config/helpers/micromenus/javascripts/scriptaculous.js +51 -0
- data/new_projects/config/helpers/micromenus/javascripts/slider.js +278 -0
- data/new_projects/config/helpers/micromenus/javascripts/unittest.js +557 -0
- data/new_projects/config/helpers/micromenus/stylesheets/firefox.css +10 -0
- data/new_projects/config/helpers/micromenus/stylesheets/firefox.xul.css +44 -0
- data/new_projects/config/helpers/weather.yml +1 -0
- data/new_projects/config/helpers/xbmc.yml +1 -0
- data/new_projects/config/migration.rb +53 -0
- data/new_projects/extensions.rb +56 -0
- data/new_projects/helpers/drb_server.rb +32 -0
- data/new_projects/helpers/factorial.alien.c +32 -0
- data/new_projects/helpers/manager_proxy.rb +43 -0
- data/new_projects/helpers/micromenus.rb +374 -0
- data/new_projects/helpers/oscar_wilde_quotes.rb +197 -0
- data/new_projects/helpers/weather.rb +85 -0
- data/new_projects/helpers/xbmc.rb +12 -0
- data/new_projects/logs/database.log +0 -0
- data/test/core_extensions_test.rb +26 -0
- data/test/dial_test.rb +43 -0
- data/test/stress_tests/test.rb +13 -0
- data/test/stress_tests/test.yml +13 -0
- data/test/test_micromenus.rb +0 -0
- metadata +131 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
window {
|
2
|
+
background-color: grey;
|
3
|
+
padding: 20px 10px;
|
4
|
+
}
|
5
|
+
#haupt {
|
6
|
+
text-align: center;
|
7
|
+
display: block;
|
8
|
+
-moz-border-radius: 20px;
|
9
|
+
border: 10px solid black;
|
10
|
+
background-color: white;
|
11
|
+
padding: 10px;
|
12
|
+
padding-bottom: 20px;
|
13
|
+
}
|
14
|
+
|
15
|
+
label.header {
|
16
|
+
font-size: 30px;
|
17
|
+
text-align: center;
|
18
|
+
}
|
19
|
+
|
20
|
+
a {
|
21
|
+
background-color: #EEE;
|
22
|
+
padding: 2px;
|
23
|
+
display: block;
|
24
|
+
margin: 4px 0;
|
25
|
+
border: 5px #EEE solid;
|
26
|
+
-moz-border-radius: 5px;
|
27
|
+
color: black;
|
28
|
+
text-align: left;
|
29
|
+
text-decoration: none;
|
30
|
+
}
|
31
|
+
|
32
|
+
a:hover {
|
33
|
+
color: white;
|
34
|
+
background-color: black;
|
35
|
+
padding: 2px;
|
36
|
+
border: 5px black solid;
|
37
|
+
-moz-border-radius: 5px;
|
38
|
+
}
|
39
|
+
|
40
|
+
description {
|
41
|
+
text-align: center;
|
42
|
+
padding: 15px;
|
43
|
+
width: 100%;
|
44
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
units: fahrenheit # can be 'fahrenheit' or 'celsuis' (case sensitive)
|
@@ -0,0 +1 @@
|
|
1
|
+
ip: 192.168.1.136:80
|
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'yaml'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'active_record'
|
5
|
+
|
6
|
+
# A migration script uses a database configuration and creates tables
|
7
|
+
# very conveniently in a database-agnostic way. Below, add any customizations
|
8
|
+
# to the sample schema or leave it as-is. When done, execute this script.
|
9
|
+
|
10
|
+
ActiveRecord::Base.establish_connection YAML.load_file('config/database.yml')
|
11
|
+
|
12
|
+
class CreateUsers < ActiveRecord::Migration
|
13
|
+
# Available column types are :primary_key, :string, :text, :integer,
|
14
|
+
# :float, :datetime, :timestamp, :time, :date, :binary, and :boolean
|
15
|
+
def self.up
|
16
|
+
create_table :users do |t|
|
17
|
+
t.column :name, :string
|
18
|
+
t.column :callerid_name, :string
|
19
|
+
t.column :callerid_num, :string
|
20
|
+
t.column :group_id, :integer # Foreign key
|
21
|
+
t.column :ivr_extension, :string
|
22
|
+
t.column :extension, :string
|
23
|
+
t.column :email, :string
|
24
|
+
t.column :im_username, :string
|
25
|
+
t.column :im_provider, :string
|
26
|
+
# t.column :billed_time, :integer, :null => false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.down
|
31
|
+
drop_table :users
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class CreateGroups < ActiveRecord::Migration
|
36
|
+
def self.up
|
37
|
+
create_table :groups do |t|
|
38
|
+
t.column :name, :string
|
39
|
+
t.column :administrator_email, :string
|
40
|
+
t.column :callerid_name, :string
|
41
|
+
t.column :callerid_num, :string
|
42
|
+
#t.column :usage_limit, :integer
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.down
|
47
|
+
drop_table :groups
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Run "rake migrate" to run this script properly.
|
52
|
+
# CreateUsers.up
|
53
|
+
# CreateGroups.up
|
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
from_pwnyourphone {
|
3
|
+
# You rock! Be awesome and record us a message! We'll put supportive messages on the podcast!"
|
4
|
+
# Get ready to record. 5, 4, 3, 1 *BEEP*
|
5
|
+
play %(you-rock record-us-a-message we-put-messages-on-podcast get-ready five-countdown beep)
|
6
|
+
record :for => 3.minutes, :to => "/recordings/pwner.#{Time.now.to_i}.#{calleridnumber}.gsm"
|
7
|
+
}
|
8
|
+
|
9
|
+
internal {
|
10
|
+
callee = User.find_by_ivr_extension extension
|
11
|
+
if callee
|
12
|
+
|
13
|
+
voicemail extension if last_dial_status != :answer
|
14
|
+
|
15
|
+
#elsif extension.to_s =~ //#/^(((\d{1,3})?\d{1{3})?[1-9]\d{2})?[1-9]\d{6}$/
|
16
|
+
# puts "got here"
|
17
|
+
# dial "SIP/#{extension}@sipphone"
|
18
|
+
else
|
19
|
+
case extension
|
20
|
+
when 21 then play weather_report
|
21
|
+
when 32 then dial "SIP/zoip@demo.zoip.org"
|
22
|
+
when 888 then loop { print '.'; XBMC.sendkey XBMC.translate(wait_for_digit) }
|
23
|
+
when 9999
|
24
|
+
if rand(20) == 0
|
25
|
+
play %W(a-connect-charge-of #{rand(50) + 10} cents-per-minute will-apply)
|
26
|
+
sleep 1.second
|
27
|
+
play %w(just-kidding-not-upset)
|
28
|
+
end
|
29
|
+
check_voicemail caller.voicemailbox
|
30
|
+
else
|
31
|
+
play 'all-your-base'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
}
|
35
|
+
|
36
|
+
old {
|
37
|
+
case extension
|
38
|
+
when 1 then play weather_report("Richardson, Texas")
|
39
|
+
when 2 then play weather_report(input(5, :play => 'zip-code'))
|
40
|
+
when 3 then +xbmc
|
41
|
+
when 4
|
42
|
+
dial :tweedledee
|
43
|
+
end
|
44
|
+
}
|
45
|
+
|
46
|
+
xbmc {
|
47
|
+
loop {
|
48
|
+
print '.'
|
49
|
+
XBMC.sendkey XBMC.translate(wait_for_digit)
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
from_gizmo {
|
54
|
+
#dial :tweedledum
|
55
|
+
+xbmc
|
56
|
+
}
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
require 'drb'
|
3
|
+
require 'drb/acl'
|
4
|
+
require 'thread'
|
5
|
+
|
6
|
+
# Load the access control list
|
7
|
+
config = $HELPERS['drb_server']
|
8
|
+
|
9
|
+
permissions = []
|
10
|
+
# For greater control over the ACL
|
11
|
+
if config['raw_acl']
|
12
|
+
permissions = config['raw_acl'].flatten
|
13
|
+
else
|
14
|
+
[config['deny']].flatten.each { |ip| permissions << "deny" << ip }
|
15
|
+
[config['allow']].flatten.each { |ip| permissions << "allow" << ip }
|
16
|
+
end
|
17
|
+
|
18
|
+
DRb.install_acl ACL.new(permissions)
|
19
|
+
|
20
|
+
host = config['host'] || 'localhost'
|
21
|
+
port = config['port'] || 9050
|
22
|
+
DRb.start_service "druby://#{host}:#{port}", PBX
|
23
|
+
|
24
|
+
puts "Started DRb server on #{DRb.uri}."
|
25
|
+
puts "DRb Server Access Control List:"
|
26
|
+
0.step permissions.length-1, 2 do |i|
|
27
|
+
puts " #{permissions[i].upcase} #{permissions[i+1]}"
|
28
|
+
end
|
29
|
+
|
30
|
+
$HUTDOWN.hook do
|
31
|
+
DRb.stop_service
|
32
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
/*
|
3
|
+
=begin Adhearsion metadata
|
4
|
+
|
5
|
+
name: Native Factorial
|
6
|
+
author:
|
7
|
+
name: Jay Phillips
|
8
|
+
blog: http://jicksta.com
|
9
|
+
email: Jicksta -at- Gmail.com
|
10
|
+
gems:
|
11
|
+
- soap4r
|
12
|
+
- rubyinline: >= 0.8.2
|
13
|
+
instructions: >
|
14
|
+
Yes, this is a pure C file!!!
|
15
|
+
This is an example of writing Adhearsion extensions in
|
16
|
+
other languages. The first time this file is executed
|
17
|
+
it will be compiled and the binary form will be cached.
|
18
|
+
|
19
|
+
If your Adhearsion system is heavily dependent on
|
20
|
+
an intensive helper, it may be advantageous to rewrite
|
21
|
+
it in a language such as C or C++ and use it like this.
|
22
|
+
|
23
|
+
=end
|
24
|
+
*/
|
25
|
+
|
26
|
+
int fast_factorial(int input) {
|
27
|
+
int sum = 0, count = 1;
|
28
|
+
while(count <= input) {
|
29
|
+
sum += count++;
|
30
|
+
}
|
31
|
+
return sum;
|
32
|
+
}
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rami'
|
2
|
+
class PBX
|
3
|
+
include Rami
|
4
|
+
|
5
|
+
@@sip_users = {}
|
6
|
+
|
7
|
+
@@rami_server_thread = Thread.current
|
8
|
+
|
9
|
+
@@rami_server = Rami::Server.new $HELPERS.manager_proxy
|
10
|
+
@@rami_server.console = 1
|
11
|
+
@@rami_server.run
|
12
|
+
|
13
|
+
@@rami_client = Client.new @@rami_server
|
14
|
+
@@rami_client.timeout = 10
|
15
|
+
|
16
|
+
def self.rami_client() @@rami_client end
|
17
|
+
|
18
|
+
$HUTDOWN.hook do
|
19
|
+
@@rami_client.stop
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.sip_users
|
23
|
+
if !@@sip_users[:expiration] || @@sip_users[:expiration] <= Time.now
|
24
|
+
sip_db = PBX.rami_client.command("database show SIP/Registry").first
|
25
|
+
sip_db = sip_db[ sip_db.keys.select { |x| x.is_a? Fixnum }.first ]
|
26
|
+
sip_db = sip_db.gsub( /--[A-Z ]+?--/ , '').strip
|
27
|
+
users = sip_db.split "\n"
|
28
|
+
users.collect! do |user|
|
29
|
+
fields = user.split ':'
|
30
|
+
{ :username => fields[4],
|
31
|
+
:ip => fields[1].strip,
|
32
|
+
:port => fields[2],
|
33
|
+
:address => fields[6] }
|
34
|
+
end
|
35
|
+
@@sip_users[:users] = users
|
36
|
+
@@sip_users[:expiration] = 90.seconds.from_now
|
37
|
+
end
|
38
|
+
@@sip_users[:users]
|
39
|
+
end
|
40
|
+
def self.record channel, file, format, mix
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,374 @@
|
|
1
|
+
# Micromenus Adhearsion helper
|
2
|
+
# Copyright 2006 Jay Phillips
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU General Public License
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
7
|
+
# of the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
+
|
18
|
+
require 'rubygems'
|
19
|
+
require 'builder'
|
20
|
+
require 'webrick'
|
21
|
+
require 'stringio'
|
22
|
+
|
23
|
+
class WEBrick::HTTPRequest
|
24
|
+
def ip() @peeraddr[3] end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Micromenu catchers are special hooks that allow integration
|
28
|
+
# between micromenus and incoming calls (specifically, incoming
|
29
|
+
# calls generated by the micromenus)
|
30
|
+
$MICROMENU_CALL_HOOKS = []
|
31
|
+
class << $MICROMENU_CALL_HOOKS
|
32
|
+
def purge_expired!
|
33
|
+
self.synchronize { |hooks| hooks.delete_if { |h| h.expiration < Time.now } }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class MicromenusServlet < WEBrick::HTTPServlet::AbstractServlet
|
38
|
+
|
39
|
+
class MicromenuGenerator
|
40
|
+
|
41
|
+
def initialize request, model, io=$stdout
|
42
|
+
@request, @io, @config = request, io, []
|
43
|
+
@xml = Builder::XmlMarkup.new(:target => @io, :indent => 3)
|
44
|
+
self.extend model
|
45
|
+
end
|
46
|
+
|
47
|
+
attr_accessor :name, :io, :config, :request
|
48
|
+
|
49
|
+
def process request
|
50
|
+
route = request.dup
|
51
|
+
|
52
|
+
if route.empty?
|
53
|
+
start "Error" do
|
54
|
+
build_text "Bad URL!"
|
55
|
+
build_text "Must point to a micromenu!"
|
56
|
+
end
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
title = " Adhearsion Micromenus"
|
61
|
+
|
62
|
+
filename = route.shift
|
63
|
+
load_menu filename
|
64
|
+
until route.empty?
|
65
|
+
segment = route.shift
|
66
|
+
broken = segment.match(/^([\w_.]+);?(\d*)$/)
|
67
|
+
segment, id = broken[1], broken[2]
|
68
|
+
id = nil if id.empty?
|
69
|
+
|
70
|
+
matches = @config.select do |x|
|
71
|
+
x[:type] == :menu && x[:text].nameify == segment
|
72
|
+
end
|
73
|
+
dest = matches[(id.simplify || 1) - 1]
|
74
|
+
|
75
|
+
|
76
|
+
if dest then
|
77
|
+
@config.clear
|
78
|
+
title = dest[:text]
|
79
|
+
dest[:block].call
|
80
|
+
else
|
81
|
+
start "Error" do
|
82
|
+
item '404 Not found!'
|
83
|
+
item "Req: #{request.inspect}"
|
84
|
+
return
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Let any headers override the default title
|
90
|
+
@config.each do |item|
|
91
|
+
if item[:type] == :heading
|
92
|
+
title = @config.delete(item)[:text]
|
93
|
+
break
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
start title do
|
98
|
+
@config.each do |item|
|
99
|
+
case item[:type]
|
100
|
+
when :menu
|
101
|
+
build_menu item[:text], item[:uri], request
|
102
|
+
when :item
|
103
|
+
build_text item[:text]
|
104
|
+
when :heading
|
105
|
+
build_header item[:text]
|
106
|
+
when :image
|
107
|
+
build_image item[:text]
|
108
|
+
when :call
|
109
|
+
build_call item[:number], item[:text]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def load_menu filename
|
116
|
+
@config.clear
|
117
|
+
file = File.join('config','helpers', 'micromenus', filename + '.rb')
|
118
|
+
unless File.readable? file
|
119
|
+
item "Bad URL!"
|
120
|
+
item "File inexistent!"
|
121
|
+
else
|
122
|
+
eval File.read(File.join('config','helpers', 'micromenus', filename + '.rb'))
|
123
|
+
end
|
124
|
+
@config
|
125
|
+
end
|
126
|
+
def join_url url, *pages
|
127
|
+
url *= '/' if url.is_a? Array
|
128
|
+
'/' + if url.empty?
|
129
|
+
pages * '/'
|
130
|
+
else
|
131
|
+
((url[-1] == ?/) ? url : url + "/") + pages * '/'
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def get_refresh() @refresh end
|
136
|
+
|
137
|
+
module PolycomPhone
|
138
|
+
|
139
|
+
def content_type() "text/html" end
|
140
|
+
|
141
|
+
def start name='', &block
|
142
|
+
@xml.html do
|
143
|
+
@xml.head do
|
144
|
+
@xml.title name
|
145
|
+
end
|
146
|
+
@xml.body do
|
147
|
+
yield
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def build_menu str, uri, request
|
153
|
+
#build_menu item[:text], item[:uri], request
|
154
|
+
#request.flatten! if request.is_a? Array
|
155
|
+
@xml.p { @xml.a str, :href => join_url(request, uri) }
|
156
|
+
end
|
157
|
+
|
158
|
+
def build_text str
|
159
|
+
@xml.p str
|
160
|
+
end
|
161
|
+
|
162
|
+
def build_prompt
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
def build_image filename, hash=nil
|
167
|
+
@xml.img :src => "/images/#{filename}"
|
168
|
+
end
|
169
|
+
|
170
|
+
def build_header str
|
171
|
+
@xml.h1 str
|
172
|
+
end
|
173
|
+
|
174
|
+
def build_call number, name=number
|
175
|
+
@xml.a name, :href => "tel://#{number}"
|
176
|
+
@xml.br
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
module XulUi
|
181
|
+
|
182
|
+
include PolycomPhone
|
183
|
+
def content_type() "application/vnd.mozilla.xul+xml" end
|
184
|
+
|
185
|
+
def start name='', &block
|
186
|
+
@xml.instruct!
|
187
|
+
@xml.instruct! 'xml-stylesheet', :href => "chrome://global/skin/", :type => "text/css"
|
188
|
+
@xml.instruct! 'xml-stylesheet', :href => "/stylesheets/firefox.xul.css", :type => "text/css"
|
189
|
+
@xml.window :title => name,
|
190
|
+
'xmlns:html' => "http://www.w3.org/1999/xhtml",
|
191
|
+
:xmlns => "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" do
|
192
|
+
@xml.vbox :id => 'haupt' do
|
193
|
+
#@xml.label name, :class => 'header'
|
194
|
+
yield
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def build_call number, name=number
|
200
|
+
# The tel:// UI doesn't do Firefox much good. Mexuar, maybe?
|
201
|
+
@xml.html:a, name, :href => "tel://#{number}"
|
202
|
+
@xml.html:br
|
203
|
+
end
|
204
|
+
|
205
|
+
def build_menu str, uri, request
|
206
|
+
@xml.html:a, str, :href => join_url(request, uri)
|
207
|
+
end
|
208
|
+
|
209
|
+
def build_text str
|
210
|
+
@xml.description str
|
211
|
+
@xml.html:br
|
212
|
+
end
|
213
|
+
|
214
|
+
def build_image filename, hash=nil
|
215
|
+
@xml.html:img, :src => "/images/#{filename}"
|
216
|
+
end
|
217
|
+
|
218
|
+
def build_header str
|
219
|
+
@xml.label str, :class => 'header'
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
|
224
|
+
|
225
|
+
module FirefoxUi
|
226
|
+
|
227
|
+
include PolycomPhone
|
228
|
+
|
229
|
+
def start name='', &block
|
230
|
+
@xml.html do
|
231
|
+
@xml.head do
|
232
|
+
@xml.title name
|
233
|
+
@xml.link :rel => 'stylesheet', :media => 'all', :href => '/stylesheets/firefox.css'
|
234
|
+
end
|
235
|
+
@xml.body do
|
236
|
+
yield
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
def build_call number, name=number
|
241
|
+
# The tel:// UI doesn't do Firefox much good. Mexuar, maybe?
|
242
|
+
@xml.a name, :href => "tel://#{number}"
|
243
|
+
@xml.br
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
private
|
250
|
+
|
251
|
+
|
252
|
+
def image name
|
253
|
+
name += '.bmp' unless name.index ?.
|
254
|
+
@config << {:type => :image, :text => name}
|
255
|
+
end
|
256
|
+
|
257
|
+
def refresh_every time
|
258
|
+
@refresh = time
|
259
|
+
end
|
260
|
+
|
261
|
+
def heading str
|
262
|
+
@config << {:type => :heading, :text => str}
|
263
|
+
end
|
264
|
+
alias header heading
|
265
|
+
|
266
|
+
def item title, &block
|
267
|
+
hash = {:text => title, :type => :item }
|
268
|
+
if block_given?
|
269
|
+
hash[:block], hash[:type], hash[:uri] = block, :menu, title.nameify
|
270
|
+
collisions = @config.select { |c| c[:type] == :menu && c[:text].nameify == hash[:uri]}.length
|
271
|
+
hash[:uri] += ";#{collisions + 1}" if collisions.nonzero?
|
272
|
+
end
|
273
|
+
@config << hash
|
274
|
+
end
|
275
|
+
def items array
|
276
|
+
array.each { |x| item x }
|
277
|
+
end
|
278
|
+
|
279
|
+
def guess_sip_user
|
280
|
+
return @guessed_user if @guessed_user
|
281
|
+
selection = PBX.sip_users.select { |x| x[:ip] == request.ip }.first
|
282
|
+
@guessed_user = selection ? selection.username : nil
|
283
|
+
end
|
284
|
+
|
285
|
+
def call number, name=number, &block
|
286
|
+
instance = {:type => :call, :text => name, :number => number}
|
287
|
+
if block_given?
|
288
|
+
$MICROMENU_CALL_HOOKS.purge_expired!
|
289
|
+
num = "555551337#{rand(8_999_999_999) + 1_000_000_000}"
|
290
|
+
instance[:number] = num
|
291
|
+
$MICROMENU_CALL_HOOKS.synchronize do |hooks|
|
292
|
+
hooks << { :expiration => 90.seconds.from_now, :extension => num, :hook => block }
|
293
|
+
end
|
294
|
+
end
|
295
|
+
@config << instance
|
296
|
+
end
|
297
|
+
|
298
|
+
def action title, &block
|
299
|
+
# Just like menu() but without a submenu.
|
300
|
+
# Useful for performing an action and refreshing.
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
USER_AGENT_MAP = {
|
305
|
+
"Polycom" => MicromenuGenerator::PolycomPhone,
|
306
|
+
"Firefox" => MicromenuGenerator::XulUi#FirefoxUi
|
307
|
+
}
|
308
|
+
|
309
|
+
def do_GET(request, response)
|
310
|
+
puts 'trying do'
|
311
|
+
response.status = 200
|
312
|
+
puts "Request from: " + request['User-Agent']
|
313
|
+
|
314
|
+
route = request.path[1..-1].split '/'
|
315
|
+
if route.first == 'images'
|
316
|
+
file = File.join(%w(config helpers micromenus images), route[1..-1])
|
317
|
+
# TODO: Handle missing files
|
318
|
+
response.content_type = WEBrick::HTTPUtils::mime_type file, WEBrick::HTTPUtils::DefaultMimeTypes
|
319
|
+
response.body = File.read file
|
320
|
+
|
321
|
+
elsif route.first == 'stylesheets'
|
322
|
+
file = File.join %w(config helpers micromenus stylesheets), route[1..-1]
|
323
|
+
response.content_type = 'text/css'
|
324
|
+
response.body = File.read file
|
325
|
+
else
|
326
|
+
mg = MicromenuGenerator.new request, resolve_brand(request['User-Agent']), StringIO.new
|
327
|
+
response.content_type = mg.content_type
|
328
|
+
response['Expires'] = 2
|
329
|
+
|
330
|
+
mg.process route
|
331
|
+
|
332
|
+
refresh = mg.get_refresh
|
333
|
+
response['Refresh'] = refresh if refresh
|
334
|
+
|
335
|
+
response.body = mg.io.string
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
def resolve_brand useragent
|
340
|
+
USER_AGENT_MAP.each do |k,v|
|
341
|
+
return v if useragent.index k
|
342
|
+
end
|
343
|
+
MicromenuGenerator::PolycomPhone # Polycom's the default since it's XHTML
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
$MICROMENU_THREAD = Thread.new do
|
348
|
+
micromenu_server = WEBrick::HTTPServer.new :Port => ($HELPERS.micromenus.port || 1337)
|
349
|
+
micromenu_server.logger = Logger.new 'logs/database.log', 10, 1.megabyte
|
350
|
+
micromenu_server.mount '/', MicromenusServlet
|
351
|
+
$HUTDOWN.hook {
|
352
|
+
micromenu_server.stop
|
353
|
+
}
|
354
|
+
micromenu_server.start
|
355
|
+
end
|
356
|
+
|
357
|
+
|
358
|
+
# This before_call hook is the magic behind the call() method in the micromenus.
|
359
|
+
before_call :low do
|
360
|
+
# PSEUDOCODE
|
361
|
+
# Check extension for format. next unless it matches
|
362
|
+
# Delete all expired hooks
|
363
|
+
# Find first match in the collection of hooks
|
364
|
+
# Pull the first match out of the collection
|
365
|
+
# Execute that match's block finish
|
366
|
+
extension = Thread.current[:VARS]['extension'].to_s
|
367
|
+
|
368
|
+
next unless extension.length == 19 && extension.starts_with?("555551337")
|
369
|
+
$MICROMENU_CALL_HOOKS.purge_expired!
|
370
|
+
match = $MICROMENU_CALL_HOOKS.detect { |x| x.extension == extension }
|
371
|
+
next unless match
|
372
|
+
Thread.current[:VARS]['context'] = :interrupted
|
373
|
+
+match.hook
|
374
|
+
end
|