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.
- 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
|