diff --git a/.gitignore b/.gitignore index dd8c626f3..ead788319 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ .*.sw* *~ *.DS_Store +credentials.sh diff --git a/.travis.yml b/.travis.yml index d350b77ee..0928a1e53 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,25 @@ +--- language: ruby +rvm: +- 1.9.3 script: - - bundle exec rake db:migrate --trace - - bundle exec rspec spec/ +- bundle exec rake db:migrate --trace +- bundle exec rspec spec/ -notifications: - recipients: - - discuss@lists.growstuff.org - email: - on_success: change - on_failure: always - irc: - channels: - - "irc.parrot.org#growstuff" - on_success: change - on_failure: change +# after_success: +# - if [[ "$TRAVIS_BRANCH" == "dev" ]]; then git remote add heroku git@heroku.com:growstuff-dev.git +# - wget -qO- https://toolbelt.heroku.com/install-ubuntu.sh | sh +# - echo "Host heroku.com" >> ~/.ssh/config +# - echo " StrictHostKeyChecking no" >> ~/.ssh/config +# - echo " CheckHostIP no" >> ~/.ssh/config +# - echo " UserKnownHostsFile=/dev/null" >> ~/.ssh/config +# - heroku keys:clear +# - yes | heroku keys:add +# - yes | git push heroku dev:master +# - heroku run rake db:migrate +# - heroku restart +# - fi + +env: + global: + secure: "QFQbCdNGyjeatp/H0j0y0oGiue45fpG2w6eA2QAbq2RmvhabgXbd5WIobN90\ndrae3S7TRxPDpMpus90icykX6EzOTLXCEvaC4rh9pCcRktj3SZqq5b9rVTvs\n1MvlS6HhtsVqsrKjQUb0WmPpnganIzTs0RtGaQspo2joPJO18A4=" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..b43c0d134 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,34 @@ +Thanks for contributing to Growstuff! We have different contribution +guidelines depending on whether your change is a small one (a one-line +bugfix or similar) or a larger one. + +## Small changes (no more than a couple of lines) + +Send us a pull request! We will get one of our pairs of coders to +review it. + +If you are interested in becoming a more regular contributor or working +on larger features, please see the [Growstuff +wiki](http://wiki.growstuff.org/) for more information. + +## Larger changes + +Growstuff does pair programming (two coders working together) for all +significant changes. This means that if you submit a pull request and +weren't working in a pair, we can't merge it into the project as-is. + +If you would like to work on any larger change, we would appreciate it +if you would get in touch with us, preferably via our [mailing +list](http://lists.growstuff.org/mailman/listinfo/discuss), and talk to +us about it first. We'll try and hook you up with a partner so you can +work as a pair, either in person or remotely depending on where you are. +The [Growstuff wiki](http://wiki.growstuff.org/) has lots more +information on our dev process, to get you started if you would like to +join us. + +If you submit a significant change without working in a pair, we will +treat your work as an experimental "spike" and get one of our pairs of +programmers to look over it and maybe use what you've done as the basis +for re-implementing it using our processes. **We'd much rather work with +you, so please talk to us first!** + diff --git a/Gemfile b/Gemfile index 39f3a06dd..3f4318236 100644 --- a/Gemfile +++ b/Gemfile @@ -75,6 +75,12 @@ gem 'geocoder' # For easy calendar selection gem 'bootstrap-datepicker-rails' +# For connecting to other services (eg Twitter) +gem 'omniauth' +gem 'omniauth-twitter' +gem 'omniauth-flickr' +gem 'authbuttons-rails' + # for phusion passenger (i.e. mod_rails) on the server gem 'passenger' gem 'rake', '>= 10.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index a7528ea3a..35ab7f28b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -29,6 +29,7 @@ GEM i18n (= 0.6.1) multi_json (~> 1.0) arel (3.0.2) + authbuttons-rails (0.1.2) bcrypt-ruby (3.0.1) bluecloth (2.2.0) bootstrap-datepicker-rails (1.0.0) @@ -95,6 +96,7 @@ GEM activesupport (>= 3.1, < 4.1) haml (>= 3.1, < 4.1) railties (>= 3.1, < 4.1) + hashie (1.2.0) highline (1.6.16) hike (1.2.1) i18n (0.6.1) @@ -124,8 +126,19 @@ GEM net-ssh-gateway (1.2.0) net-ssh (>= 2.6.5) newrelic_rpm (3.5.8.72) - nokogiri (1.5.7) nokogiri (1.5.8) + oauth (0.4.7) + omniauth (1.1.3) + hashie (~> 1.2) + rack + omniauth-flickr (0.0.11) + omniauth-oauth (~> 1.0) + omniauth-oauth (1.0.1) + oauth + omniauth (~> 1.0) + omniauth-twitter (0.0.16) + multi_json (~> 1.3) + omniauth-oauth (~> 1.0) orm_adapter (0.4.0) passenger (3.0.19) daemon_controller (>= 1.0.0) @@ -220,6 +233,7 @@ PLATFORMS ruby DEPENDENCIES + authbuttons-rails bluecloth bootstrap-datepicker-rails bundler (>= 1.1.5) @@ -242,6 +256,9 @@ DEPENDENCIES json (~> 1.7.7) less-rails newrelic_rpm + omniauth + omniauth-flickr + omniauth-twitter passenger pg rack (~> 1.4.5) diff --git a/app/controllers/about_controller.rb b/app/controllers/about_controller.rb new file mode 100644 index 000000000..f1549add2 --- /dev/null +++ b/app/controllers/about_controller.rb @@ -0,0 +1,2 @@ +class AboutController < ApplicationController +end diff --git a/app/controllers/authentications_controller.rb b/app/controllers/authentications_controller.rb new file mode 100644 index 000000000..96456d996 --- /dev/null +++ b/app/controllers/authentications_controller.rb @@ -0,0 +1,54 @@ +class AuthenticationsController < ApplicationController + # GET /authentications + # GET /authentications.json + def index + @authentications = current_member.authentications if member_signed_in? + + respond_to do |format| + format.html # index.html.erb + format.json { render json: @authentications } + end + end + + # POST /authentications + # POST /authentications.json + def create + auth = request.env['omniauth.auth'] + @authentication = nil + if auth + + name = '' + case auth['provider'] + when 'twitter' + name = auth['info']['nickname'] + when 'flickr' + name = auth['info']['name'] + else + name = auth['info']['name'] + end + + @authentication = current_member.authentications.find_or_create_by_provider_and_uid( + :provider => auth['provider'], + :uid => auth['uid'], + :name => name, + :token => auth['credentials']['token'], + :secret => auth['credentials']['secret']) + flash[:notice] = "Authentication successful." + else + flash[:notice] = "Authentication failed." + end + redirect_to edit_member_registration_path + end + + # DELETE /authentications/1 + # DELETE /authentications/1.json + def destroy + @authentication = Authentication.find(params[:id]) + @authentication.destroy + + respond_to do |format| + format.html { redirect_to edit_member_registration_path } + format.json { head :no_content } + end + end +end diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index 53e453410..d6bc30bd1 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -29,6 +29,7 @@ class CommentsController < ApplicationController @post = Post.find_by_id(params[:post_id]) if @post + @comments = @post.comments respond_to do |format| format.html # new.html.erb format.json { render json: @comment } @@ -42,6 +43,7 @@ class CommentsController < ApplicationController # GET /comments/1/edit def edit @comment = Comment.find(params[:id]) + @comments = @comment.post.comments end # POST /comments diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb index 797ee229d..d05c85f01 100644 --- a/app/controllers/members_controller.rb +++ b/app/controllers/members_controller.rb @@ -11,8 +11,10 @@ class MembersController < ApplicationController end def show - @member = Member.confirmed.find(params[:id]) - @posts = @member.posts + @member = Member.confirmed.find(params[:id]) + @twitter_auth = @member.authentications.find_by_provider('twitter') + @flickr_auth = @member.authentications.find_by_provider('flickr') + @posts = @member.posts # The garden form partial is called from the "New Garden" tab; # it requires a garden to be passed in @garden. # The new garden is not persisted unless Garden#save is called. @@ -35,8 +37,20 @@ class MembersController < ApplicationController else @location = nil end - @distance = 100 - @nearby_members = @location ? Member.near(@location, @distance) : [] + + if !params[:distance].blank? + @distance = params[:distance] + else + @distance = 100 + end + + if params[:units] == "mi" + @units = :mi + else + @units = :km + end + + @nearby_members = @location ? Member.near(@location, @distance, :units => @units) : [] respond_to do |format| format.html # nearby.html.haml format.json { render json: @nearby_members } diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 566ecb94c..e23bebf0c 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -1,8 +1,17 @@ -# we need this subclass so that Devise doesn't force people to change their -# password every time they want to edit their settings. Code copied from -# https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-account-without-providing-a-password class RegistrationsController < Devise::RegistrationsController + + def edit + @twitter_auth = current_member.authentications.find_by_provider('twitter') + @flickr_auth = current_member.authentications.find_by_provider('flickr') + render "edit" + end + +# we need this subclassed method so that Devise doesn't force people to +# change their password every time they want to edit their settings. +# Code copied from +# https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-account-without-providing-a-password + def update # required for settings form to submit when password is left blank if params[:member][:password].blank? @@ -12,11 +21,12 @@ class RegistrationsController < Devise::RegistrationsController end @member = Member.find(current_member.id) + if @member.update_attributes(params[:member]) set_flash_message :notice, :updated # Sign in the member bypassing validation in case his password changed sign_in @member, :bypass => true - redirect_to after_update_path_for(@member) + redirect_to edit_member_registration_path else render "edit" end diff --git a/app/models/ability.rb b/app/models/ability.rb index c5e8c4905..e8ffaf75c 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -8,6 +8,7 @@ class Ability can :read, :all cannot :read, Notification cannot :create, Notification + cannot :read, Authentication # nobody should be able to view this except admins cannot :read, Role @@ -43,6 +44,10 @@ class Ability can :manage, ScientificName end + # can create & destroy their own authentications against other sites. + can :create, Authentication + can :destroy, Authentication, :member_id => member.id + # anyone can create a post, or comment on a post, # but only the author can edit/destroy it. can :create, Post diff --git a/app/models/authentication.rb b/app/models/authentication.rb new file mode 100644 index 000000000..325bffe40 --- /dev/null +++ b/app/models/authentication.rb @@ -0,0 +1,4 @@ +class Authentication < ActiveRecord::Base + belongs_to :member + attr_accessible :provider, :uid, :token, :secret, :name +end diff --git a/app/models/member.rb b/app/models/member.rb index 18396862a..3e3790a46 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -10,6 +10,7 @@ class Member < ActiveRecord::Base has_and_belongs_to_many :roles has_many :notifications, :foreign_key => 'recipient_id' has_many :sent_notifications, :foreign_key => 'sender_id' + has_many :authentications default_scope order("lower(login_name) asc") scope :confirmed, where('confirmed_at IS NOT NULL') diff --git a/app/views/about/contact.html.haml b/app/views/about/contact.html.haml new file mode 100644 index 000000000..c4de94d5c --- /dev/null +++ b/app/views/about/contact.html.haml @@ -0,0 +1,17 @@ +-content_for :title, 'Contact' + +%dl + %dt General contact email + %dd= link_to 'info@growstuff.org', 'mailto:info@growstuff.org' +%dl + %dt + Support and accounts enquiries (not covered by the + =succeed ")" do + =link_to 'FAQ', url_for(:controller => '/support') + %dd= link_to 'support@growstuff.org', 'mailto:support@growstuff.org' +%dl + %dt Media/Press enquiries + %dd= link_to 'media@growstuff.org', 'mailto:media@growstuff.org' +%dl + %dt Twitter + %dd= link_to '@Growstuff', 'http:/twitter.com/Growstuff' diff --git a/app/views/authentications/index.html.haml b/app/views/authentications/index.html.haml new file mode 100644 index 000000000..61cd83250 --- /dev/null +++ b/app/views/authentications/index.html.haml @@ -0,0 +1,22 @@ +%h1 Linked accounts on other sites + +- if @authentications + - unless @authentications.empty? + .authentications + - @authentications.each do |authentication| + %a{ :href => "http://twitter.com/#{authentication.name}" } + .authentication + = image_tag "#{authentication.provider}_64.png", :size => "64x64" + .provider + = authentication.provider.titleize + .name + = authentication.name + = link_to "X", authentication, :confirm => "Are you sure you want to remove this authentication?", :method => :delete, :class => "remove" + .clear + %p + %strong Link another external account: + - else + %p + %strong Link to an external account: + += link_to image_tag("twitter_64.png", :size => "64x64"), "/auth/twitter", :class => "auth_provider" diff --git a/app/views/comments/_form.html.haml b/app/views/comments/_form.html.haml index e023bc968..06b6d0f8b 100644 --- a/app/views/comments/_form.html.haml +++ b/app/views/comments/_form.html.haml @@ -1,5 +1,7 @@ = render :partial => "posts/single", :locals => { :post => @post || @comment.post, :subject => true } += render :partial => "posts/comments" + %h2 Your comment = form_for @comment do |f| - if @comment.errors.any? diff --git a/app/views/crops/_thumbnail.html.haml b/app/views/crops/_thumbnail.html.haml index a6c453a1e..5e4c6cb63 100644 --- a/app/views/crops/_thumbnail.html.haml +++ b/app/views/crops/_thumbnail.html.haml @@ -8,6 +8,10 @@ %i %small = crop.scientific_names.first.scientific_name + %br/ + %small + Planted + = pluralize(crop.plantings_count, "time") - else = image_tag('http://placehold.it/150x150', :alt => '', :class => 'img-rounded') Sample crop diff --git a/app/views/crops/show.html.haml b/app/views/crops/show.html.haml index e3364fa15..3b92068c5 100644 --- a/app/views/crops/show.html.haml +++ b/app/views/crops/show.html.haml @@ -2,11 +2,19 @@ .row .span9 - %p - =link_to "Plant this", new_planting_path(:crop_id => @crop.id), :class => 'btn btn-primary' - %h2 Who's growing this? - - @crop.plantings.each do |p| - = render :partial => "plantings/thumbnail", :locals => { :planting => p, :title => 'owner' } + - if @crop.plantings_count > 0 + %p + Planted + = pluralize(@crop.plantings_count, "time") + by #{Growstuff::Application.config.site_name} members + %p= link_to "Plant this", new_planting_path(:crop_id => @crop.id), :class => 'btn btn-primary' + + - @crop.plantings.each do |p| + = render :partial => "plantings/thumbnail", :locals => { :planting => p, :title => 'owner' } + + - else + %p Nobody is growing this yet. You could be the first! + %p= link_to "Plant this", new_planting_path(:crop_id => @crop.id), :class => 'btn btn-primary' .span3 %h4 Scientific names: diff --git a/app/views/devise/mailer/confirmation_instructions.html.haml b/app/views/devise/mailer/confirmation_instructions.html.haml index 42250d7b9..f336d874e 100644 --- a/app/views/devise/mailer/confirmation_instructions.html.haml +++ b/app/views/devise/mailer/confirmation_instructions.html.haml @@ -5,8 +5,7 @@ Your account on #{site_name} has been created. You just need to confirm your email address through the link below: -%p - = link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) +%p= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %p Once you're confirmed, you can sign in with your login name @@ -17,22 +16,23 @@ %p We're excited to have you as a member, and hope you'll enjoy what #{site_name} has to offer. Take a look around the site, - =link_to 'plant some things', url_for(:controller => '/crops', :only_path => false) - , and feel free to drop in on the - =link_to 'forums', url_for(:controller => '/forums', :only_path => false) + = succeed "," do + = link_to('plant some things', url_for(:controller => '/crops', :only_path => false)) + and feel free to drop in on the + = link_to 'forums', url_for(:controller => '/forums', :only_path => false) if you have any questions or feedback. %p We'd also appreciate it if you'd read our - =link_to 'Community Guidelines', url_for(:controller => '/policy', :action => 'community', :only_path => false) - , and make sure you follow them. We want #{site_name} to be a + = succeed "," do + = link_to 'Community Guidelines', url_for(:controller => '/policy', :action => 'community', :only_path => false) + and make sure you follow them. We want #{site_name} to be a friendly, welcoming environment for everyone, and we hope you'll help us keep it that way. -%p - Looking forward to seeing you! +%p Looking forward to seeing you! %p The #{site_name} team. %br/ - =link_to root_url, root_url + = link_to root_url, root_url diff --git a/app/views/devise/mailer/reset_password_instructions.html.haml b/app/views/devise/mailer/reset_password_instructions.html.haml index 13f3a123a..95aa0ee09 100644 --- a/app/views/devise/mailer/reset_password_instructions.html.haml +++ b/app/views/devise/mailer/reset_password_instructions.html.haml @@ -1,19 +1,21 @@ - site_name = Growstuff::Application.config.site_name %p Hello #{@resource.login_name}, -%p Someone has requested a link to reset your password on #{site_name}. -We presume this was you, in which case you can do so through this link: +%p + Someone has requested a link to reset your password on #{site_name}. + We presume this was you, in which case you can do so through this link: + +%p= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %p - = link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) - -%p If it wasn't you, then someone's made a typo or has been messing -around. In this case, you can safely ignore this email, and your -password will not be changed. + If it wasn't you, then someone's made a typo or has been messing + around. In this case, you can safely ignore this email, and your + password will not be changed. %p See you soon, -%p The #{site_name} team. -%br/ -=link_to root_url, root_url +%p + The #{site_name} team. + %br/ + =link_to root_url, root_url diff --git a/app/views/devise/mailer/unlock_instructions.html.haml b/app/views/devise/mailer/unlock_instructions.html.haml index 7c25dce77..91177fd55 100644 --- a/app/views/devise/mailer/unlock_instructions.html.haml +++ b/app/views/devise/mailer/unlock_instructions.html.haml @@ -10,11 +10,14 @@ %p= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token) -%p If you have actually forgotten your password, you can -= link_to 'reset your password', new_password_url(@resource) -after you've unlocked your account. +%p + If you have actually forgotten your password, you can + = link_to 'reset your password', new_password_url(@resource) + after you've unlocked your account. %p See you soon, -%p The #{site_name} team. -%br/ -=link_to root_url, root_url + +%p + The #{site_name} team. + %br/ + =link_to root_url, root_url diff --git a/app/views/devise/registrations/edit.html.haml b/app/views/devise/registrations/edit.html.haml index 235658336..d8a36e1ce 100644 --- a/app/views/devise/registrations/edit.html.haml +++ b/app/views/devise/registrations/edit.html.haml @@ -25,7 +25,8 @@ %p %br/ To change your profile picture, visit - = link_to 'gravatar.com', "http://gravatar.com/" + = succeed "." do + = link_to 'gravatar.com', "http://gravatar.com/" .control-group =f.label :location, 'Your location', :class => 'control-label' @@ -38,6 +39,28 @@ = f.check_box :show_email Show email publicly on your profile page + %h2 Linked accounts + + .control-group + .controls + %p + = image_tag "twitter_32.png", :size => "32x32", :alt => 'Twitter logo' + - if @twitter_auth + You are connected to Twitter as + = succeed "." do + =link_to @twitter_auth.name, "http://twitter.com/#{@twitter_auth.name}" + = link_to "Disconnect", @twitter_auth, :confirm => "Are you sure you want to remove this connection?", :method => :delete, :class => "remove" + - else + =link_to 'Connect to Twitter', '/auth/twitter' + %p + - if @flickr_auth + You are connected to Flickr as + = succeed "." do + =link_to @flickr_auth.name, "http://flickr.com/photos/#{@flickr_auth.uid}" + = link_to "Disconnect", @flickr_auth, :confirm => "Are you sure you want to remove this connection?", :method => :delete, :class => "remove" + - else + =link_to 'Connect to Flickr', '/auth/flickr' + %h2 Change password %p %span.help-block Leave blank if you don't want to change your password. diff --git a/app/views/layouts/_footer.html.haml b/app/views/layouts/_footer.html.haml index 9ca101d7a..88d130a1e 100644 --- a/app/views/layouts/_footer.html.haml +++ b/app/views/layouts/_footer.html.haml @@ -4,6 +4,7 @@ .container %ul.nav %li= link_to "About", "http://wiki.growstuff.org" + %li= link_to "Contact", url_for(:controller => '/about', :action => 'contact') %li= link_to "Terms of Service", url_for(:controller => '/policy', :action => 'tos') %li= link_to "Privacy Policy", url_for(:controller => %'/policy', :action => 'privacy') %li= link_to "Community Guidelines", url_for(:controller => '/policy', :action => 'community') diff --git a/app/views/layouts/_meta.html.haml b/app/views/layouts/_meta.html.haml index c9f52d851..edd940210 100644 --- a/app/views/layouts/_meta.html.haml +++ b/app/views/layouts/_meta.html.haml @@ -10,7 +10,6 @@ %title = content_for?(:title) ? yield(:title) + " - #{ Growstuff::Application.config.site_name} " : Growstuff::Application.config.site_name - = stylesheet_link_tag "application", :media => "all" = csrf_meta_tags / Le HTML5 shim, for IE6-8 support of HTML elements /[if lt IE 9] diff --git a/app/views/members/nearby.html.haml b/app/views/members/nearby.html.haml index 9dba3a259..cbcf51da6 100644 --- a/app/views/members/nearby.html.haml +++ b/app/views/members/nearby.html.haml @@ -4,7 +4,10 @@ %p = form_tag(nearby_members_path, :method => :get, :class => 'form-inline') do - = label_tag :location, "Find members near", :class => 'control-label' + = label_tag :distance, "Find members within", :class => 'control-label' + = text_field_tag :distance, @distance, :class => 'input-mini' + = select_tag :units, options_for_select({"miles" => :mi, "km" => :km}, @units), :class => 'input-small' + = label_tag :location, "miles of", :class => 'control-label' = text_field_tag :location, @location = submit_tag "Search", :class => 'btn btn-primary' diff --git a/app/views/members/show.html.haml b/app/views/members/show.html.haml index 455cfd573..297bac8c7 100644 --- a/app/views/members/show.html.haml +++ b/app/views/members/show.html.haml @@ -12,21 +12,37 @@ =link_to 'Send Message', new_notification_path(:sender_id => current_member.id, :recipient_id => @member.id), :class => 'btn btn-primary' %p - %strong Member since: + %strong Member since: = @member.created_at.to_s(:date) + + - if @twitter_auth || @flickr_auth || @member.show_email + %h4 Contact + + - if @twitter_auth + %p + = image_tag "twitter_32.png", :size => "32x32", :alt => 'Twitter logo' + =link_to @twitter_auth.name, "http://twitter.com/#{@twitter_auth.name}" + + - if @flickr_auth + %p + Flickr: + =link_to @flickr_auth.name, "http://flickr.com/photos/#{@flickr_auth.uid}" + + - if @member.show_email + %p + Email: + = mail_to @member.email + - if @member.location.to_s != '' + %h4 Location %p - %strong Location: + = image_tag("http://maps.google.com/maps/api/staticmap?size=200x200&maptype=roadmap&sensor=false&markers=color:green|label:A|#{@member.latitude},#{@member.longitude}&zoom=12", :alt => "Map showing #{@member.location}", :width => 200, :height => 200 ) + %br/ + Location: = @member.location %br/ = link_to 'Find members near here', nearby_members_path(:location => @member.location) - %br/ - = image_tag("http://maps.google.com/maps/api/staticmap?size=200x200&maptype=roadmap&sensor=false&markers=color:green|label:A|#{@member.latitude},#{@member.longitude}&zoom=12", :alt => "Map showing #{@member.location}", :width => 200, :height => 200 ) %p - - if @member.show_email - %p - Email: - = mail_to @member.email - if current_member == @member %p = link_to 'Edit Settings', "edit", :class => 'btn btn-mini' @@ -64,6 +80,9 @@ %h3 Create a new garden = render 'gardens/form' - %h3 Updates - - @member.posts.each do |post| - = render :partial => "posts/single", :locals => { :post => post, :subject => true } + %h3 Posts + - if @member.posts.count > 0 + - @member.posts.each do |post| + = render :partial => "posts/single", :locals => { :post => post, :subject => true } + - else + %p Nothing posted yet. diff --git a/app/views/posts/_comments.html.haml b/app/views/posts/_comments.html.haml new file mode 100644 index 000000000..c06124014 --- /dev/null +++ b/app/views/posts/_comments.html.haml @@ -0,0 +1,11 @@ +%a{:name => "comments"} +- if @comments + %h2 + =pluralize(@comments.length, "comment") + - @comments.each do |c| + = render :partial => "comments/single", :locals => { :comment => c } + +- else + %h2 There are no comments yet + + diff --git a/app/views/posts/show.html.haml b/app/views/posts/show.html.haml index 532b93b15..607e9f1f7 100644 --- a/app/views/posts/show.html.haml +++ b/app/views/posts/show.html.haml @@ -10,16 +10,7 @@ = link_to 'Delete Post', @post, method: :delete, | data: { confirm: 'Are you sure?' }, :class => 'btn btn-mini' - -%a{:name => "comments"} -- if @comments - %h2 - =pluralize(@comments.length, "comment") - - @comments.each do |c| - = render :partial => "comments/single", :locals => { :comment => c } - -- else - %h2 There are no comments yet += render :partial => "comments", :locals => { :post => @post } - if can? :create, Comment .post-actions diff --git a/app/views/support/index.html.haml b/app/views/support/index.html.haml index 70ead408a..24a41dd04 100644 --- a/app/views/support/index.html.haml +++ b/app/views/support/index.html.haml @@ -5,8 +5,8 @@ ### Who runs Growstuff? - Growstuff is run by Alex Bayley ([Skud](/members/skud)) and Courtney - Webster ([phazel](/members/phazel)), two software developers from Melbourne, Australia. + Growstuff is run by Alex Bayley ([Skud](/members/skud)) an open source + software developer and keen veggie gardener from Melbourne, Australia. ### How did Growstuff get started? diff --git a/config/environments/test.rb b/config/environments/test.rb index 0de67c729..5f48eccfa 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -66,3 +66,12 @@ Geocoder::Lookup::Test.add_stub( } ] ) + +Geocoder::Lookup::Test.add_stub( + "Edinburgh", [ + { + 'latitude' => 55.953252, + 'longitude' => -3.188267, + } + ] +) diff --git a/config/initializers/geocoder.rb b/config/initializers/geocoder.rb new file mode 100644 index 000000000..cd284077b --- /dev/null +++ b/config/initializers/geocoder.rb @@ -0,0 +1 @@ +Geocoder.configure(:units => :km) diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb new file mode 100644 index 000000000..ad19ed66d --- /dev/null +++ b/config/initializers/omniauth.rb @@ -0,0 +1,4 @@ +Rails.application.config.middleware.use OmniAuth::Builder do + provider :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET'] + provider :flickr, ENV['FLICKR_KEY'], ENV['FLICKR_SECRET'] +end diff --git a/config/initializers/secret_token.rb b/config/initializers/secret_token.rb index 57bd5fc79..8ebcc27d3 100644 --- a/config/initializers/secret_token.rb +++ b/config/initializers/secret_token.rb @@ -4,4 +4,4 @@ # If you change this key, all old signed cookies will become invalid! # Make sure the secret is at least 30 characters and all random, # no regular words or you'll be exposed to dictionary attacks. -Growstuff::Application.config.secret_token = '6b61c7a7c5f5684945c445d80ea3df725ee2bb3101a3a57a4bec349657e092a562aa714cb4706c038d7fb1bdbd497c4b103aa1247f3bdf79044e03363ea8ee50' +Growstuff::Application.config.secret_token = ENV['RAILS_SECRET_TOKEN'] || "this is not a real secret token but it's here to make life easier for developers" diff --git a/config/routes.rb b/config/routes.rb index 871c85670..cf757e9af 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,6 +2,7 @@ Growstuff::Application.routes.draw do devise_for :members, :controllers => { :registrations => "registrations" } + resources :authentications resources :plantings resources :gardens resources :posts @@ -17,6 +18,8 @@ Growstuff::Application.routes.draw do match 'search/members/nearby' => 'members#nearby', :as => :nearby_members + match 'auth/:provider/callback' => 'authentications#create' + # The priority is based upon order of creation: # first created -> highest priority. @@ -77,5 +80,7 @@ Growstuff::Application.routes.draw do match '/policy/:action' => 'policy#:action' match '/support' => 'support#index' match '/support/:action' => 'support#:action' + match '/about' => 'about#index' + match '/about/:action' => 'about#:action' end diff --git a/credentials.example b/credentials.example new file mode 100755 index 000000000..74b7fa9df --- /dev/null +++ b/credentials.example @@ -0,0 +1,33 @@ +#!/bin/bash + +# This file is an empty sample file which you (i.e. Growstuff +# developers) can copy and use to store your API credentials for +# external APIs used by the Growstuff application. + +# To use this file, simply copy it to credentials.sh (which is +# .gitignore'd) and then fill in whatever credentials you need. Then in +# the window where you run "rails s", first run: +# +# source credentials.sh +# +# If you then run "rails s", it will have all the environment variables +# available to it. + +### CREDENTIALS ### + +# Mandrill is used to send transactional email (eg. signup +# confirmations). If using Heroku connect to Mandrill via Heroku addons +# list then go to tools menu (upper right) and choose "SMTP and API +# Credentials" +export MANDRILL_USERNAME="" +export MANDRILL_APIKEY="" + +# Used for connecting member accounts to Twitter +# Get Twitter key from https://dev.twitter.com/apps +export TWITTER_KEY="" +export TWITTER_SECRET="" + +# Used for connecting member accounts to Flickr +# Get Flickr key from http://www.flickr.com/services/apps/create/apply/ +export FLICKR_KEY="" +export FLICKR_SECRET="" diff --git a/db/migrate/20130404174459_create_authentications.rb b/db/migrate/20130404174459_create_authentications.rb new file mode 100644 index 000000000..3acdf6080 --- /dev/null +++ b/db/migrate/20130404174459_create_authentications.rb @@ -0,0 +1,13 @@ +class CreateAuthentications < ActiveRecord::Migration + def change + create_table :authentications do |t| + t.integer :member_id, :null => false + t.string :provider, :null => false + t.string :uid + t.string :token + t.string :secret + t.timestamps + end + add_index :authentications, :member_id + end +end diff --git a/db/migrate/20130409162140_add_name_to_authentications.rb b/db/migrate/20130409162140_add_name_to_authentications.rb new file mode 100644 index 000000000..c8e981b5f --- /dev/null +++ b/db/migrate/20130409162140_add_name_to_authentications.rb @@ -0,0 +1,5 @@ +class AddNameToAuthentications < ActiveRecord::Migration + def change + add_column :authentications, :name, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index f13ce0fd1..4c82164fe 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,20 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130329045744) do +ActiveRecord::Schema.define(:version => 20130418102558) do + + create_table "authentications", :force => true do |t| + t.integer "member_id", :null => false + t.string "provider", :null => false + t.string "uid" + t.string "token" + t.string "secret" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "name" + end + + add_index "authentications", ["member_id"], :name => "index_authentications_on_member_id" create_table "comments", :force => true do |t| t.integer "post_id", :null => false @@ -107,6 +120,14 @@ ActiveRecord::Schema.define(:version => 20130329045744) do t.datetime "updated_at", :null => false end + create_table "payments", :force => true do |t| + t.integer "payer_id" + t.string "payment_type" + t.decimal "amount" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "plantings", :force => true do |t| t.integer "garden_id", :null => false t.integer "crop_id", :null => false diff --git a/db/seeds.rb b/db/seeds.rb index d05a986fc..427caf22b 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -18,8 +18,8 @@ CSV.foreach(Rails.root.join('db', 'seeds', 'crops.csv')) do |row| end puts "Finished loading crops" -puts "Loading test users..." if Rails.env.development? or Rails.env.test? + puts "Loading test users..." (1..3).each do |i| @user = Member.create( :login_name => "test#{i}", @@ -30,5 +30,34 @@ if Rails.env.development? or Rails.env.test? @user.confirm! @user.save! end + puts "Finished loading test users" + + puts "Creating admin role..." + @admin = Role.create(:name => 'Admin') + puts "Creating crop wrangler role..." + @wrangler = Role.create(:name => 'Crop Wrangler') + + puts "Adding admin and crop wrangler members..." + @admin_user = Member.create( + :login_name => "admin1", + :email => "admin1@example.com", + :password => "password1", + :tos_agreement => true + ) + @admin_user.confirm! + @admin_user.roles << @admin + @admin_user.save! + + @wrangler_user = Member.create( + :login_name => "wrangler1", + :email => "wrangler1@example.com", + :password => "password1", + :tos_agreement => true + ) + @wrangler_user.confirm! + @wrangler_user.roles << @wrangler + @wrangler_user.save! + puts "Done!" + end -puts "Finished loading test users" + diff --git a/spec/controllers/authentications_controller_spec.rb b/spec/controllers/authentications_controller_spec.rb new file mode 100644 index 000000000..393f0509a --- /dev/null +++ b/spec/controllers/authentications_controller_spec.rb @@ -0,0 +1,77 @@ +require 'spec_helper' + +describe AuthenticationsController do + + before(:each) do + @member = FactoryGirl.create(:member) + sign_in @member + controller.stub(:current_member) { @member } + @auth = FactoryGirl.create(:authentication, :member => @member) + request.env['omniauth.auth'] = { + 'provider' => 'foo', + 'uid' => 'bar', + 'info' => { 'nickname' => 'blah' }, + 'credentials' => { 'token' => 'blah', 'secret' => 'blah' } + } + end + + describe "GET index" do + it "assigns all authentications as @authentications" do + get :index, {} + assigns(:authentications).should eq([@auth]) + end + end + + describe "POST create" do + describe "with valid params" do + it "creates a new Authentication" do + expect { + post :create, {:authentication => @auth} + }.to change(Authentication, :count).by(1) + end + + it "assigns a newly created authentication as @authentication" do + post :create, {:authentication => @auth} + assigns(:authentication).should be_a(Authentication) + assigns(:authentication).should be_persisted + end + + it "redirects to the settings page" do + post :create, {:authentication => @auth } + response.should redirect_to(edit_member_registration_path) + end + end + + describe "with invalid params" do + it "assigns a newly created but unsaved authentication as @authentication" do + # Trigger the behavior that occurs when invalid params are submitted + Authentication.any_instance.stub(:save).and_return(false) + post :create, {:authentication => { "member_id" => "invalid value" }} + assigns(:authentication).should be_a_new(Authentication) + end + + it "redirects to settings page" do + # Trigger the behavior that occurs when invalid params are submitted + Authentication.any_instance.stub(:save).and_return(false) + post :create, {:authentication => { "member_id" => "invalid value" }} + response.should redirect_to(edit_member_registration_path) + end + end + end + + describe "DELETE destroy" do + it "destroys the requested authentication" do + authentication = @auth + expect { + delete :destroy, {:id => authentication.to_param} + }.to change(Authentication, :count).by(-1) + end + + it "redirects to the settings page" do + authentication = @auth + delete :destroy, {:id => authentication.to_param} + response.should redirect_to(edit_member_registration_path) + end + end + +end diff --git a/spec/controllers/comments_controller_spec.rb b/spec/controllers/comments_controller_spec.rb index a3b085553..587d24407 100644 --- a/spec/controllers/comments_controller_spec.rb +++ b/spec/controllers/comments_controller_spec.rb @@ -38,6 +38,13 @@ describe CommentsController do assigns(:post).should eq(post) end + it "assigns the old comments as @comments" do + post = FactoryGirl.create(:post) + old_comment = FactoryGirl.create(:comment, :post => post) + get :new, {:post_id => post.id} + assigns(:comments).should eq [old_comment] + end + it "dies if no post specified" do get :new response.should redirect_to(root_url) @@ -50,6 +57,15 @@ describe CommentsController do get :edit, {:id => comment.to_param} assigns(:comment).should eq(comment) end + + it "assigns previous comments as @comments" do + post = FactoryGirl.create(:post) + old_comment = FactoryGirl.create(:comment, :post => post) + comment = FactoryGirl.create(:comment, :post => post) + get :edit, {:id => comment.to_param} + assigns(:comments).should eq([old_comment, comment]) + end + end describe "POST create" do diff --git a/spec/controllers/home_controller_spec.rb b/spec/controllers/home_controller_spec.rb index 839545b51..2038c823d 100644 --- a/spec/controllers/home_controller_spec.rb +++ b/spec/controllers/home_controller_spec.rb @@ -21,7 +21,7 @@ describe HomeController do end it 'assigns interesting members' do - @member = FactoryGirl.create(:geolocated_member) + @member = FactoryGirl.create(:london_member) get :index, {} assigns(:interesting_members).should eq [@member] end diff --git a/spec/controllers/member_controller_spec.rb b/spec/controllers/member_controller_spec.rb index 71a9fb68b..f15cd049c 100644 --- a/spec/controllers/member_controller_spec.rb +++ b/spec/controllers/member_controller_spec.rb @@ -5,6 +5,8 @@ describe MembersController do before :each do @member = FactoryGirl.create(:member) @posts = [ FactoryGirl.create(:post, :author => @member) ] + @twitter_auth = FactoryGirl.create(:authentication, :member => @member) + @flickr_auth = FactoryGirl.create(:flickr_authentication, :member => @member) end describe "GET index" do @@ -37,6 +39,16 @@ describe MembersController do assigns(:posts).should eq(@posts) end + it "assigns @twitter_auth" do + get :show, {:id => @member.id} + assigns(:twitter_auth).should eq(@twitter_auth) + end + + it "assigns @flickr_auth" do + get :show, {:id => @member.id} + assigns(:flickr_auth).should eq(@flickr_auth) + end + it "doesn't show completely nonsense members" do lambda { get :show, {:id => 9999} }.should raise_error end @@ -59,13 +71,13 @@ describe MembersController do describe "GET nearby members" do before(:each) do - @member_near = FactoryGirl.create(:geolocated_member) - @member_far = FactoryGirl.create(:lonely_geolocated_member) + @member_london = FactoryGirl.create(:london_member) + @member_south_pole = FactoryGirl.create(:south_pole_member) end context "when the user is logged in and has set their location" do before(:each) do - @member = FactoryGirl.create(:geolocated_member) + @member = FactoryGirl.create(:london_member) controller.stub(:current_member) { @member } end @@ -76,23 +88,40 @@ describe MembersController do it "assigns nearby members as nearby" do get :nearby - assigns(:nearby_members).should include @member_near + assigns(:nearby_members).should include @member_london end it "doesn't assign far-off members as nearby" do get :nearby - assigns(:nearby_members).should_not include @member_far + assigns(:nearby_members).should_not include @member_south_pole end it "gets members near the specified location if one is set" do - get :nearby, { :location => @member_far.location } - assigns(:nearby_members).should include @member_far + get :nearby, { :location => @member_south_pole.location } + assigns(:nearby_members).should include @member_south_pole end it "does not assign members near current_member if a location is set" do - get :nearby, { :location => @member_far.location } - assigns(:nearby_members).should_not include @member_near + get :nearby, { :location => @member_south_pole.location } + assigns(:nearby_members).should_not include @member_london end + + it "finds faraway members if you increase the distance" do + get :nearby, { :distance => "50000" } + assigns(:nearby_members).should include @member_south_pole + end + + # Edinburgh and London are approximately 330mi/530km apart + it "finds London members within 350 miles of Edinburgh" do + get :nearby, { :distance => "350", :units => :mi, :location => "Edinburgh" } + assigns(:nearby_members).should include @member_london + end + + it "doesn't find London members within 350 km of Edinburgh" do + get :nearby, { :distance => "350", :units => :km, :location => "Edinburgh" } + assigns(:nearby_members).should_not include @member_london + end + end context "when the user is logged in but hasn't set their location" do @@ -107,13 +136,13 @@ describe MembersController do end it "assigns nearby members if a location is set" do - get :nearby, { :location => @member_near.location } - assigns(:nearby_members).should include @member_near + get :nearby, { :location => @member_london.location } + assigns(:nearby_members).should include @member_london end it "does not assign far members if a location is set" do - get :nearby, { :location => @member_near.location } - assigns(:nearby_members).should_not include @member_far + get :nearby, { :location => @member_london.location } + assigns(:nearby_members).should_not include @member_south_pole end end @@ -130,13 +159,13 @@ describe MembersController do end it "assigns nearby members if a location is set" do - get :nearby, { :location => @member_near.location } - assigns(:nearby_members).should include @member_near + get :nearby, { :location => @member_london.location } + assigns(:nearby_members).should include @member_london end it "does not assign far members if a location is set" do - get :nearby, { :location => @member_near.location } - assigns(:nearby_members).should_not include @member_far + get :nearby, { :location => @member_london.location } + assigns(:nearby_members).should_not include @member_south_pole end end end diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb new file mode 100644 index 000000000..0ba0e29e6 --- /dev/null +++ b/spec/controllers/registrations_controller_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe RegistrationsController do + + before :each do + @member = FactoryGirl.create(:member) + sign_in @member + controller.stub(:current_user) { @member } + controller.stub(:devise_mapping).and_return(Devise.mappings[:member]) + end + + describe "GET edit" do + it "assigns the requested member as @member" do + get :edit + assigns(:member).should eq(@member) + end + + it "picks up the twitter auth" do + @auth = FactoryGirl.create(:authentication, :member => @member) + get :edit + assigns(:twitter_auth).should eq @auth + end + + it "picks up the flickr auth" do + @auth = FactoryGirl.create(:flickr_authentication, :member => @member) + get :edit + assigns(:flickr_auth).should eq @auth + end + end + +end diff --git a/spec/factories/authentications.rb b/spec/factories/authentications.rb new file mode 100644 index 000000000..8b26549f0 --- /dev/null +++ b/spec/factories/authentications.rb @@ -0,0 +1,16 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :authentication do + member + provider 'twitter' + uid 'foo' + secret 'bar' + name 'baz' + + factory :flickr_authentication do + provider 'flickr' + uid 'blah@blah' + end + end +end diff --git a/spec/factories/comments.rb b/spec/factories/comments.rb index 404ac5313..b1a88a7a6 100644 --- a/spec/factories/comments.rb +++ b/spec/factories/comments.rb @@ -2,6 +2,7 @@ FactoryGirl.define do factory :comment do post author - body "OMG LOL" + sequence(:body) { |n| "OMG LOL #{n}" } # because our commenters are more + # polite than YouTube's end end diff --git a/spec/factories/member.rb b/spec/factories/member.rb index 9c3531a1c..5e5b7f455 100644 --- a/spec/factories/member.rb +++ b/spec/factories/member.rb @@ -24,7 +24,7 @@ FactoryGirl.define do show_email true end - factory :geolocated_member do + factory :london_member do sequence(:login_name) { |n| "JohnH#{n}" } # for the astronomer who figured out longitude location 'Greenwich, UK' # including lat/long explicitly because geocoder doesn't work with FG @@ -32,7 +32,7 @@ FactoryGirl.define do longitude 0.004 end - factory :lonely_geolocated_member do + factory :south_pole_member do sequence(:login_name) { |n| "ScottRF#{n}" } location 'Amundsen-Scott Base, Antarctica' latitude -90 diff --git a/spec/models/authentication_spec.rb b/spec/models/authentication_spec.rb new file mode 100644 index 000000000..2a9df9aec --- /dev/null +++ b/spec/models/authentication_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' + +describe Authentication do + + it 'creates an authentication' do + @auth = FactoryGirl.create(:authentication) + @auth.should be_an_instance_of Authentication + @auth.member.should be_an_instance_of Member + end +end diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index e1f756634..cd5decf14 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -221,9 +221,9 @@ describe 'member' do # 1) confirmed # 2) ordered by the most recent sign in it 'finds interesting members' do - @member1 = FactoryGirl.create(:geolocated_member) - @member2 = FactoryGirl.create(:geolocated_member) - @member3 = FactoryGirl.create(:geolocated_member) + @member1 = FactoryGirl.create(:london_member) + @member2 = FactoryGirl.create(:london_member) + @member3 = FactoryGirl.create(:london_member) @member4 = FactoryGirl.create(:unconfirmed_member) @member1.updated_at = 3.days.ago diff --git a/spec/requests/authentications_spec.rb b/spec/requests/authentications_spec.rb new file mode 100644 index 000000000..01843d6bc --- /dev/null +++ b/spec/requests/authentications_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' + +describe "Authentications" do + describe "GET /authentications" do + it "works! (now write some real specs)" do + # Run the generator again with the --webrat flag if you want to use webrat methods/matchers + get authentications_path + response.status.should be(200) + end + end +end diff --git a/spec/routing/authentications_routing_spec.rb b/spec/routing/authentications_routing_spec.rb new file mode 100644 index 000000000..1a639dead --- /dev/null +++ b/spec/routing/authentications_routing_spec.rb @@ -0,0 +1,35 @@ +require "spec_helper" + +describe AuthenticationsController do + describe "routing" do + + it "routes to #index" do + get("/authentications").should route_to("authentications#index") + end + + it "routes to #new" do + get("/authentications/new").should route_to("authentications#new") + end + + it "routes to #show" do + get("/authentications/1").should route_to("authentications#show", :id => "1") + end + + it "routes to #edit" do + get("/authentications/1/edit").should route_to("authentications#edit", :id => "1") + end + + it "routes to #create" do + post("/authentications").should route_to("authentications#create") + end + + it "routes to #update" do + put("/authentications/1").should route_to("authentications#update", :id => "1") + end + + it "routes to #destroy" do + delete("/authentications/1").should route_to("authentications#destroy", :id => "1") + end + + end +end diff --git a/spec/views/about/contact_spec.rb b/spec/views/about/contact_spec.rb new file mode 100644 index 000000000..85d232126 --- /dev/null +++ b/spec/views/about/contact_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +describe 'about/contact.html.haml', :type => "view" do + before(:each) do + render + end + + it 'should show support faq' do + render + rendered.should contain 'General contact email' + end +end diff --git a/spec/views/authentications/index.html.haml_spec.rb b/spec/views/authentications/index.html.haml_spec.rb new file mode 100644 index 000000000..f780966ae --- /dev/null +++ b/spec/views/authentications/index.html.haml_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe "authentications/index" do + before(:each) do + assign(:authentications, [ + stub_model(Authentication, + :member_id => 1, + :provider => "Provider", + :uid => "Uid", + :name => "Name" + ), + stub_model(Authentication, + :member_id => 1, + :provider => "Provider", + :uid => "Uid", + :name => "Name" + ) + ]) + end + + it "renders a list of authentications" do + render + # Run the generator again with the --webrat flag if you want to use webrat matchers + assert_select ".authentication", :count => 2 + assert_select ".provider", :text => "Provider".to_s, :count => 2 + assert_select ".name", :text => "Name".to_s, :count => 2 + end +end diff --git a/spec/views/comments/new.html.haml_spec.rb b/spec/views/comments/new.html.haml_spec.rb index 1fe66ef5f..90b5e1cb3 100644 --- a/spec/views/comments/new.html.haml_spec.rb +++ b/spec/views/comments/new.html.haml_spec.rb @@ -3,20 +3,32 @@ require 'spec_helper' describe "comments/new" do before(:each) do controller.stub(:current_user) { nil } - assign(:comment, FactoryGirl.create(:comment)) + @post = FactoryGirl.create(:post) + @previous_comment = FactoryGirl.create(:comment, :post => @post) + assign(:comment, FactoryGirl.create(:comment, :post => @post)) + assign(:comments, [@previous_comment]) + render + end + + it "shows the text of the post under discussion" do + rendered.should contain @post.body + end + + it "shows previous comments" do + rendered.should contain @previous_comment.body + end + + it "shows the correct comment count" do + rendered.should contain "1 comment" end it "renders new comment form" do - render - - # Run the generator again with the --webrat flag if you want to use webrat matchers assert_select "form", :action => comments_path, :method => "post" do assert_select "textarea#comment_body", :name => "comment[body]" end end it 'shows markdown help' do - render rendered.should contain 'Markdown' end diff --git a/spec/views/devise/registrations/edit_spec.rb b/spec/views/devise/registrations/edit_spec.rb index 9d15b523e..594bb9657 100644 --- a/spec/views/devise/registrations/edit_spec.rb +++ b/spec/views/devise/registrations/edit_spec.rb @@ -11,14 +11,18 @@ describe 'devise/registrations/edit.html.haml', :type => "view" do @view.stub(:resource_name).and_return("member") @view.stub(:resource_class).and_return(Member) @view.stub(:devise_mapping).and_return(Devise.mappings[:member]) - render end it 'should have some fields' do - rendered.should contain 'Email' + render + rendered.should contain 'Email' end context 'email section' do + before(:each) do + render + end + it 'has a heading' do assert_select "h2", "Email settings" end @@ -29,6 +33,10 @@ describe 'devise/registrations/edit.html.haml', :type => "view" do end context 'profile section' do + before(:each) do + render + end + it 'has a heading' do assert_select "h2", "Profile details" end @@ -46,7 +54,56 @@ describe 'devise/registrations/edit.html.haml', :type => "view" do end end + context 'other sites section' do + it 'has a heading' do + render + assert_select "h2", "Linked accounts" + end + + context 'not connected to twitter' do + it 'has a link to connect' do + render + assert_select "a", "Connect to Twitter" + end + end + context 'connected to twitter' do + before(:each) do + @twitter_auth = FactoryGirl.create(:authentication, :member => @member) + render + end + it 'has a link to twitter profile' do + assert_select "a", :href => "http://twitter.com/#{@twitter_auth.name}" + end + it 'has a link to disconnect' do + render + assert_select "a", :href => @twitter_auth, :text => "Disconnect" + end + end + + context 'not connected to flickr' do + it 'has a link to connect' do + render + assert_select "a", "Connect to Flickr" + end + end + context 'connected to flickr' do + before(:each) do + @flickr_auth = FactoryGirl.create(:flickr_authentication, :member => @member) + render + end + it 'has a link to flickr photostream' do + assert_select "a", :href => "http://flickr.com/photos/#{@flickr_auth.uid}" + end + it 'has a link to disconnect' do + render + assert_select "a", :href => @flickr_auth, :text => "Disconnect" + end + end + + end + it 'should have a password section' do + render assert_select "h2", "Change password" end diff --git a/spec/views/home/index_spec.rb b/spec/views/home/index_spec.rb index b75f6b491..e123927f5 100644 --- a/spec/views/home/index_spec.rb +++ b/spec/views/home/index_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe 'home/index.html.haml', :type => "view" do context 'logged out' do before(:each) do - @member = FactoryGirl.create(:geolocated_member) + @member = FactoryGirl.create(:london_member) @member.updated_at = 2.days.ago @post = FactoryGirl.create(:post, :author => @member) @planting = FactoryGirl.create(:planting, :garden => @member.gardens.first) @@ -35,7 +35,7 @@ describe 'home/index.html.haml', :type => "view" do context 'logged in' do before(:each) do - @member = FactoryGirl.create(:geolocated_member) + @member = FactoryGirl.create(:london_member) controller.stub(:current_user) { @member } sign_in @member assign(:member, @member) diff --git a/spec/views/layouts/_footer_spec.rb b/spec/views/layouts/_footer_spec.rb index a746f2e70..c3dc3a9b7 100644 --- a/spec/views/layouts/_footer_spec.rb +++ b/spec/views/layouts/_footer_spec.rb @@ -8,6 +8,7 @@ describe 'layouts/_footer.html.haml', :type => "view" do it 'should have links in footer' do rendered.should contain 'About' + rendered.should contain 'Contact' assert_select("a[href=/policy/tos]", 'Terms of Service') assert_select("a[href=/policy/privacy]", 'Privacy Policy') assert_select("a[href=/policy/community]", 'Community Guidelines') diff --git a/spec/views/members/index.html.haml_spec.rb b/spec/views/members/index.html.haml_spec.rb index 3da0b4f40..22fd9c04f 100644 --- a/spec/views/members/index.html.haml_spec.rb +++ b/spec/views/members/index.html.haml_spec.rb @@ -6,7 +6,7 @@ describe "members/index" do page = 1 per_page = 2 total_entries = 2 - @member = FactoryGirl.create(:geolocated_member) + @member = FactoryGirl.create(:london_member) members = WillPaginate::Collection.create(page, per_page, total_entries) do |pager| pager.replace([ @member, @member ]) end diff --git a/spec/views/members/nearby.html.haml_spec.rb b/spec/views/members/nearby.html.haml_spec.rb index 0199a8605..c00ab7b9b 100644 --- a/spec/views/members/nearby.html.haml_spec.rb +++ b/spec/views/members/nearby.html.haml_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe "members/nearby" do before(:each) do - @member = FactoryGirl.create(:geolocated_member) + @member = FactoryGirl.create(:london_member) @nearby_members = [FactoryGirl.create(:member)] end @@ -18,6 +18,16 @@ describe "members/nearby" do assert_select "#location", :value => @location end + it "shows the default distance in the textbox" do + assert_select "#distance", :value => "100" + end + + it "shows a dropdown with miles and km" do + assert_select "select#units" + assert_select "select#units option[value=km]" + assert_select "select#units option[value=mi]" + end + it "shows the names of nearby members" do @nearby_members.each do |m| rendered.should contain m.login_name diff --git a/spec/views/members/show.html.haml_spec.rb b/spec/views/members/show.html.haml_spec.rb index f141694ec..aab5be98b 100644 --- a/spec/views/members/show.html.haml_spec.rb +++ b/spec/views/members/show.html.haml_spec.rb @@ -23,6 +23,38 @@ describe "members/show" do end end + context 'twitter' do + context "no twitter" do + it "doesn't show twitter link" do + render + assert_select "a[href^=http://twitter.com/]", :count => 0 + end + end + context 'has twitter' do + it "shows twitter link" do + @twitter_auth = FactoryGirl.create(:authentication, :member => @member) + render + assert_select "a", :href => "http://twitter.com/#{@twitter_auth.name}" + end + end + end + + context 'flickr' do + context "no flickr" do + it "doesn't show flickr link" do + render + assert_select "a[href^=http://flickr.com/]", :count => 0 + end + end + context 'has flickr' do + it "shows flickr link" do + @flickr_auth = FactoryGirl.create(:flickr_authentication, :member => @member) + render + assert_select "a", :href => "http://flickr.com/photos/#{@flickr_auth.uid}" + end + end + end + context "gardens and plantings" do before(:each) do @planting = FactoryGirl.create(:planting, :garden => @garden) @@ -59,7 +91,7 @@ describe "members/show" do it "does not contain a 'New Garden' tab" do assert_select "#garden_new", false - end + end end context "signed in member" do @@ -93,11 +125,11 @@ describe "members/show" do it "does not contain a 'New Garden' link" do assert_select "a[href=#garden_new]", false end - + it "does not contain a 'New Garden' tab" do assert_select "#garden_new", false - end - + end + it "contains no edit settings button" do rendered.should_not contain "Edit Settings" end @@ -124,7 +156,7 @@ describe "members/show" do context "geolocations" do before(:each) do - @member = FactoryGirl.create(:geolocated_member) + @member = FactoryGirl.create(:london_member) render end it "shows the location" do diff --git a/spec/views/plantings/show.html.haml_spec.rb b/spec/views/plantings/show.html.haml_spec.rb index 8eabcd535..414209f1d 100644 --- a/spec/views/plantings/show.html.haml_spec.rb +++ b/spec/views/plantings/show.html.haml_spec.rb @@ -57,7 +57,7 @@ describe "plantings/show" do context "location set" do before(:each) do controller.stub(:current_user) { nil } - @member = FactoryGirl.create(:geolocated_member) + @member = FactoryGirl.create(:london_member) create_planting_for(@member) render end diff --git a/spec/views/posts/show.html.haml_spec.rb b/spec/views/posts/show.html.haml_spec.rb index 34c21f9e0..d9682b015 100644 --- a/spec/views/posts/show.html.haml_spec.rb +++ b/spec/views/posts/show.html.haml_spec.rb @@ -107,7 +107,4 @@ describe "posts/show" do end - - - end diff --git a/spec/views/support/index_spec.rb b/spec/views/support/index_spec.rb index 7945ff5d3..0c7491ed7 100644 --- a/spec/views/support/index_spec.rb +++ b/spec/views/support/index_spec.rb @@ -6,7 +6,11 @@ describe 'support/index.html.haml', :type => "view" do end it 'should show support faq' do - render rendered.should contain 'About Growstuff' end + + it 'should not mention Courtney any more' do + rendered.should_not contain 'Courtney' + rendered.should_not contain 'phazel' + end end