diff --git a/.gitignore b/.gitignore index ead788319..9b5a1cf4b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ *~ *.DS_Store credentials.sh +Pathogen: +custom_plan.rb +zeus.json diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 000000000..024bd1578 --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,39 @@ +This is a list of contributors to Growstuff's codebase. We maintain +this list because we work in pairs, but Github only knows about the +person who actually does the commits. This gives credit to both members +of the pair. + +If you contribute, please add yourself to the bottom of the list and +submit the change with your pull request. + +## Committers + +- Alex Bayley / [Skud](https://github.com/Skud) +- Cesy / [cesy](https://github.com/cesy) +- Miles Gould / [pozorvlak](https://github.com/pozorvlak) +- Joseph Caudle / [jcaudle](https://github.com/jcaudle) + +## Contributors + +- Ricky Amianym / [amianym](https://github.com/amianym) +- Juliet Kemp / [julietk](https://github.com/julietk) +- Federico Mena Quintero / [federicomenaquintero](https://github.com/federicomenaquintero) +- Jay Springett / [thejaymo](https://github.com/thejaymo) +- Maia Sauren / [sauramaia](https://github.com/sauramaia) +- Norman Ancajas / [nbancajas](https://github.com/nbancajas) +- Jonathan "Duke" Leto / [leto](https://github.com/leto) +- Mackenzie Morgan / [maco](https://github.com/maco) +- Amy Hendrix / [sabreuse](https://github.com/sabreuse) +- CephLPod / [cephLpod](https://github.com/cephLpod/) +- Gemma Mason / [gemmaellen](https://github.com/gemmaellen) +- Courtney Webster / [phazel](https://github.com/phazel/) +- Cathy Sullivan / [cesullivan](https://github.com/cesullivan) +- Jared McCorkindale / [jmcorkindale](https://github.com/jmcorkindale) +- Florian Vallen / [flov](https://github.com/flov) +- Samuel Cochrane / [sj26](https://github.com/sj26) +- Craig Read / [Catharz](https://github.com/Catharz) +- Ju Transcendancing / [transcendancing](https://github.com/transcendancing) +- Lillian Ryan / [attlebish](https://github.com/attlebish) +- Gary Traffanstedt / [blimey85](https://github.com/blimey85) +- Yaw Boakye / [yawboakye](https://github.com/yawboakye) +- Ryan Clark / [IAMRYO](https://github.com/IAMRYO) diff --git a/Gemfile b/Gemfile index 6eb4bbca7..7261c172f 100644 --- a/Gemfile +++ b/Gemfile @@ -80,7 +80,6 @@ gem 'bootstrap-datepicker-rails' gem 'omniauth' gem 'omniauth-twitter' gem 'omniauth-flickr' -gem 'authbuttons-rails' # for phusion passenger (i.e. mod_rails) on the server gem 'passenger' diff --git a/Gemfile.lock b/Gemfile.lock index 6c7782522..9a96e9df5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -29,7 +29,6 @@ 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) @@ -234,7 +233,6 @@ PLATFORMS ruby DEPENDENCIES - authbuttons-rails bluecloth bootstrap-datepicker-rails bundler (>= 1.1.5) diff --git a/app/assets/images/flickr_32.png b/app/assets/images/flickr_32.png new file mode 100644 index 000000000..82a350e22 Binary files /dev/null and b/app/assets/images/flickr_32.png differ diff --git a/app/assets/images/twitter_32.png b/app/assets/images/twitter_32.png new file mode 100644 index 000000000..550232344 Binary files /dev/null and b/app/assets/images/twitter_32.png differ diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index d6bc30bd1..c14b0123b 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -2,12 +2,12 @@ class CommentsController < ApplicationController # GET /comments # GET /comments.json def index - @comments = Comment.all - @recent_comments = Comment.limit(100).order('created_at desc').all + @comments = Comment.paginate(:page => params[:page]) respond_to do |format| format.html # index.html.erb format.json { render json: @comments } + format.rss { render :layout => false } end end diff --git a/app/controllers/crops_controller.rb b/app/controllers/crops_controller.rb index f0cfe2525..26a3de663 100644 --- a/app/controllers/crops_controller.rb +++ b/app/controllers/crops_controller.rb @@ -5,7 +5,6 @@ class CropsController < ApplicationController # GET /crops.json def index @crops = Crop.paginate(:page => params[:page]) - @new_crops = Crop.limit(20).order('created_at desc').all respond_to do |format| format.html # index.html.haml diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index 7b49c326f..790e5d3ac 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -1,4 +1,5 @@ class NotificationsController < ApplicationController + include NotificationsHelper load_and_authorize_resource # GET /notifications def index @@ -14,6 +15,7 @@ class NotificationsController < ApplicationController @notification = Notification.find(params[:id]) @notification.read = true @notification.save + @reply_link = reply_link(@notification) respond_to do |format| format.html # show.html.erb @@ -25,6 +27,7 @@ class NotificationsController < ApplicationController def new @notification = Notification.new @recipient = Member.find_by_id(params[:recipient_id]) + @subject = params[:subject] || "" respond_to do |format| format.html # new.html.erb @@ -49,7 +52,7 @@ class NotificationsController < ApplicationController respond_to do |format| if @notification.save - format.html { redirect_to @recipient, notice: 'Message was successfully sent.' } + format.html { redirect_to notifications_path, notice: 'Message was successfully sent.' } else format.html { render action: "new" } end diff --git a/app/controllers/plantings_controller.rb b/app/controllers/plantings_controller.rb index 98f038870..7a7c61119 100644 --- a/app/controllers/plantings_controller.rb +++ b/app/controllers/plantings_controller.rb @@ -3,8 +3,7 @@ class PlantingsController < ApplicationController # GET /plantings # GET /plantings.json def index - @plantings = Planting.all - @recent_plantings = Planting.limit(100).order('created_at desc').all + @plantings = Planting.paginate(:page => params[:page]) respond_to do |format| format.html # index.html.erb @@ -85,10 +84,11 @@ class PlantingsController < ApplicationController # DELETE /plantings/1.json def destroy @planting = Planting.find(params[:id]) + @garden = @planting.garden @planting.destroy respond_to do |format| - format.html { redirect_to plantings_url } + format.html { redirect_to @garden } format.json { head :no_content } end end diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 1ca2b5f94..de7c80091 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -5,7 +5,6 @@ class PostsController < ApplicationController def index @posts = Post.paginate(:page => params[:page]) - @recent_posts = Post.limit(100).order('created_at desc').all respond_to do |format| format.html # index.html.haml @@ -23,6 +22,10 @@ class PostsController < ApplicationController respond_to do |format| format.html # show.html.haml format.json { render json: @post } + format.rss { render( + :layout => false, + :locals => { :post => @post } + )} end end diff --git a/app/controllers/scientific_names_controller.rb b/app/controllers/scientific_names_controller.rb index b54cac6af..691c51872 100644 --- a/app/controllers/scientific_names_controller.rb +++ b/app/controllers/scientific_names_controller.rb @@ -26,6 +26,7 @@ class ScientificNamesController < ApplicationController # GET /scientific_names/new.json def new @scientific_name = ScientificName.new + @crop = Crop.find_by_id(params[:crop_id]) || Crop.new respond_to do |format| format.html # new.html.haml @@ -45,7 +46,7 @@ class ScientificNamesController < ApplicationController respond_to do |format| if @scientific_name.save - format.html { redirect_to @scientific_name, notice: 'Scientific name was successfully created.' } + format.html { redirect_to @scientific_name.crop, notice: 'Scientific name was successfully created.' } format.json { render json: @scientific_name, status: :created, location: @scientific_name } else format.html { render action: "new" } @@ -61,7 +62,7 @@ class ScientificNamesController < ApplicationController respond_to do |format| if @scientific_name.update_attributes(params[:scientific_name]) - format.html { redirect_to @scientific_name, notice: 'Scientific name was successfully updated.' } + format.html { redirect_to @scientific_name.crop, notice: 'Scientific name was successfully updated.' } format.json { head :no_content } else format.html { render action: "edit" } @@ -74,10 +75,11 @@ class ScientificNamesController < ApplicationController # DELETE /scientific_names/1.json def destroy @scientific_name = ScientificName.find(params[:id]) + @crop = @scientific_name.crop @scientific_name.destroy respond_to do |format| - format.html { redirect_to scientific_names_url } + format.html { redirect_to @crop } format.json { head :no_content } end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb deleted file mode 100644 index 11d1c7f13..000000000 --- a/app/helpers/application_helper.rb +++ /dev/null @@ -1,7 +0,0 @@ -module ApplicationHelper - - def random_crop - Crop.random - end - -end diff --git a/app/helpers/comments_helper.rb b/app/helpers/comments_helper.rb deleted file mode 100644 index 0ec9ca5f2..000000000 --- a/app/helpers/comments_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module CommentsHelper -end diff --git a/app/helpers/crops_helper.rb b/app/helpers/crops_helper.rb deleted file mode 100644 index da6fb9a78..000000000 --- a/app/helpers/crops_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module CropsHelper -end diff --git a/app/helpers/forums_helper.rb b/app/helpers/forums_helper.rb deleted file mode 100644 index 2e531fd46..000000000 --- a/app/helpers/forums_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module ForumsHelper -end diff --git a/app/helpers/gardens_helper.rb b/app/helpers/gardens_helper.rb deleted file mode 100644 index 015e7835f..000000000 --- a/app/helpers/gardens_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module GardensHelper -end diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb deleted file mode 100644 index 23de56ac6..000000000 --- a/app/helpers/home_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module HomeHelper -end diff --git a/app/helpers/members_helper.rb b/app/helpers/members_helper.rb deleted file mode 100644 index 51a8599e2..000000000 --- a/app/helpers/members_helper.rb +++ /dev/null @@ -1,9 +0,0 @@ -module MembersHelper - def shorten(str, max_length) - if str.length > max_length - return str.slice(0, max_length - 3) + "..." - else - return str - end - end -end diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index 7342393a7..dab53df95 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -1,2 +1,16 @@ module NotificationsHelper + def reply_link(notification) + if notification.post + # comment on the post in question + new_comment_url(:post_id => notification.post.id) + else + # by default, reply link sends a PM in return + new_notification_url( + :recipient_id => notification.sender.id, + :subject => notification.subject =~ /^Re: / ? + notification.subject : + "Re: " + notification.subject + ) + end + end end diff --git a/app/helpers/plantings_helper.rb b/app/helpers/plantings_helper.rb deleted file mode 100644 index bc609d05e..000000000 --- a/app/helpers/plantings_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module PlantingsHelper -end diff --git a/app/helpers/posts_helper.rb b/app/helpers/posts_helper.rb deleted file mode 100644 index a7b8cec89..000000000 --- a/app/helpers/posts_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module PostsHelper -end diff --git a/app/helpers/roles_helper.rb b/app/helpers/roles_helper.rb deleted file mode 100644 index 6b6b6cc22..000000000 --- a/app/helpers/roles_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module RolesHelper -end diff --git a/app/helpers/scientific_names_helper.rb b/app/helpers/scientific_names_helper.rb deleted file mode 100644 index 3b1a12371..000000000 --- a/app/helpers/scientific_names_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module ScientificNamesHelper -end diff --git a/app/mailers/notifier.rb b/app/mailers/notifier.rb index 50612c353..579b2a8ad 100644 --- a/app/mailers/notifier.rb +++ b/app/mailers/notifier.rb @@ -1,8 +1,11 @@ class Notifier < ActionMailer::Base + include NotificationsHelper default from: "Growstuff " def notify(notification) @notification = notification + @reply_link = reply_link(@notification) + mail(:to => @notification.recipient.email, :subject => @notification.subject) end diff --git a/app/models/comment.rb b/app/models/comment.rb index ea2082bf9..f797848e0 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -3,7 +3,8 @@ class Comment < ActiveRecord::Base belongs_to :author, :class_name => 'Member' belongs_to :post - default_scope order("created_at asc") + default_scope order("created_at DESC") + scope :post_order, reorder("created_at ASC") # for display on post page after_create do recipient = self.post.author.id diff --git a/app/models/crop.rb b/app/models/crop.rb index 7190e048b..105afe0b5 100644 --- a/app/models/crop.rb +++ b/app/models/crop.rb @@ -1,11 +1,22 @@ class Crop < ActiveRecord::Base extend FriendlyId friendly_id :system_name, use: :slugged - attr_accessible :en_wikipedia_url, :system_name + attr_accessible :en_wikipedia_url, :system_name, :parent_id + has_many :scientific_names has_many :plantings + + belongs_to :parent, :class_name => 'Crop' + has_many :varieties, :class_name => 'Crop', :foreign_key => 'parent_id' + default_scope order("lower(system_name) asc") + validates :en_wikipedia_url, + :format => { + :with => /^https?:\/\/en\.wikipedia\.org\/wiki/, + :message => 'is not a valid English Wikipedia URL' + } + def Crop.random @crop = Crop.offset(rand(Crop.count)).first return @crop diff --git a/app/models/garden.rb b/app/models/garden.rb index d8ed4056d..13d8927c6 100644 --- a/app/models/garden.rb +++ b/app/models/garden.rb @@ -7,8 +7,15 @@ class Garden < ActiveRecord::Base has_many :plantings, :order => 'created_at DESC', :dependent => :destroy has_many :crops, :through => :plantings + # before_create :replace_blank_name + default_scope order("lower(name) asc") + validates :name, + :format => { + :with => /\S/ + } + def garden_slug "#{owner.login_name}-#{name}".downcase.gsub(' ', '-') end diff --git a/app/views/comments/_form.html.haml b/app/views/comments/_form.html.haml index 06b6d0f8b..3e3f6f66b 100644 --- a/app/views/comments/_form.html.haml +++ b/app/views/comments/_form.html.haml @@ -1,8 +1,3 @@ -= 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? #error_explanation diff --git a/app/views/comments/edit.html.haml b/app/views/comments/edit.html.haml index 7b07b546d..912c8a8b8 100644 --- a/app/views/comments/edit.html.haml +++ b/app/views/comments/edit.html.haml @@ -1,3 +1,7 @@ = content_for :title, "Editing comment" +%p + Editing comment on + = link_to @comment.post.subject, @comment.post + = render 'form' diff --git a/app/views/comments/index.html.haml b/app/views/comments/index.html.haml index a6a95d2d9..64915fc3c 100644 --- a/app/views/comments/index.html.haml +++ b/app/views/comments/index.html.haml @@ -1,7 +1,15 @@ = content_for :title, "Recent comments" -- @recent_comments.each do |comment| +%div.pagination + = page_entries_info @comments, :model => "comments" + = will_paginate @comments + +- @comments.each do |comment| %h2 Comment on = link_to comment.post.subject, comment.post = render :partial => "single", :locals => { :comment => comment } + +%div.pagination + = page_entries_info @comments, :model => "comments" + = will_paginate @comments diff --git a/app/views/comments/index.rss.haml b/app/views/comments/index.rss.haml new file mode 100644 index 000000000..5e4b227c5 --- /dev/null +++ b/app/views/comments/index.rss.haml @@ -0,0 +1,23 @@ + +%rss{:version => 2.0} + %channel + %title #{Growstuff::Application.config.site_name} - Recent comments on all posts + %link= comments_url + - @comments.each do |comment| + %item + %title Comment by #{comment.author.login_name} on #{comment.post.subject} + %author= comment.author.login_name + %description + + :escaped +

+ Comment on + #{ link_to comment.post.subject, post_url(comment.post) } +

+ + :escaped_markdown + #{ strip_tags comment.body } + + %pubDate= comment.created_at.to_s(:rfc822) + %link= post_url(comment.post) + %guid= post_url(comment.post) diff --git a/app/views/comments/new.html.haml b/app/views/comments/new.html.haml index 66393a209..c01ee755f 100644 --- a/app/views/comments/new.html.haml +++ b/app/views/comments/new.html.haml @@ -1,3 +1,9 @@ = content_for :title, "New comment" += render :partial => "posts/single", :locals => { :post => @post || @comment.post, :subject => true } + += render :partial => "posts/comments", :locals => {:post => @post || @comment.post} + +%p Your comment: + = render 'form' diff --git a/app/views/crops/_form.html.haml b/app/views/crops/_form.html.haml index 12c917702..2b75de0b6 100644 --- a/app/views/crops/_form.html.haml +++ b/app/views/crops/_form.html.haml @@ -1,4 +1,4 @@ -= form_for @crop do |f| += form_for @crop, :html => {:class => 'form-horizontal'} do |f| - if @crop.errors.any? #error_explanation %h3= "#{pluralize(@crop.errors.count, "error")} prohibited this crop from being saved:" @@ -6,11 +6,26 @@ - @crop.errors.full_messages.each do |msg| %li= msg - .field - = f.label :system_name - = f.text_field :system_name - .field - = f.label :en_wikipedia_url - = f.text_field :en_wikipedia_url - .actions - = f.submit 'Save' + %p + %span.help-block + For detailed crop wrangling guidelines, please consult the + =link_to "crop wrangling guide", "http://wiki.growstuff.org/index.php/Crop_wrangling" + on the Growstuff wiki. + + .control-group + = f.label :system_name, :class => 'control-label' + .controls + = f.text_field :system_name + %span.help-inline Name in US English; singular; capitalize proper nouns only. + .control-group + = f.label :en_wikipedia_url, 'Wikipedia URL', :class => 'control-label' + .controls + = f.text_field :en_wikipedia_url + %span.help-inline Link to this crop's page on the English language Wikipedia. + .control-group + = f.label :parent_id, 'Parent crop', :class => 'control-label' + .controls + = collection_select(:crop, :parent_id, Crop.all, :id, :system_name, {:include_blank => true}) + %span.help-inline Optional. For setting up crop hierarchies for varieties etc. + .form-actions + = f.submit 'Save', :class => 'btn btn-primary' diff --git a/app/views/crops/index.rss.builder b/app/views/crops/index.rss.builder deleted file mode 100644 index 9410a6080..000000000 --- a/app/views/crops/index.rss.builder +++ /dev/null @@ -1,16 +0,0 @@ -xml.instruct! :xml, :version => "1.0" -xml.rss :version => "2.0" do - xml.channel do - xml.title "#{Growstuff::Application.config.site_name} - Recently added crops" - xml.link crops_url - - for crop in @new_crops - xml.item do - xml.title crop.system_name - xml.pubDate crop.created_at.to_s(:rfc822) - xml.link crop_url(crop) - xml.guid crop_url(crop) - end - end - end -end diff --git a/app/views/crops/index.rss.haml b/app/views/crops/index.rss.haml new file mode 100644 index 000000000..aabd178ac --- /dev/null +++ b/app/views/crops/index.rss.haml @@ -0,0 +1,11 @@ + +%rss{:version => 2.0} + %channel + %title #{Growstuff::Application.config.site_name} - Recently added crops + %link= crops_url + - @crops.each do |crop| + %item + %title= crop.system_name + %pubDate= crop.created_at.to_s(:rfc822) + %link= post_url(crop) + %guid= post_url(crop) diff --git a/app/views/crops/show.html.haml b/app/views/crops/show.html.haml index 3b92068c5..f9a08a076 100644 --- a/app/views/crops/show.html.haml +++ b/app/views/crops/show.html.haml @@ -2,6 +2,19 @@ .row .span9 + - if @crop.parent + %p + = @crop.system_name + is a variety of + = succeed "." do + = link_to @crop.parent, @crop.parent + - if @crop.varieties.count > 0 + %p + Varieties of + = succeed ":" do + = @crop.system_name + != @crop.varieties.map{ |c| link_to c, c }.join(", ") + - if @crop.plantings_count > 0 %p Planted @@ -17,19 +30,30 @@ %p= link_to "Plant this", new_planting_path(:crop_id => @crop.id), :class => 'btn btn-primary' .span3 + - if can? :edit, @crop or can? :destroy, @crop + %h4 Crop wrangling + %p + You are a + = succeed "." do + %strong CROP WRANGLER + %p + - if can? :edit, @crop + = link_to 'Edit crop', edit_crop_path(@crop), { :class => 'btn btn-mini' } + - if can? :destroy, @crop + = link_to 'Delete crop', @crop, method: :delete, data: { confirm: 'Are you sure?' }, :class => 'btn btn-mini' + %h4 Scientific names: %ul - @crop.scientific_names.each do |sn| - %li= sn.scientific_name + %li + = sn.scientific_name + - if can? :edit, sn + = link_to 'Edit', edit_scientific_name_path(sn), { :class => 'btn btn-mini' } + - if can? :destroy, sn + = link_to 'Delete', sn, method: :delete, data: { confirm: 'Are you sure?' }, :class => 'btn btn-mini' + %p + - if can? :edit, @crop + = link_to 'Add', new_scientific_name_path( :crop_id => @crop.id ), { :class => 'btn btn-mini' } %h4 More information: %ul %li= link_to 'Wikipedia (English)', @crop.en_wikipedia_url - - - if can? :edit, @crop or can? :destroy, @crop - %h4 Crop wrangling: - %p - - if can? :edit, @crop - = link_to 'Edit', edit_crop_path(@crop), { :class => 'btn btn-mini' } - - if can? :destroy, @crop - = link_to 'Delete', @crop, method: :delete, data: { confirm: 'Are you sure?' }, :class => 'btn btn-mini' - diff --git a/app/views/devise/registrations/edit.html.haml b/app/views/devise/registrations/edit.html.haml index d8a36e1ce..14733b9a6 100644 --- a/app/views/devise/registrations/edit.html.haml +++ b/app/views/devise/registrations/edit.html.haml @@ -53,6 +53,7 @@ - else =link_to 'Connect to Twitter', '/auth/twitter' %p + = image_tag "flickr_32.png", :size => "32x32", :alt => 'Flickr logo' - if @flickr_auth You are connected to Flickr as = succeed "." do diff --git a/app/views/members/nearby.html.haml b/app/views/members/nearby.html.haml index cbcf51da6..059196ae2 100644 --- a/app/views/members/nearby.html.haml +++ b/app/views/members/nearby.html.haml @@ -7,7 +7,7 @@ = 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' + = label_tag :location, "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 297bac8c7..918f02376 100644 --- a/app/views/members/show.html.haml +++ b/app/views/members/show.html.haml @@ -9,7 +9,7 @@ -if can? :create, Notification and current_member != @member %p %br/ - =link_to 'Send Message', new_notification_path(:sender_id => current_member.id, :recipient_id => @member.id), :class => 'btn btn-primary' + =link_to 'Send Message', new_notification_path(:recipient_id => @member.id), :class => 'btn btn-primary' %p %strong Member since: @@ -25,7 +25,7 @@ - if @flickr_auth %p - Flickr: + = image_tag "flickr_32.png", :size => "32x32", :alt => 'Flickr logo' =link_to @flickr_auth.name, "http://flickr.com/photos/#{@flickr_auth.uid}" - if @member.show_email @@ -66,7 +66,7 @@ %div :markdown - #{g.description} + #{ strip_tags g.description } %h3 What's planted here? - g.featured_plantings.each do |p| diff --git a/app/views/members/show.rss.builder b/app/views/members/show.rss.builder deleted file mode 100644 index 69a0ebba4..000000000 --- a/app/views/members/show.rss.builder +++ /dev/null @@ -1,18 +0,0 @@ -xml.instruct! :xml, :version => "1.0" -xml.rss :version => "2.0" do - xml.channel do - xml.title "#{Growstuff::Application.config.site_name} - #{@member.login_name}'s recent posts" - xml.link member_url(@member) - - for post in @posts - xml.item do - xml.author @member.login_name - xml.title post.subject - xml.description post.body - xml.pubDate post.created_at.to_s(:rfc822) - xml.link post_url(post) - xml.guid post_url(post) - end - end - end -end diff --git a/app/views/members/show.rss.haml b/app/views/members/show.rss.haml new file mode 100644 index 000000000..f6244623d --- /dev/null +++ b/app/views/members/show.rss.haml @@ -0,0 +1,15 @@ + +%rss{:version => 2.0} + %channel + %title #{Growstuff::Application.config.site_name} - #{@member.login_name}'s recent posts + %link= member_url(@member) + - @posts.each do |post| + %item + %pubDate= post.created_at.to_s(:rfc822) + %link= post_url(post) + %guid= post_url(post) + %author= @member.login_name + %title= post.subject + %description + :escaped_markdown + #{ strip_tags post.body } diff --git a/app/views/notifications/_form.html.haml b/app/views/notifications/_form.html.haml index a0b9f6d55..babb75fd0 100644 --- a/app/views/notifications/_form.html.haml +++ b/app/views/notifications/_form.html.haml @@ -13,7 +13,7 @@ To: = link_to @recipient, @recipient = label_tag :notification, "Subject:" - = f.text_field :subject, :class => 'input-block-level' + = f.text_field :subject, :value => @subject, :class => 'input-block-level' = label_tag :body, "Type your message here:" = f.text_area :body, :rows => 12, :class => 'input-block-level' %span.help-block diff --git a/app/views/notifications/index.html.haml b/app/views/notifications/index.html.haml index 08c00121b..ace28ca92 100644 --- a/app/views/notifications/index.html.haml +++ b/app/views/notifications/index.html.haml @@ -1,4 +1,4 @@ -- content_for :title, "Notifications" +- content_for :title, "Inbox" - if @notifications.length > 0 %table.table.table-striped diff --git a/app/views/notifications/show.html.haml b/app/views/notifications/show.html.haml index cbd66aeaf..3d4280640 100644 --- a/app/views/notifications/show.html.haml +++ b/app/views/notifications/show.html.haml @@ -15,4 +15,5 @@ #{ strip_tags(@notification.body) } %p - =link_to 'Delete', @notification, method: :delete, data: { confirm: 'Are you sure?' }, :class => 'btn btn-mini' + =link_to 'Delete', @notification, method: :delete, data: { confirm: 'Are you sure?' }, :class => 'btn' + =link_to 'Reply', @reply_link, :class => 'btn btn-primary' diff --git a/app/views/notifier/notify.html.haml b/app/views/notifier/notify.html.haml index cbbb82bcd..7ccf9bea6 100644 --- a/app/views/notifier/notify.html.haml +++ b/app/views/notifier/notify.html.haml @@ -14,6 +14,8 @@ #{strip_tags @notification.body} %p + = link_to "Reply to this message", @reply_link + %br/ = link_to "View this message in your inbox", notification_url(@notification) %br/ = link_to "Turn off these notifications", edit_member_registration_url diff --git a/app/views/plantings/index.html.haml b/app/views/plantings/index.html.haml index d359a342d..6e98960ad 100644 --- a/app/views/plantings/index.html.haml +++ b/app/views/plantings/index.html.haml @@ -2,7 +2,13 @@ %p Here are the latest things planted by #{Growstuff::Application.config.site_name} members. -- @recent_plantings.each do |p| +%div.pagination + = page_entries_info @plantings, :model => "plantings" + = will_paginate @plantings + +- @plantings.each do |p| = render :partial => "plantings/thumbnail", :locals => { :planting => p } - +%div.pagination + = page_entries_info @plantings, :model => "plantings" + = will_paginate @plantings diff --git a/app/views/plantings/index.rss.builder b/app/views/plantings/index.rss.builder deleted file mode 100644 index f203ca488..000000000 --- a/app/views/plantings/index.rss.builder +++ /dev/null @@ -1,18 +0,0 @@ -xml.instruct! :xml, :version => "1.0" -xml.rss :version => "2.0" do - xml.channel do - xml.title "#{Growstuff::Application.config.site_name} - Recent plantings from all members" - xml.link plantings_url - - for planting in @recent_plantings - xml.item do - xml.author planting.owner.login_name - xml.title planting.crop.system_name - xml.pubDate planting.created_at.to_s(:rfc822) - xml.description planting.description - xml.link planting_url(planting) - xml.guid planting_url(planting) - end - end - end -end diff --git a/app/views/plantings/index.rss.haml b/app/views/plantings/index.rss.haml new file mode 100644 index 000000000..d6ed95542 --- /dev/null +++ b/app/views/plantings/index.rss.haml @@ -0,0 +1,15 @@ + +%rss{:version => 2.0} + %channel + %title #{Growstuff::Application.config.site_name} - Recent plantings from all members + %link= plantings_url + - @plantings.each do |planting| + %item + %author= planting.owner.login_name + %title= planting.crop.system_name + %pubDate= planting.created_at.to_s(:rfc822) + %description + :escaped_markdown + #{ strip_tags planting.description } + %link= planting_url(planting) + %guid= planting_url(planting) diff --git a/app/views/posts/_comments.html.haml b/app/views/posts/_comments.html.haml index c06124014..bf565debc 100644 --- a/app/views/posts/_comments.html.haml +++ b/app/views/posts/_comments.html.haml @@ -1,8 +1,8 @@ %a{:name => "comments"} -- if @comments +- if post.comments %h2 - =pluralize(@comments.length, "comment") - - @comments.each do |c| + =pluralize(post.comments.length, "comment") + - post.comments.post_order.each do |c| = render :partial => "comments/single", :locals => { :comment => c } - else diff --git a/app/views/posts/index.rss.builder b/app/views/posts/index.rss.builder deleted file mode 100644 index 88025252c..000000000 --- a/app/views/posts/index.rss.builder +++ /dev/null @@ -1,18 +0,0 @@ -xml.instruct! :xml, :version => "1.0" -xml.rss :version => "2.0" do - xml.channel do - xml.title "#{Growstuff::Application.config.site_name} - Recent posts from all members" - xml.link posts_url - - for post in @recent_posts - xml.item do - xml.author post.author.login_name - xml.title post.subject - xml.description post.body - xml.pubDate post.created_at.to_s(:rfc822) - xml.link post_url(post) - xml.guid post_url(post) - end - end - end -end diff --git a/app/views/posts/index.rss.haml b/app/views/posts/index.rss.haml new file mode 100644 index 000000000..12402d0cf --- /dev/null +++ b/app/views/posts/index.rss.haml @@ -0,0 +1,15 @@ + +%rss{:version => 2.0} + %channel + %title #{Growstuff::Application.config.site_name} - Recent posts from all members + %link= posts_url + - @posts.each do |post| + %item + %author= post.author.login_name + %title= post.subject + %description + :escaped_markdown + #{ strip_tags post.body } + %pubDate= post.created_at.to_s(:rfc822) + %link= post_url(post) + %guid= post_url(post) diff --git a/app/views/posts/show.rss.haml b/app/views/posts/show.rss.haml new file mode 100644 index 000000000..4f260a038 --- /dev/null +++ b/app/views/posts/show.rss.haml @@ -0,0 +1,23 @@ + +%rss{:version => 2.0} + %channel + %title #{Growstuff::Application.config.site_name} - Recent comments on #{@post.subject} + %link= post_url(@post) + - @post.comments.each do |comment| + %item + %title Comment by #{comment.author.login_name} on #{comment.created_at.to_s} + %author= comment.author.login_name + %description + + :escaped +

+ Comment on + #{ link_to @post.subject, post_url(@post) } +

+ + :escaped_markdown + #{ strip_tags comment.body } + + %pubDate= comment.created_at.to_s(:rfc822) + %link= post_url(@post) + %guid= post_url(@post) diff --git a/app/views/scientific_names/_form.html.haml b/app/views/scientific_names/_form.html.haml index d2389e58d..503d324a2 100644 --- a/app/views/scientific_names/_form.html.haml +++ b/app/views/scientific_names/_form.html.haml @@ -1,4 +1,4 @@ -= form_for @scientific_name do |f| += form_for @scientific_name, :html => {:class => 'form-horizontal'} do |f| - if @scientific_name.errors.any? #error_explanation %h2= "#{pluralize(@scientific_name.errors.count, "error")} prohibited this scientific_name from being saved:" @@ -6,11 +6,19 @@ - @scientific_name.errors.full_messages.each do |msg| %li= msg - .field - = f.label :scientific_name - = f.text_field :scientific_name - .field - = f.label :crop_id - = f.number_field :crop_id - .actions - = f.submit 'Save' + %p + %span.help-block + For detailed crop wrangling guidelines, please consult the + =link_to "crop wrangling guide", "http://wiki.growstuff.org/index.php/Crop_wrangling" + on the Growstuff wiki. + + .control-group + = f.label :crop_id, :class => 'control-label' + .controls + = collection_select(:scientific_name, :crop_id, Crop.all, :id, :system_name, :selected => @scientific_name.crop_id || @crop.id) + .control-group + = f.label :scientific_name, :class => 'control-label' + .controls + = f.text_field :scientific_name + .form-actions + = f.submit 'Save', :class => 'btn btn-primary' diff --git a/app/views/scientific_names/edit.html.haml b/app/views/scientific_names/edit.html.haml index 58d434c3e..493ee3e44 100644 --- a/app/views/scientific_names/edit.html.haml +++ b/app/views/scientific_names/edit.html.haml @@ -1,3 +1,3 @@ -%h1 Editing scientific_name +- content_for :title, "Edit scientific name" = render 'form' diff --git a/app/views/scientific_names/new.html.haml b/app/views/scientific_names/new.html.haml index af8bef174..104a4b5a6 100644 --- a/app/views/scientific_names/new.html.haml +++ b/app/views/scientific_names/new.html.haml @@ -1,3 +1,3 @@ -%h1 New scientific_name +- content_for :title, "New scientific name" = render 'form' diff --git a/config/initializers/escaped_markdown.rb b/config/initializers/escaped_markdown.rb new file mode 100644 index 000000000..87837b0c5 --- /dev/null +++ b/config/initializers/escaped_markdown.rb @@ -0,0 +1 @@ +require 'haml/filters/escaped_markdown' diff --git a/db/migrate/20130514124515_add_parent_to_crop.rb b/db/migrate/20130514124515_add_parent_to_crop.rb new file mode 100644 index 000000000..8a6019808 --- /dev/null +++ b/db/migrate/20130514124515_add_parent_to_crop.rb @@ -0,0 +1,5 @@ +class AddParentToCrop < ActiveRecord::Migration + def change + add_column :crops, :parent_id, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 99a501fb4..426d275d0 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,23 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130509123711) do +ActiveRecord::Schema.define(:version => 20130518002942) do + + create_table "account_types", :force => true do |t| + t.string "name", :null => false + t.boolean "is_paid" + t.boolean "is_permanent_paid" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "accounts", :force => true do |t| + t.integer "member_id", :null => false + t.integer "account_type_id" + t.datetime "paid_until" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end create_table "authentications", :force => true do |t| t.integer "member_id", :null => false @@ -40,6 +56,7 @@ ActiveRecord::Schema.define(:version => 20130509123711) do t.datetime "created_at", :null => false t.datetime "updated_at", :null => false t.string "slug" + t.integer "parent_id" end add_index "crops", ["slug"], :name => "index_crops_on_slug", :unique => true @@ -120,10 +137,19 @@ ActiveRecord::Schema.define(:version => 20130509123711) do t.datetime "updated_at", :null => false end + create_table "order_items", :force => true do |t| + t.integer "order_id" + t.integer "product_id" + t.integer "price" + t.integer "quantity" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "orders", :force => true do |t| - t.string "member_id", :null => false - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.integer "member_id", :limit => 255, :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.datetime "completed_at" end @@ -132,14 +158,6 @@ ActiveRecord::Schema.define(:version => 20130509123711) do t.integer "product_id" 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 "photos", :force => true do |t| t.integer "owner_id", :null => false t.integer "flickr_photo_id", :null => false @@ -181,11 +199,13 @@ ActiveRecord::Schema.define(:version => 20130509123711) do add_index "posts", ["slug"], :name => "index_updates_on_slug", :unique => true create_table "products", :force => true do |t| - t.string "name", :null => false - t.string "description", :null => false - t.decimal "min_price", :null => false - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.string "name", :null => false + t.string "description", :null => false + t.integer "min_price", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "account_type_id" + t.integer "paid_months" end create_table "roles", :force => true do |t| diff --git a/lib/haml/filters/escaped_markdown.rb b/lib/haml/filters/escaped_markdown.rb new file mode 100644 index 000000000..c60256ac9 --- /dev/null +++ b/lib/haml/filters/escaped_markdown.rb @@ -0,0 +1,17 @@ +require 'bluecloth' + +module Haml::Filters + module EscapedMarkdown + include Haml::Filters::Base + + def render(text) + bc = BlueCloth.new(text) + return Haml::Helpers.html_escape bc.to_html + end + end + +# Register it as the handler for the :escaped_markdown HAML command. +# The automatic system gives us :escapedmarkdown, which is ugly. +defined['escaped_markdown'] = EscapedMarkdown + +end diff --git a/lib/tasks/growstuff.rake b/lib/tasks/growstuff.rake index e75887bd8..fb87ea319 100644 --- a/lib/tasks/growstuff.rake +++ b/lib/tasks/growstuff.rake @@ -26,6 +26,20 @@ namespace :growstuff do end end + task :empty_garden_names => :environment do + desc "May 2013: replace any empty garden names with Garden" + + # this is inefficient as it checks every Garden, but the + # site is small and there aren't many of them, so it shouldn't matter + # for this one-off script. + Garden.all.each do |g| + if g.name.nil? or g.name =~ /^\s*$/ + g.name = "Garden" + g.save + end + end + end + end end diff --git a/lib/tasks/testing.rake b/lib/tasks/testing.rake index 033245928..cb58df2c9 100644 --- a/lib/tasks/testing.rake +++ b/lib/tasks/testing.rake @@ -1,5 +1,9 @@ require 'rake' begin require 'rspec/core/rake_task' + task(:spec).clear + RSpec::Core::RakeTask.new(:spec) do |t| + t.verbose = false + end rescue LoadError end diff --git a/spec/controllers/comments_controller_spec.rb b/spec/controllers/comments_controller_spec.rb index 587d24407..128bcdd4e 100644 --- a/spec/controllers/comments_controller_spec.rb +++ b/spec/controllers/comments_controller_spec.rb @@ -14,7 +14,15 @@ describe CommentsController do comment = Comment.create! valid_attributes get :index, {} assigns(:comments).should eq([comment]) - assigns(:recent_comments).should eq([comment]) + end + end + + describe "GET RSS feed" do + it "returns an RSS feed" do + get :index, :format => "rss" + response.should be_success + response.should render_template("comments/index") + response.content_type.should eq("application/rss+xml") end end @@ -63,7 +71,7 @@ describe CommentsController do 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]) + assigns(:comments).should eq([comment, old_comment]) end end diff --git a/spec/controllers/crops_controller_spec.rb b/spec/controllers/crops_controller_spec.rb index ed7b0e0e9..f56c7ee3b 100644 --- a/spec/controllers/crops_controller_spec.rb +++ b/spec/controllers/crops_controller_spec.rb @@ -5,7 +5,10 @@ describe CropsController do login_member(:crop_wrangling_member) def valid_attributes - { :system_name => "Tomato" } + { + :system_name => "Tomato", + :en_wikipedia_url => 'http://en.wikipedia.org/wiki/Tomato' + } end describe "GET index" do @@ -13,7 +16,6 @@ describe CropsController do crop = Crop.create! valid_attributes get :index, {} assigns(:crops).should eq([crop]) - assigns(:new_crops).should eq([crop]) end end diff --git a/spec/controllers/notifications_controller_spec.rb b/spec/controllers/notifications_controller_spec.rb index 155273f07..be3fe5400 100644 --- a/spec/controllers/notifications_controller_spec.rb +++ b/spec/controllers/notifications_controller_spec.rb @@ -7,7 +7,8 @@ describe NotificationsController do def valid_attributes { "recipient_id" => subject.current_member.id, - "sender_id" => FactoryGirl.create(:member).id + "sender_id" => FactoryGirl.create(:member).id, + "subject" => 'test' } end @@ -19,7 +20,8 @@ describe NotificationsController do def valid_attributes_for_sender { "sender_id" => subject.current_member.id, - "recipient_id" => FactoryGirl.create(:member).id + "recipient_id" => FactoryGirl.create(:member).id, + "subject" => 'test' } end @@ -42,6 +44,28 @@ describe NotificationsController do assigns(:notification).should eq(notification) end + it "assigns the reply link for a PM" do + notification = FactoryGirl.create(:notification, :recipient_id => subject.current_member.id, :post_id => nil) + subject = "Re: " + notification.subject + + get :show, {:id => notification.to_param} + assigns(:reply_link).should_not be_nil + assigns(:reply_link).should eq new_notification_url( + :recipient_id => notification.sender_id, + :subject => subject + ) + end + + it "assigns the reply link for a post comment" do + notification = FactoryGirl.create(:notification, :recipient_id => subject.current_member.id) + + get :show, {:id => notification.to_param} + assigns(:reply_link).should_not be_nil + assigns(:reply_link).should eq new_comment_url( + :post_id => notification.post.id + ) + end + it "marks notifications as read" do notification = FactoryGirl.create(:notification, :recipient_id => subject.current_member.id) get :show, {:id => notification.to_param} @@ -95,8 +119,8 @@ describe NotificationsController do it "redirects to the recipient's profile" do @recipient = FactoryGirl.create(:member) - post :create, { :notification => { :recipient_id => @recipient.id } } - response.should redirect_to(@recipient) + post :create, { :notification => { :recipient_id => @recipient.id, :subject => 'foo' } } + response.should redirect_to(notifications_path) end end diff --git a/spec/controllers/plantings_controller_spec.rb b/spec/controllers/plantings_controller_spec.rb index 77f9c519a..fec82c7e8 100644 --- a/spec/controllers/plantings_controller_spec.rb +++ b/spec/controllers/plantings_controller_spec.rb @@ -16,7 +16,6 @@ describe PlantingsController do planting = Planting.create! valid_attributes get :index, {} assigns(:plantings).should eq([planting]) - assigns(:recent_plantings).should eq([planting]) end end @@ -161,10 +160,11 @@ describe PlantingsController do }.to change(Planting, :count).by(-1) end - it "redirects to the plantings list" do + it "redirects to the garden" do planting = Planting.create! valid_attributes + garden = planting.garden delete :destroy, {:id => planting.to_param} - response.should redirect_to(plantings_url) + response.should redirect_to(garden) end end diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb index 5fe9c1ddd..bdae1ce48 100644 --- a/spec/controllers/posts_controller_spec.rb +++ b/spec/controllers/posts_controller_spec.rb @@ -13,7 +13,6 @@ describe PostsController do post = Post.create! valid_attributes get :index, {} assigns(:posts).should eq([post]) - assigns(:recent_posts).should eq([post]) end end @@ -34,6 +33,16 @@ describe PostsController do end end + describe "GET RSS feed for individual post" do + it "returns an RSS feed" do + post = Post.create! valid_attributes + get :show, { :format => "rss", :id => post.slug } + response.should be_success + response.should render_template("posts/show") + response.content_type.should eq("application/rss+xml") + end + end + describe "GET new" do it "assigns a new post as @post" do get :new, {} diff --git a/spec/controllers/scientific_names_controller_spec.rb b/spec/controllers/scientific_names_controller_spec.rb index dda252d6b..0cf431a4a 100644 --- a/spec/controllers/scientific_names_controller_spec.rb +++ b/spec/controllers/scientific_names_controller_spec.rb @@ -4,8 +4,12 @@ describe ScientificNamesController do login_member(:crop_wrangling_member) + before(:each) do + @crop = FactoryGirl.create(:tomato) + end + def valid_attributes - { :scientific_name => 'Solanum lycopersicum', :crop_id => 1 } + { :scientific_name => 'Solanum lycopersicum', :crop_id => @crop.id } end describe "GET index" do @@ -29,6 +33,32 @@ describe ScientificNamesController do get :new, {} assigns(:scientific_name).should be_a_new(ScientificName) end + + it "assigns crop if specified" do + get :new, { :crop_id => 1 } + assigns(:crop).should be_an_instance_of Crop + end + + it "assigns crop if specified" do + end + end + + describe "GET edit" do + it "assigns the requested scientific_name as @scientific_name" do + scientific_name = ScientificName.create! valid_attributes + get :edit, {:id => scientific_name.to_param} + assigns(:scientific_name).should eq(scientific_name) + end + end + + describe "POST create" do + describe "with valid params" do + it "creates a new ScientificName" do + expect { + post :create, {:scientific_name => valid_attributes} + }.to change(ScientificName, :count).by(1) + end + end end describe "GET edit" do @@ -55,7 +85,7 @@ describe ScientificNamesController do it "redirects to the created scientific_name" do post :create, {:scientific_name => valid_attributes} - response.should redirect_to(ScientificName.last) + response.should redirect_to(ScientificName.last.crop) end end @@ -97,7 +127,7 @@ describe ScientificNamesController do it "redirects to the scientific_name" do scientific_name = ScientificName.create! valid_attributes put :update, {:id => scientific_name.to_param, :scientific_name => valid_attributes} - response.should redirect_to(scientific_name) + response.should redirect_to(scientific_name.crop) end end @@ -130,8 +160,9 @@ describe ScientificNamesController do it "redirects to the scientific_names list" do scientific_name = ScientificName.create! valid_attributes + crop = scientific_name.crop delete :destroy, {:id => scientific_name.to_param} - response.should redirect_to(scientific_names_url) + response.should redirect_to(crop) end end diff --git a/spec/factories/crop.rb b/spec/factories/crop.rb index 4ae2367d0..76fd31433 100644 --- a/spec/factories/crop.rb +++ b/spec/factories/crop.rb @@ -30,6 +30,16 @@ FactoryGirl.define do system_name "Pear" end + # for testing varieties + factory :roma do + system_name "Roma tomato" + end + + factory :popcorn do + system_name "popcorn" + end + + # This should have a name that is alphabetically earlier than :uppercase # crop to ensure that the ordering tests work. factory :lowercasecrop do diff --git a/spec/factories/scientific_name.rb b/spec/factories/scientific_name.rb index 7bb3e4cd9..e1bffb5cd 100644 --- a/spec/factories/scientific_name.rb +++ b/spec/factories/scientific_name.rb @@ -1,12 +1,17 @@ FactoryGirl.define do factory :scientific_name do + association :crop, factory: :crop + scientific_name "Beanus Magicus" + factory :zea_mays do association :crop, factory: :maize scientific_name "Zea mays" end + factory :solanum_lycopersicum do association :crop, factory: :tomato scientific_name "Solanum lycopersicum" end + end end diff --git a/spec/helpers/notifications_helper_spec.rb b/spec/helpers/notifications_helper_spec.rb new file mode 100644 index 000000000..9db3a95aa --- /dev/null +++ b/spec/helpers/notifications_helper_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe NotificationsHelper do + describe "reply_link" do + + before(:each) do + @member = FactoryGirl.create(:member) + end + + it "replies to PMs with PMs" do + notification = FactoryGirl.create(:notification, :recipient_id => @member.id, :post_id => nil) + subject = "Re: " + notification.subject + + link = helper.reply_link(notification) + link.should_not be_nil + link.should eq new_notification_url( + :recipient_id => notification.sender_id, + :subject => subject + ) + end + + it "replies to post comments with post comments" do + notification = FactoryGirl.create(:notification, :recipient_id => @member.id) + + link = helper.reply_link(notification) + link.should_not be_nil + link.should eq new_comment_url( + :post_id => notification.post.id + ) + end + + + end +end diff --git a/spec/helpers/plantings_helper_spec.rb b/spec/helpers/plantings_helper_spec.rb deleted file mode 100644 index 25a84ec96..000000000 --- a/spec/helpers/plantings_helper_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'spec_helper' - -# Specs in this file have access to a helper object that includes -# the PlantingsHelper. For example: -# -# describe PlantingsHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# helper.concat_strings("this","that").should == "this that" -# end -# end -# end -describe PlantingsHelper do -end diff --git a/spec/lib/haml/filters/escaped_markdown_spec.rb b/spec/lib/haml/filters/escaped_markdown_spec.rb new file mode 100644 index 000000000..99c779e88 --- /dev/null +++ b/spec/lib/haml/filters/escaped_markdown_spec.rb @@ -0,0 +1,16 @@ +require 'haml/filters' +require 'haml/filters/escaped_markdown' +require 'haml/helpers' + +describe 'Haml::Filters::Escaped_Markdown' do + it 'is registered as the handler for :escaped_markdown' do + Haml::Filters::defined['escaped_markdown'].should == + Haml::Filters::EscapedMarkdown + end + + it 'converts Markdown to escaped HTML' do + rendered = Haml::Filters::EscapedMarkdown.render("**foo**") + rendered.should == "<p><strong>foo</strong></p>" + end + +end diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb index 7bad161bf..d487d0b91 100644 --- a/spec/models/comment_spec.rb +++ b/spec/models/comment_spec.rb @@ -2,40 +2,65 @@ require 'spec_helper' describe Comment do - before(:each) do - @comment = FactoryGirl.create(:comment) + context "basic" do + before(:each) do + @comment = FactoryGirl.create(:comment) + end + + it "belongs to a post" do + @comment.post.should be_an_instance_of Post + end + + it "belongs to an author" do + @comment.author.should be_an_instance_of Member + end end - it "belongs to a post" do - @comment.post.should be_an_instance_of Post + context "notifications" do + before(:each) do + @comment = FactoryGirl.create(:comment) + end + + it "sends a notification when a comment is posted" do + expect { + FactoryGirl.create(:comment) + }.to change(Notification, :count).by(1) + end + + it "sets the notification fields" do + @c = FactoryGirl.create(:comment) + @n = Notification.first + @n.sender.should eq @c.author + @n.recipient.should eq @c.post.author + @n.subject.should match /commented on/ + @n.body.should eq @c.body + @n.post.should eq @c.post + end + + it "doesn't send notifications to yourself" do + @m = FactoryGirl.create(:member) + @p = FactoryGirl.create(:post, :author => @m) + expect { + FactoryGirl.create(:comment, :post => @p, :author => @m) + }.to change(Notification, :count).by(0) + end end - it "belongs to an author" do - @comment.author.should be_an_instance_of Member - end + context "ordering" do + before(:each) do + @m = FactoryGirl.create(:member) + @p = FactoryGirl.create(:post, :author => @m) + @c1 = FactoryGirl.create(:comment, :post => @p, :author => @m) + @c2 = FactoryGirl.create(:comment, :post => @p, :author => @m) + end - it "sends a notification when a comment is posted" do - expect { - FactoryGirl.create(:comment) - }.to change(Notification, :count).by(1) - end + it 'is in DESC order by default' do + Comment.all.should eq [@c2, @c1] + end - it "sets the notification fields" do - @c = FactoryGirl.create(:comment) - @n = Notification.first - @n.sender.should eq @c.author - @n.recipient.should eq @c.post.author - @n.subject.should match /commented on/ - @n.body.should eq @c.body - @n.post.should eq @c.post - end - - it "doesn't send notifications to yourself" do - @m = FactoryGirl.create(:member) - @p = FactoryGirl.create(:post, :author => @m) - expect { - FactoryGirl.create(:comment, :post => @p, :author => @m) - }.to change(Notification, :count).by(0) + it 'has a scope for ASC order for displaying on post page' do + Comment.post_order.should eq [@c1, @c2] + end end end diff --git a/spec/models/crop_spec.rb b/spec/models/crop_spec.rb index 8e336d2ba..bea69ed01 100644 --- a/spec/models/crop_spec.rb +++ b/spec/models/crop_spec.rb @@ -27,22 +27,11 @@ describe Crop do context 'invalid data' do it 'should not save a crop without a system name' do - @crop = Crop.new + @crop = FactoryGirl.build(:crop, :system_name => nil) expect { @crop.save }.to raise_error ActiveRecord::StatementInvalid end end - context 'random' do - before(:each) do - @crop = FactoryGirl.create(:tomato) - end - - it 'should find a random crop' do - @rand_crop = Crop.random - @rand_crop.system_name.should == 'Tomato' - end - end - context 'ordering' do it "should be sorted case-insensitively" do uppercase = FactoryGirl.create(:uppercasecrop) @@ -64,4 +53,20 @@ describe Crop do FactoryGirl.create(:planting, :crop => @c) @c.plantings_count.should eq 1 end + + it 'validates en_wikipedia_url' do + @crop = FactoryGirl.build(:tomato, :en_wikipedia_url => 'this is not valid') + @crop.should_not be_valid + @crop = FactoryGirl.build(:tomato, :en_wikipedia_url => 'http://en.wikipedia.org/wiki/SomePage') + @crop.should be_valid + end + + context 'varieties' do + it 'has a crop hierarchy' do + @tomato = FactoryGirl.create(:tomato) + @roma = FactoryGirl.create(:roma, :parent_id => @tomato.id) + @roma.parent.should eq @tomato + @tomato.varieties.should eq [@roma] + end + end end diff --git a/spec/models/garden_spec.rb b/spec/models/garden_spec.rb index 1523c504b..cbbf3a950 100644 --- a/spec/models/garden_spec.rb +++ b/spec/models/garden_spec.rb @@ -14,6 +14,21 @@ describe Garden do @garden.description.should == "This is a **totally** cool garden" end + it "doesn't allow a nil name" do + @garden = FactoryGirl.build(:garden, :name => nil) + @garden.should_not be_valid + end + + it "doesn't allow a blank name" do + @garden = FactoryGirl.build(:garden, :name => "") + @garden.should_not be_valid + end + + it "doesn't allow a name with only spaces" do + @garden = FactoryGirl.build(:garden, :name => " ") + @garden.should_not be_valid + end + it "should have an owner" do @garden.owner.should be_an_instance_of Member end diff --git a/spec/views/comments/index.html.haml_spec.rb b/spec/views/comments/index.html.haml_spec.rb index f2633c648..dac2dcc5b 100644 --- a/spec/views/comments/index.html.haml_spec.rb +++ b/spec/views/comments/index.html.haml_spec.rb @@ -3,10 +3,17 @@ require 'spec_helper' describe "comments/index" do before(:each) do controller.stub(:current_user) { nil } - assign(:recent_comments, [ - FactoryGirl.create(:comment), - FactoryGirl.create(:comment, :body => 'ROFL') - ]) + page = 1 + per_page = 2 + total_entries = 2 + comments = WillPaginate::Collection.create(page, per_page, total_entries) do |pager| + pager.replace([ + FactoryGirl.create(:comment), + FactoryGirl.create(:comment, :body => 'ROFL') + ]) + end + assign(:comments, comments) + render end it "renders a list of comments" do diff --git a/spec/views/comments/index.rss.haml_spec.rb b/spec/views/comments/index.rss.haml_spec.rb new file mode 100644 index 000000000..e5eeeac95 --- /dev/null +++ b/spec/views/comments/index.rss.haml_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe 'comments/index.rss.haml' do + before(:each) do + controller.stub(:current_user) { nil } + @author = FactoryGirl.create(:member) + @post = FactoryGirl.create(:post) + assign(:comments, [ + FactoryGirl.create(:comment, :author => @author, :post => @post), + FactoryGirl.create(:comment, :author => @author, :post => @post) + ]) + render + end + + it 'shows RSS feed title' do + rendered.should contain "Recent comments on all posts" + end + + it 'shows item title' do + rendered.should contain "Comment by #{@author.login_name}" + end + + it 'escapes html for link to post' do + # it's then unescaped by 'render' so we don't actually look for < + rendered.should contain ' @post) - assign(:comment, FactoryGirl.create(:comment, :post => @post)) - assign(:comments, [@previous_comment]) + @comment = FactoryGirl.create(:comment, :post => @post) + assign(:comment, @comment) + assign(:comments, [@comment]) render end @@ -15,7 +15,7 @@ describe "comments/new" do end it "shows previous comments" do - rendered.should contain @previous_comment.body + rendered.should contain @comment.body end it "shows the correct comment count" do diff --git a/spec/views/crops/index.rss.haml_spec.rb b/spec/views/crops/index.rss.haml_spec.rb new file mode 100644 index 000000000..8790a7d80 --- /dev/null +++ b/spec/views/crops/index.rss.haml_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe 'crops/index.rss.haml' do + before(:each) do + controller.stub(:current_user) { nil } + @author = FactoryGirl.create(:member) + assign(:crops, [ + FactoryGirl.create(:tomato), + FactoryGirl.create(:maize) + ]) + render + end + + it 'shows RSS feed title' do + rendered.should contain "Recently added crops" + end + + it 'shows names of crops' do + rendered.should contain "Tomato" + rendered.should contain "Maize" + end + +end diff --git a/spec/views/crops/new.html.haml_spec.rb b/spec/views/crops/new.html.haml_spec.rb index 548e0acd3..73e22dacc 100644 --- a/spec/views/crops/new.html.haml_spec.rb +++ b/spec/views/crops/new.html.haml_spec.rb @@ -10,12 +10,16 @@ describe "crops/new" do end it "renders new crop form" do - render # Run the generator again with the --webrat flag if you want to use webrat matchers assert_select "form", :action => crops_path, :method => "post" do assert_select "input#crop_system_name", :name => "crop[system_name]" assert_select "input#crop_en_wikipedia_url", :name => "crop[en_wikipedia_url]" + assert_select "select#crop_parent_id", :name => "crop[parent_id]" end end + it "shows a link to crop wrangling guidelines" do + assert_select "a[href^=http://wiki.growstuff.org]", "crop wrangling guide" + end + end diff --git a/spec/views/crops/show.html.haml_spec.rb b/spec/views/crops/show.html.haml_spec.rb index 8467e19b9..0b34be50e 100644 --- a/spec/views/crops/show.html.haml_spec.rb +++ b/spec/views/crops/show.html.haml_spec.rb @@ -44,6 +44,25 @@ describe "crops/show" do rendered.should contain "Springfield Community Garden" end + context 'varieties' do + before(:each) do + @popcorn = FactoryGirl.create(:popcorn, :parent_id => @crop.id) + @ubercrop = FactoryGirl.create(:crop, :system_name => 'ubercrop') + @crop.parent_id = @ubercrop.id + @crop.save + render + end + + it 'shows popcorn as a child variety' do + rendered.should contain @popcorn.system_name + end + + it 'shows parent crop' do + rendered.should contain @ubercrop.system_name + end + + end + context "logged in and crop wrangler" do before(:each) do @@ -54,7 +73,15 @@ describe "crops/show" do end it "links to the edit crop form" do - rendered.should contain "Edit" + assert_select "a[href=#{edit_crop_path(@crop)}]", :text => "Edit crop" + end + + it "links to the add scientific name form" do + assert_select "a[href^=#{new_scientific_name_path}]", :text => "Add" + end + + it "links to the edit scientific name form" do + assert_select "a[href=#{edit_scientific_name_path(@crop.scientific_names.first)}]", :text => "Edit" end end diff --git a/spec/views/members/show.rss.builder_spec.rb b/spec/views/members/show.rss.haml_spec.rb similarity index 51% rename from spec/views/members/show.rss.builder_spec.rb rename to spec/views/members/show.rss.haml_spec.rb index 320229e3a..adb6e0bc3 100644 --- a/spec/views/members/show.rss.builder_spec.rb +++ b/spec/views/members/show.rss.haml_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' -describe 'members/show.rss.builder', :type => "view" do +describe 'members/show.rss.haml', :type => "view" do before(:each) do @member = assign(:member, FactoryGirl.create(:member)) assign(:posts, [ FactoryGirl.build(:post, :id => 1, :author => @member), - FactoryGirl.build(:post, :id => 2, :author => @member) + FactoryGirl.build(:markdown_post, :id => 2, :author => @member) ]) render end @@ -18,4 +18,10 @@ describe 'members/show.rss.builder', :type => "view" do rendered.should contain "This is some text." end + it 'renders post bodies to HTML and XML-escapes them' do +# The variable "rendered" has been entity-replaced and tag-stripped +# The literal string output contains "<strong>" etc. + rendered.should contain "strong" + end + end diff --git a/spec/views/notifications/new.html.haml_spec.rb b/spec/views/notifications/new.html.haml_spec.rb index 441905ef0..d96358da8 100644 --- a/spec/views/notifications/new.html.haml_spec.rb +++ b/spec/views/notifications/new.html.haml_spec.rb @@ -5,7 +5,6 @@ describe "notifications/new" do @recipient = FactoryGirl.create(:member) @sender = FactoryGirl.create(:member) assign(:notification, FactoryGirl.create(:notification, :recipient_id => @recipient.id, :sender_id => @sender.id)) -# assign(:forum, Forum.new) sign_in @sender controller.stub(:current_user) { @sender} end @@ -23,6 +22,22 @@ describe "notifications/new" do rendered.should contain @recipient.login_name end + it "puts the recipient in a hidden field" do + render + assert_select "input#notification_recipient_id[type=hidden]", :name => "notification[recipient_id]" + end + + it "fills in the subject if provided" do + assign(:subject, 'Foo') + render + assert_select "input#notification_subject", :value => "Foo" + end + + it "leaves the subject empty if not provided" do + render + assert_select "input#notification_subject", :value => "" + end + it "Tells you to write your message here" do render rendered.should contain "Type your message here" diff --git a/spec/views/notifications/show.html.haml_spec.rb b/spec/views/notifications/show.html.haml_spec.rb index 26251395b..3028e8f0e 100644 --- a/spec/views/notifications/show.html.haml_spec.rb +++ b/spec/views/notifications/show.html.haml_spec.rb @@ -5,6 +5,7 @@ describe "notifications/show" do @member = FactoryGirl.create(:member) @notification = FactoryGirl.create(:notification, :recipient => @member) assign(:notification, @notification) + @reply_link = assign(:reply_link, new_notification_path) controller.stub(:current_user) { @member } render end @@ -18,4 +19,8 @@ describe "notifications/show" do assert_select "a", "Delete" end + it "includes a reply button" do + assert_select "a[href=#{@reply_link}]", "Reply" + end + end diff --git a/spec/views/notifier/notify.html.haml_spec.rb b/spec/views/notifier/notify.html.haml_spec.rb index 3f70895c1..687e702cc 100644 --- a/spec/views/notifier/notify.html.haml_spec.rb +++ b/spec/views/notifier/notify.html.haml_spec.rb @@ -4,6 +4,8 @@ describe 'notifier/notify.html.haml', :type => "view" do before(:each) do @notification = FactoryGirl.create(:notification) + @reply_link = "http://example.com" + assign(:reply_link, @reply_link) render end @@ -16,6 +18,10 @@ describe 'notifier/notify.html.haml', :type => "view" do rendered.should contain @notification.post.subject end + it 'should include a reply link' do + assert_select "a[href=#{@reply_link}]", :text => /Reply/ + end + it 'should contain a link to your inbox' do assert_select "a[href*=notifications]" end diff --git a/spec/views/plantings/index.html.haml_spec.rb b/spec/views/plantings/index.html.haml_spec.rb index cb7cec5a5..16339f665 100644 --- a/spec/views/plantings/index.html.haml_spec.rb +++ b/spec/views/plantings/index.html.haml_spec.rb @@ -7,18 +7,24 @@ describe "plantings/index" do @garden = FactoryGirl.create(:garden, :owner => @member) @tomato = FactoryGirl.create(:tomato) @maize = FactoryGirl.create(:maize) - assign(:recent_plantings, [ - FactoryGirl.create(:planting, - :garden => @garden, - :crop => @tomato - ), - FactoryGirl.create(:planting, - :garden => @garden, - :crop => @maize, - :description => '', - :planted_at => Time.local(2013, 1, 13) - ) - ]) + page = 1 + per_page = 2 + total_entries = 2 + plantings = WillPaginate::Collection.create(page, per_page, total_entries) do |pager| + pager.replace([ + FactoryGirl.create(:planting, + :garden => @garden, + :crop => @tomato + ), + FactoryGirl.create(:planting, + :garden => @garden, + :crop => @maize, + :description => '', + :planted_at => Time.local(2013, 1, 13) + ) + ]) + end + assign(:plantings, plantings) render end diff --git a/spec/views/plantings/index.rss.builder_spec.rb b/spec/views/plantings/index.rss.haml_spec.rb similarity index 58% rename from spec/views/plantings/index.rss.builder_spec.rb rename to spec/views/plantings/index.rss.haml_spec.rb index abc847d45..a689707fc 100644 --- a/spec/views/plantings/index.rss.builder_spec.rb +++ b/spec/views/plantings/index.rss.haml_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe 'plantings/index.rss.builder', :type => "view" do +describe 'plantings/index.rss.haml' do before(:each) do controller.stub(:current_user) { nil } - assign(:recent_plantings, [ + assign(:plantings, [ FactoryGirl.create(:planting) ]) render @@ -13,8 +13,8 @@ describe 'plantings/index.rss.builder', :type => "view" do rendered.should contain "Recent plantings from all members" end - it 'shows content of posts' do - rendered.should contain "This is a *really* good plant." + it 'shows formatted content of posts' do + rendered.should contain "This is a really good plant." end end diff --git a/spec/views/posts/index.rss.builder_spec.rb b/spec/views/posts/index.rss.haml_spec.rb similarity index 84% rename from spec/views/posts/index.rss.builder_spec.rb rename to spec/views/posts/index.rss.haml_spec.rb index f2b6c87e4..cd5f74c4d 100644 --- a/spec/views/posts/index.rss.builder_spec.rb +++ b/spec/views/posts/index.rss.haml_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -describe 'posts/index.rss.builder', :type => "view" do +describe 'posts/index.rss.haml', :type => "view" do before(:each) do controller.stub(:current_user) { nil } @author = FactoryGirl.create(:member) - assign(:recent_posts, [ + assign(:posts, [ FactoryGirl.build(:post, :id => 1, :author => @author), FactoryGirl.build(:post, :id => 2, :author => @author) ]) diff --git a/spec/views/posts/show.rss.haml_spec.rb b/spec/views/posts/show.rss.haml_spec.rb new file mode 100644 index 000000000..2f2780d09 --- /dev/null +++ b/spec/views/posts/show.rss.haml_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe 'posts/show.rss.haml' do + before(:each) do + controller.stub(:current_user) { nil } + @author = FactoryGirl.create(:member) + @post = FactoryGirl.create(:post) + FactoryGirl.create(:comment, :author => @author, :post => @post) + FactoryGirl.create(:comment, :author => @author, :post => @post) + assign(:post, @post) + render + end + + it 'shows RSS feed title' do + rendered.should contain "Recent comments on #{@post.subject}" + end + + it 'shows item title' do + rendered.should contain "Comment by #{@author.login_name}" + end + + it 'escapes html for link to post' do + # it's then unescaped by 'render' so we don't actually look for < + rendered.should contain ' scientific_names_path(@scientific_name), :method => "post" do assert_select "input#scientific_name_scientific_name", :name => "scientific_name[scientific_name]" - assert_select "input#scientific_name_crop_id", :name => "scientific_name[crop_id]" + assert_select "select#scientific_name_crop_id", :name => "scientific_name[crop_id]" end end end diff --git a/spec/views/scientific_names/new.html.haml_spec.rb b/spec/views/scientific_names/new.html.haml_spec.rb index 9d4fc93ae..9c892f5ef 100644 --- a/spec/views/scientific_names/new.html.haml_spec.rb +++ b/spec/views/scientific_names/new.html.haml_spec.rb @@ -18,7 +18,7 @@ describe "scientific_names/new" do # Run the generator again with the --webrat flag if you want to use webrat matchers assert_select "form", :action => scientific_names_path, :method => "post" do assert_select "input#scientific_name_scientific_name", :name => "scientific_name[scientific_name]" - assert_select "input#scientific_name_crop_id", :name => "scientific_name[crop_id]" + assert_select "select#scientific_name_crop_id", :name => "scientific_name[crop_id]" end end