instedd-pigeon 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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
+