instedd-pigeon 0.1.1

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.
Files changed (92) hide show
  1. data/.gitignore +25 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +9 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +140 -0
  6. data/Rakefile +1 -0
  7. data/app/assets/images/pigeon/android_local_gateway_preview.png +0 -0
  8. data/app/assets/images/pigeon/android_local_gateway_qr.png +0 -0
  9. data/app/assets/images/pigeon/other_local_gateway_preview.png +0 -0
  10. data/app/assets/javascripts/pigeon/qst-server-wizard.js.coffee +58 -0
  11. data/app/assets/javascripts/pigeon/template.js.coffee +43 -0
  12. data/app/assets/javascripts/pigeon/twitter-template.js.coffee +38 -0
  13. data/app/assets/javascripts/pigeon/wizard.js.coffee +95 -0
  14. data/app/assets/javascripts/pigeon.js +2 -0
  15. data/app/assets/stylesheets/pigeon.css.sass +13 -0
  16. data/app/controllers/pigeon/twitter_controller.rb +44 -0
  17. data/app/helpers/.gitkeep +0 -0
  18. data/app/helpers/pigeon/channel_helper.rb +93 -0
  19. data/app/helpers/pigeon/renderer/base.rb +172 -0
  20. data/app/helpers/pigeon/renderer/channel_renderer.rb +58 -0
  21. data/app/helpers/pigeon/renderer.rb +8 -0
  22. data/app/helpers/pigeon/tag_helper.rb +26 -0
  23. data/app/helpers/pigeon/template_helper.rb +20 -0
  24. data/app/helpers/pigeon/text_helper.rb +16 -0
  25. data/app/models/.gitkeep +0 -0
  26. data/app/models/pigeon/channel.rb +220 -0
  27. data/app/models/pigeon/channel_attribute.rb +64 -0
  28. data/app/models/pigeon/channel_schema.rb +113 -0
  29. data/app/models/pigeon/nested_attribute.rb +23 -0
  30. data/app/models/pigeon/nested_scopes.rb +34 -0
  31. data/app/models/pigeon/nuntium_channel.rb +92 -0
  32. data/app/models/pigeon/verboice_channel.rb +82 -0
  33. data/app/views/pigeon/twitter/callback.html.erb +6 -0
  34. data/config/routes.rb +4 -0
  35. data/config/schemas/nuntium/nuntium.yml +210 -0
  36. data/config/schemas/nuntium/qst-server.yml +180 -0
  37. data/config/schemas/nuntium/twitter.yml +56 -0
  38. data/config/schemas/verboice/verboice.yml +76 -0
  39. data/lib/pigeon/engine.rb +30 -0
  40. data/lib/pigeon/errors.rb +28 -0
  41. data/lib/pigeon/initializer.rb +13 -0
  42. data/lib/pigeon/nuntium.rb +10 -0
  43. data/lib/pigeon/verboice.rb +10 -0
  44. data/lib/pigeon/version.rb +3 -0
  45. data/lib/pigeon.rb +25 -0
  46. data/pigeon.gemspec +24 -0
  47. data/spec/data/test_schemas.yml +21 -0
  48. data/spec/dummy/README.rdoc +261 -0
  49. data/spec/dummy/Rakefile +7 -0
  50. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  51. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  52. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  53. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  54. data/spec/dummy/app/mailers/.gitkeep +0 -0
  55. data/spec/dummy/app/models/.gitkeep +0 -0
  56. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  57. data/spec/dummy/config/application.rb +59 -0
  58. data/spec/dummy/config/boot.rb +10 -0
  59. data/spec/dummy/config/database.yml +25 -0
  60. data/spec/dummy/config/environment.rb +5 -0
  61. data/spec/dummy/config/environments/development.rb +37 -0
  62. data/spec/dummy/config/environments/production.rb +67 -0
  63. data/spec/dummy/config/environments/test.rb +37 -0
  64. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  65. data/spec/dummy/config/initializers/inflections.rb +15 -0
  66. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  67. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  68. data/spec/dummy/config/initializers/session_store.rb +8 -0
  69. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  70. data/spec/dummy/config/locales/en.yml +5 -0
  71. data/spec/dummy/config/routes.rb +3 -0
  72. data/spec/dummy/config.ru +4 -0
  73. data/spec/dummy/lib/assets/.gitkeep +0 -0
  74. data/spec/dummy/log/.gitkeep +0 -0
  75. data/spec/dummy/public/404.html +26 -0
  76. data/spec/dummy/public/422.html +26 -0
  77. data/spec/dummy/public/500.html +25 -0
  78. data/spec/dummy/public/favicon.ico +0 -0
  79. data/spec/dummy/script/rails +6 -0
  80. data/spec/helpers/pigeon/channel_helper_spec.rb +173 -0
  81. data/spec/helpers/pigeon/renderer/base_spec.rb +109 -0
  82. data/spec/helpers/pigeon/renderer/channel_renderer_spec.rb +61 -0
  83. data/spec/helpers/pigeon/tag_helper_spec.rb +36 -0
  84. data/spec/helpers/pigeon/template_helper_spec.rb +57 -0
  85. data/spec/models/pigeon/channel_attribute_spec.rb +98 -0
  86. data/spec/models/pigeon/channel_schema_spec.rb +63 -0
  87. data/spec/models/pigeon/channel_spec.rb +205 -0
  88. data/spec/models/pigeon/nuntium_channel_spec.rb +53 -0
  89. data/spec/spec_helper.rb +43 -0
  90. data/spec/support/active_model_lint.rb +15 -0
  91. data/spec/support/test_schemas.rb +12 -0
  92. metadata +254 -0
@@ -0,0 +1,23 @@
1
+ module Pigeon
2
+ class NestedAttribute
3
+ attr_reader :name, :scope
4
+
5
+ def initialize(name, scope = nil)
6
+ @name = name
7
+ @scope = scope
8
+ end
9
+
10
+ def scoped_name(prefix = nil)
11
+ if scope.nil?
12
+ if prefix.present?
13
+ "#{prefix}[#{name}]"
14
+ else
15
+ name
16
+ end
17
+ else
18
+ "#{scope.scoped_name(prefix)}[#{name}]"
19
+ end
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,34 @@
1
+ module Pigeon
2
+ module NestedScopes
3
+
4
+ def find_attr_recursive(name, attributes)
5
+ hash, key = find_attr_ref_recursive(name, attributes)
6
+ hash && hash[key]
7
+ end
8
+
9
+ def find_attr_ref_recursive(name, attributes)
10
+ fold_scoped_name(name, [attributes, nil]) do |(hash, key), name|
11
+ if key.nil?
12
+ [hash, name]
13
+ elsif !hash.nil? && hash[key].is_a?(Hash)
14
+ [hash[key], name]
15
+ else
16
+ [nil, nil]
17
+ end
18
+ end
19
+ end
20
+
21
+ def fold_scoped_name(name, initial, &block)
22
+ m = name.to_s.match(/\A(?<name>\w+)(\[(?<nested>\w+)\](?<rest>.*))?\Z/)
23
+ if m.nil?
24
+ raise ArgumentError, "invalid scoped name #{name}"
25
+ elsif m[:nested].nil?
26
+ yield(initial, m[:name])
27
+ else
28
+ fold_scoped_name(m[:nested] + m[:rest], yield(initial, m[:name]), &block)
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+
@@ -0,0 +1,92 @@
1
+ module Pigeon
2
+ class NuntiumChannel < Channel
3
+
4
+ class << self
5
+ def type() :nuntium end
6
+
7
+ def schemas
8
+ @schemas ||= load_schemas
9
+ end
10
+
11
+ def list
12
+ nuntium.channels.map do |data|
13
+ data['name']
14
+ end
15
+ end
16
+
17
+ def nuntium
18
+ @nuntium ||= Nuntium.from_config
19
+ end
20
+
21
+ private
22
+
23
+ def find_single(id)
24
+ new nuntium.channel(id), true
25
+ end
26
+
27
+ def load_schemas
28
+ Pigeon::ChannelSchema.list_from_hash(:nuntium, PigeonConfig::NuntiumChannelSchemas)
29
+ end
30
+ end
31
+
32
+ channel_accessor :direction, :priority, :protocol, :enabled, :configuration
33
+ channel_accessor :restrictions, :at_rules, :ao_rules
34
+
35
+ validates_numericality_of :priority, only_integer: true
36
+ validates_presence_of :protocol
37
+ validates_inclusion_of :direction,
38
+ :in => %w(incoming outgoing bidirectional),
39
+ :message => "must be incoming, outgoing or bidirectional"
40
+
41
+ def save
42
+ return false unless valid?
43
+
44
+ begin
45
+ if !persisted?
46
+ self.class.nuntium.create_channel attributes
47
+ @persisted = true
48
+ else
49
+ self.class.nuntium.update_channel attributes
50
+ end
51
+ true
52
+ rescue Nuntium::Exception => e
53
+ Rails.logger.warn "error saving Nuntium channel: #{e.message}"
54
+ e.properties.each do |name, message|
55
+ if attributes.include? name
56
+ errors.add name, message
57
+ elsif configuration.include? name
58
+ errors.add "configuration[#{name}]", message
59
+ else
60
+ errors.add :base, "#{name} #{message}"
61
+ end
62
+ end
63
+ false
64
+ end
65
+ end
66
+
67
+ def destroy
68
+ if !destroyed?
69
+ begin
70
+ if persisted?
71
+ self.class.nuntium.delete_channel(name)
72
+ end
73
+ @destroyed = true
74
+ rescue Nuntium::Exception => e
75
+ Rails.logger.warn "error deleting Nuntium channel: #{e.message}"
76
+ end
77
+ end
78
+ end
79
+
80
+ protected
81
+
82
+ def load_default_values
83
+ self.protocol = 'sms'
84
+ self.priority = 100
85
+ self.enabled = true
86
+ self.direction = 'bidirectional'
87
+ self.configuration = {}
88
+ end
89
+
90
+ end
91
+ end
92
+
@@ -0,0 +1,82 @@
1
+ module Pigeon
2
+ class VerboiceChannel < Channel
3
+
4
+ class << self
5
+ def type() :verboice end
6
+
7
+ def schemas
8
+ @schemas ||= load_schemas
9
+ end
10
+
11
+ def list
12
+ verboice.list_channels
13
+ end
14
+
15
+ def verboice
16
+ @verboice ||= Verboice.from_config
17
+ end
18
+
19
+ private
20
+
21
+ def find_single(id)
22
+ new verboice.channel(id), true
23
+ end
24
+
25
+ def load_schemas
26
+ Pigeon::ChannelSchema.list_from_hash(:verboice, PigeonConfig::VerboiceChannelSchemas)
27
+ end
28
+ end
29
+
30
+ channel_accessor :call_flow, :config
31
+
32
+ validates_presence_of :call_flow
33
+
34
+ def save
35
+ return false unless valid?
36
+
37
+ begin
38
+ puts attributes
39
+ if !persisted?
40
+ self.class.verboice.create_channel attributes
41
+ @persisted = true
42
+ else
43
+ self.class.verboice.update_channel attributes
44
+ end
45
+ true
46
+ rescue Verboice::Exception => e
47
+ Rails.logger.warn "error saving Verboice channel: #{e.message}"
48
+ e.properties.each do |name, message|
49
+ if attributes.include? name
50
+ errors.add name, message
51
+ elsif config.include? name
52
+ errors.add "config[#{name}]", message
53
+ else
54
+ errors.add :base, "#{name} #{message}"
55
+ end
56
+ end
57
+ false
58
+ end
59
+ end
60
+
61
+ def destroy
62
+ if !destroyed?
63
+ begin
64
+ if persisted?
65
+ self.class.verboice.delete_channel(name)
66
+ end
67
+ @destroyed = true
68
+ rescue Verboice::Exception => e
69
+ Rails.logger.warn "error deleting Verboice channel: #{e.message}"
70
+ end
71
+ end
72
+ end
73
+
74
+ protected
75
+
76
+ def load_default_values
77
+ self.config = {}
78
+ self.call_flow = Pigeon.config.verboice_default_call_flow
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,6 @@
1
+ <!doctype html>
2
+ <title>Twitter callback</title>
3
+ <script type="text/javascript">
4
+ window.opener['<%= @callback %>']("<%= @access_token.token %>", "<%= @access_token.secret %>", "<%= @screen_name %>");
5
+ window.close();
6
+ </script>
data/config/routes.rb ADDED
@@ -0,0 +1,4 @@
1
+ Pigeon::Engine.routes.draw do
2
+ get 'twitter' => 'twitter#begin'
3
+ get 'twitter/callback' => 'twitter#callback'
4
+ end
@@ -0,0 +1,210 @@
1
+ clickatell:
2
+ humanized_name: 'Clickatell channel'
3
+ attributes:
4
+ - name: protocol
5
+ value: sms
6
+ user: false
7
+ - name: configuration
8
+ attributes:
9
+ - name: user
10
+ - name: password
11
+ type: password
12
+ - name: api_id
13
+ label: API ID
14
+ - name: from
15
+ - name: network
16
+ type: enum
17
+ options:
18
+ - value: '61'
19
+ display: '+61'
20
+ - value: '44a'
21
+ display: '+44 [A]'
22
+ - value: '46'
23
+ display: '+46'
24
+ - value: '49'
25
+ display: '+49'
26
+ - value: '45'
27
+ display: '+45'
28
+ - value: '44b'
29
+ display: '+44 [B]'
30
+ - value: 'usa'
31
+ display: 'USA shortcode'
32
+ - name: concatenation
33
+ - name: incoming_password
34
+ type: password
35
+ humanized_name: Incoming password
36
+ label: Incoming password (to use for the callback URLs)
37
+
38
+ dtac:
39
+ humanized_name: 'DTAC channel'
40
+ attributes:
41
+ - name: protocol
42
+ value: sms
43
+ user: false
44
+ - name: configuration
45
+ attributes:
46
+ - name: username
47
+ - name: password
48
+ type: password
49
+
50
+ ipop:
51
+ humanized_name: 'I-POP channel'
52
+ attributes:
53
+ - name: protocol
54
+ value: sms
55
+ user: false
56
+ - name: configuration
57
+ attributes:
58
+ - name: url
59
+ label: URL
60
+ - name: username
61
+ label: Username (optional)
62
+ - name: password
63
+ type: password
64
+ label: Password (optional)
65
+ - name: cid
66
+ label: Connection ID (cid)
67
+ - name: bid
68
+ label: Billing Level (bid)
69
+
70
+ msn:
71
+ humanized_name: 'MSN channel'
72
+ attributes:
73
+ - name: protocol
74
+ value: msn
75
+ user: false
76
+ - name: configuration
77
+ attributes:
78
+ - name: email
79
+ - name: password
80
+ type: password
81
+
82
+ isms:
83
+ humanized_name: 'Multimodem iSms channel'
84
+ attributes:
85
+ - name: protocol
86
+ value: sms
87
+ user: false
88
+ - name: configuration
89
+ attributes:
90
+ - name: host
91
+ - name: port
92
+ type: integer
93
+ default_value: 81
94
+ - name: user
95
+ - name: password
96
+ type: password
97
+ - name: time_zone
98
+ type: timezone
99
+
100
+ pop3:
101
+ humanized_name: 'POP3 channel'
102
+ attributes:
103
+ - name: protocol
104
+ value: mailto
105
+ user: false
106
+ - name: configuration
107
+ attributes:
108
+ - name: host
109
+ - name: port
110
+ type: integer
111
+ - name: user
112
+ - name: password
113
+ type: password
114
+ - name: use_ssl
115
+ type: boolean
116
+ label: "Use SSL?"
117
+ - name: remove_quoted_text_or_text_after_first_empty_line
118
+ type: boolean
119
+
120
+ qst:
121
+ humanized_name: 'QST client channel'
122
+ attributes:
123
+ - name: protocol
124
+ value: sms
125
+ user: false
126
+ - name: configuration
127
+ attributes:
128
+ - name: url
129
+ label: URL
130
+ - name: user
131
+ - name: password
132
+ type: password
133
+
134
+ smpp:
135
+ humanized_name: 'SMPP channel'
136
+ attributes:
137
+ - name: protocol
138
+ value: sms
139
+ user: false
140
+ - name: configuration
141
+ attributes:
142
+ - name: host
143
+ - name: port
144
+ type: integer
145
+ - name: user
146
+ - name: password
147
+ type: password
148
+ - name: system_type
149
+ default_value: vma
150
+ - name: source_ton
151
+ - name: source_npi
152
+ - name: destination_ton
153
+ - name: destination_npi
154
+ - name: service_type
155
+
156
+ smtp:
157
+ humanized_name: 'SMTP channel'
158
+ attributes:
159
+ - name: protocol
160
+ value: mailto
161
+ user: false
162
+ - name: configuration
163
+ attributes:
164
+ - name: host
165
+ - name: port
166
+ type: integer
167
+ - name: user
168
+ - name: password
169
+ type: password
170
+ - name: use_ssl
171
+ type: boolean
172
+ label: "Use SSL?"
173
+
174
+ twilio:
175
+ humanized_name: 'Twilio channel'
176
+ attributes:
177
+ - name: protocol
178
+ value: sms
179
+ user: false
180
+ - name: configuration
181
+ attributes:
182
+ - name: account_sid
183
+ - name: auth_token
184
+ - name: from
185
+ - name: incoming_password
186
+ type: password
187
+ label: Incoming password (to use for the callback URLs)
188
+
189
+ xmpp:
190
+ humanized_name: 'XMPP channel'
191
+ attributes:
192
+ - name: protocol
193
+ value: xmpp
194
+ user: false
195
+ - name: configuration
196
+ attributes:
197
+ - name: user
198
+ - name: domain
199
+ - name: password
200
+ type: password
201
+ - name: server
202
+ - name: port
203
+ type: integer
204
+ default_value: 5222
205
+ - name: resource
206
+ - name: status
207
+ - name: send_offline
208
+ type: boolean
209
+ label: Send if user is offline
210
+
@@ -0,0 +1,180 @@
1
+ qst_server:
2
+ humanized_name: 'QST server (local gateway) channel'
3
+ attributes:
4
+ - name: protocol
5
+ value: sms
6
+ user: false
7
+ - name: phone_type
8
+ type: hidden
9
+ user: false
10
+ - name: ticket_code
11
+ - name: ticket_message
12
+ - name: use_ticket
13
+ type: boolean
14
+ - name: configuration
15
+ attributes:
16
+ - name: password
17
+ type: password
18
+ template:
19
+ - "@wizard"
20
+ - type: qst-server
21
+ -
22
+ - "@hidden"
23
+ - phone_type
24
+ -
25
+ - "@hidden"
26
+ - ticket_message
27
+ -
28
+ - "@hidden"
29
+ - use_ticket
30
+ -
31
+ - "@hidden"
32
+ - configuration[password]
33
+ -
34
+ - "@page"
35
+ - id: persisted
36
+ -
37
+ - h1
38
+ - Channel already configured for mobile phone.
39
+ -
40
+ - button#qst-reconfigure
41
+ - type: button
42
+ - Reconfigure channel
43
+ -
44
+ - "@page"
45
+ - id: start
46
+ -
47
+ - h1
48
+ - Choose your type of mobile phone
49
+ -
50
+ - input
51
+ - type: radio
52
+ class: phone_type
53
+ id: phone_type_android
54
+ name: phone_type
55
+ value: android
56
+ -
57
+ - label
58
+ - for: phone_type_android
59
+ - Android mobile phone
60
+ -
61
+ - p
62
+ - If you have an Android powered mobile phone, you don't need a
63
+ computer to set up your gateway. Just download the application and
64
+ follow the configuration steps, and the phone will be used for sending
65
+ and receiving messages.
66
+ -
67
+ - input
68
+ - type: radio
69
+ class: phone_type
70
+ id: phone_type_other
71
+ name: phone_type
72
+ value: other
73
+ -
74
+ - label
75
+ - for: phone_type_other
76
+ - Other mobile phone
77
+ -
78
+ - p
79
+ - If you use another type of mobile phone all you need to do is plug
80
+ your phone into your computer, download the client, and it will be used
81
+ for sending and receiving messages.
82
+ -
83
+ - "@page"
84
+ - id: android_download
85
+ phone-type: android
86
+ -
87
+ - h1
88
+ - Go to Android Market and install Nuntium Local Gateway
89
+ -
90
+ - p
91
+ - Use the following QR code to access the app or use the search engine
92
+ with the keyword Nuntium
93
+ -
94
+ - img
95
+ - src: pigeon/android_local_gateway_qr.png
96
+ -
97
+ - a
98
+ - href: market.android.com/details?id=org.instedd.geochat.lgw
99
+ - market.android.com/details?id=org.instedd.geochat.lgw
100
+ -
101
+ - "@page"
102
+ - id: android_setup
103
+ phone-type: android
104
+ -
105
+ - h1
106
+ - Enter the gateway code from your mobile phone
107
+ -
108
+ - img
109
+ - src: pigeon/android_local_gateway_preview.png
110
+ -
111
+ - p
112
+ - Copy the code for automated setup from the Nuntium Local Gateway in
113
+ here to bind your local gateway to this poll.
114
+ -
115
+ - "@field"
116
+ - class: ticket_code
117
+ - ticket_code
118
+ -
119
+ - "@page"
120
+ - id: other_download
121
+ phone-type: other
122
+ -
123
+ - h1
124
+ - Download and install Nuntium Local Gateway
125
+ -
126
+ - p
127
+ - Nuntium Local Gateway is a program that runs on your local computer
128
+ and requires a mobile phone to be plugged in. The Gateway will use the
129
+ plugged-in phone for sending and receiving text messsages.
130
+ -
131
+ - p
132
+ - Download the client corresponding to your operating system and install it.
133
+ -
134
+ - ul
135
+ -
136
+ - li
137
+ -
138
+ - a
139
+ - href: https://bitbucket.org/instedd/local-gateway/downloads/LocalGateway-MacOS.tar.gz
140
+ - Download for MacOS
141
+ -
142
+ - li
143
+ -
144
+ - a
145
+ - href: https://bitbucket.org/instedd/local-gateway/downloads/LocalGateway.exe
146
+ - Download for Windows
147
+ -
148
+ - li
149
+ -
150
+ - a
151
+ - href: https://bitbucket.org/instedd/local-gateway/downloads/LocalGateway.exe
152
+ - Download for Ubuntu
153
+ -
154
+ - p
155
+ - Alternatively, you can install the Gateway running the following commands:
156
+ -
157
+ - pre
158
+ - |
159
+ sudo add-apt-repository ppa:instedd/nuntium-local-gateway
160
+ sudo apt-get update
161
+ sudo apt-get install nuntium-local-gateway
162
+ -
163
+ - "@page"
164
+ - id: other_setup
165
+ phone-type: other
166
+ -
167
+ - h1
168
+ - Enter the gateway code from your mobile phone
169
+ -
170
+ - img
171
+ - src: pigeon/other_local_gateway_preview.png
172
+ -
173
+ - p
174
+ - Copy the code for automated setup from the Nuntium Local Gateway in
175
+ here to bind your local gateway to this poll.
176
+ -
177
+ - "@field"
178
+ - class: ticket_code
179
+ - ticket_code
180
+
@@ -0,0 +1,56 @@
1
+ twitter:
2
+ humanized_name: 'Twitter channel'
3
+ attributes:
4
+ - name: protocol
5
+ value: twitter
6
+ user: false
7
+ - name: configuration
8
+ attributes:
9
+ - name: token
10
+ type: hidden
11
+ - name: secret
12
+ type: hidden
13
+ - name: screen_name
14
+ humanized_name: Screen name
15
+ - name: welcome_message
16
+ label: Welcome message for new followers
17
+ tooltip: (leave blank if not desired)
18
+ humanized_name: Welcome message
19
+ template:
20
+ - "@template"
21
+ - type: twitter
22
+ -
23
+ - "@hidden"
24
+ - configuration[token]
25
+ -
26
+ - "@hidden"
27
+ - configuration[secret]
28
+ -
29
+ - "@hidden"
30
+ - configuration[screen_name]
31
+ -
32
+ - div.twitter_not_authorized
33
+ -
34
+ - h2
35
+ - Twitter account not configured yet.
36
+ -
37
+ - button
38
+ - type: button
39
+ class: twitter_authorize_button
40
+ - Authorize Twitter account
41
+ -
42
+ - div.twitter_authorized
43
+ -
44
+ - h2
45
+ - "Screen name: "
46
+ -
47
+ - span.screen_name
48
+ -
49
+ - button
50
+ - type: button
51
+ class: twitter_authorize_button
52
+ - Authorize another Twitter account
53
+ -
54
+ - "@attr"
55
+ - configuration[welcome_message]
56
+