instedd-pigeon 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +25 -0
- data/.rvmrc +1 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +140 -0
- data/Rakefile +1 -0
- data/app/assets/images/pigeon/android_local_gateway_preview.png +0 -0
- data/app/assets/images/pigeon/android_local_gateway_qr.png +0 -0
- data/app/assets/images/pigeon/other_local_gateway_preview.png +0 -0
- data/app/assets/javascripts/pigeon/qst-server-wizard.js.coffee +58 -0
- data/app/assets/javascripts/pigeon/template.js.coffee +43 -0
- data/app/assets/javascripts/pigeon/twitter-template.js.coffee +38 -0
- data/app/assets/javascripts/pigeon/wizard.js.coffee +95 -0
- data/app/assets/javascripts/pigeon.js +2 -0
- data/app/assets/stylesheets/pigeon.css.sass +13 -0
- data/app/controllers/pigeon/twitter_controller.rb +44 -0
- data/app/helpers/.gitkeep +0 -0
- data/app/helpers/pigeon/channel_helper.rb +93 -0
- data/app/helpers/pigeon/renderer/base.rb +172 -0
- data/app/helpers/pigeon/renderer/channel_renderer.rb +58 -0
- data/app/helpers/pigeon/renderer.rb +8 -0
- data/app/helpers/pigeon/tag_helper.rb +26 -0
- data/app/helpers/pigeon/template_helper.rb +20 -0
- data/app/helpers/pigeon/text_helper.rb +16 -0
- data/app/models/.gitkeep +0 -0
- data/app/models/pigeon/channel.rb +220 -0
- data/app/models/pigeon/channel_attribute.rb +64 -0
- data/app/models/pigeon/channel_schema.rb +113 -0
- data/app/models/pigeon/nested_attribute.rb +23 -0
- data/app/models/pigeon/nested_scopes.rb +34 -0
- data/app/models/pigeon/nuntium_channel.rb +92 -0
- data/app/models/pigeon/verboice_channel.rb +82 -0
- data/app/views/pigeon/twitter/callback.html.erb +6 -0
- data/config/routes.rb +4 -0
- data/config/schemas/nuntium/nuntium.yml +210 -0
- data/config/schemas/nuntium/qst-server.yml +180 -0
- data/config/schemas/nuntium/twitter.yml +56 -0
- data/config/schemas/verboice/verboice.yml +76 -0
- data/lib/pigeon/engine.rb +30 -0
- data/lib/pigeon/errors.rb +28 -0
- data/lib/pigeon/initializer.rb +13 -0
- data/lib/pigeon/nuntium.rb +10 -0
- data/lib/pigeon/verboice.rb +10 -0
- data/lib/pigeon/version.rb +3 -0
- data/lib/pigeon.rb +25 -0
- data/pigeon.gemspec +24 -0
- data/spec/data/test_schemas.yml +21 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config/application.rb +59 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/helpers/pigeon/channel_helper_spec.rb +173 -0
- data/spec/helpers/pigeon/renderer/base_spec.rb +109 -0
- data/spec/helpers/pigeon/renderer/channel_renderer_spec.rb +61 -0
- data/spec/helpers/pigeon/tag_helper_spec.rb +36 -0
- data/spec/helpers/pigeon/template_helper_spec.rb +57 -0
- data/spec/models/pigeon/channel_attribute_spec.rb +98 -0
- data/spec/models/pigeon/channel_schema_spec.rb +63 -0
- data/spec/models/pigeon/channel_spec.rb +205 -0
- data/spec/models/pigeon/nuntium_channel_spec.rb +53 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/support/active_model_lint.rb +15 -0
- data/spec/support/test_schemas.rb +12 -0
- metadata +254 -0
data/.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
*.swp
|
4
|
+
.bundle
|
5
|
+
.config
|
6
|
+
.yardoc
|
7
|
+
Gemfile.lock
|
8
|
+
InstalledFiles
|
9
|
+
_yardoc
|
10
|
+
coverage
|
11
|
+
doc/
|
12
|
+
lib/bundler/man
|
13
|
+
pkg
|
14
|
+
rdoc
|
15
|
+
spec/reports
|
16
|
+
test/tmp
|
17
|
+
test/version_tmp
|
18
|
+
tmp
|
19
|
+
spec/dummy/db/*.sqlite3
|
20
|
+
spec/dummy/log/*.log
|
21
|
+
spec/dummy/tmp/
|
22
|
+
spec/dummy/.sass-cache
|
23
|
+
spec/dummy/config/initializers/pigeon.rb
|
24
|
+
|
25
|
+
|
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.3@pigeon --create
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Ary Borenszweig
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
# Pigeon
|
2
|
+
|
3
|
+
This gem handles creating, updating and destroying
|
4
|
+
[Nuntium](http://nuntium.instedd.org) and
|
5
|
+
[Verboice](http://verboice.instedd.org) channels in your Rails application.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'instedd-pigeon', :require => 'pigeon'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
Create an initializer to configure Pigeon (eg. `config/initializers/pigeon.rb`):
|
20
|
+
|
21
|
+
Pigeon.setup do |config|
|
22
|
+
config.application_name = 'My application'
|
23
|
+
|
24
|
+
config.nuntium_host = 'http://nuntium.instedd.org'
|
25
|
+
config.nuntium_account = 'nuntium_account'
|
26
|
+
config.nuntium_app = 'nuntium_application'
|
27
|
+
config.nuntium_app_password = 'password'
|
28
|
+
|
29
|
+
config.verboice_host = 'http://verboice.instedd.org'
|
30
|
+
config.verboice_account = 'account@example.com'
|
31
|
+
config.verboice_password = 'password'
|
32
|
+
config.verboice_default_call_flow = 'Default Call Flow'
|
33
|
+
|
34
|
+
# If you want to support Nuntium Twitter channels, get your Twitter
|
35
|
+
# consumer keys from https://dev.twitter.com/apps
|
36
|
+
config.twitter_consumer_key = 'CONSUMER_KEY'
|
37
|
+
config.twitter_consumer_secret = 'CONSUMER_SECRET'
|
38
|
+
end
|
39
|
+
|
40
|
+
Add Pigeon assets to your application, for example by adding to
|
41
|
+
`app/assets/javascripts/application.js` the line
|
42
|
+
|
43
|
+
//= require pigeon
|
44
|
+
|
45
|
+
and to `app/assets/stylesheets/application.css`
|
46
|
+
|
47
|
+
/*
|
48
|
+
*= require pigeon
|
49
|
+
*/
|
50
|
+
|
51
|
+
If you need to support Nuntium Twitter channels, mount the Pigeon engine by
|
52
|
+
adding to your `routes.rb`
|
53
|
+
|
54
|
+
mount Pigeon::Engine => '/pigeon'
|
55
|
+
|
56
|
+
It is **strongly advised** to filter the engine's request through
|
57
|
+
authentication. If your application uses Devise, you can easily do it by
|
58
|
+
mounting the engine with `authenticate`:
|
59
|
+
|
60
|
+
authenticate :user do
|
61
|
+
mount Pigeon::Engine => '/pigeon'
|
62
|
+
end
|
63
|
+
|
64
|
+
Once properly configured, the gem provides a couple of classes
|
65
|
+
`Pigeon::NuntiumChannel` and `Pigeon::VerboiceChannel` to manipulate channels
|
66
|
+
which act as ActiveModels and provide a similar API to
|
67
|
+
[ActiveResource](https://github.com/rails/activeresource).
|
68
|
+
|
69
|
+
Pigeon channels should have a `kind` linking them to schemas which provide
|
70
|
+
information about the specific attributes required to configure each channel
|
71
|
+
type. The class methods `schemas` and `find_schema` provide access to all known
|
72
|
+
schemas.
|
73
|
+
|
74
|
+
Sample interaction session:
|
75
|
+
|
76
|
+
> c = Pigeon::NuntiumChannel.new name: 'foo', kind: 'pop3'
|
77
|
+
=> #<Pigeon::NuntiumChannel:0xa45938c>
|
78
|
+
> c.schema.kind
|
79
|
+
=> "pop3"
|
80
|
+
> c.attributes
|
81
|
+
=> {"protocol"=>"mailto", "priority"=>100, "enabled"=>true, "direction"=>"bidirectional", "configuration"=>{}, "name"=>"foo", "kind"=>"pop3"}
|
82
|
+
> c.schema.user_attributes
|
83
|
+
=> ["configuration[host]", "configuration[port]", "configuration[user]", "configuration[password]", "configuration[use_ssl]", "configuration[remove_quoted_text_or_text_after_first_empty_line]"]
|
84
|
+
> c.configuration[:host] = 'example.com'
|
85
|
+
=> "example.com"
|
86
|
+
> c.assign_attributes('configuration[user]' => 'foo', 'configuration[password]' => 'bar')
|
87
|
+
=> {"configuration[user]"=>"foo", "configuration[password]"=>"bar"}
|
88
|
+
> c.save
|
89
|
+
=> false
|
90
|
+
> c.errors.full_messages
|
91
|
+
=> ["port is not a number"]
|
92
|
+
> c.write_attribute('configuration[port]', 110)
|
93
|
+
=> 110
|
94
|
+
> c.new_record?
|
95
|
+
=> true
|
96
|
+
> c.save
|
97
|
+
=> true
|
98
|
+
> Pigeon::NuntiumChannel.list
|
99
|
+
=> ["foo"]
|
100
|
+
> c = Pigeon::NuntiumChannel.find('foo')
|
101
|
+
=> #<Pigeon::NuntiumChannel:0xb027708>
|
102
|
+
> c.kind
|
103
|
+
=> "pop3"
|
104
|
+
> c.new_record?
|
105
|
+
=> false
|
106
|
+
> c.destroy
|
107
|
+
=> true
|
108
|
+
> c.destroyed?
|
109
|
+
=> true
|
110
|
+
|
111
|
+
The gem also provides helpers to aid in the rendering of the channel's
|
112
|
+
configuration form. The most important ones are
|
113
|
+
`pigeon_nuntium_channel_kinds_for_select` and
|
114
|
+
`pigeon_verboice_channel_kinds_for_select` to use as options generators for the
|
115
|
+
`select_tag` Rails helper, and `pigeon_render_channel` which will render
|
116
|
+
the fields required for a user to configure the given channel.
|
117
|
+
|
118
|
+
For example, in the view:
|
119
|
+
|
120
|
+
<%= form_tag('/channels') do %>
|
121
|
+
<%= hidden_field_tag :name, @channel.name %>
|
122
|
+
<%= pigeon_render_channel @channel %>
|
123
|
+
<%= end %>
|
124
|
+
|
125
|
+
Then in the controller:
|
126
|
+
|
127
|
+
def update
|
128
|
+
@channel = Pigeon::NuntiumChannel.find(params[:name])
|
129
|
+
@channel.assign_attributes(params[:channel])
|
130
|
+
@channel.save!
|
131
|
+
redirect_to channels_path
|
132
|
+
end
|
133
|
+
|
134
|
+
## Contributing
|
135
|
+
|
136
|
+
1. Fork it
|
137
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
138
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
139
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
140
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#= require pigeon/wizard
|
2
|
+
|
3
|
+
class QstServerWizard extends PigeonWizard
|
4
|
+
onPhoneType: (type) ->
|
5
|
+
@pages.each (index, page) ->
|
6
|
+
page_phone_type = $(page).data('phone-type')
|
7
|
+
if page_phone_type
|
8
|
+
page.disabled = page_phone_type != type
|
9
|
+
true
|
10
|
+
@attribute('phone_type').val(type)
|
11
|
+
@updateNavigation()
|
12
|
+
|
13
|
+
initPhoneType: (type) ->
|
14
|
+
@phone_type_radios.each (index, radio) ->
|
15
|
+
$(radio).attr('checked', radio.value == type)
|
16
|
+
@onPhoneType type
|
17
|
+
|
18
|
+
initWizard: ->
|
19
|
+
self = this
|
20
|
+
|
21
|
+
@navigation.show()
|
22
|
+
@pages[0].disabled = true
|
23
|
+
|
24
|
+
@ticket_code_fields = $('.ticket_code', @div)
|
25
|
+
@phone_type_radios = $('.phone_type', @div)
|
26
|
+
|
27
|
+
@initPhoneType @attribute('phone_type').val()
|
28
|
+
@phone_type_radios.click ->
|
29
|
+
self.onPhoneType @value
|
30
|
+
|
31
|
+
@ticket_code_fields.change ->
|
32
|
+
# update both ticket_code fields to make sure the current value gets
|
33
|
+
# posted
|
34
|
+
self.attribute('ticket_code').val(@value)
|
35
|
+
|
36
|
+
password_field = @attribute('configuration[password]')
|
37
|
+
if password_field.val() == ''
|
38
|
+
password_field.val(@generatePassword())
|
39
|
+
|
40
|
+
@attribute('ticket_message').val("This phone will be used for #{@app_name}.")
|
41
|
+
@attribute('use_ticket').val(true)
|
42
|
+
|
43
|
+
initPersisted: ->
|
44
|
+
$('#qst-reconfigure', @div).click =>
|
45
|
+
@initWizard()
|
46
|
+
@selectPage 1
|
47
|
+
@navigation.hide()
|
48
|
+
@selectPage 0
|
49
|
+
|
50
|
+
run: ->
|
51
|
+
if @persisted
|
52
|
+
@initPersisted()
|
53
|
+
else
|
54
|
+
@initWizard()
|
55
|
+
@selectDefaultPage()
|
56
|
+
|
57
|
+
PigeonWizard.registerClass 'qst-server', QstServerWizard
|
58
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class PigeonTemplate
|
2
|
+
@klasses = {}
|
3
|
+
|
4
|
+
@registerClass: (type, klass) ->
|
5
|
+
@klasses[type] = klass
|
6
|
+
|
7
|
+
constructor: (@div) ->
|
8
|
+
div = $(@div)
|
9
|
+
@scope = div.data('scope')
|
10
|
+
@channel_name = div.data('name')
|
11
|
+
@channel_kind = div.data('kind')
|
12
|
+
@persisted = div.data('persisted')
|
13
|
+
@app_name = div.data('application-name') || 'Pigeon'
|
14
|
+
|
15
|
+
scoped_name: (name) ->
|
16
|
+
if @scope
|
17
|
+
bracket = name.indexOf('[')
|
18
|
+
if bracket >= 0
|
19
|
+
@scope + '[' + name[0..bracket-1] + ']' + name[bracket..-1]
|
20
|
+
else
|
21
|
+
@scope + '[' + name + ']'
|
22
|
+
else
|
23
|
+
name
|
24
|
+
|
25
|
+
attribute: (name) ->
|
26
|
+
$("[name=\"#{@scoped_name(name)}\"]", @div)
|
27
|
+
|
28
|
+
run: ->
|
29
|
+
|
30
|
+
window.PigeonTemplate = PigeonTemplate
|
31
|
+
|
32
|
+
jQuery.fn.pigeonTemplate = ->
|
33
|
+
@each (index, template) ->
|
34
|
+
type = $(template).data('type')
|
35
|
+
klass = PigeonTemplate.klasses[type]
|
36
|
+
if klass
|
37
|
+
new klass(template).run()
|
38
|
+
else
|
39
|
+
new PigeonTemplate(template).run()
|
40
|
+
|
41
|
+
jQuery ->
|
42
|
+
$('.pigeon.pigeon_template').pigeonTemplate()
|
43
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#= require pigeon/template
|
2
|
+
|
3
|
+
class TwitterTemplate extends PigeonTemplate
|
4
|
+
onAuthorized: ->
|
5
|
+
$('.twitter_not_authorized').hide()
|
6
|
+
$('.twitter_authorized').show()
|
7
|
+
$('.screen_name').text(@screen_name.val())
|
8
|
+
|
9
|
+
beginTwitterAuthorization: ->
|
10
|
+
callback = 'pigeon_callback_' + (Math.round(Math.random() * 1000000))
|
11
|
+
window[callback] = (token, secret, screen_name) =>
|
12
|
+
@token.val(token)
|
13
|
+
@secret.val(secret)
|
14
|
+
@screen_name.val(screen_name)
|
15
|
+
@onAuthorized()
|
16
|
+
|
17
|
+
url = @twitter_auth_url + '?js_callback=' + callback
|
18
|
+
window.open(url, 'pigeon_twitter', 'menubar=no,location=yes,width=593,height=533')
|
19
|
+
|
20
|
+
run: ->
|
21
|
+
@screen_name = @attribute('configuration[screen_name]')
|
22
|
+
@token = @attribute('configuration[token]')
|
23
|
+
@secret = @attribute('configuration[secret]')
|
24
|
+
|
25
|
+
@button = $('.twitter_authorize_button')
|
26
|
+
@twitter_auth_url = $(@div).data('twitter-path')
|
27
|
+
|
28
|
+
if @screen_name.val() != ''
|
29
|
+
@onAuthorized()
|
30
|
+
else
|
31
|
+
$('.twitter_not_authorized').show()
|
32
|
+
$('.twitter_authorized').hide()
|
33
|
+
|
34
|
+
@button.click =>
|
35
|
+
@beginTwitterAuthorization()
|
36
|
+
|
37
|
+
PigeonTemplate.registerClass 'twitter', TwitterTemplate
|
38
|
+
|
@@ -0,0 +1,95 @@
|
|
1
|
+
#= require pigeon/template
|
2
|
+
|
3
|
+
class PigeonWizard extends PigeonTemplate
|
4
|
+
@klasses = {}
|
5
|
+
|
6
|
+
@registerClass: (type, klass) ->
|
7
|
+
@klasses[type] = klass
|
8
|
+
|
9
|
+
constructor: (@div) ->
|
10
|
+
super
|
11
|
+
@prevButton = $('<button type="button">Prev</button>')
|
12
|
+
@nextButton = $('<button type="button">Next</button>')
|
13
|
+
@pages = $(@div).find('.pigeon_wizard_page')
|
14
|
+
@navigation = $('<div class="pigeon_wizard_navigation"></div>').append(@prevButton).append(@nextButton).appendTo(@div)
|
15
|
+
@current = -1
|
16
|
+
|
17
|
+
@prevButton.click =>
|
18
|
+
prev = @prevActivePage(@current)
|
19
|
+
@selectPage prev if prev >= 0
|
20
|
+
|
21
|
+
@nextButton.click =>
|
22
|
+
next = @nextActivePage(@current)
|
23
|
+
@selectPage next if next >= 0
|
24
|
+
|
25
|
+
prevActivePage: (index) ->
|
26
|
+
index--
|
27
|
+
while index >= 0 && @pages[index].disabled
|
28
|
+
index--
|
29
|
+
index
|
30
|
+
|
31
|
+
nextActivePage: (index) ->
|
32
|
+
index++
|
33
|
+
while index < @pages.length && @pages[index].disabled
|
34
|
+
index++
|
35
|
+
if index >= @pages.length then -1 else index
|
36
|
+
|
37
|
+
selectPage: (index) ->
|
38
|
+
$(@pages[@current]).removeClass('active') unless @current < 0
|
39
|
+
$(@pages[index]).addClass('active')
|
40
|
+
@current = index
|
41
|
+
@updateNavigation()
|
42
|
+
|
43
|
+
updateNavigation: ->
|
44
|
+
if @prevActivePage(@current) < 0
|
45
|
+
@prevButton.attr('disabled', 'disabled')
|
46
|
+
else
|
47
|
+
@prevButton.removeAttr('disabled')
|
48
|
+
if @nextActivePage(@current) < 0
|
49
|
+
@nextButton.attr('disabled', 'disabled')
|
50
|
+
else
|
51
|
+
@nextButton.removeAttr('disabled')
|
52
|
+
|
53
|
+
selectDefaultPage: ->
|
54
|
+
# If any active page has a field with an error, select that
|
55
|
+
# Otherwise, select the first active page
|
56
|
+
pages_with_errors = $('.field_with_errors').
|
57
|
+
parents('.pigeon_wizard_page').filter -> !@disabled
|
58
|
+
found = -1
|
59
|
+
if pages_with_errors.length > 0
|
60
|
+
pages_with_errors.each (index, page) =>
|
61
|
+
index_in_pages = $.inArray(page, @pages)
|
62
|
+
if found < 0 || (index_in_pages >= 0 && index_in_pages < found)
|
63
|
+
found = index_in_pages
|
64
|
+
|
65
|
+
if found >= 0
|
66
|
+
@selectPage found
|
67
|
+
else
|
68
|
+
@selectPage @nextActivePage(-1)
|
69
|
+
|
70
|
+
run: ->
|
71
|
+
@selectDefaultPage()
|
72
|
+
|
73
|
+
generatePassword: (length = 8) ->
|
74
|
+
charset = "abcdefghijklnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
75
|
+
retVal = ""
|
76
|
+
n = charset.length
|
77
|
+
for i in [0..length]
|
78
|
+
retVal += charset.charAt(Math.floor(Math.random() * n))
|
79
|
+
retVal
|
80
|
+
|
81
|
+
|
82
|
+
window.PigeonWizard = PigeonWizard
|
83
|
+
|
84
|
+
jQuery.fn.pigeonWizard = ->
|
85
|
+
@each (index, wizard) ->
|
86
|
+
type = $(wizard).data('type')
|
87
|
+
klass = PigeonWizard.klasses[type]
|
88
|
+
if klass
|
89
|
+
new klass(wizard).run()
|
90
|
+
else
|
91
|
+
new PigeonWizard(wizard).run()
|
92
|
+
|
93
|
+
jQuery ->
|
94
|
+
$('.pigeon.pigeon_wizard').pigeonWizard()
|
95
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'twitter_oauth'
|
2
|
+
|
3
|
+
module Pigeon
|
4
|
+
class TwitterController < ActionController::Base
|
5
|
+
def begin
|
6
|
+
client = new_twitter_client
|
7
|
+
|
8
|
+
request_token = client.request_token(:oauth_callback => twitter_callback_url)
|
9
|
+
session[:pigeon] ||= {}
|
10
|
+
session[:pigeon][:twitter] = {
|
11
|
+
token: request_token.token,
|
12
|
+
secret: request_token.secret
|
13
|
+
}
|
14
|
+
session[:pigeon][:js_callback] = params[:js_callback]
|
15
|
+
redirect_to request_token.authorize_url
|
16
|
+
end
|
17
|
+
|
18
|
+
def callback
|
19
|
+
if session[:pigeon].nil? || session[:pigeon][:twitter].nil?
|
20
|
+
redirect_to twitter_url
|
21
|
+
end
|
22
|
+
|
23
|
+
client = new_twitter_client
|
24
|
+
|
25
|
+
request_token = session[:pigeon][:twitter]
|
26
|
+
@access_token = client.authorize(
|
27
|
+
request_token[:token],
|
28
|
+
request_token[:secret],
|
29
|
+
:oauth_verifier => params[:oauth_verifier]
|
30
|
+
)
|
31
|
+
@callback = session[:pigeon][:js_callback] || 'pigeon_twitter_callback'
|
32
|
+
@screen_name = client.info['screen_name']
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def new_twitter_client
|
38
|
+
client = TwitterOAuth::Client.new(
|
39
|
+
:consumer_key => Pigeon.config.twitter_consumer_key,
|
40
|
+
:consumer_secret => Pigeon.config.twitter_consumer_secret
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
File without changes
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Pigeon
|
2
|
+
module ChannelHelper
|
3
|
+
include ActionView::Helpers::CaptureHelper
|
4
|
+
include ActionView::Helpers::FormHelper
|
5
|
+
include ActionView::Helpers::FormOptionsHelper
|
6
|
+
|
7
|
+
def pigeon_attribute_field(attribute, value = nil, options = {})
|
8
|
+
scope = options.delete(:scope)
|
9
|
+
field_name = attribute.scoped_name(scope)
|
10
|
+
field_value = value || ''
|
11
|
+
forced_type = options.delete(:type)
|
12
|
+
render_type = (forced_type || attribute.type).to_s
|
13
|
+
|
14
|
+
case render_type
|
15
|
+
when "string"
|
16
|
+
text_field_tag(field_name, field_value, options)
|
17
|
+
when "password"
|
18
|
+
password_field_tag(field_name, field_value, options)
|
19
|
+
when "integer"
|
20
|
+
number_field_tag(field_name, field_value, options)
|
21
|
+
when "enum"
|
22
|
+
choices = attribute.options
|
23
|
+
if choices.length > 0 && choices[0].is_a?(Hash)
|
24
|
+
choices = choices.map { |h| [h["display"], h["value"]] }
|
25
|
+
end
|
26
|
+
select_tag(field_name, options_for_select(choices, field_value), options)
|
27
|
+
when "timezone"
|
28
|
+
select_tag(field_name, time_zone_options_for_select(field_value), options)
|
29
|
+
when "boolean"
|
30
|
+
check_box_tag(field_name, '1', field_value.present?, options)
|
31
|
+
when "hidden"
|
32
|
+
hidden_field_tag(field_name, field_value, options)
|
33
|
+
else
|
34
|
+
text_field_tag(field_name, field_value,
|
35
|
+
{ :type => render_type }.merge(options))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def pigeon_attribute_label(attribute, options = {})
|
40
|
+
return '' if attribute.type == :hidden
|
41
|
+
scope = options.delete(:scope)
|
42
|
+
field_name = attribute.scoped_name(scope)
|
43
|
+
label_tag(field_name, attribute.label, options)
|
44
|
+
end
|
45
|
+
|
46
|
+
def pigeon_attribute(attribute, value = nil, options = {})
|
47
|
+
scope = options.delete(:scope)
|
48
|
+
field_with_errors = options.delete(:field_with_errors)
|
49
|
+
content_tag :div, options do
|
50
|
+
label = pigeon_attribute_label(attribute, scope: scope)
|
51
|
+
field = pigeon_attribute_field(attribute, value, scope: scope)
|
52
|
+
if field_with_errors.present?
|
53
|
+
field = content_tag :div, field, :class => 'field_with_errors'
|
54
|
+
end
|
55
|
+
if attribute.type == :boolean
|
56
|
+
safe_join([field, label])
|
57
|
+
elsif attribute.type == :hidden
|
58
|
+
field
|
59
|
+
else
|
60
|
+
safe_join([label, tag(:br), field])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def pigeon_channel_attribute_field(channel, attr_name, options = {})
|
66
|
+
value = channel.read_attribute(attr_name)
|
67
|
+
attribute = channel.schema.try(:find_attribute, attr_name)
|
68
|
+
attribute ||= ChannelAttribute.build_default(attr_name, value)
|
69
|
+
field = pigeon_attribute_field(attribute, value, options)
|
70
|
+
if channel.errors.include?(attr_name.to_sym)
|
71
|
+
content_tag :div, field, :class => 'field_with_errors'
|
72
|
+
else
|
73
|
+
field
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def pigeon_channel_attribute_label(channel, attr_name, options = {})
|
78
|
+
attribute = channel.schema.try(:find_attribute, attr_name)
|
79
|
+
attribute ||= ChannelAttribute.build_default(attr_name)
|
80
|
+
pigeon_attribute_label(attribute, options)
|
81
|
+
end
|
82
|
+
|
83
|
+
def pigeon_channel_attribute(channel, attr_name, options = {})
|
84
|
+
value = channel.read_attribute(attr_name)
|
85
|
+
attribute = channel.schema.try(:find_attribute, attr_name)
|
86
|
+
attribute ||= ChannelAttribute.build_default(attr_name, value)
|
87
|
+
if channel.errors.include?(attr_name.to_sym)
|
88
|
+
options[:field_with_errors] = true
|
89
|
+
end
|
90
|
+
pigeon_attribute(attribute, value, options)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|