open_porch 0.5.0
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/Gemfile +41 -0
- data/Gemfile.lock +130 -0
- data/README.md +170 -0
- data/Rakefile +19 -0
- data/VERSION +1 -0
- data/app/controllers/admin/areas/base_controller.rb +11 -0
- data/app/controllers/admin/areas/issues_controller.rb +67 -0
- data/app/controllers/admin/areas/memberships_controller.rb +7 -0
- data/app/controllers/admin/areas/posts_controller.rb +44 -0
- data/app/controllers/admin/areas_controller.rb +89 -0
- data/app/controllers/admin/base_controller.rb +16 -0
- data/app/controllers/admin/user_activity_controller.rb +29 -0
- data/app/controllers/admin/users_controller.rb +57 -0
- data/app/controllers/application_controller.rb +15 -0
- data/app/controllers/areas/base_controller.rb +25 -0
- data/app/controllers/areas/issues_controller.rb +35 -0
- data/app/controllers/areas/posts_controller.rb +28 -0
- data/app/controllers/areas_controller.rb +30 -0
- data/app/controllers/passwords_controller.rb +42 -0
- data/app/controllers/registrations_controller.rb +42 -0
- data/app/controllers/sessions_controller.rb +33 -0
- data/app/controllers/users_controller.rb +77 -0
- data/app/helpers/open_porch_helper.rb +24 -0
- data/app/mailers/user_mailer.rb +35 -0
- data/app/models/address.rb +57 -0
- data/app/models/area.rb +171 -0
- data/app/models/area_activity.rb +24 -0
- data/app/models/email_message.rb +104 -0
- data/app/models/issue.rb +66 -0
- data/app/models/issue_number.rb +22 -0
- data/app/models/membership.rb +27 -0
- data/app/models/post.rb +64 -0
- data/app/models/session_user.rb +69 -0
- data/app/models/user.rb +140 -0
- data/app/models/user_activity.rb +31 -0
- data/app/models/user_authority_check.rb +14 -0
- data/app/views/admin/areas/_form.html.haml +8 -0
- data/app/views/admin/areas/_nav.html.haml +12 -0
- data/app/views/admin/areas/edit.html.haml +22 -0
- data/app/views/admin/areas/edit_borders.html.haml +44 -0
- data/app/views/admin/areas/index.html.haml +61 -0
- data/app/views/admin/areas/issues/_post.html.haml +15 -0
- data/app/views/admin/areas/issues/add_posts.js.rjs +3 -0
- data/app/views/admin/areas/issues/edit.html.haml +37 -0
- data/app/views/admin/areas/issues/index.html.haml +31 -0
- data/app/views/admin/areas/issues/remove_posts.js.rjs +3 -0
- data/app/views/admin/areas/issues/show.html.haml +13 -0
- data/app/views/admin/areas/memberships/index.html.haml +17 -0
- data/app/views/admin/areas/new.html.haml +6 -0
- data/app/views/admin/areas/new.js.haml +4 -0
- data/app/views/admin/areas/posts/_edit.html.haml +6 -0
- data/app/views/admin/areas/posts/_post_status.html.haml +1 -0
- data/app/views/admin/areas/posts/destroy.js.rjs +1 -0
- data/app/views/admin/areas/posts/edit.js.rjs +1 -0
- data/app/views/admin/areas/posts/show.js.rjs +1 -0
- data/app/views/admin/areas/posts/toggle_reviewed_by.js.rjs +1 -0
- data/app/views/admin/areas/posts/update.js.rjs +5 -0
- data/app/views/admin/areas/show.html.haml +5 -0
- data/app/views/admin/user_activity/show.html.haml +5 -0
- data/app/views/admin/users/_form.html.haml +21 -0
- data/app/views/admin/users/edit.html.haml +5 -0
- data/app/views/admin/users/index.html.haml +31 -0
- data/app/views/admin/users/new.html.haml +5 -0
- data/app/views/areas/issues/index.html.haml +31 -0
- data/app/views/areas/issues/show.html.haml +22 -0
- data/app/views/areas/posts/_post.html.haml +8 -0
- data/app/views/areas/posts/_posts_search_form.html.haml +8 -0
- data/app/views/areas/posts/index.html.haml +27 -0
- data/app/views/areas/posts/new.html.haml +10 -0
- data/app/views/areas/show.html.haml +47 -0
- data/app/views/layouts/_account_nav.html.haml +18 -0
- data/app/views/layouts/_flash_message.html.haml +4 -0
- data/app/views/layouts/_footer.html.haml +9 -0
- data/app/views/layouts/_head.html.haml +16 -0
- data/app/views/layouts/admin/_nav.html.haml +13 -0
- data/app/views/layouts/admin.html.haml +15 -0
- data/app/views/layouts/application.html.haml +14 -0
- data/app/views/layouts/area_editor.html.haml +11 -0
- data/app/views/passwords/edit.html.haml +9 -0
- data/app/views/passwords/new.html.haml +13 -0
- data/app/views/registrations/_address_form.html.haml +7 -0
- data/app/views/registrations/create.html.haml +49 -0
- data/app/views/registrations/index.html.haml +30 -0
- data/app/views/registrations/new.html.haml +17 -0
- data/app/views/sessions/new.html.haml +18 -0
- data/app/views/stylesheets/common.sass +239 -0
- data/app/views/stylesheets/content.sass +193 -0
- data/app/views/stylesheets/reset.sass +46 -0
- data/app/views/stylesheets/structure.sass +11 -0
- data/app/views/stylesheets/typography.sass +57 -0
- data/app/views/user_mailer/email_verification.html.erb +5 -0
- data/app/views/user_mailer/email_verification.text.erb +7 -0
- data/app/views/user_mailer/new_issue.html.erb +32 -0
- data/app/views/user_mailer/new_issue.text.erb +26 -0
- data/app/views/user_mailer/password_reset.html.erb +7 -0
- data/app/views/user_mailer/password_reset.text.erb +6 -0
- data/app/views/users/_form.html.haml +5 -0
- data/app/views/users/edit.html.haml +11 -0
- data/app/views/users/new.html.haml +41 -0
- data/app/views/users/show.html.haml +30 -0
- data/bin/open_porch_engine +135 -0
- data/config/application.rb +43 -0
- data/config/boot.rb +13 -0
- data/config/database_example.yml +59 -0
- data/config/environment.rb +5 -0
- data/config/environments/development.rb +26 -0
- data/config/environments/production.rb +49 -0
- data/config/environments/test.rb +35 -0
- data/config/initializers/backtrace_silencers.rb +7 -0
- data/config/initializers/email_regex.rb +38 -0
- data/config/initializers/geokit_config.rb +61 -0
- data/config/initializers/inflections.rb +20 -0
- data/config/initializers/meta_search.rb +7 -0
- data/config/initializers/mime_types.rb +5 -0
- data/config/initializers/open_porch.rb +41 -0
- data/config/initializers/sass.rb +1 -0
- data/config/initializers/secret_token.rb +7 -0
- data/config/initializers/session_store.rb +8 -0
- data/config/initializers/states_provinces.rb +2 -0
- data/config/initializers/will_paginate.rb +2 -0
- data/config/locales/en.yml +5 -0
- data/config/open_porch_example.yml +23 -0
- data/config/routes.rb +54 -0
- data/config/schedule.rb +9 -0
- data/config.ru +4 -0
- data/db/migrate/01_create_areas.rb +21 -0
- data/db/migrate/02_create_users.rb +28 -0
- data/db/migrate/03_create_memberships.rb +14 -0
- data/db/migrate/04_create_posts.rb +20 -0
- data/db/migrate/05_create_issue_numbers.rb +13 -0
- data/db/migrate/06_create_issues.rb +21 -0
- data/db/migrate/20110204173301_add_published_to_areas.rb +10 -0
- data/db/migrate/20110204194840_create_user_activities.rb +13 -0
- data/db/migrate/20110208163604_add_zip_to_areas.rb +11 -0
- data/db/migrate/20110209175723_add_counters_to_areas.rb +11 -0
- data/db/migrate/20110209182244_remove_subject_from_issues.rb +9 -0
- data/db/migrate/20110209190146_add_reviewer_info_to_posts.rb +9 -0
- data/db/migrate/20110215173144_add_email_validation_key_to_users.rb +13 -0
- data/db/migrate/20110215182716_remove_published_from_areas.rb +10 -0
- data/db/migrate/20110215211012_create_area_activities.rb +19 -0
- data/db/migrate/20110215213802_create_email_messages.rb +19 -0
- data/db/migrate/20110217165018_change_send_mode_to_string.rb +17 -0
- data/db/migrate/20110223160609_denormalize_user_info_in_posts.rb +19 -0
- data/db/seeds.rb +7 -0
- data/doc/README_FOR_APP +2 -0
- data/lib/generators/open_porch_generator.rb +37 -0
- data/lib/open_porch/engine.rb +20 -0
- data/lib/open_porch.rb +3 -0
- data/lib/tasks/.gitkeep +0 -0
- data/lib/tasks/open_porch.rake +10 -0
- data/lib/tasks/postageapp_tasks.rake +78 -0
- data/open_porch.gemspec +335 -0
- data/public/404.html +26 -0
- data/public/422.html +26 -0
- data/public/500.html +26 -0
- data/public/favicon.ico +0 -0
- data/public/images/icons/ajax-loader.gif +0 -0
- data/public/images/icons/calendar.png +0 -0
- data/public/images/icons/post_new.png +0 -0
- data/public/images/icons/post_reviewed.png +0 -0
- data/public/javascripts/application.js +3 -0
- data/public/javascripts/google_maps.js +153 -0
- data/public/javascripts/highcharts.js +162 -0
- data/public/javascripts/highcharts_init.js +30 -0
- data/public/javascripts/issue_edit.js +57 -0
- data/public/javascripts/jquery-ui.min.js +191 -0
- data/public/javascripts/jquery.js +154 -0
- data/public/javascripts/rails.js +137 -0
- data/public/javascripts/region_editor.js +616 -0
- data/public/javascripts/user_activity.js +29 -0
- data/public/robots.txt +5 -0
- data/public/stylesheets/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/public/stylesheets/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/public/stylesheets/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/public/stylesheets/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/public/stylesheets/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/public/stylesheets/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/public/stylesheets/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/public/stylesheets/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/public/stylesheets/images/ui-icons_222222_256x240.png +0 -0
- data/public/stylesheets/images/ui-icons_2e83ff_256x240.png +0 -0
- data/public/stylesheets/images/ui-icons_454545_256x240.png +0 -0
- data/public/stylesheets/images/ui-icons_888888_256x240.png +0 -0
- data/public/stylesheets/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/public/stylesheets/jquery-ui.css +362 -0
- data/script/rails +6 -0
- data/test/dummy/area.rb +5 -0
- data/test/dummy/area_activity.rb +7 -0
- data/test/dummy/issue.rb +4 -0
- data/test/dummy/membership.rb +4 -0
- data/test/dummy/post.rb +12 -0
- data/test/dummy/user.rb +29 -0
- data/test/dummy/user_activity.rb +11 -0
- data/test/functional/admin/areas/issues_controller_test.rb +48 -0
- data/test/functional/admin/areas/memberships_controller_test.rb +45 -0
- data/test/functional/admin/areas/posts_controller_test.rb +54 -0
- data/test/functional/admin/areas_controller_test.rb +134 -0
- data/test/functional/admin/base_controller_test.rb +8 -0
- data/test/functional/admin/user_activity_controller_test.rb +28 -0
- data/test/functional/admin/users_controller_test.rb +144 -0
- data/test/functional/areas/issues_controller_test.rb +33 -0
- data/test/functional/areas/posts_controller_test.rb +50 -0
- data/test/functional/areas_controller_test.rb +12 -0
- data/test/functional/passwords_controller_test.rb +64 -0
- data/test/functional/registrations_controller_test.rb +64 -0
- data/test/functional/sessions_controller_test.rb +120 -0
- data/test/functional/users_controller_test.rb +144 -0
- data/test/performance/browsing_test.rb +9 -0
- data/test/test_helper.rb +92 -0
- data/test/unit/address_test.rb +25 -0
- data/test/unit/area_activity_test.rb +57 -0
- data/test/unit/area_test.rb +92 -0
- data/test/unit/email_message_test.rb +8 -0
- data/test/unit/issue_number_test.rb +25 -0
- data/test/unit/issue_test.rb +154 -0
- data/test/unit/membership_test.rb +20 -0
- data/test/unit/post_test.rb +69 -0
- data/test/unit/session_user_test.rb +65 -0
- data/test/unit/user_activity_test.rb +31 -0
- data/test/unit/user_mailer_test.rb +20 -0
- data/test/unit/user_test.rb +61 -0
- data/vendor/plugins/.gitkeep +0 -0
- metadata +456 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class ApplicationController < ActionController::Base
|
|
2
|
+
protect_from_forgery
|
|
3
|
+
before_filter :login_from_cookie
|
|
4
|
+
before_filter :login_required
|
|
5
|
+
|
|
6
|
+
protected
|
|
7
|
+
def login_required
|
|
8
|
+
redirect_to login_path unless logged_in?
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def redirect_if_logged_in
|
|
12
|
+
redirect_to area_path(current_user.areas.first) if logged_in?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
class Areas::BaseController < ApplicationController
|
|
2
|
+
before_filter :load_area
|
|
3
|
+
before_filter :initialize_search
|
|
4
|
+
|
|
5
|
+
protected
|
|
6
|
+
def load_area
|
|
7
|
+
if current_user.is_admin?
|
|
8
|
+
@area = Area.find(params[:area_id] || params[:id])
|
|
9
|
+
else
|
|
10
|
+
@area = current_user.areas.find(params[:area_id] || params[:id])
|
|
11
|
+
end
|
|
12
|
+
rescue ActiveRecord::RecordNotFound
|
|
13
|
+
flash[:alert] = "The area you were looking for was not found"
|
|
14
|
+
if current_user.areas.empty?
|
|
15
|
+
redirect_to user_path
|
|
16
|
+
else
|
|
17
|
+
redirect_to area_path(current_user.areas.first)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize_search
|
|
22
|
+
params[:search] ||= {}
|
|
23
|
+
@search = @area.posts.search(params[:search])
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
class Areas::IssuesController < Areas::BaseController
|
|
2
|
+
before_filter :load_issue,
|
|
3
|
+
:only => :show
|
|
4
|
+
|
|
5
|
+
def index
|
|
6
|
+
if params[:year].present? and params[:month].present?
|
|
7
|
+
@current_month = Time.parse([params[:year], params[:month], '01'].join('-')).to_date
|
|
8
|
+
else
|
|
9
|
+
@current_month = Date.today.beginning_of_month
|
|
10
|
+
end
|
|
11
|
+
@issues = @area.issues.sent.in_month(@current_month.strftime("%Y-%m")).order('sent_at DESC')
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def show
|
|
15
|
+
@prev_issue = @area.issues.find_by_number(@issue.number-1)
|
|
16
|
+
@next_issue = @area.issues.find_by_number(@issue.number+1)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def current
|
|
20
|
+
@issue = @area.issues.sent.last
|
|
21
|
+
if @issue.present?
|
|
22
|
+
render :template => 'areas/issues/show'
|
|
23
|
+
else
|
|
24
|
+
render :text => 'Issue not found', :status => 404
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
protected
|
|
29
|
+
def load_issue
|
|
30
|
+
@issue = @area.issues.sent.find_by_number!(params[:id])
|
|
31
|
+
rescue ActiveRecord::RecordNotFound
|
|
32
|
+
render :text => 'Issue not found', :status => 404
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
class Areas::PostsController < Areas::BaseController
|
|
2
|
+
before_filter :build_post,
|
|
3
|
+
:only => [:new, :create]
|
|
4
|
+
|
|
5
|
+
def index
|
|
6
|
+
params[:search] ||= {}
|
|
7
|
+
@search = @area.posts.order('created_at DESC').joins(:issue).where('sent_at IS NOT NULL').search(params[:search])
|
|
8
|
+
@posts = @search.paginate(:page => params[:page])
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create
|
|
15
|
+
@post.save!
|
|
16
|
+
redirect_to(area_path(@area), :notice => 'Your message has been successfully posted.')
|
|
17
|
+
rescue ActiveRecord::RecordInvalid
|
|
18
|
+
render :action => :new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
protected
|
|
22
|
+
|
|
23
|
+
def build_post
|
|
24
|
+
@post = @area.posts.new(params[:post])
|
|
25
|
+
@post.user = current_user
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
class AreasController < Areas::BaseController
|
|
2
|
+
skip_before_filter :login_required, :only => :show
|
|
3
|
+
before_filter :load_area, :only => :show
|
|
4
|
+
before_filter :initialize_search
|
|
5
|
+
|
|
6
|
+
def show
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
protected
|
|
10
|
+
def load_area
|
|
11
|
+
if logged_in?
|
|
12
|
+
if current_user.is_admin?
|
|
13
|
+
@area = Area.find(params[:id])
|
|
14
|
+
else
|
|
15
|
+
@area = current_user.areas.find(params[:id])
|
|
16
|
+
end
|
|
17
|
+
else
|
|
18
|
+
@session_user = SessionUser.new
|
|
19
|
+
@area = Area.find(params[:id])
|
|
20
|
+
end
|
|
21
|
+
rescue ActiveRecord::RecordNotFound
|
|
22
|
+
flash[:alert] = "The area you were looking for was not found"
|
|
23
|
+
if current_user.areas.empty?
|
|
24
|
+
redirect_to user_path
|
|
25
|
+
else
|
|
26
|
+
redirect_to area_path(current_user.areas.first)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
class PasswordsController < ApplicationController
|
|
2
|
+
skip_before_filter :login_required
|
|
3
|
+
before_filter :redirect_if_logged_in, :only => [ :new, :create, :edit, :update ]
|
|
4
|
+
before_filter :load_user_by_perishable_token, :only => [:edit, :update]
|
|
5
|
+
|
|
6
|
+
def new
|
|
7
|
+
# ...
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def create
|
|
11
|
+
user = User.find_by_email!(params[:email])
|
|
12
|
+
user.reset_perishable_token!
|
|
13
|
+
UserMailer.password_reset(user).deliver
|
|
14
|
+
flash[:notice] = 'Email to reset password successfully sent.'
|
|
15
|
+
redirect_to login_path
|
|
16
|
+
rescue ActiveRecord::RecordNotFound
|
|
17
|
+
flash.now[:alert] = 'Your email was not found. Did you mistype?'
|
|
18
|
+
render :action => :new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def edit
|
|
22
|
+
# ...
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def update
|
|
26
|
+
@user.update_attributes!(params[:user].merge(:perishable_token => nil))
|
|
27
|
+
login_as_user(@user)
|
|
28
|
+
flash[:notice] = 'Your password was successfully updated!'
|
|
29
|
+
redirect_to user_path(@user)
|
|
30
|
+
rescue ActiveRecord::RecordInvalid
|
|
31
|
+
flash[:notice] = 'An error occurred. Please try again.'
|
|
32
|
+
redirect_to login_path
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
protected
|
|
37
|
+
def load_user_by_perishable_token
|
|
38
|
+
@user = User.find_by_perishable_token!(params[:id])
|
|
39
|
+
rescue ActiveRecord::RecordNotFound
|
|
40
|
+
redirect_to login_path
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
class RegistrationsController < ApplicationController
|
|
2
|
+
skip_before_filter :login_required
|
|
3
|
+
before_filter :redirect_if_logged_in
|
|
4
|
+
before_filter :build_address, :only => [:new, :index, :create]
|
|
5
|
+
|
|
6
|
+
def index
|
|
7
|
+
# ...
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def new
|
|
11
|
+
# ...
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create
|
|
15
|
+
if @address.valid?
|
|
16
|
+
@areas = @address.closest_regions
|
|
17
|
+
if @areas.empty?
|
|
18
|
+
flash[:alert] = "Sorry, we couldn't find any neighbourhoods close to you!"
|
|
19
|
+
redirect_to root_path
|
|
20
|
+
else
|
|
21
|
+
@selected_area = @areas.shift
|
|
22
|
+
@address.area_id = @selected_area.id
|
|
23
|
+
|
|
24
|
+
@user = User.new(:address_attributes => @address)
|
|
25
|
+
@user.memberships.build(:area_id => @selected_area.id)
|
|
26
|
+
end
|
|
27
|
+
else
|
|
28
|
+
flash[:alert] = "Please enter a valid address"
|
|
29
|
+
redirect_to root_path
|
|
30
|
+
end
|
|
31
|
+
rescue ActiveRecord::StatementInvalid
|
|
32
|
+
flash[:alert] = "Sorry, we couldn't find the address you specified"
|
|
33
|
+
redirect_to root_path
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
protected
|
|
38
|
+
def build_address
|
|
39
|
+
@address = Address.new(params[:address])
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
class SessionsController < ApplicationController
|
|
2
|
+
skip_before_filter :login_required, :except => :destroy
|
|
3
|
+
before_filter :redirect_if_logged_in, :only => [ :new, :create ]
|
|
4
|
+
before_filter :build_user_session, :only => [ :new, :create ]
|
|
5
|
+
|
|
6
|
+
def create
|
|
7
|
+
if @session_user.save
|
|
8
|
+
login(@session_user)
|
|
9
|
+
if current_user.areas.empty?
|
|
10
|
+
redirect_to(user_path, :notice => "Welcome, you are now logged in.")
|
|
11
|
+
else
|
|
12
|
+
redirect_to(area_path(current_user.areas.first), :notice => "Welcome, you are now logged in.")
|
|
13
|
+
end
|
|
14
|
+
else
|
|
15
|
+
flash.now[:alert] = 'Login failed. Did you mistype?'
|
|
16
|
+
render :action => :new
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def destroy
|
|
21
|
+
logout
|
|
22
|
+
redirect_to root_path
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
protected
|
|
26
|
+
def build_user_session
|
|
27
|
+
@session_user = SessionUser.new(params[:session_user])
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def redirect_if_logged_in
|
|
31
|
+
redirect_to user_path if logged_in?
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
class UsersController < ApplicationController
|
|
2
|
+
skip_before_filter :login_required, :only => [:new, :create, :verify_email, :resend_email_verification]
|
|
3
|
+
before_filter :redirect_if_logged_in, :only => [ :new, :create, :verify_email, :resend_email_verification ]
|
|
4
|
+
before_filter :build_user, :only => [:new, :create]
|
|
5
|
+
before_filter :load_user, :only => [:edit, :update, :destroy]
|
|
6
|
+
|
|
7
|
+
def new
|
|
8
|
+
# ...
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def create
|
|
12
|
+
@user.set_email_verification_key
|
|
13
|
+
@user.save!
|
|
14
|
+
UserMailer::email_verification(@user).deliver
|
|
15
|
+
redirect_to root_path, :notice => "Thank you, please check your email to complete the registration."
|
|
16
|
+
rescue ActiveRecord::RecordInvalid
|
|
17
|
+
render :action => :new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def edit; end
|
|
21
|
+
|
|
22
|
+
def update
|
|
23
|
+
@user.update_attributes!(params[:user])
|
|
24
|
+
redirect_to(user_path, :notice => 'Your profile has been updated')
|
|
25
|
+
rescue ActiveRecord::RecordInvalid
|
|
26
|
+
render :action => :edit
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def verify_email
|
|
30
|
+
if @user = User.where(:email_verification_key => params[:email_verification_key]).first
|
|
31
|
+
@user.update_attributes(
|
|
32
|
+
:verified_at => Time.now,
|
|
33
|
+
:email_verification_key => nil
|
|
34
|
+
)
|
|
35
|
+
flash[:notice] = "Your email address has been verified. You can now login"
|
|
36
|
+
redirect_to login_path
|
|
37
|
+
else
|
|
38
|
+
flash[:alert] = "We were not able to verify your account. Please contact us for assistance."
|
|
39
|
+
redirect_to root_path
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def resend_email_verification
|
|
44
|
+
if @user = User.where(:email_verification_key => params[:email_verification_key]).first
|
|
45
|
+
# Just ignore it if the user is already verified
|
|
46
|
+
if @user.is_verified?
|
|
47
|
+
flash[:notice] = "Your account has already been validated. You can now login."
|
|
48
|
+
else
|
|
49
|
+
UserMailer::email_verification(@user).deliver
|
|
50
|
+
flash[:notice] = "The email verification has been sent to #{@user.email}"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
redirect_to login_path
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def destroy
|
|
57
|
+
logout
|
|
58
|
+
@user.destroy
|
|
59
|
+
flash[:alert] = "Your account has been deleted."
|
|
60
|
+
redirect_to root_path
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
protected
|
|
64
|
+
def build_user
|
|
65
|
+
@user = User.new(params[:user])
|
|
66
|
+
if @user.memberships.empty?
|
|
67
|
+
redirect_to root_path
|
|
68
|
+
else
|
|
69
|
+
@area = @user.memberships.first.area
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def load_user
|
|
74
|
+
@user = current_user
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module OpenPorchHelper
|
|
2
|
+
def show_user_activity_for_url(url)
|
|
3
|
+
content_for(:doc_ready) do
|
|
4
|
+
raw %{get_user_activity('#{current_user.full_name}', '#{url}', '#{UserActivity::EXPIRES * 1000 }');} # multiply by 1000 because javascript requires miliseconds
|
|
5
|
+
end
|
|
6
|
+
content_tag(:div, '', :id => "user_activity")
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def update_user_activity_for_url(url)
|
|
10
|
+
content_for(:doc_ready) do
|
|
11
|
+
raw %{update_user_activity('#{current_user.full_name}', '#{url}', '#{(UserActivity::EXPIRES-2) * 1000}');}
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def show_ad_for(zone_name, area)
|
|
16
|
+
return unless defined?(OPEN_PORCH_ZONES)
|
|
17
|
+
time = Time.now.to_i
|
|
18
|
+
|
|
19
|
+
link_to("http://d1.openx.org/ck.php?cb=#{time}&n=a8417ca6", :class => "ad #{zone_name}", :target => '_blank') do
|
|
20
|
+
image_tag("http://d1.openx.org/avw.php?zoneid=#{OPEN_PORCH_ZONES[zone_name.to_s]}®ion=#{@area.slug}&cb=#{time}&n=a8417ca6")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
class UserMailer < PostageApp::Mailer
|
|
2
|
+
default :from => 'open_porch@example.com'
|
|
3
|
+
|
|
4
|
+
if defined?(ActionController::Base)
|
|
5
|
+
default_url_options[:host] = defined?(HOST) ? HOST : 'localhost:3000'
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def password_reset(user)
|
|
9
|
+
postageapp_template 'main_layout'
|
|
10
|
+
@user = user
|
|
11
|
+
mail(
|
|
12
|
+
:to => user.email,
|
|
13
|
+
:subject => "You have requested a new password"
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def new_issue(issue)
|
|
18
|
+
postageapp_template 'main_layout'
|
|
19
|
+
@issue = issue
|
|
20
|
+
mail(
|
|
21
|
+
:subject => "#{issue.number}-#{issue.area.name}",
|
|
22
|
+
:to => issue.area.users.collect(&:email)
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def email_verification(user)
|
|
27
|
+
postageapp_template 'main_layout'
|
|
28
|
+
@user = user
|
|
29
|
+
mail(
|
|
30
|
+
:subject => "Your account has been created. Please verify your email address",
|
|
31
|
+
:to => user.email
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
class Address
|
|
2
|
+
include ActiveModel::Validations
|
|
3
|
+
include GeoKit::Geocoders
|
|
4
|
+
|
|
5
|
+
# == Constants ============================================================
|
|
6
|
+
|
|
7
|
+
# == Attributes =====================================================
|
|
8
|
+
|
|
9
|
+
# == Attribute ==========================================================
|
|
10
|
+
|
|
11
|
+
attr_accessor :address, :city, :state, :area_id
|
|
12
|
+
attr_accessor :location, :lat, :lng
|
|
13
|
+
|
|
14
|
+
# == Validations ==========================================================
|
|
15
|
+
|
|
16
|
+
validates :address, :city, :state,
|
|
17
|
+
:presence => {:message => 'Please enter your address'}
|
|
18
|
+
|
|
19
|
+
# == Instance Methods =====================================================
|
|
20
|
+
|
|
21
|
+
def initialize(params = {})
|
|
22
|
+
if params
|
|
23
|
+
@address = params[:address]
|
|
24
|
+
@city = params[:city]
|
|
25
|
+
@state = params[:state]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_key; end
|
|
30
|
+
|
|
31
|
+
def full
|
|
32
|
+
[self.address, self.city, self.state].join(', ')
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def closest_regions
|
|
36
|
+
location = self.geolocate
|
|
37
|
+
if location.all.length > 1 || location.street_address.nil?
|
|
38
|
+
[]
|
|
39
|
+
else
|
|
40
|
+
point = Point.new()
|
|
41
|
+
point.set_x_y(self.lat, self.lng)
|
|
42
|
+
Area.closest_from(point, 400)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
protected
|
|
47
|
+
|
|
48
|
+
def geolocate
|
|
49
|
+
@location ||= begin
|
|
50
|
+
loc = Geokit::Geocoders::GoogleGeocoder.geocode(self.full)
|
|
51
|
+
self.lat = loc.lat
|
|
52
|
+
self.lng = loc.lng
|
|
53
|
+
loc
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
data/app/models/area.rb
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
class Area < ActiveRecord::Base
|
|
2
|
+
|
|
3
|
+
# == Constants ============================================================
|
|
4
|
+
|
|
5
|
+
SEND_MODES = %w{immediate batched}
|
|
6
|
+
|
|
7
|
+
# == Relationships ========================================================
|
|
8
|
+
|
|
9
|
+
has_many :memberships,
|
|
10
|
+
:dependent => :destroy
|
|
11
|
+
has_many :users,
|
|
12
|
+
:through => :memberships
|
|
13
|
+
has_many :posts,
|
|
14
|
+
:dependent => :destroy
|
|
15
|
+
has_one :issue_number,
|
|
16
|
+
:dependent => :destroy
|
|
17
|
+
has_many :issues,
|
|
18
|
+
:dependent => :destroy
|
|
19
|
+
has_many :activities,
|
|
20
|
+
:dependent => :destroy,
|
|
21
|
+
:class_name => 'AreaActivity'
|
|
22
|
+
|
|
23
|
+
# == Validations ==========================================================
|
|
24
|
+
|
|
25
|
+
validates :name,
|
|
26
|
+
:presence => {:message => 'Please enter the name of this area'}
|
|
27
|
+
validates :slug,
|
|
28
|
+
:uniqueness => true,
|
|
29
|
+
:format => { :with => /^[\w.-]+$/ },
|
|
30
|
+
:allow_nil => true
|
|
31
|
+
validates :send_mode,
|
|
32
|
+
:presence => true,
|
|
33
|
+
:inclusion => SEND_MODES
|
|
34
|
+
|
|
35
|
+
# == Scopes ===============================================================
|
|
36
|
+
|
|
37
|
+
scope :closest_from, lambda {|point, distance|
|
|
38
|
+
where("ST_DWithin(border, ST_GeomFromEWKT('SRID=4326;POINT(#{point.text_representation})'), #{distance})").
|
|
39
|
+
order("ST_Distance(border, ST_GeomFromEWKT('SRID=4326;POINT(#{point.text_representation})'))")
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# Search Scope
|
|
43
|
+
scope :full_name_search,
|
|
44
|
+
lambda {|str|
|
|
45
|
+
like_str = "%#{str}%"
|
|
46
|
+
id = str
|
|
47
|
+
where("((name || ', ' || city || ', ' || state) ILIKE ?)", like_str)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if defined?(MetaSearch)
|
|
51
|
+
search_methods :full_name_search
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# == Callbacks ============================================================
|
|
55
|
+
|
|
56
|
+
before_validation :set_send_mode, :on => :create
|
|
57
|
+
after_create :initialize_issue_numbers
|
|
58
|
+
after_save :check_send_mode_change, :on => :update
|
|
59
|
+
|
|
60
|
+
# == Class Methods ========================================================
|
|
61
|
+
|
|
62
|
+
# Return a hash with the form {area_id => posts.count}
|
|
63
|
+
def self.newposts_count
|
|
64
|
+
connection.select_rows("
|
|
65
|
+
SELECT area_id, COUNT(posts.id)
|
|
66
|
+
FROM posts LEFT JOIN areas ON (posts.area_id = areas.id)
|
|
67
|
+
WHERE issue_id IS NULL
|
|
68
|
+
GROUP BY area_id
|
|
69
|
+
").inject({}){|h, count| h[count[0].to_i] = count[1].to_i; h}
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# == Instance Methods =====================================================
|
|
73
|
+
|
|
74
|
+
def coordinates=(points)
|
|
75
|
+
coords = points.collect{|k, v| [v[0].to_f, v[1].to_f]}
|
|
76
|
+
self.border = Polygon.from_coordinates([coords + [coords.first]])
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# Returns an array with coordinates
|
|
81
|
+
# removing the last (repeated) point
|
|
82
|
+
def to_a
|
|
83
|
+
if self.border.present?
|
|
84
|
+
{
|
|
85
|
+
:id => self.id,
|
|
86
|
+
:name => self.name,
|
|
87
|
+
:points => self.border.first.points[0..-2].collect{|p| [p.x, p.y]}
|
|
88
|
+
}
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Returns a Point object
|
|
93
|
+
def center
|
|
94
|
+
if self.border.present?
|
|
95
|
+
border.envelope.center
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def bounds
|
|
100
|
+
return [] unless self.border.present?
|
|
101
|
+
[
|
|
102
|
+
[self.border.envelope.lower_corner.x, self.border.envelope.lower_corner.y],
|
|
103
|
+
[self.border.envelope.upper_corner.x, self.border.envelope.upper_corner.y]
|
|
104
|
+
]
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def border_coordinates
|
|
108
|
+
@border_coordinates ||= border.rings.first.points.collect{|point| "new google.maps.LatLng(#{point.x}, #{point.y})"}.join(',')
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def location
|
|
112
|
+
[self.city, self.state].compact.join(', ')
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def current_issue
|
|
116
|
+
@current_issue ||= self.issues.where(:sent_at => nil).first
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def send_mode?(mode)
|
|
120
|
+
self.send_mode == mode.to_s
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def record_activity_for!(field)
|
|
124
|
+
field = field.to_s
|
|
125
|
+
if AreaActivity::TRACKABLE.include?(field)
|
|
126
|
+
activity = self.activities.find_or_create_by_day(Time.now.utc.to_date)
|
|
127
|
+
activity.increment!([field, 'count'].join('_'), 1)
|
|
128
|
+
activity.reload
|
|
129
|
+
else
|
|
130
|
+
raise "Cannot find field in the list of trackable fields. Currently tracking: #{AreaActivity::TRACKABLE.join(', ')}"
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def self.for_select
|
|
135
|
+
Area.order('state, city, name').collect{|a| [[a.name, a.city, a.state].join(', '), a.id]}
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def email
|
|
139
|
+
if defined?(OPEN_PORCH_POP3)
|
|
140
|
+
"#{self.slug}@#{OPEN_PORCH_POP3['mailto']}"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
protected
|
|
145
|
+
def initialize_issue_numbers
|
|
146
|
+
self.create_issue_number(:sequence_number => 0)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def set_send_mode
|
|
150
|
+
self.send_mode ||= 'immediate'
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def check_send_mode_change
|
|
154
|
+
# Detach any posts from the current issue
|
|
155
|
+
if self.send_mode_changed? && self.send_mode?(:immediate)
|
|
156
|
+
if self.current_issue
|
|
157
|
+
self.current_issue.posts.each do |post|
|
|
158
|
+
post.update_attribute(:issue_id, nil)
|
|
159
|
+
end
|
|
160
|
+
# Delete the current issue
|
|
161
|
+
self.current_issue.reload
|
|
162
|
+
self.current_issue.destroy
|
|
163
|
+
@current_issue = nil
|
|
164
|
+
end
|
|
165
|
+
# Send any post left over
|
|
166
|
+
self.posts.in_issue(nil).each do |post|
|
|
167
|
+
post.send_immediatelly!
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
class AreaActivity < ActiveRecord::Base
|
|
2
|
+
|
|
3
|
+
TRACKABLE = %w(quitters new_users new_posts issues_published).freeze
|
|
4
|
+
|
|
5
|
+
# == Relationships ========================================================
|
|
6
|
+
|
|
7
|
+
belongs_to :area
|
|
8
|
+
|
|
9
|
+
# == Validations ==========================================================
|
|
10
|
+
|
|
11
|
+
validates :day,
|
|
12
|
+
:presence => true
|
|
13
|
+
validates :area_id,
|
|
14
|
+
:presence => true,
|
|
15
|
+
:uniqueness => {:scope => :day}
|
|
16
|
+
|
|
17
|
+
# == Scopes ===============================================================
|
|
18
|
+
|
|
19
|
+
default_scope order('day ASC')
|
|
20
|
+
|
|
21
|
+
scope :grouped_by_day,
|
|
22
|
+
select([['day'] + TRACKABLE.collect{|a| "sum(#{a}_count) as sum_#{a}_count"}].join(', ')).group(:day)
|
|
23
|
+
|
|
24
|
+
end
|