Skip to content
This repository
Browse code

Merge master into widgets

  • Loading branch information...
commit a9c7c41300adf95518549a2b5495e1a7d5a87921 1 parent c18106d
authored July 06, 2011

Showing 81 changed files with 3,105 additions and 238 deletions. Show diff stats Hide diff stats

  1. 0  .gitmodules
  2. 25  Gemfile
  3. 92  Gemfile.lock
  4. 29  app/controllers/account_controller.rb
  5. 7  app/controllers/admin/contact_congress_controller.rb
  6. 63  app/controllers/application_controller.rb
  7. 87  app/controllers/battle_royale_controller.rb
  8. 125  app/controllers/contact_congress_letters_controller.rb
  9. 1  app/controllers/contact_controller.rb
  10. 13  app/controllers/districts_controller.rb
  11. 20  app/helpers/application_helper.rb
  12. 2  app/helpers/contact_congress_letters_helper.rb
  13. 5  app/helpers/contact_helper.rb
  14. 37  app/models/bill.rb
  15. 2  app/models/bill_text_node.rb
  16. 4  app/models/bill_text_version.rb
  17. 14  app/models/contact_congress_letter.rb
  18. 4  app/models/contact_congress_letters_formageddon_thread.rb
  19. 21  app/models/crp_interest_group.rb
  20. 3  app/models/district.rb
  21. 35  app/models/person.rb
  22. 8  app/models/state.rb
  23. 11  app/models/user.rb
  24. 2  app/models/user_observer.rb
  25. 33  app/views/account/facebook_complete.html.haml
  26. 69  app/views/account/login.html.erb
  27. 40  app/views/account/login.html.haml
  28. 16  app/views/admin/contact_congress/index.html.haml
  29. 8  app/views/bill/major.rxml
  30. 50  app/views/bill/show.html.erb
  31. 13  app/views/contact_congress_letters/_contact_congress_share.html.haml
  32. 10  app/views/contact_congress_letters/_contact_congress_status_bar.html.haml
  33. 38  app/views/contact_congress_letters/_contact_recipients.html.haml
  34. 7  app/views/contact_congress_letters/_message_builder_commentary.html.haml
  35. 39  app/views/contact_congress_letters/_message_builder_contribution_data.html.haml
  36. 5  app/views/contact_congress_letters/_will_add_text_box.html.haml
  37. 65  app/views/contact_congress_letters/create.html.haml
  38. 10  app/views/contact_congress_letters/get_recipients.js.erb
  39. 173  app/views/contact_congress_letters/new.html.haml
  40. 35  app/views/contact_congress_letters/select_position.html.haml
  41. 48  app/views/contact_congress_letters/show.html.haml
  42. 18  app/views/contact_congress_letters/showthread.html.haml
  43. 5  app/views/districts/index.html.erb
  44. 12  app/views/districts/show.html.erb
  45. 16  app/views/layouts/_header.html.erb
  46. 2  app/views/layouts/frontpage.html.erb
  47. 18  app/views/people/_person.html.haml
  48. 2  app/views/person/_topic.html.erb
  49. 7  app/views/shared/_comments.html.haml
  50. 4  app/views/shared/_comments_list.html.haml
  51. 6  app/views/shared/_error_messages.html.haml
  52. 43  app/views/shared/_user_content.html.erb
  53. 44  app/views/states/show.html.erb
  54. 256  bin/build_formageddon_recipients.rb
  55. 27  bin/get_sunlightlabs_data.rb
  56. 2  config/assets.yml
  57. 1  config/initializers/facebooker2.rb
  58. 19  config/initializers/formageddon.rb
  59. 25  config/routes.rb
  60. 10  db/migrate/20110306192052_comments_trigger_modify.rb
  61. 19  db/migrate/20110526181158_contact_congress_letters.rb
  62. 93  db/migrate/20110526194928_create_formageddon_tables.rb
  63. 11  db/migrate/20110610165044_user_facebook.rb
  64. BIN  public/images/comment.png
  65. BIN  public/images/facebook-bg.png
  66. BIN  public/images/letter-approve.png
  67. BIN  public/images/letter-oppose.png
  68. BIN  public/images/letter-tracking.png
  69. BIN  public/images/letters.png
  70. BIN  public/images/pencil.png
  71. BIN  public/images/people-arrow.png
  72. BIN  public/images/promo.gif
  73. BIN  public/images/tiny-arrow.png
  74. BIN  public/images/triangle.png
  75. BIN  public/images/widg-bg.jpg
  76. 19  public/stylesheets/account.css
  77. 127  public/stylesheets/bill.css
  78. 3  public/stylesheets/contact.css
  79. 1,196  public/stylesheets/contact_congress_letters.css
  80. 76  public/stylesheets/master.css
  81. 13  public/stylesheets/states.css
0  .gitmodules
No changes.
25  Gemfile
... ...
@@ -1,6 +1,7 @@
1 1
 source 'http://rubygems.org'
2 2
 
3 3
 gem 'rails', '3.0.7'
  4
+gem 'rake', '0.8.7'
4 5
 
5 6
 # database gems -- need both pg and mysql for app and wiki
6 7
 gem 'pg'
@@ -12,6 +13,9 @@ gem "settingslogic"
12 13
 # HAML support
13 14
 gem "haml"
14 15
 
  16
+# RABL for API / JSON
  17
+gem 'rabl'
  18
+
15 19
 # RMagick
16 20
 gem 'rmagick', '2.13.1'
17 21
 gem 'simple_captcha', :git => 'git://github.com/galetahub/simple-captcha.git'
@@ -47,6 +51,7 @@ gem 'RedCloth'
47 51
 gem 'bluecloth'
48 52
 gem 'htmlentities'
49 53
 gem "json"
  54
+gem "nokogiri"
50 55
 
51 56
 # Deployment
52 57
 gem 'capistrano'
@@ -57,13 +62,31 @@ gem 'newrelic_rpm'
57 62
 
58 63
 # oauth
59 64
 gem 'oauth'
  65
+gem 'facebooker2'
60 66
 
61 67
 gem 'will_paginate', '~> 3.0.pre2'
62 68
 
63 69
 gem "validates_captcha"
64 70
 gem "okkez-open_id_authentication"
65 71
 
66  
-gem "acts-as-taggable-on", :git => 'git://github.com/mbleigh/acts-as-taggable-on.git'
  72
+gem "acts-as-taggable-on", :git => 'http://github.com/mbleigh/acts-as-taggable-on.git'
  73
+
  74
+### temp just for showing to drm
  75
+gem 'mechanize'
  76
+#gem 'formageddon', '0.0.0', :require => 'formageddon', :path => '/Users/aross/pcf-work/gitbranches/formageddon'
  77
+gem 'formageddon', :git => 'git://github.com/opencongress/formageddon.git'
  78
+
  79
+gem 'rspec'
  80
+gem 'rspec-rails', '~> 2.4'
  81
+gem 'cucumber', '0.8.5'
  82
+gem 'cucumber-rails'
  83
+gem 'webrat'
  84
+gem 'selenium-client'
  85
+
  86
+gem 'capybara'
  87
+gem 'capybara-envjs'
  88
+
  89
+gem 'autotest'
67 90
 
68 91
 # Testing
69 92
 group :test, :development do
92  Gemfile.lock
@@ -5,7 +5,16 @@ GIT
5 5
     simple_captcha (0.1.1)
6 6
 
7 7
 GIT
8  
-  remote: git://github.com/mbleigh/acts-as-taggable-on.git
  8
+  remote: git://github.com/opencongress/formageddon.git
  9
+  revision: 887a36965fb1ec87c54c36128d6ae7fbff7380dd
  10
+  specs:
  11
+    formageddon (0.0.0)
  12
+      delayed_job (~> 2.1)
  13
+      haml
  14
+      mechanize
  15
+
  16
+GIT
  17
+  remote: http://github.com/mbleigh/acts-as-taggable-on.git
9 18
   revision: 2752cfef4c66318d0925fbe880d5392ad188bb50
10 19
   specs:
11 20
     acts-as-taggable-on (2.0.6)
@@ -46,18 +55,18 @@ GEM
46 55
       activemodel (= 3.0.7)
47 56
       activesupport (= 3.0.7)
48 57
     activesupport (3.0.7)
49  
-    addressable (2.2.5)
50  
-    arel (2.0.9)
  58
+    addressable (2.2.6)
  59
+    arel (2.0.10)
51 60
     autotest (4.4.6)
52 61
       ZenTest (>= 4.4.1)
53 62
     bluecloth (2.1.0)
54 63
     builder (2.1.2)
55  
-    capistrano (2.5.21)
  64
+    capistrano (2.6.0)
56 65
       highline
57 66
       net-scp (>= 1.0.0)
58 67
       net-sftp (>= 2.0.0)
59 68
       net-ssh (>= 2.0.14)
60  
-      net-ssh-gateway (>= 1.0.0)
  69
+      net-ssh-gateway (>= 1.1.0)
61 70
     capistrano-ext (1.2.1)
62 71
       capistrano (>= 1.0.0)
63 72
     capybara (0.4.1.2)
@@ -72,10 +81,10 @@ GEM
72 81
     capybara-envjs (0.4.0)
73 82
       capybara (~> 0.4.0)
74 83
       envjs (>= 0.3.7)
75  
-    carrierwave (0.5.3)
  84
+    carrierwave (0.5.4)
76 85
       activesupport (~> 3.0)
77 86
     celerity (0.8.9)
78  
-    childprocess (0.1.8)
  87
+    childprocess (0.1.9)
79 88
       ffi (~> 1.0.6)
80 89
     closure-compiler (1.1.1)
81 90
     crack (0.1.8)
@@ -88,6 +97,10 @@ GEM
88 97
     cucumber-rails (0.3.2)
89 98
       cucumber (>= 0.8.0)
90 99
     culerity (0.2.15)
  100
+    daemons (1.1.3)
  101
+    delayed_job (2.1.4)
  102
+      activesupport (~> 3.0)
  103
+      daemons
91 104
     diff-lcs (1.1.2)
92 105
     em-websocket (0.2.1)
93 106
       addressable (>= 2.1.1)
@@ -98,10 +111,12 @@ GEM
98 111
       abstract (>= 1.0.0)
99 112
     eventmachine (0.12.10)
100 113
     excon (0.6.3)
  114
+    facebooker2 (0.0.11)
  115
+      mogli (>= 0.0.12)
  116
+      ruby-hmac
101 117
     fastercsv (1.5.4)
102  
-    ffi (1.0.7)
103  
-      rake (>= 0.8.7)
104  
-    fog (0.8.1)
  118
+    ffi (1.0.9)
  119
+    fog (0.8.2)
105 120
       builder
106 121
       excon (~> 0.6.1)
107 122
       formatador (>= 0.1.3)
@@ -110,7 +125,7 @@ GEM
110 125
       net-ssh (>= 2.1.3)
111 126
       nokogiri (>= 1.4.4)
112 127
       ruby-hmac
113  
-    formatador (0.1.3)
  128
+    formatador (0.1.4)
114 129
     gherkin (2.1.5)
115 130
       trollop (~> 1.16.2)
116 131
     govkit (0.6.1)
@@ -118,25 +133,24 @@ GEM
118 133
       httparty (>= 0.5.2)
119 134
       json (>= 1.4.3)
120 135
       nokogiri (>= 1.4.4)
121  
-    guard (0.3.4)
  136
+    guard (0.4.1)
122 137
       thor (~> 0.14.6)
123  
-    guard-livereload (0.1.10)
  138
+    guard-livereload (0.1.11)
124 139
       em-websocket (~> 0.2.0)
125 140
       guard (>= 0.2.2)
126 141
       json (~> 1.5.1)
127  
-    haml (3.1.1)
128  
-    highline (1.6.1)
129  
-    hoptoad_notifier (2.4.9)
  142
+    haml (3.1.2)
  143
+    highline (1.6.2)
  144
+    hoptoad_notifier (2.4.11)
130 145
       activesupport
131 146
       builder
132 147
     hpricot (0.8.4)
133 148
     htmlentities (4.3.0)
134  
-    httparty (0.7.7)
  149
+    httparty (0.7.8)
135 150
       crack (= 0.1.8)
136 151
     i18n (0.5.0)
137  
-    jammit (0.6.0)
138  
-      closure-compiler (>= 0.1.0)
139  
-      yui-compressor (>= 0.9.1)
  152
+    jammit (0.6.3)
  153
+      yui-compressor (>= 0.9.3)
140 154
     johnson (2.0.0.pre3)
141 155
       stackdeck (~> 0.2)
142 156
     json (1.5.1)
@@ -146,19 +160,23 @@ GEM
146 160
       i18n (>= 0.4.0)
147 161
       mime-types (~> 1.16)
148 162
       treetop (~> 1.4.8)
  163
+    mechanize (1.0.0)
  164
+      nokogiri (>= 1.2.1)
149 165
     mediacloth (0.0.3)
150 166
     memcache (1.2.13)
151 167
     memcache-client (1.8.5)
152 168
     mime-types (1.16)
  169
+    mogli (0.0.28)
  170
+      httparty (>= 0.4.3)
153 171
     mysql (2.8.1)
154 172
     net-scp (1.0.4)
155 173
       net-ssh (>= 1.99.1)
156 174
     net-sftp (2.0.5)
157 175
       net-ssh (>= 2.0.9)
158 176
     net-ssh (2.1.4)
159  
-    net-ssh-gateway (1.0.1)
  177
+    net-ssh-gateway (1.1.0)
160 178
       net-ssh (>= 1.99.1)
161  
-    newrelic_rpm (2.14.1)
  179
+    newrelic_rpm (3.0.1)
162 180
     nokogiri (1.4.4)
163 181
     oauth (0.4.4)
164 182
     okkez-open_id_authentication (1.0.1)
@@ -166,7 +184,8 @@ GEM
166 184
     open4 (1.0.1)
167 185
     pg (0.11.0)
168 186
     polyglot (0.3.1)
169  
-    rack (1.2.2)
  187
+    rabl (0.2.8)
  188
+    rack (1.2.3)
170 189
     rack-mount (0.6.14)
171 190
       rack (>= 1.0.0)
172 191
     rack-openid (1.3.1)
@@ -189,24 +208,24 @@ GEM
189 208
       thor (~> 0.14.4)
190 209
     rake (0.8.7)
191 210
     rmagick (2.13.1)
192  
-    rspec (2.5.0)
193  
-      rspec-core (~> 2.5.0)
194  
-      rspec-expectations (~> 2.5.0)
195  
-      rspec-mocks (~> 2.5.0)
196  
-    rspec-core (2.5.1)
197  
-    rspec-expectations (2.5.0)
  211
+    rspec (2.6.0)
  212
+      rspec-core (~> 2.6.0)
  213
+      rspec-expectations (~> 2.6.0)
  214
+      rspec-mocks (~> 2.6.0)
  215
+    rspec-core (2.6.4)
  216
+    rspec-expectations (2.6.0)
198 217
       diff-lcs (~> 1.1.2)
199  
-    rspec-mocks (2.5.0)
200  
-    rspec-rails (2.5.0)
  218
+    rspec-mocks (2.6.0)
  219
+    rspec-rails (2.6.1)
201 220
       actionpack (~> 3.0)
202 221
       activesupport (~> 3.0)
203 222
       railties (~> 3.0)
204  
-      rspec (~> 2.5.0)
  223
+      rspec (~> 2.6.0)
205 224
     ruby-hmac (0.4.0)
206 225
     ruby-openid (2.1.8)
207 226
     rubyzip (0.9.4)
208 227
     selenium-client (1.2.18)
209  
-    selenium-webdriver (0.2.0)
  228
+    selenium-webdriver (0.2.1)
210 229
       childprocess (>= 0.1.7)
211 230
       ffi (>= 1.0.7)
212 231
       json_pure
@@ -253,7 +272,9 @@ DEPENDENCIES
253 272
   closure-compiler
254 273
   cucumber (= 0.8.5)
255 274
   cucumber-rails
  275
+  facebooker2
256 276
   fog
  277
+  formageddon!
257 278
   govkit
258 279
   guard
259 280
   guard-livereload
@@ -263,17 +284,22 @@ DEPENDENCIES
263 284
   htmlentities
264 285
   jammit
265 286
   json
  287
+  mechanize
266 288
   mediacloth
267 289
   memcache
268 290
   memcache-client
269 291
   mysql
270 292
   newrelic_rpm
  293
+  nokogiri
271 294
   oauth
272 295
   okkez-open_id_authentication
273 296
   pg
  297
+  rabl
274 298
   rack-openid
275 299
   rails (= 3.0.7)
  300
+  rake (= 0.8.7)
276 301
   rmagick (= 2.13.1)
  302
+  rspec
277 303
   rspec-rails (~> 2.4)
278 304
   ruby-openid
279 305
   selenium-client
29  app/controllers/account_controller.rb
@@ -36,7 +36,7 @@ def get_user_full_name
36 36
     end
37 37
   end
38 38
 
39  
-  def login
  39
+  def login    
40 40
     if params[:login_action]
41 41
       session[:login_action] = {:url => session[:return_to], :action_result => params[:login_action]}
42 42
     end
@@ -104,8 +104,32 @@ def accept_tos
104 104
     end
105 105
   end
106 106
 
  107
+  def facebook_complete
  108
+    @page_title = 'Facebook Connect'
  109
+    
  110
+    @user = User.where(['facebook_uid=?', @facebook_user.id]).first
  111
+    if @user.nil?
  112
+      @user = User.new
  113
+    end
  114
+    
  115
+    if request.post?
  116
+      @user.facebook_uid = @facebook_user.id
  117
+      @user.email = @facebook_user.email
  118
+      @user.update_attributes(params[:user])
  119
+      
  120
+      if @user.save
  121
+        @user.activate
  122
+        self.current_user = @user
  123
+        flash[:notice] = 'You have successfully signed up with your Facebook Account!'
  124
+        
  125
+        redirect_to welcome_url
  126
+        return
  127
+      end
  128
+    end
  129
+  end
  130
+  
107 131
   def signup
108  
-   @page_title = "Create a New Account"
  132
+    @page_title = "Create a New Account"
109 133
 
110 134
     logger.info session.inspect
111 135
 
@@ -178,6 +202,7 @@ def activate
178 202
   end
179 203
 
180 204
   def welcome
  205
+    @page_title = 'Welcome to OpenCongress!'
181 206
     @user = current_user
182 207
     @show_tracked_list = true
183 208
 
7  app/controllers/admin/contact_congress_controller.rb
... ...
@@ -0,0 +1,7 @@
  1
+class Admin::ContactCongressController < Admin::IndexController
  2
+  before_filter :admin_login_required
  3
+
  4
+  def index
  5
+    @people = Person.all_sitting
  6
+  end
  7
+end
63  app/controllers/application_controller.rb
@@ -5,14 +5,74 @@ class ApplicationController < ActionController::Base
5 5
 
6 6
   include AuthenticatedSystem
7 7
   include SimpleCaptcha::ControllerHelpers
  8
+  include Facebooker2::Rails::Controller
8 9
   include UrlHelper
9 10
 
  11
+  before_filter :facebook_check
10 12
   before_filter :store_location
11 13
   before_filter :current_tab
12 14
   before_filter :has_accepted_tos?
13 15
   before_filter :get_site_text_page
14 16
   before_filter :is_banned?
15 17
 
  18
+  def facebook_check
  19
+    # check to see if the user is logged into and has connected to OC
  20
+    if current_facebook_user and current_facebook_client
  21
+      begin
  22
+        @facebook_user = Mogli::User.find(current_facebook_user.id, current_facebook_client)
  23
+      rescue Mogli::Client::HTTPException
  24
+        set_fb_cookie(nil,nil,nil,nil)
  25
+        @facebook_user = nil
  26
+      end
  27
+    else
  28
+      @facebook_user = nil
  29
+    end
  30
+    
  31
+    if @facebook_user
  32
+      # the user isn't logged in, try to find the account based on email
  33
+      if current_user == :false
  34
+        oc_user = User.where(["email=?", @facebook_user.email]).first
  35
+      else
  36
+        # if the logged-in user's email matches the one from facebook, use that user
  37
+        # otherwise, cancel the facebook connect attempt
  38
+        if current_user.email == @facebook_user.email
  39
+          return unless current_user.facebook_uid.blank?
  40
+          oc_user = current_user
  41
+        else
  42
+          flash[:error] = "The email addresses in your Facebook and OpenCongress accounts do not match.  Could not connect."
  43
+          set_fb_cookie(nil,nil,nil,nil)
  44
+          @facebook_user = nil
  45
+          return
  46
+        end
  47
+      end
  48
+        
  49
+      if oc_user
  50
+        # if, for some reason, we don't have these fields, require them
  51
+        if oc_user.login.blank? or oc_user.zipcode.blank? or !oc_user.accepted_tos
  52
+          redirect_to :controller => 'account', :action => 'facebook_complete' unless params[:action] == 'facebook_complete'
  53
+          return
  54
+        end 
  55
+      
  56
+        # make sure we have facebook uid
  57
+        if oc_user.facebook_uid.blank?
  58
+          oc_user.facebook_uid = @facebook_user.id
  59
+          oc_user.save
  60
+          
  61
+          flash.now[:notice] = 'Your Facebook account has now been linked to this OpenCongress account!'
  62
+        else
  63
+          flash.now[:notice] = "Welcome, #{oc_user.login}."
  64
+        end
  65
+      
  66
+        # log the user in
  67
+        self.current_user = oc_user
  68
+      else
  69
+        # new user.  redirect to get essential info
  70
+        redirect_to :controller => 'account', :action => 'facebook_complete' unless params[:action] == 'facebook_complete'
  71
+        return
  72
+      end
  73
+    end
  74
+  end
  75
+  
16 76
   def is_valid_email?(e, with_headers = false)
17 77
     if with_headers == false
18 78
       email_check = Regexp.new('^[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$')
@@ -136,5 +196,6 @@ def news_blog_count(count)
136 196
     else
137 197
       count
138 198
     end
139  
-  end  
  199
+  end
  200
+  
140 201
 end
87  app/controllers/battle_royale_controller.rb
@@ -10,10 +10,97 @@ def senators
10 10
 
11 11
   def representatives
12 12
     redirect_to :controller => 'people', :action => 'representatives'
  13
+    return
  14
+    
  15
+    @person = Person.find(params[:person]) if params[:person]
  16
+
  17
+    sort = params[:sort] ||= "bookmark_count_1"
  18
+		order = params[:order] ||= "desc"
  19
+    @p_title_class = "reps"
  20
+    @p_title = "Representatives"
  21
+    if order == "asc"
  22
+      @p_subtitle = "Least "
  23
+    else
  24
+      @p_subtitle = "Most "
  25
+    end
  26
+    case sort
  27
+      when "bookmark_count_1"
  28
+       @p_subtitle << "Users Tracking"
  29
+      when "p_approval_count"
  30
+        @p_subtitle << "User Approval Votes"
  31
+      when "p_approval_avg"
  32
+        @p_subtitle << "Average User Approval"
  33
+      when "total_comments"
  34
+        @p_subtitle << "Comments"
  35
+    end
  36
+    page = params[:page] ||= 1
  37
+#    @cache_key = "br-reps-#{page}-#{sort}-#{order}-#{logged_in? ? current_user.login : nil}-#{@range}-#{params[:q].blank? ? nil : Digest::SHA1.hexdigest(params[:q])}"
  38
+#    unless read_fragment(@cache_key)
  39
+      unless params[:q].blank?    
  40
+        @r_count = Person.count_all_by_most_tracked_for_range(@range, :search => prepare_tsearch_query(params[:q]), :order => sort + " " + order, :per_page => 20, :page => page, :person_type => "Rep.")
  41
+        @results = Person.paginate_by_most_tracked_for_range(@range, :search => prepare_tsearch_query(params[:q]), :order => sort + " " + order, :per_page => 20, :page => page, :person_type => "Rep.", :total_entries => @r_count)
  42
+      else
  43
+        @r_count = Person.count_all_by_most_tracked_for_range(@range, :order => sort + " " + order, :per_page => 20, :page => page, :person_type => "Rep.")
  44
+        @results = Person.paginate_by_most_tracked_for_range(@range, :order => sort + " " + order, :per_page => 20, :page => page, :person_type => "Rep.", :total_entries => @r_count)
  45
+      end
  46
+#    end
  47
+#    get_counts
  48
+    
  49
+    respond_to do |format|
  50
+     format.html {
  51
+       render :action => 'person_by_approval_rating'
  52
+     }
  53
+     format.xml {
  54
+       render :xml => @results.to_xml(:except => [:bookmark_count_2,
  55
+                                                  :fti_names,:current_support_pb, :support_count_1, :rolls, :hot_bill_category_id, 
  56
+                                                  :support_count_2, :vote_count_2]) 
  57
+     }
  58
+
  59
+    end    
  60
+
13 61
   end
14 62
 
15 63
   def issues
16 64
     redirect_to :controller => 'issues', :action => 'index'
  65
+    return
  66
+
  67
+    @issue = Subject.find(params[:issue]) if params[:issue]
  68
+    
  69
+    sort = params[:sort] ||= "bookmark_count_1"
  70
+		order = params[:order] ||= "desc"
  71
+    @p_title_class = "issues"
  72
+    @p_title = "Issues"
  73
+    if order == "asc"
  74
+      @p_subtitle = "Least "
  75
+    else
  76
+      @p_subtitle = "Most "
  77
+    end
  78
+    case sort
  79
+      when "bookmark_count_1"
  80
+       @p_subtitle << "Users Tracking"
  81
+      when "total_comments"
  82
+        @p_subtitle << "Comments"
  83
+    end
  84
+    page = params[:page] ||= 1
  85
+#    @cache_key = "br-issues-#{page}-#{sort}-#{order}-#{logged_in? ? current_user.login : nil}-#{@range}-#{params[:q].blank? ? nil : Digest::SHA1.hexdigest(params[:q])}"
  86
+#    unless read_fragment(@cache_key)
  87
+      unless params[:q].blank?   
  88
+        @r_count = Subject.count_all_by_most_tracked_for_range(@range, :search => prepare_tsearch_query(params[:q]), :order => sort + " " + order, :per_page => 20, :page => page)
  89
+        @results = Subject.paginate_by_most_tracked_for_range(@range, :search => prepare_tsearch_query(params[:q]), :order => sort + " " + order, :per_page => 20, :page => page, :total_entries => @r_count)
  90
+      else
  91
+        @r_count = Subject.count_all_by_most_tracked_for_range(@range, :order => sort + " " + order, :per_page => 20, :page => page)
  92
+        @results = Subject.paginate_by_most_tracked_for_range(@range, :order => sort + " " + order, :per_page => 20, :page => page, :total_entries => @r_count)
  93
+      end
  94
+#    end
  95
+    respond_to do |format|
  96
+     format.html {
  97
+       render :action => 'most_tracked_issues'
  98
+     }
  99
+     format.xml {
  100
+       render :xml => @results.to_xml(:except => [:bookmark_count_2,:fti_names,:current_support_pb, :support_count_1, :rolls, :hot_bill_category_id, :support_count_2, :vote_count_2]) 
  101
+     }
  102
+
  103
+    end   
17 104
   end    
18 105
 
19 106
   def show_bill_details
125  app/controllers/contact_congress_letters_controller.rb
... ...
@@ -0,0 +1,125 @@
  1
+class ContactCongressLettersController < ApplicationController
  2
+  
  3
+  def new
  4
+    @page_title = "Contact Congress"
  5
+    @bill = Bill.find_by_ident(params[:bill])
  6
+  
  7
+
  8
+    if logged_in?
  9
+      @sens = current_user.my_sens
  10
+      @reps = current_user.my_reps
  11
+  
  12
+      if @sens.empty? && @reps.empty?
  13
+        flash[:notice] = "In order to contact your representatives in Congress, you must configure your account.  Please enter your zipcode and address in the form below."
  14
+        redirect_to user_profile
  15
+      end
  16
+    else
  17
+      @sens = @reps = []
  18
+    end
  19
+  
  20
+    if params[:position].nil?
  21
+      render 'select_position'
  22
+      return
  23
+    end
  24
+      
  25
+    formageddon_configured = false
  26
+    ### loop through recipients and see if formageddon is configured
  27
+    
  28
+    
  29
+    @position = params[:position]
  30
+  
  31
+    case @position
  32
+    when 'support'
  33
+      message_start = "I support #{@bill.typenumber} - #{@bill.title_common}, and am tracking it using OpenCongress.org, the free public resource website for government transparency and accountability."      
  34
+    when 'oppose'
  35
+      message_start = "I oppose #{@bill.typenumber} - #{@bill.title_common}, and am tracking it using OpenCongress.org, the free public resource website for government transparency and accountability."      
  36
+    else
  37
+      message_start = "I'm tracking #{@bill.typenumber} - #{@bill.title_common} using OpenCongress.org, the free public resource website for government transparency and accountability."
  38
+    end
  39
+  
  40
+    @formageddon_thread = Formageddon::FormageddonThread.new
  41
+    @formageddon_thread.prepare(:user => current_user, :subject => @bill.typenumber, :message => message_start)
  42
+  end
  43
+
  44
+  
  45
+  def get_recipients
  46
+    @bill = Bill.find_by_ident(params[:bill])
  47
+    
  48
+    unless params[:zip4].blank?
  49
+      @sens, @reps = Person.find_current_congresspeople_by_zipcode(params[:zip5], params[:zip4])
  50
+    else
  51
+      yg = YahooGeocoder.new("#{params[:address]}, #{params[:zip5]}")
  52
+      unless yg.zip5.nil?
  53
+        @sens, @reps = Person.find_current_congresspeople_by_zipcode(yg.zip5, yg.zip4)
  54
+        @zip4 = yg.zip4
  55
+      end
  56
+      
  57
+      
  58
+      @sens, @reps = Person.find_current_congresspeople_by_address_and_zipcode(params[:address], params[:zip5])
  59
+    end
  60
+
  61
+    @sens = [] unless @sens
  62
+    @reps = [] unless @reps and @reps.size == 1
  63
+    
  64
+    #render :partial => 'contact/contact_recipients', :locals => { :show_checkboxes => true }
  65
+  end
  66
+  
  67
+  def show
  68
+    @contact_congress_letter = ContactCongressLetter.find(params[:id])
  69
+  end
  70
+  
  71
+  def create_from_formageddon
  72
+    ## dont forget to check privacy settings
  73
+    unless params[:letter_ids].blank?
  74
+      letter_ids = params[:letter_ids].split(/,/)
  75
+      @letters = Formageddon::FormageddonLetter.find(letter_ids)
  76
+    end
  77
+    
  78
+    bill = Bill.find_by_ident(params[:bill])
  79
+
  80
+    @letters.each do |l|  
  81
+      cclft = ContactCongressLettersFormageddonThread.find_by_formageddon_thread_id(l.formageddon_thread.id)
  82
+      if cclft.nil?
  83
+        if @contact_congress_letter.nil?
  84
+          @contact_congress_letter = ContactCongressLetter.new
  85
+          @contact_congress_letter.disposition = params[:disposition]
  86
+          @contact_congress_letter.bill = bill unless bill.nil?
  87
+          @contact_congress_letter.save
  88
+        end
  89
+        
  90
+        @contact_congress_letter.formageddon_threads << l.formageddon_thread
  91
+      else
  92
+        @contact_congress_letter = cclft.contact_congress_letter
  93
+        break
  94
+      end
  95
+    end
  96
+    
  97
+    if @contact_congress_letter.nil? 
  98
+      # something weird happened
  99
+      redirect_to '/'
  100
+      return
  101
+    else
  102
+      if @contact_congress_letter.user.nil?
  103
+        if current_user == :false
  104
+          user = create_new_user_from_formageddon_thread(@contact_congress_letter.formageddon_threads.first)
  105
+          @contact_congress_letter.user = user
  106
+          @new_user_notice = true
  107
+        else
  108
+          @contact_congress_letter.user = current_user
  109
+          @new_user_notice = false
  110
+        end
  111
+        @contact_congress_letter.save
  112
+      else
  113
+        @new_user_notice = false
  114
+      end
  115
+    end
  116
+    
  117
+    render :action => 'create'
  118
+  end
  119
+  
  120
+  private
  121
+   
  122
+  def create_new_user_from_formageddon_thread(thread)
  123
+    return nil
  124
+  end
  125
+end
1  app/controllers/contact_controller.rb
... ...
@@ -1,2 +1,3 @@
1 1
 class ContactController < ApplicationController
  2
+
2 3
 end
13  app/controllers/districts_controller.rb
... ...
@@ -1,10 +1,19 @@
1 1
 class DistrictsController < ApplicationController
2 2
   before_filter :get_state
3 3
 
4  
-  # GET /districts/1
5  
-  # GET /districts/1.xml
  4
+  # GET /states/:state_id/districts
  5
+  def index
  6
+    @districts = @state.districts
  7
+  end
  8
+
  9
+  # GET /states/:state_id/districts/:id
6 10
   def show
7 11
     @district = @state.districts.find_by_district_number(params[:id])
  12
+
  13
+    @representative = Person.find_current_representative_by_state_and_district(@state.abbreviation, @district.district_number)
  14
+
  15
+    @senators = Person.find_current_senators_by_state(@state.abbreviation)
  16
+
8 17
     @page_title = "#{@state.name.titleize}'s #{@district.ordinalized_number} Congressional District"
9 18
     @users = @district.users
10 19
     @tracking_suggestions = @district.tracking_suggestions
20  app/helpers/application_helper.rb
@@ -554,21 +554,13 @@ def inline_determine_support(bill, support = 10)
554 554
 		end
555 555
 	end
556 556
   
557  
-  def user_bill_result(bill)
558  
-    vt = bill.bill_votes.count
559  
-    if vt == 0
560  
-      result = nil
561  
-    else
562  
-      bs = bill.bill_votes.count(:all, :conditions => "support = 0")
563  
-      bo = bill.bill_votes.count(:all, :conditions => "support = 1")      
564  
-      result = (bs.to_f / vt) * 100
565  
-      result = result.round
566  
-    end                    
567  
-    color = percent_to_color(result)
  557
+  def user_bill_result(bill)                   
  558
+    color = percent_to_color(bill.users_percentage_at_position('support'))
568 559
     %Q{<div id="users_result">
569  
-    <h3 class="clearfix" style="color:#{color};" id="support_#{bill.id.to_s}">#{result.nil? ? "-" : result}%</h3>
  560
+    <h3 class="clearfix" style="color:#{color};" id="support_#{bill.id.to_s}">
  561
+    #{bill.users_percentage_at_position('support').nil? ? "-" : bill.users_percentage_at_position('support')}%</h3>
570 562
     <h4>Users Support Bill</h4>
571  
-    <font>#{bs} in favor / #{bo} opposed</font>
  563
+    <font>#{bill.users_at_position('support')} in favor / #{bill.users_at_position('oppose')} opposed</font>
572 564
     </div>}.html_safe
573 565
   end
574 566
   
@@ -843,6 +835,8 @@ def bitly_url(object)
843 835
     case object
844 836
     when Article
845 837
       url = url_for(:only_path => false, :controller => 'articles', :action => 'view', :id => object)
  838
+    when ContactCongressLetter
  839
+      url = url_for(:only_path => false, :controller => 'contact', :action => 'letter', :id => object)
846 840
     end
847 841
     
848 842
     begin
2  app/helpers/contact_congress_letters_helper.rb
... ...
@@ -0,0 +1,2 @@
  1
+module ContactCongressLettersHelper
  2
+end
5  app/helpers/contact_helper.rb
... ...
@@ -1,2 +1,7 @@
1 1
 module ContactHelper
  2
+  def twitter_share_for_letter(letter)
  3
+    "http://twitter.com/home?status=" + 
  4
+      u("Wrote my members of #Congress on @opencongress to let them know I'm tracking #USbill #" +
  5
+        letter.bill.typenumber) 
  6
+  end
2 7
 end
37  app/models/bill.rb
@@ -896,6 +896,13 @@ def top_recipients_for_all_interest_groups(disposition = 'support', chamber = 'h
896 896
      LIMIT ?", groups_ids, Settings.current_opensecrets_cycle, groups_ids, Settings.current_opensecrets_cycle, title, num])
897 897
   end
898 898
   
  899
+  def bill_position_organizations_support
  900
+    bill_position_organizations.where("bill_position_organizations.disposition='support'")
  901
+  end
  902
+  def bill_position_organizations_oppose
  903
+    bill_position_organizations.where("bill_position_organizations.disposition='oppose'")
  904
+  end
  905
+  
899 906
   class << self
900 907
     def client_id_to_url(client_id)
901 908
       client_id.slice!(/\d+_/)
@@ -1151,6 +1158,18 @@ def bill_status_hash
1151 1158
     return status_hash
1152 1159
   end
1153 1160
     
  1161
+  def vote_on_passage(person)
  1162
+    if (chamber == 'house' and person.title == 'Rep.') or (chamber == 'senate' and person.title = 'Sen.')
  1163
+      roll = originating_chamber_vote
  1164
+    else
  1165
+      roll = other_chamber_vote
  1166
+    end
  1167
+    
  1168
+    return "Not Voted Yet" if roll.nil? or roll.roll_call.nil?
  1169
+    
  1170
+    roll.roll_call.vote_for_person(person)
  1171
+  end
  1172
+  
1154 1173
   def self.full_text_search(q, options = {})
1155 1174
     congresses = options[:congresses] || Settings.default_congress
1156 1175
     
@@ -1237,6 +1256,24 @@ def obj_title
1237 1256
     typenumber
1238 1257
   end
1239 1258
 
  1259
+
  1260
+  # methods about user interaction
  1261
+  def users_at_position(position = 'support')
  1262
+    bill_votes.count(:all, :conditions => ["support = ?", position == 'support' ? 0 : 1])
  1263
+  end
  1264
+  
  1265
+  def users_percentage_at_position(position = 'support')
  1266
+    vt = bill_votes.count
  1267
+    if vt == 0
  1268
+      result = nil
  1269
+    else
  1270
+      bs = users_at_position('support')
  1271
+      bo = users_at_position('oppose')
  1272
+      result = ((position == 'support' ? bs.to_f : bo.to_f) / vt) * 100
  1273
+      result = result.round
  1274
+    end
  1275
+  end
  1276
+
1240 1277
   def as_json(ops = {})
1241 1278
     super(stylize_serialization(ops))
1242 1279
   end
2  app/models/bill_text_node.rb
@@ -3,6 +3,8 @@ class BillTextNode < ActiveRecord::Base
3 3
 
4 4
   has_many :comments, :as => :commentable
5 5
   
  6
+  attr_accessor :bill_text_cache
  7
+  
6 8
   def display_object_name
7 9
     "Bill Text"
8 10
   end
4  app/models/bill_text_version.rb
@@ -82,10 +82,10 @@ def pretty_version
82 82
   end
83 83
   
84 84
   def top_comment_nodes(limit = 3)
85  
-    BillTextNode.find_by_sql(["SELECT bill_text_nodes.id, bill_text_nodes.nid, count(comments.id) as comment_count
  85
+    BillTextNode.find_by_sql(["SELECT bill_text_nodes.id, bill_text_nodes.nid, bill_text_nodes.bill_text_version_id, count(comments.id) as comment_count
86 86
                   FROM bill_text_nodes INNER JOIN comments ON bill_text_nodes.id=comments.commentable_id 
87 87
                   WHERE bill_text_nodes.bill_text_version_id=? AND comments.commentable_type='BillTextNode' 
88  
-                  GROUP BY bill_text_nodes.id, bill_text_nodes.nid ORDER BY count(comments.id) DESC LIMIT ?;", self.id, limit])
  88
+                  GROUP BY bill_text_nodes.id, bill_text_nodes.nid, bill_text_nodes.bill_text_version_id ORDER BY count(comments.id) DESC LIMIT ?;", self.id, limit])
89 89
   end
90 90
   
91 91
 end
14  app/models/contact_congress_letter.rb
... ...
@@ -0,0 +1,14 @@
  1
+class ContactCongressLetter < ActiveRecord::Base 
  2
+  has_many :formageddon_threads, :through => :contact_congress_letters_formageddon_threads, :class_name => 'Formageddon::FormageddonThread'
  3
+  has_many :contact_congress_letters_formageddon_threads
  4
+  
  5
+  belongs_to :bill
  6
+  belongs_to :user
  7
+  
  8
+  has_many :comments, :as => :commentable
  9
+  
  10
+  def to_param
  11
+    subject = formageddon_threads.first.formageddon_letters.first.subject
  12
+    subject.blank? ? "#{id}" : "#{id}-#{subject.gsub(/[^a-z0-9]+/i, '-')}"
  13
+  end
  14
+end
4  app/models/contact_congress_letters_formageddon_thread.rb
... ...
@@ -0,0 +1,4 @@
  1
+class ContactCongressLettersFormageddonThread < ActiveRecord::Base  
  2
+  belongs_to :formageddon_thread
  3
+  belongs_to :contact_congress_letter
  4
+end
21  app/models/crp_interest_group.rb
@@ -21,4 +21,25 @@ def top_recipients(chamber = 'house', num = 10, cycle = Settings.current_opensec
21 21
      ORDER BY contrib_total DESC
22 22
      LIMIT ?", osid, Settings.current_opensecrets_cycle, osid, Settings.current_opensecrets_cycle, title, num])
23 23
   end
  24
+  
  25
+  def contrib_for_person(person)
  26
+    p = Person.find_by_sql(["SELECT people.*, top_recips_ind.ind_contrib_total, top_recips_pac.pac_contrib_total, (COALESCE(top_recips_ind.ind_contrib_total, 0) + COALESCE(top_recips_pac.pac_contrib_total, 0)) AS contrib_total FROM people
  27
+      LEFT JOIN 
  28
+        (SELECT recipient_osid, SUM(crp_contrib_individual_to_candidate.amount) as ind_contrib_total 
  29
+         FROM crp_contrib_individual_to_candidate
  30
+         WHERE crp_interest_group_osid=? AND cycle=? AND recipient_osid=? AND crp_contrib_individual_to_candidate.contrib_type IN ('10', '11', '15 ', '15', '15E', '15J', '22Y')
  31
+         GROUP BY recipient_osid) 
  32
+        top_recips_ind ON people.osid=top_recips_ind.recipient_osid
  33
+      LEFT JOIN
  34
+        (SELECT recipient_osid, SUM(crp_contrib_pac_to_candidate.amount) as pac_contrib_total 
  35
+         FROM crp_contrib_pac_to_candidate
  36
+         WHERE crp_contrib_pac_to_candidate.crp_interest_group_osid=? AND crp_contrib_pac_to_candidate.cycle=? AND crp_contrib_pac_to_candidate.recipient_osid=?
  37
+         GROUP BY crp_contrib_pac_to_candidate.recipient_osid) 
  38
+        top_recips_pac ON people.osid=top_recips_pac.recipient_osid
  39
+     WHERE people.id=?
  40
+     ORDER BY contrib_total DESC
  41
+     LIMIT 1", osid, Settings.current_opensecrets_cycle, person.osid, osid, Settings.current_opensecrets_cycle, person.osid, person.id])
  42
+     
  43
+     return p.first.nil? ? 0 : p.first.attributes['contrib_total'].to_i
  44
+  end
24 45
 end
3  app/models/district.rb
@@ -127,7 +127,6 @@ def tracking_suggestions
127 127
     end
128 128
   end
129 129
 
130  
-
131 130
   def ordinalized_number
132 131
     if self.district_number == 0
133 132
       "At Large"
@@ -135,7 +134,7 @@ def ordinalized_number
135 134
       district_number.ordinalize
136 135
     end
137 136
   end
138  
-  
  137
+
139 138
   def district_state_text
140 139
     self.state.abbreviation + "-" + self.district_number.to_s
141 140
   end
35  app/models/person.rb
@@ -52,6 +52,8 @@ class Person < ViewableObject
52 52
   
53 53
 #  acts_as_bookmarkable
54 54
 
  55
+  acts_as_formageddon_recipient
  56
+  
55 57
   has_one :person_stats, :dependent => :destroy
56 58
   
57 59
   has_one :wiki_link, :as => "wikiable"
@@ -998,6 +1000,10 @@ def consecutive_years
998 1000
     end
999 1001
   end
1000 1002
 
  1003
+  def in_a_valid_district?
  1004
+    (representative? && district != '0')
  1005
+  end
  1006
+
1001 1007
   def parse_facets(facets, primary_facet, selected_facets)
1002 1008
     my_trackers = 0
1003 1009
     facet_results_hsh = {}
@@ -1355,6 +1361,24 @@ def average_approval_state
1355 1361
     average_approval_from_state(self.state)    
1356 1362
   end
1357 1363
 
  1364
+  def contrib_for_interest_group(num = 10, cycle = Settings.current_opensecrets_cycle)
  1365
+    igs = CrpInterestGroup.find_by_sql(["SELECT crp_interest_groups.*, top_ind_igs.ind_contrib_total, top_pac_igs.pac_contrib_total, (COALESCE(top_ind_igs.ind_contrib_total, 0) + COALESCE(top_pac_igs.pac_contrib_total, 0)) AS contrib_total FROM crp_interest_groups
  1366
+    LEFT JOIN
  1367
+      (SELECT crp_interest_group_osid, SUM(crp_contrib_individual_to_candidate.amount)::integer as ind_contrib_total 
  1368
+      FROM crp_contrib_individual_to_candidate
  1369
+      WHERE cycle=? AND recipient_osid=? AND crp_contrib_individual_to_candidate.contrib_type IN ('10', '11', '15 ', '15', '15E', '15J', '22Y')
  1370
+      GROUP BY crp_interest_group_osid)
  1371
+        top_ind_igs ON crp_interest_groups.osid=top_ind_igs.crp_interest_group_osid
  1372
+    LEFT JOIN
  1373
+      (SELECT crp_interest_group_osid, SUM(crp_contrib_pac_to_candidate.amount)::integer as pac_contrib_total 
  1374
+      FROM crp_contrib_pac_to_candidate
  1375
+      WHERE cycle=? AND recipient_osid=?
  1376
+      GROUP BY crp_interest_group_osid)
  1377
+        top_pac_igs ON crp_interest_groups.osid=top_pac_igs.crp_interest_group_osid
  1378
+    ORDER BY contrib_total DESC
  1379
+    LIMIT ?", cycle, osid, cycle, osid, num])
  1380
+  end
  1381
+
1358 1382
   def top_interest_groups(num = 10, cycle = Settings.current_opensecrets_cycle)
1359 1383
     igs = CrpInterestGroup.find_by_sql(["SELECT crp_interest_groups.*, top_ind_igs.ind_contrib_total, top_pac_igs.pac_contrib_total, (COALESCE(top_ind_igs.ind_contrib_total, 0) + COALESCE(top_pac_igs.pac_contrib_total, 0)) AS contrib_total FROM crp_interest_groups
1360 1384
     LEFT JOIN
@@ -1441,6 +1465,10 @@ def contact_link
1441 1465
     end
1442 1466
   end
1443 1467
   
  1468
+  def office_zip
  1469
+    senator? ? "20510" : "20515"
  1470
+  end
  1471
+  
1444 1472
   # expiring the cache
1445 1473
   def fragment_cache_key
1446 1474
     "person_#{id}"
@@ -1501,6 +1529,13 @@ def cleanup_commentaries
1501 1529
     end
1502 1530
     deleted
1503 1531
   end
  1532
+  
  1533
+  def formageddon_display_address
  1534
+    addr = ""
  1535
+    addr += "#{title_long} #{firstname} #{lastname}\n"
  1536
+    addr += "#{congress_office}\n" unless congress_office.blank?
  1537
+    addr += "Washington, DC #{office_zip}\n"
  1538
+  end
1504 1539
 
1505 1540
   SERIALIZATION_OPS = {:methods => [:oc_user_comments, :oc_users_tracking], :include => [:recent_news, :recent_blogs]}.freeze
1506 1541
 
8  app/models/state.rb
@@ -132,6 +132,14 @@ def self.make_download_script
132 132
     end
133 133
   end
134 134
   
  135
+  def available_in_og?
  136
+    ['CA','LA','MD','WI','MN','TX'].include?(abbreviation)
  137
+  end
  138
+  
  139
+  def og_link
  140
+    'http://' + abbreviation.downcase + '.opengovernment.org'
  141
+  end
  142
+  
135 143
   def m_thumb_path
136 144
     "/images/states/thumbs_250/#{self.image_name}"
137 145
   end
11  app/models/user.rb
@@ -166,6 +166,10 @@ def total_number_of_actions
166 166
     self.comments.count + self.friends.count + self.bill_votes.count + self.person_approvals.count + self.bookmarks.count
167 167
   end
168 168
 
  169
+  def state
  170
+    my_state.empty? ? nil : my_state.first
  171
+  end
  172
+  
169 173
   def my_state
170 174
     ZipcodeDistrict.zip_lookup(self.zipcode, self.zip_four).collect {|p| p.state}.uniq
171 175
   end
@@ -761,6 +765,10 @@ def self.fix_duplicate_users
761 765
 
762 766
    end
763 767
 
  768
+   def facebook_connect_user?
  769
+     !facebook_uid.blank?
  770
+   end
  771
+   
764 772
    protected
765 773
 
766 774
    def make_password_reset_code
@@ -788,12 +796,13 @@ def make_privacy_options
788 796
    end
789 797
 
790 798
    def password_required?
791  
-     !openid? && ( crypted_password.blank? || !password.blank? )
  799
+     !openid? && !facebook_connect_user? && ( crypted_password.blank? || !password.blank? )
792 800
    end
793 801
 
794 802
    def openid?
795 803
     !identity_url.blank?
796 804
    end
  805
+  
797 806
    
798 807
    private
799 808
    def cache_district_and_state
2  app/models/user_observer.rb
... ...
@@ -1,6 +1,6 @@
1 1
 class UserObserver < ActiveRecord::Observer
2 2
   def after_create(user)
3  
-    UserNotifier.deliver_signup_notification(user)
  3
+    UserNotifier.deliver_signup_notification(user) unless user.facebook_connect_user?
4 4
   end
5 5
 
6 6
   def after_save(user)
33  app/views/account/facebook_complete.html.haml
... ...
@@ -0,0 +1,33 @@
  1
+#heading
  2
+  %h2 Facebook Connect
  3
+  
  4
+.padding
  5
+  %h2 Almost Done!
  6
+  %p.behave We just need a little more info to create and link your OpenCongress account.
  7
+  %ul
  8
+    %li 
  9
+      %strong Create a user name.
  10
+      This is what will show up when you leave comments and track bills.
  11
+    %li 
  12
+      %strong Enter your zipcode.
  13
+      We'll use this to figure out who is representing you in Congress
  14
+    %li
  15
+      %strong Accept our Terms of Service
  16
+      
  17
+  = form_for @user, :url => { :action => 'facebook_complete' } do |f|
  18
+    = render "shared/error_messages", :target => @user
  19
+  
  20
+    %p
  21
+      = f.label :login, 'Username:'
  22
+      = f.text_field :login
  23
+  
  24
+    %p
  25
+      = f.label :zipcode, 'Zipcode:'
  26
+      = f.text_field :zipcode
  27
+  
  28
+    %p
  29
+      = f.check_box :accepted_tos
  30
+      I agree to the <a href="/about/terms_of_service">Terms of Service</a> and <a href="/about/privacy_policy">Privacy Policy.</a>
  31
+  
  32
+    %p
  33
+      = f.submit 'Connect', :class => 'large button silver'
69  app/views/account/login.html.erb
... ...
@@ -1,69 +0,0 @@
1  
-<% @page_title = "Login" -%>
2  
-
3  
-<div id="heading">
4  
-<h2>Please Login or Register for a Free Account</h2>
5  
-</div>
6  
-
7  
-<div class="padding">
8  
-
9  
-<div id="login-left" class="floatleft padding paddingtopnone">
10  
-
11  
-<h3>I have an account:</h3>
12  
-
13  
-<%= form_for :user do |f| %>
14  
-
15  
-<div title="Account login" id="loginform" class="floatleft form">
16  
-    
17  
-    <p><label for="user_login">Login:</label>
18  
-    <%= f.text_field :login, :size => 30 %>
19