mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-02-02 21:51:01 -05:00
merge
This commit is contained in:
@@ -52,4 +52,6 @@ submit the change with your pull request.
|
||||
- Kevin Yang / [kevieyang](https://github.com/kevieyang)
|
||||
- Justin Hamman / [juzham](https://github.com/juzham)
|
||||
- Rocky Jaiswal / [rocky-jaiswal](https://github.com/rocky-jaiswal)
|
||||
- Robert Landreaux / [robertlandreaux](https://github.com/robertlandreaux)
|
||||
- Savant Krishna / [sksavant](https://github.com/sksavant)
|
||||
|
||||
|
||||
2
Gemfile
2
Gemfile
@@ -33,6 +33,8 @@ gem 'gibbon' # for Mailchimp newsletter subscriptions
|
||||
gem 'csv_shaper' # CSV export
|
||||
gem 'ruby-units' # for unit conversion
|
||||
|
||||
gem 'comfortable_mexican_sofa', '~> 1.12.0' # content management system
|
||||
|
||||
# vendored activemerchant for testing- needed for bogus paypal
|
||||
# gateway monkeypatch
|
||||
gem 'activemerchant', '1.33.0',
|
||||
|
||||
42
Gemfile.lock
42
Gemfile.lock
@@ -33,6 +33,8 @@ GEM
|
||||
activesupport (= 4.1.9)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
active_link_to (1.0.2)
|
||||
actionpack
|
||||
activemodel (4.1.9)
|
||||
activesupport (= 4.1.9)
|
||||
builder (~> 3.1)
|
||||
@@ -48,6 +50,9 @@ GEM
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.3.6)
|
||||
arel (5.0.1.20140414130214)
|
||||
autoprefixer-rails (5.1.1)
|
||||
execjs
|
||||
json
|
||||
bcrypt (3.1.9)
|
||||
better_errors (2.0.0)
|
||||
coderay (>= 1.0.0)
|
||||
@@ -59,6 +64,10 @@ GEM
|
||||
bonsai-elasticsearch-rails (0.0.4)
|
||||
bootstrap-datepicker-rails (1.3.0.2)
|
||||
railties (>= 3.0)
|
||||
bootstrap-sass (3.3.3)
|
||||
autoprefixer-rails (>= 5.0.0.1)
|
||||
sass (>= 3.2.19)
|
||||
bootstrap_form (2.2.0)
|
||||
builder (3.2.2)
|
||||
byebug (3.5.1)
|
||||
columnize (~> 0.8)
|
||||
@@ -74,7 +83,13 @@ GEM
|
||||
capybara-email (2.4.0)
|
||||
capybara (~> 2.4)
|
||||
mail
|
||||
climate_control (0.0.3)
|
||||
activesupport (>= 3.0)
|
||||
cliver (0.3.2)
|
||||
cocaine (0.5.5)
|
||||
climate_control (>= 0.0.3, < 1.0)
|
||||
codemirror-rails (4.8)
|
||||
railties (>= 3.0, < 5)
|
||||
coderay (1.1.0)
|
||||
coffee-rails (4.1.0)
|
||||
coffee-script (>= 2.2.0)
|
||||
@@ -84,6 +99,21 @@ GEM
|
||||
execjs
|
||||
coffee-script-source (1.8.0)
|
||||
columnize (0.9.0)
|
||||
comfortable_mexican_sofa (1.12.7)
|
||||
active_link_to (>= 1.0.0)
|
||||
bootstrap-sass (>= 3.2.0)
|
||||
bootstrap_form (>= 2.2.0)
|
||||
codemirror-rails (>= 3.0.0)
|
||||
coffee-rails (>= 3.1.0)
|
||||
haml-rails (>= 0.3.0)
|
||||
jquery-rails (>= 3.0.0)
|
||||
jquery-ui-rails (>= 5.0.0)
|
||||
kramdown (>= 1.0.0)
|
||||
paperclip (>= 4.0.0)
|
||||
plupload-rails (>= 1.2.1)
|
||||
rails (>= 4.0.0, < 5)
|
||||
rails-i18n (>= 4.0.0)
|
||||
sass-rails (>= 4.0.3)
|
||||
commonjs (0.2.7)
|
||||
coveralls (0.7.1)
|
||||
multi_json (~> 1.3)
|
||||
@@ -187,6 +217,7 @@ GEM
|
||||
sprockets-rails
|
||||
json (1.8.2)
|
||||
kgio (2.9.2)
|
||||
kramdown (1.5.0)
|
||||
launchy (2.4.3)
|
||||
addressable (~> 2.3)
|
||||
leaflet-markercluster-rails (0.7.0)
|
||||
@@ -229,7 +260,14 @@ GEM
|
||||
multi_json (~> 1.3)
|
||||
omniauth-oauth (~> 1.0)
|
||||
orm_adapter (0.5.0)
|
||||
paperclip (4.2.1)
|
||||
activemodel (>= 3.0.0)
|
||||
activesupport (>= 3.0.0)
|
||||
cocaine (~> 0.5.3)
|
||||
mime-types
|
||||
pg (0.17.1)
|
||||
plupload-rails (1.2.1)
|
||||
rails (>= 3.1)
|
||||
poltergeist (1.5.1)
|
||||
capybara (~> 2.1)
|
||||
cliver (~> 0.3.1)
|
||||
@@ -254,6 +292,9 @@ GEM
|
||||
bundler (>= 1.3.0, < 2.0)
|
||||
railties (= 4.1.9)
|
||||
sprockets-rails (~> 2.0)
|
||||
rails-i18n (4.0.3)
|
||||
i18n (~> 0.6)
|
||||
railties (~> 4.0)
|
||||
rails_12factor (0.0.3)
|
||||
rails_serve_static_assets
|
||||
rails_stdout_logging
|
||||
@@ -367,6 +408,7 @@ DEPENDENCIES
|
||||
capybara
|
||||
capybara-email
|
||||
coffee-rails (~> 4.1.0)
|
||||
comfortable_mexican_sofa (~> 1.12.0)
|
||||
coveralls
|
||||
csv_shaper
|
||||
dalli
|
||||
|
||||
1
app/assets/javascripts/comfy/admin/cms/custom.js.coffee
Normal file
1
app/assets/javascripts/comfy/admin/cms/custom.js.coffee
Normal file
@@ -0,0 +1 @@
|
||||
# Custom JS for the admin area
|
||||
1
app/assets/stylesheets/comfy/admin/cms/custom.sass
Normal file
1
app/assets/stylesheets/comfy/admin/cms/custom.sass
Normal file
@@ -0,0 +1 @@
|
||||
// custom CSS for admin area
|
||||
@@ -9,10 +9,12 @@ class CropsController < ApplicationController
|
||||
@sort = params[:sort]
|
||||
if @sort == 'alpha'
|
||||
# alphabetical order
|
||||
@crops = Crop.includes(:scientific_names, {:plantings => :photos}).paginate(:page => params[:page])
|
||||
@crops = Crop.includes(:scientific_names, {:plantings => :photos})
|
||||
@paginated_crops = @crops.paginate(:page => params[:page])
|
||||
else
|
||||
# default to sorting by popularity
|
||||
@crops = Crop.popular.includes(:scientific_names, {:plantings => :photos}).paginate(:page => params[:page])
|
||||
@crops = Crop.popular.includes(:scientific_names, {:plantings => :photos})
|
||||
@paginated_crops = @crops.paginate(:page => params[:page])
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
@@ -32,7 +34,18 @@ class CropsController < ApplicationController
|
||||
|
||||
# GET /crops/wrangle
|
||||
def wrangle
|
||||
@crops = Crop.recent.paginate(:page => params[:page])
|
||||
@approval_status = params[:approval_status]
|
||||
case @approval_status
|
||||
when "pending"
|
||||
@crops = Crop.pending_approval
|
||||
when "rejected"
|
||||
@crops = Crop.rejected
|
||||
else
|
||||
@crops = Crop.recent
|
||||
end
|
||||
|
||||
@crops = @crops.paginate(:page => params[:page])
|
||||
|
||||
@crop_wranglers = Role.crop_wranglers
|
||||
respond_to do |format|
|
||||
format.html
|
||||
@@ -50,8 +63,9 @@ class CropsController < ApplicationController
|
||||
# GET /crops/search
|
||||
def search
|
||||
@search = params[:search]
|
||||
@all_matches = Crop.search(params[:search])
|
||||
exact_match = Crop.find_by_name(params[:search])
|
||||
@all_matches = Crop.search(@search)
|
||||
@paginated_matches = @all_matches.paginate(:page => params[:page])
|
||||
exact_match = Crop.find_by_name(@search)
|
||||
if exact_match
|
||||
@all_matches.delete(exact_match)
|
||||
@all_matches.unshift(exact_match)
|
||||
@@ -59,7 +73,7 @@ class CropsController < ApplicationController
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json { render :json => Crop.search(params[:term]) }
|
||||
format.json { render :json => Crop.search(@search) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -95,17 +109,35 @@ class CropsController < ApplicationController
|
||||
# GET /crops/1/edit
|
||||
def edit
|
||||
@crop = Crop.find(params[:id])
|
||||
|
||||
(3 - @crop.scientific_names.length).times do
|
||||
@crop.scientific_names.build
|
||||
end
|
||||
end
|
||||
|
||||
# POST /crops
|
||||
# POST /crops.json
|
||||
def create
|
||||
params[:crop][:creator_id] = current_member.id
|
||||
@crop = Crop.new(crop_params)
|
||||
|
||||
if current_member.has_role? :crop_wrangler
|
||||
@crop.creator = current_member
|
||||
success_msg = "Crop was successfully created."
|
||||
else
|
||||
@crop.requester = current_member
|
||||
@crop.approval_status = "pending"
|
||||
success_msg = "Crop was successfully requested."
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
if @crop.save
|
||||
format.html { redirect_to @crop, notice: 'Crop was successfully created.' }
|
||||
unless current_member.has_role? :crop_wrangler
|
||||
Role.crop_wranglers.each do |w|
|
||||
Notifier.new_crop_request(w, @crop).deliver!
|
||||
end
|
||||
end
|
||||
|
||||
format.html { redirect_to @crop, notice: success_msg }
|
||||
format.json { render json: @crop, status: :created, location: @crop }
|
||||
else
|
||||
format.html { render action: "new" }
|
||||
@@ -119,8 +151,18 @@ class CropsController < ApplicationController
|
||||
def update
|
||||
@crop = Crop.find(params[:id])
|
||||
|
||||
previous_status = @crop.approval_status
|
||||
|
||||
@crop.creator = current_member if previous_status == "pending"
|
||||
|
||||
respond_to do |format|
|
||||
if @crop.update(crop_params)
|
||||
if previous_status == "pending"
|
||||
requester = @crop.requester
|
||||
new_status = @crop.approval_status
|
||||
Notifier.crop_request_approved(requester, @crop).deliver! if new_status == "approved"
|
||||
Notifier.crop_request_rejected(requester, @crop).deliver! if new_status == "rejected"
|
||||
end
|
||||
format.html { redirect_to @crop, notice: 'Crop was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
@@ -145,6 +187,6 @@ class CropsController < ApplicationController
|
||||
private
|
||||
|
||||
def crop_params
|
||||
params.require(:crop).permit(:en_wikipedia_url, :name, :parent_id, :creator_id, :scientific_names_attributes => [:scientific_name])
|
||||
params.require(:crop).permit(:en_wikipedia_url, :name, :parent_id, :creator_id, :approval_status, :request_notes, :reason_for_rejection, :rejection_notes, :scientific_names_attributes => [:scientific_name, :_destroy, :id])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,7 +6,12 @@ class MembersController < ApplicationController
|
||||
after_action :expire_cache_fragments, :only => :create
|
||||
|
||||
def index
|
||||
@members = Member.confirmed.paginate(:page => params[:page])
|
||||
@sort = params[:sort]
|
||||
if @sort == 'recently_joined'
|
||||
@members = Member.confirmed.recently_joined.paginate(:page => params[:page])
|
||||
else
|
||||
@members = Member.confirmed.paginate(:page => params[:page])
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.haml
|
||||
|
||||
@@ -110,7 +110,9 @@ class SeedsController < ApplicationController
|
||||
private
|
||||
|
||||
def seed_params
|
||||
params.require(:seed).permit(:owner_id, :crop_id, :description, :quantity, :plant_before,
|
||||
:tradable_to, :slug)
|
||||
params.require(:seed).permit(
|
||||
:owner_id, :crop_id, :description, :quantity, :plant_before,
|
||||
:days_until_maturity_min, :days_until_maturity_max, :organic, :gmo,
|
||||
:heirloom, :tradable_to, :slug)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,4 +22,19 @@ class Notifier < ActionMailer::Base
|
||||
end
|
||||
end
|
||||
|
||||
def new_crop_request(member, request)
|
||||
@member, @request = member, request
|
||||
mail(:to => @member.email, :subject => "#{@request.requester.login_name} has requested #{@request.name} as a new crop")
|
||||
end
|
||||
|
||||
def crop_request_approved(member, crop)
|
||||
@member, @crop = member, crop
|
||||
mail(:to => @member.email, :subject => "#{crop.name.capitalize} has been approved")
|
||||
end
|
||||
|
||||
def crop_request_rejected(member, crop)
|
||||
@member, @crop = member, crop
|
||||
mail(:to => @member.email, :subject => "#{crop.name.capitalize} has been rejected")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -21,7 +21,24 @@ class Ability
|
||||
cannot :read, Account
|
||||
cannot :read, AccountType
|
||||
|
||||
# nobody should be able to view unapproved crops unless they
|
||||
# are wranglers or admins
|
||||
cannot :read, Crop
|
||||
can :read, Crop, :approval_status => "approved"
|
||||
# scientific names should only be viewable if associated crop is approved
|
||||
cannot :read, ScientificName
|
||||
can :read, ScientificName do |sn|
|
||||
sn.crop.approved?
|
||||
end
|
||||
# ... same for alternate names
|
||||
cannot :read, AlternateName
|
||||
can :read, AlternateName do |an|
|
||||
an.crop.approved?
|
||||
end
|
||||
|
||||
if member
|
||||
# members can see even rejected or pending crops if they requested it
|
||||
can :read, Crop, :requester_id => member.id
|
||||
|
||||
# managing your own user settings
|
||||
can :update, Member, :id => member.id
|
||||
@@ -45,6 +62,9 @@ class Ability
|
||||
can :manage, AlternateName
|
||||
end
|
||||
|
||||
# any member can create a crop provisionally
|
||||
can :create, Crop
|
||||
|
||||
# can create & destroy their own authentications against other sites.
|
||||
can :create, Authentication
|
||||
can :destroy, Authentication, :member_id => member.id
|
||||
|
||||
@@ -14,6 +14,7 @@ class Crop < ActiveRecord::Base
|
||||
has_many :harvests
|
||||
has_many :plant_parts, -> { uniq }, :through => :harvests
|
||||
belongs_to :creator, :class_name => 'Member'
|
||||
belongs_to :requester, :class_name => 'Member'
|
||||
|
||||
belongs_to :parent, :class_name => 'Crop'
|
||||
has_many :varieties, :class_name => 'Crop', :foreign_key => 'parent_id'
|
||||
@@ -21,17 +22,31 @@ class Crop < ActiveRecord::Base
|
||||
before_destroy {|crop| crop.posts.clear}
|
||||
|
||||
default_scope { order("lower(name) asc") }
|
||||
scope :recent, -> { reorder("created_at desc") }
|
||||
scope :toplevel, -> { where(:parent_id => nil) }
|
||||
scope :popular, -> { reorder("plantings_count desc, lower(name) asc") }
|
||||
scope :randomized, -> { reorder('random()') } # ok on sqlite and psql, but not on mysql
|
||||
scope :recent, -> { where(:approval_status => "approved").reorder("created_at desc") }
|
||||
scope :toplevel, -> { where(:approval_status => "approved", :parent_id => nil) }
|
||||
scope :popular, -> { where(:approval_status => "approved").reorder("plantings_count desc, lower(name) asc") }
|
||||
scope :randomized, -> { where(:approval_status => "approved").reorder('random()') } # ok on sqlite and psql, but not on mysql
|
||||
scope :pending_approval, -> { where(:approval_status => "pending") }
|
||||
scope :rejected, -> { where(:approval_status => "rejected") }
|
||||
|
||||
## Wikipedia urls are only necessary when approving a crop
|
||||
validates :en_wikipedia_url,
|
||||
:format => {
|
||||
:with => /\Ahttps?:\/\/en\.wikipedia\.org\/wiki/,
|
||||
:message => 'is not a valid English Wikipedia URL'
|
||||
}
|
||||
|
||||
},
|
||||
:if => :approved?
|
||||
|
||||
## Reasons are only necessary when rejecting
|
||||
validates :reason_for_rejection, :presence => true, :if => :rejected?
|
||||
|
||||
## This validation addresses a race condition
|
||||
validate :approval_status_cannot_be_changed_again
|
||||
|
||||
validate :must_be_rejected_if_rejected_reasons_present
|
||||
|
||||
validate :must_have_meaningful_reason_for_rejection
|
||||
|
||||
####################################
|
||||
# Elastic search configuration
|
||||
include Elasticsearch::Model
|
||||
@@ -81,13 +96,18 @@ class Crop < ActiveRecord::Base
|
||||
include: {
|
||||
scientific_names: { only: :scientific_name },
|
||||
alternate_names: { only: :name }
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
# update the Elasticsearch index (only if we're using it in this
|
||||
# environment)
|
||||
def update_index(name_obj)
|
||||
__elasticsearch__.index_document if ENV['GROWSTUFF_ELASTICSEARCH'] == "true"
|
||||
if ENV["GROWSTUFF_ELASTICSEARCH"] == "true"
|
||||
__elasticsearch__.index_document
|
||||
end
|
||||
end
|
||||
####################################
|
||||
|
||||
# End Elasticsearch section
|
||||
|
||||
def to_s
|
||||
return name
|
||||
@@ -160,6 +180,26 @@ class Crop < ActiveRecord::Base
|
||||
return true
|
||||
end
|
||||
|
||||
def pending?
|
||||
approval_status == "pending"
|
||||
end
|
||||
|
||||
def approved?
|
||||
approval_status == "approved"
|
||||
end
|
||||
|
||||
def rejected?
|
||||
approval_status == "rejected"
|
||||
end
|
||||
|
||||
def approval_statuses
|
||||
[ 'rejected', 'pending', 'approved' ]
|
||||
end
|
||||
|
||||
def reasons_for_rejection
|
||||
[ "already in database", "not edible", "not enough information", "other" ]
|
||||
end
|
||||
|
||||
# Crop.interesting
|
||||
# returns a list of interesting crops, for use on the homepage etc
|
||||
def Crop.interesting
|
||||
@@ -276,7 +316,31 @@ class Crop < ActiveRecord::Base
|
||||
)
|
||||
return response.records.to_a
|
||||
else
|
||||
where("name ILIKE ?", "%#{query}%")
|
||||
where("name ILIKE ?", "%#{query}%").load
|
||||
end
|
||||
end
|
||||
|
||||
# Custom validations
|
||||
|
||||
def approval_status_cannot_be_changed_again
|
||||
previous = previous_changes.include?(:approval_status) ? previous_changes.approval_status : {}
|
||||
if previous.include?(:rejected) || previous.include?(:approved)
|
||||
errors.add(:approval_status, "has already been set to #{approval_status}")
|
||||
end
|
||||
end
|
||||
|
||||
def must_be_rejected_if_rejected_reasons_present
|
||||
unless rejected?
|
||||
if reason_for_rejection.present? || rejection_notes.present?
|
||||
errors.add(:approval_status, "must be rejected if a reason for rejection is present")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def must_have_meaningful_reason_for_rejection
|
||||
if reason_for_rejection == "other" && rejection_notes.blank?
|
||||
errors.add(:rejection_notes, "must be added if the reason for rejection is \"other\"")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -34,7 +34,9 @@ class Garden < ActiveRecord::Base
|
||||
}
|
||||
|
||||
validates :area,
|
||||
:numericality => { :only_integer => false, :greater_than_or_equal_to => 0 },
|
||||
:numericality => {
|
||||
:only_integer => false,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
|
||||
AREA_UNITS_VALUES = {
|
||||
|
||||
@@ -20,10 +20,14 @@ class Harvest < ActiveRecord::Base
|
||||
|
||||
default_scope { order('created_at DESC') }
|
||||
|
||||
validates :crop, :approved => true
|
||||
|
||||
validates :crop, :presence => {:message => "must be present and exist in our database"}
|
||||
|
||||
validates :quantity,
|
||||
:numericality => { :only_integer => false },
|
||||
:numericality => {
|
||||
:only_integer => false,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
|
||||
UNITS_VALUES = {
|
||||
|
||||
@@ -32,6 +32,7 @@ class Member < ActiveRecord::Base
|
||||
scope :confirmed, -> { where('confirmed_at IS NOT NULL') }
|
||||
scope :located, -> { where("location <> '' and latitude IS NOT NULL and longitude IS NOT NULL") }
|
||||
scope :recently_signed_in, -> { reorder('updated_at DESC') }
|
||||
scope :recently_joined, -> { reorder("confirmed_at desc") }
|
||||
scope :wants_newsletter, -> { where(:newsletter => true) }
|
||||
|
||||
has_many :follows, :class_name => "Follow", :foreign_key => "follower_id"
|
||||
|
||||
@@ -30,10 +30,14 @@ class Planting < ActiveRecord::Base
|
||||
|
||||
default_scope { order("created_at desc") }
|
||||
|
||||
validates :crop_id, :presence => {:message => "must be present and exist in our database"}
|
||||
validates :crop, :approved => true
|
||||
|
||||
validates :crop, :presence => {:message => "must be present and exist in our database"}
|
||||
|
||||
validates :quantity,
|
||||
:numericality => { :only_integer => true },
|
||||
:numericality => {
|
||||
:only_integer => true,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
|
||||
SUNNINESS_VALUES = %w(sun semi-shade shade)
|
||||
|
||||
@@ -2,8 +2,11 @@ class Product < ActiveRecord::Base
|
||||
has_and_belongs_to_many :orders
|
||||
belongs_to :account_type
|
||||
|
||||
validates :paid_months, :numericality => { :only_integer => true,
|
||||
:allow_nil => true }
|
||||
validates :paid_months,
|
||||
:numericality => {
|
||||
:only_integer => true,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
|
||||
def to_s
|
||||
name
|
||||
|
||||
@@ -7,9 +7,23 @@ class Seed < ActiveRecord::Base
|
||||
|
||||
default_scope { order("created_at desc") }
|
||||
|
||||
validates :crop, :approved => true
|
||||
|
||||
validates :crop, :presence => {:message => "must be present and exist in our database"}
|
||||
validates :quantity,
|
||||
:numericality => { :only_integer => true },
|
||||
:numericality => {
|
||||
:only_integer => true,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
validates :days_until_maturity_min,
|
||||
:numericality => {
|
||||
:only_integer => true,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
validates :days_until_maturity_max,
|
||||
:numericality => {
|
||||
:only_integer => true,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
|
||||
scope :tradable, -> { where("tradable_to != 'nowhere'") }
|
||||
@@ -20,6 +34,32 @@ class Seed < ActiveRecord::Base
|
||||
:allow_nil => false,
|
||||
:allow_blank => false
|
||||
|
||||
ORGANIC_VALUES = [
|
||||
'certified organic',
|
||||
'non-certified organic',
|
||||
'conventional/non-organic',
|
||||
'unknown']
|
||||
validates :organic, :inclusion => { :in => ORGANIC_VALUES,
|
||||
:message => "You must say whether the seeds are organic or not, or that you don't know" },
|
||||
:allow_nil => false,
|
||||
:allow_blank => false
|
||||
|
||||
GMO_VALUES = [
|
||||
'certified GMO-free',
|
||||
'non-certified GMO-free',
|
||||
'GMO',
|
||||
'unknown']
|
||||
validates :gmo, :inclusion => { :in => GMO_VALUES,
|
||||
:message => "You must say whether the seeds are genetically modified or not, or that you don't know" },
|
||||
:allow_nil => false,
|
||||
:allow_blank => false
|
||||
|
||||
HEIRLOOM_VALUES = %w(heirloom hybrid unknown)
|
||||
validates :heirloom, :inclusion => { :in => HEIRLOOM_VALUES,
|
||||
:message => "You must say whether the seeds are heirloom, hybrid, or unknown" },
|
||||
:allow_nil => false,
|
||||
:allow_blank => false
|
||||
|
||||
def tradable?
|
||||
if self.tradable_to == 'nowhere'
|
||||
return false
|
||||
|
||||
7
app/validators/approved_validator.rb
Normal file
7
app/validators/approved_validator.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
class ApprovedValidator < ActiveModel::EachValidator
|
||||
def validate_each(record, attribute, value)
|
||||
unless record.crop.try(:approved?)
|
||||
record.errors[attribute] << (options[:message] || 'must be approved')
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -8,6 +8,7 @@
|
||||
%li= link_to "Roles", roles_path
|
||||
%li= link_to "Forums", forums_path
|
||||
%li= link_to "Newsletter subscribers", admin_newsletter_path
|
||||
%li= link_to "CMS", comfy_admin_cms_path
|
||||
|
||||
%h2 Orders
|
||||
|
||||
|
||||
@@ -6,40 +6,87 @@
|
||||
- @crop.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
||||
%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.
|
||||
- if can? :wrangle, @crop
|
||||
%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.
|
||||
|
||||
.form-group
|
||||
= f.label :name, :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_field :name, :class => 'form-control'
|
||||
%span.help-block Name in US English; singular; capitalize proper nouns only.
|
||||
%span.help-block
|
||||
- if can? :wrangle, @crop
|
||||
Name in US English; singular; capitalize proper nouns only.
|
||||
- else
|
||||
Please provide the common name for the crop, in English, if you know it.
|
||||
|
||||
.form-group
|
||||
= f.label :en_wikipedia_url, 'Wikipedia URL', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_field :en_wikipedia_url, :class => 'form-control'
|
||||
%span.help-block Link to this crop's page on the English language Wikipedia.
|
||||
.form-group
|
||||
= f.label :parent_id, 'Parent crop', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= collection_select(:crop, :parent_id, Crop.all, :id, :name, {:include_blank => true}, :class => 'form-control')
|
||||
%span.help-block Optional. For setting up crop hierarchies for varieties etc.
|
||||
%p
|
||||
%span.help-block
|
||||
You may enter up to 3 scientific names for a crop. Most crops will have only one.
|
||||
= f.fields_for :scientific_names do |sn|
|
||||
%span.help-block
|
||||
- if can? :wrangle, @crop
|
||||
Link to this crop's page on the English language Wikipedia.
|
||||
- else
|
||||
Please provide a link to the crop's page on the English language Wikipedia. Crops without Wikipedia pages cannot be added to our database at this time.
|
||||
|
||||
- if can? :wrangle, @crop
|
||||
|
||||
.form-group
|
||||
= sn.label :scientific_name, "Scientific name", :class => 'control-label col-md-2'
|
||||
= f.label :parent_id, 'Parent crop', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= sn.text_field :scientific_name, :class => 'form-control'
|
||||
.col-md-2
|
||||
- if sn.object && sn.object.persisted?
|
||||
%label.checkbox
|
||||
= sn.check_box :_destroy
|
||||
= sn.label :_destroy, "Delete"
|
||||
= collection_select(:crop, :parent_id, Crop.all, :id, :name, {:include_blank => true}, :class => 'form-control')
|
||||
%span.help-block Optional. For setting up crop hierarchies for varieties etc.
|
||||
|
||||
%p
|
||||
%span.help-block
|
||||
You may enter up to 3 scientific names for a crop. Most crops will have only one.
|
||||
= f.fields_for :scientific_names do |sn|
|
||||
.form-group
|
||||
= sn.label :scientific_name, "Scientific name", :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= sn.text_field :scientific_name, :class => 'form-control'
|
||||
.col-md-2
|
||||
- if sn.object && sn.object.persisted?
|
||||
%label.checkbox
|
||||
= sn.check_box :_destroy
|
||||
= sn.label :_destroy, "Delete"
|
||||
|
||||
- unless @crop.new_record?
|
||||
.form-group
|
||||
= f.label :approval_status, 'Approval Status', :class=> 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.select(:approval_status, @crop.approval_statuses, {}, {:class => 'form-control'})
|
||||
|
||||
.form-group
|
||||
= f.label :reason_for_rejection, 'Reason for rejection', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.select(:reason_for_rejection, @crop.reasons_for_rejection, {:include_blank => true}, {:class => 'form-control'})
|
||||
%p
|
||||
%span.help-block
|
||||
Please provide additional notes why this crop request was rejected if the above reasons do not apply.
|
||||
|
||||
.form-group
|
||||
= f.label :rejection_notes, 'Rejection Notes', :class => 'control-label col-md-2'
|
||||
.col-md-8= f.text_area :rejection_notes, :rows => 6, :class => 'form-control'
|
||||
|
||||
- else
|
||||
%p
|
||||
%span.help-block
|
||||
Provide any additional information that might help us assess your request.
|
||||
|
||||
.form-group
|
||||
= f.label :request_notes, 'Comments', :class => 'control-label col-md-2'
|
||||
.col-md-8= f.text_area :request_notes, :rows => 6, :class => 'form-control'
|
||||
|
||||
%p
|
||||
%span.help-block
|
||||
When you submit this form, your suggestion will be sent to our team of #{link_to 'volunteer crop wranglers', 'http://talk.growstuff.org/c/crop-wrangling'} for review. We'll let you know the outcome as soon as we can.
|
||||
|
||||
.form-group
|
||||
.form-actions.col-md-offset-2.col-md-8
|
||||
= f.submit 'Save', :class => 'btn btn-primary'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
- content_for :title, "Editing crop"
|
||||
- content_for :title, "Review crop: #{@crop.name}"
|
||||
|
||||
%p
|
||||
Added by
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
- content_for :title, "Crops"
|
||||
- content_for :title, "Browse crops"
|
||||
- content_for :subtitle, "#{@crops.size} total"
|
||||
|
||||
- if can? :wrangle, Crop
|
||||
= link_to 'Wrangle Crops', wrangle_crops_path, :class => 'btn btn-primary'
|
||||
%p
|
||||
#{ENV['GROWSTUFF_SITE_NAME']} tracks who's growing what, where.
|
||||
View any crop page to see which of our members have planted it and find
|
||||
@@ -12,11 +15,10 @@
|
||||
= submit_tag "Show", :class => 'btn btn-primary'
|
||||
|
||||
%div.pagination
|
||||
= page_entries_info @crops, :model => "crops"
|
||||
= will_paginate @crops
|
||||
= will_paginate @paginated_crops
|
||||
|
||||
.row
|
||||
- @crops.each do |crop|
|
||||
- @paginated_crops.each do |crop|
|
||||
.col-md-2.six-across
|
||||
= render :partial => "thumbnail", :locals => { :crop => crop }
|
||||
|
||||
@@ -25,8 +27,7 @@
|
||||
= link_to 'New Crop', new_crop_path, {:class => 'btn btn-primary'}
|
||||
|
||||
%div.pagination
|
||||
= page_entries_info @crops, :model => "crops"
|
||||
= will_paginate @crops
|
||||
= will_paginate @paginated_crops
|
||||
|
||||
|
||||
%ul.list-inline
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
- content_for :title, "New crop"
|
||||
- content_for :title, (can?(:wrangle, @crop) ? "New crop" : "Suggest a crop")
|
||||
|
||||
- unless can? :wrangler, @crop
|
||||
|
||||
%p Thanks for taking the time to suggest a crop! Our crop database is managed by volunteers, and we appreciate your help. Here are some things to consider when suggesting a new crop:
|
||||
|
||||
%ul
|
||||
%li First, you might want to #{link_to 'search our crops', crops_search_path} to make sure we don't have it already, perhaps under an alternate name.
|
||||
|
||||
%li The Growstuff database only contains edible crops. In future we hope to support other crops, but for now, if your suggestion is not edible we won't be able to add it.
|
||||
|
||||
%li At this time, we are only adding crops which have a Wikipedia page. If you want to add a specific variety of crop that doesn't have its own Wikipedia entry, please use the more general form of the crop instead and put the name of your variety in the notes/description.
|
||||
|
||||
= render 'form'
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
- content_for :title, "Crops matching #{@search}"
|
||||
- if @search
|
||||
- content_for :title, "Crops matching \"#{@search}\""
|
||||
- if @all_matches
|
||||
- content_for :subtitle, "#{@all_matches.size} total"
|
||||
- else
|
||||
- content_for :title, "Crop search"
|
||||
|
||||
%div
|
||||
= form_tag crops_search_path, :method => :get, :id => 'crop-search', :class => 'form-inline' do
|
||||
.form-group
|
||||
= label_tag :search, "Search crops:", :class => 'sr-only'
|
||||
= text_field_tag 'search', nil, :class => 'search-query input-medium form-control', :placeholder => 'Search crops', :value => @search
|
||||
= submit_tag "Search", :class => 'btn btn-primary'
|
||||
|
||||
- if @all_matches.empty?
|
||||
%h2 No results found
|
||||
@@ -9,10 +21,15 @@
|
||||
= link_to "browsing our crop database", crops_path
|
||||
instead.
|
||||
|
||||
- else
|
||||
%div#all_matches
|
||||
- else
|
||||
%div.pagination
|
||||
= will_paginate @paginated_matches
|
||||
|
||||
%div#paginated_matches
|
||||
.row
|
||||
- @all_matches.each do |c|
|
||||
- @paginated_matches.each do |c|
|
||||
.col-md-2.six-across
|
||||
= render :partial => "thumbnail", :locals => { :crop => c }
|
||||
|
||||
%div.pagination
|
||||
= will_paginate @paginated_matches
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
- content_for :title, @crop.name
|
||||
- content_for :subtitle, @crop.default_scientific_name
|
||||
- content_for :buttonbar do
|
||||
- if can? :create, Planting
|
||||
= link_to "Plant this", new_planting_path(:crop_id => @crop.id), :class => 'btn btn-default'
|
||||
|
||||
- if can? :create, Harvest
|
||||
= link_to "Harvest this", new_harvest_path(:crop_id => @crop.id), :class => 'btn btn-default'
|
||||
- if @crop.pending?
|
||||
.alert.alert-danger
|
||||
%b This crop is currently pending approval.
|
||||
%p This crop was requested by #{@crop.requester} #{time_ago_in_words(@crop.created_at)} ago.
|
||||
- unless @crop.request_notes.blank?
|
||||
%p
|
||||
Request notes: #{@crop.request_notes}
|
||||
|
||||
- if can? :create, Seed
|
||||
= link_to 'Add seeds to stash', new_seed_path(:params => { :crop_id => @crop.id }), :class => 'btn btn-default'
|
||||
- if @crop.rejected?
|
||||
.alert.alert-danger
|
||||
%b This crop was rejected for the following reason: #{@crop.reason_for_rejection == "other" ? @crop.rejection_notes : @crop.reason_for_rejection}.
|
||||
|
||||
- if @crop.approved?
|
||||
- content_for :buttonbar do
|
||||
- if can? :create, Planting
|
||||
= link_to "Plant this", new_planting_path(:crop_id => @crop.id), :class => 'btn btn-default'
|
||||
|
||||
- if can? :create, Harvest
|
||||
= link_to "Harvest this", new_harvest_path(:crop_id => @crop.id), :class => 'btn btn-default'
|
||||
|
||||
- if can? :create, Seed
|
||||
= link_to 'Add seeds to stash', new_seed_path(:params => { :crop_id => @crop.id }), :class => 'btn btn-default'
|
||||
|
||||
|
||||
.row
|
||||
|
||||
@@ -15,18 +15,38 @@
|
||||
%li.crop_wrangler
|
||||
= link_to crop_wrangler.login_name, crop_wrangler
|
||||
|
||||
%h2 Recently added crops
|
||||
.tabbable
|
||||
%ul.nav.nav-tabs
|
||||
%li{:class => @approval_status.blank? ? 'active' : ''}
|
||||
= link_to "Recently added", wrangle_crops_path
|
||||
%li{:class => @approval_status == "pending" ? 'active' : ''}
|
||||
= link_to "Pending approval", wrangle_crops_path(:approval_status => "pending")
|
||||
%li{:class => @approval_status == "rejected" ? 'active' : ''}
|
||||
= link_to "Rejected", wrangle_crops_path(:approval_status => "rejected")
|
||||
|
||||
%h2
|
||||
- if @approval_status == "pending"
|
||||
Requested Crops
|
||||
- elsif @approval_status == "rejected"
|
||||
Rejected Crops
|
||||
- else
|
||||
Recently added crops
|
||||
|
||||
|
||||
%div.pagination
|
||||
= page_entries_info @crops, :model => "crops"
|
||||
= will_paginate @crops
|
||||
|
||||
%table.table.table-striped
|
||||
%table{:class => "table table-striped", :id => @approval_status.blank? ? 'recently-added-crops' : "#{@approval_status}-crops"}
|
||||
%tr
|
||||
%th System name
|
||||
%th English Wikipedia URL
|
||||
%th Scientific names
|
||||
%th Added by
|
||||
%th Requested by
|
||||
- if @approval_status == "rejected"
|
||||
%th Rejected by
|
||||
- if @approval_status != "rejected" && @approval_status != "pending"
|
||||
%th Added by
|
||||
%th When
|
||||
- @crops.each do |c|
|
||||
%tr
|
||||
@@ -36,7 +56,9 @@
|
||||
- c.scientific_names.each do |s|
|
||||
= link_to s.scientific_name, s
|
||||
%br/
|
||||
%td= link_to c.creator, c.creator
|
||||
%td= c.requester.present? ? (link_to c.requester, c.requester) : "N/A"
|
||||
- unless @approval_status == "pending"
|
||||
%td= c.creator.present? ? (link_to c.creator, c.creator) : "N/A"
|
||||
%td
|
||||
= distance_of_time_in_words(c.created_at, Time.zone.now)
|
||||
ago.
|
||||
@@ -44,3 +66,5 @@
|
||||
%div.pagination
|
||||
= page_entries_info @crops, :model => "crops"
|
||||
= will_paginate @crops
|
||||
|
||||
|
||||
|
||||
@@ -2,28 +2,8 @@
|
||||
.container
|
||||
.row
|
||||
.col-md-4#about-growstuff
|
||||
%ul
|
||||
%li= link_to t('about'), "http://wiki.growstuff.org/index.php/About%20Growstuff"
|
||||
%li= link_to t('our_values'), "http://wiki.growstuff.org/index.php/Values"
|
||||
%li= link_to t('open_source'), "https://github.com/Growstuff/growstuff"
|
||||
%li= link_to t('growstuff_team'), "http://wiki.growstuff.org/index.php/Team"
|
||||
%li= link_to t('get_involved'), "http://wiki.growstuff.org/index.php/Get_involved"
|
||||
!= cms_snippet_content(:footer1)
|
||||
.col-md-4#policies
|
||||
%ul
|
||||
%li= link_to t('terms_of_service'), url_for(:controller => '/policy', :action => 'tos')
|
||||
%li= link_to t('privacy_policy'), url_for(:controller => '/policy', :action => 'privacy')
|
||||
%li= link_to t('data_use_policy'), url_for(:controller => '/policy', :action => 'api')
|
||||
%li= link_to t('community_guidelines'), url_for(:controller => '/policy', :action => 'community')
|
||||
!= cms_snippet_content(:footer2)
|
||||
.col-md-4#contact
|
||||
%ul
|
||||
%li= link_to t('support_'), url_for(:controller => '/support')
|
||||
%li= link_to t('contact'), url_for(:controller => '/about', :action => 'contact')
|
||||
%p
|
||||
= link_to('http://twitter.com/growstufforg', :target => "_blank") do
|
||||
= image_tag("twitter_32.png", :alt => 'Twitter: @growstufforg')
|
||||
|
||||
= link_to('https://www.facebook.com/Growstufforg', :target => "_blank") do
|
||||
= image_tag("facebook_32.png", :alt => 'Facebook')
|
||||
|
||||
= link_to('http://blog.growstuff.org/', :target => "_blank") do
|
||||
= image_tag("blog_32.png", :alt => 'Growstuff Blog')
|
||||
!= cms_snippet_content(:footer3)
|
||||
|
||||
@@ -65,12 +65,14 @@
|
||||
%li= link_to "Sign out", destroy_member_session_path, :method => :delete
|
||||
|
||||
- else
|
||||
%li= link_to 'Sign in', new_member_session_path
|
||||
%li= link_to 'Sign up', new_member_registration_path
|
||||
%li= link_to 'Sign in', new_member_session_path, :id => 'navbar-signin'
|
||||
%li= link_to 'Sign up', new_member_registration_path, :id => 'navbar-signup'
|
||||
|
||||
= form_tag crops_search_path, :method => :get, :class => 'navbar-form pull-right' do
|
||||
= form_tag crops_search_path, :method => :get, :id => 'navbar-search', :class => 'navbar-form pull-right' do
|
||||
.input
|
||||
= label_tag :search, "Search crop database:", :class => 'sr-only'
|
||||
= text_field_tag 'search', nil, :class => 'search-query input-medium form-control', :placeholder => 'Search crops'
|
||||
= submit_tag "Search", :class => 'btn sr-only'
|
||||
|
||||
- # anchor tag for accessibility link to skip the navigation menu
|
||||
%a{:name => 'skipnav'}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
= content_for :title, "#{ENV['GROWSTUFF_SITE_NAME']} members"
|
||||
|
||||
= form_tag(members_path, :method => :get, :class => 'form-inline', :role => 'form') do
|
||||
.form-group
|
||||
= label_tag :sort, "Sort by:", :class => 'sr-only'
|
||||
= select_tag "sort", options_for_select({"Sort alphabetically" => 'alpha', "Sort by recently joined" => "recently_joined"}, @sort || 'alpha'), :class => 'form-control'
|
||||
= submit_tag "Show", :class => 'btn btn-primary'
|
||||
|
||||
%div.pagination
|
||||
= page_entries_info @members, :model => "members"
|
||||
= will_paginate @members
|
||||
|
||||
6
app/views/notifier/_signature.html.haml
Normal file
6
app/views/notifier/_signature.html.haml
Normal file
@@ -0,0 +1,6 @@
|
||||
- site_name = ENV['GROWSTUFF_SITE_NAME']
|
||||
|
||||
%p
|
||||
The #{site_name} team.
|
||||
%br/
|
||||
=link_to root_url, root_url
|
||||
16
app/views/notifier/crop_request_approved.html.haml
Normal file
16
app/views/notifier/crop_request_approved.html.haml
Normal file
@@ -0,0 +1,16 @@
|
||||
- site_name = ENV['GROWSTUFF_SITE_NAME']
|
||||
%p Hello #{@member.login_name},
|
||||
|
||||
%p
|
||||
Your request for the new crop: #{link_to @crop.name, crop_url(@crop)} has been approved!
|
||||
|
||||
%ul
|
||||
%li
|
||||
= link_to "Plant #{@crop.name}", new_planting_url(crop_id: @crop.id)
|
||||
%li
|
||||
= link_to "Harvest #{@crop.name}", new_harvest_url(crop_id: @crop.id)
|
||||
%li
|
||||
= link_to "Stash seeds for #{@crop.name}", new_seed_url(crop_id: @crop.id)
|
||||
|
||||
= render :partial => 'signature'
|
||||
|
||||
9
app/views/notifier/crop_request_rejected.html.haml
Normal file
9
app/views/notifier/crop_request_rejected.html.haml
Normal file
@@ -0,0 +1,9 @@
|
||||
- site_name = ENV['GROWSTUFF_SITE_NAME']
|
||||
%p Hello #{@member.login_name},
|
||||
|
||||
%p
|
||||
Your request for the new crop: #{link_to @crop.name, crop_url(@crop)} has been rejected for the following reason.
|
||||
|
||||
= @crop.reason_for_rejection
|
||||
|
||||
= render :partial => 'signature'
|
||||
21
app/views/notifier/new_crop_request.html.haml
Normal file
21
app/views/notifier/new_crop_request.html.haml
Normal file
@@ -0,0 +1,21 @@
|
||||
- site_name = ENV['GROWSTUFF_SITE_NAME']
|
||||
|
||||
%p Hello #{@member.login_name},
|
||||
|
||||
%p
|
||||
#{@request.requester.login_name} has requested a new crop on #{site_name}.
|
||||
|
||||
%ul
|
||||
%li Name: #{@request.name}
|
||||
%li Wikipedia URL: #{@request.en_wikipedia_url.present? ? @request.en_wikipedia_url : "not specified"}
|
||||
|
||||
%p
|
||||
As a crop wrangler, you can #{link_to "approve or reject this request", edit_crop_url(@request)}.
|
||||
|
||||
%p
|
||||
Or, discuss this and other crop wrangling issues in our #{link_to "crop wrangling forum", "http://talk.growstuff.org/c/crop-wrangling"}.
|
||||
|
||||
%p
|
||||
Thanks for your help!
|
||||
|
||||
= render :partial => 'signature'
|
||||
@@ -20,7 +20,4 @@
|
||||
%br/
|
||||
= link_to "Turn off these notifications", edit_member_registration_url
|
||||
|
||||
%p
|
||||
The #{site_name} team.
|
||||
%br/
|
||||
=link_to root_url, root_url
|
||||
= render :partial => 'signature'
|
||||
@@ -59,10 +59,7 @@
|
||||
%h2
|
||||
See you soon on #{site_name}!
|
||||
|
||||
%p
|
||||
The #{site_name} team.
|
||||
%br/
|
||||
=link_to root_url, root_url
|
||||
= render :partial => 'signature'
|
||||
|
||||
%hr/
|
||||
%p
|
||||
|
||||
9
app/views/seeds/_days_until_maturity.html.haml
Normal file
9
app/views/seeds/_days_until_maturity.html.haml
Normal file
@@ -0,0 +1,9 @@
|
||||
- if seed.days_until_maturity_min.blank?
|
||||
- if seed.days_until_maturity_max.blank?
|
||||
unknown
|
||||
- else
|
||||
= seed.days_until_maturity_max.to_s
|
||||
- elsif seed.days_until_maturity_max.blank?
|
||||
= seed.days_until_maturity_min.to_s
|
||||
- else
|
||||
!= "#{seed.days_until_maturity_min}–#{seed.days_until_maturity_max}"
|
||||
@@ -21,6 +21,29 @@
|
||||
= f.label :plant_before, 'Plant before:', :class => 'control-label col-md-2'
|
||||
.col-md-2
|
||||
= f.text_field :plant_before, :class => 'add-datepicker form-control', :value => @seed.plant_before ? @seed.plant_before.to_s(:ymd) : ''
|
||||
.form-group
|
||||
= f.label :days_until_maturity_min, 'Days until maturity:', :class => 'control-label col-md-2'
|
||||
%fieldset
|
||||
.col-md-2
|
||||
= f.number_field :days_until_maturity_min, :class => 'form-control'
|
||||
.col-md-1
|
||||
= f.label :days_until_maturity_max, 'to', :class => 'control-label'
|
||||
.col-md-2
|
||||
= f.number_field :days_until_maturity_max, :class => 'form-control'
|
||||
.col-md-1
|
||||
= f.label :dummy, 'days', :class => 'control-label'
|
||||
.form-group
|
||||
= f.label :organic, 'Organic?', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.select(:organic, Seed::ORGANIC_VALUES, {}, :class => 'form-control', :default => 'unknown')
|
||||
.form-group
|
||||
= f.label :gmo, 'GMO?', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.select(:gmo, Seed::GMO_VALUES, {}, :class => 'form-control', :default => 'unknown')
|
||||
.form-group
|
||||
= f.label :heirloom, 'Heirloom?', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.select(:heirloom, Seed::HEIRLOOM_VALUES, {}, :class => 'form-control', :default => 'unknown')
|
||||
.form-group
|
||||
= f.label :description, 'Description:', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
|
||||
@@ -13,6 +13,18 @@
|
||||
%p
|
||||
%b Plant before:
|
||||
= @seed.plant_before.to_s
|
||||
%p
|
||||
%b Days until maturity:
|
||||
= render :partial => 'days_until_maturity', :locals => { :seed => @seed }
|
||||
%p
|
||||
%b Organic?
|
||||
= @seed.organic
|
||||
%p
|
||||
%b GMO?
|
||||
= @seed.gmo
|
||||
%p
|
||||
%b Heirloom?
|
||||
= @seed.heirloom
|
||||
%p
|
||||
%b Will trade:
|
||||
= @seed.tradable_to
|
||||
|
||||
103
config/initializers/comfortable_mexican_sofa.rb
Normal file
103
config/initializers/comfortable_mexican_sofa.rb
Normal file
@@ -0,0 +1,103 @@
|
||||
# encoding: utf-8
|
||||
|
||||
ComfortableMexicanSofa.configure do |config|
|
||||
# Title of the admin area
|
||||
# config.cms_title = 'ComfortableMexicanSofa CMS Engine'
|
||||
|
||||
# Controller that is inherited from CmsAdmin::BaseController
|
||||
# config.base_controller = 'ApplicationController'
|
||||
|
||||
# Module responsible for authentication. You can replace it with your own.
|
||||
# It simply needs to have #authenticate method. See http_auth.rb for reference.
|
||||
config.admin_auth = 'CmsDeviseAuth'
|
||||
|
||||
# Module responsible for authorization on admin side. It should have #authorize
|
||||
# method that returns true or false based on params and loaded instance
|
||||
# variables available for a given controller.
|
||||
# config.admin_authorization = 'ComfyAdminAuthorization'
|
||||
|
||||
# Module responsible for public authentication. Similar to the above. You also
|
||||
# will have access to @cms_site, @cms_layout, @cms_page so you can use them in
|
||||
# your logic. Default module doesn't do anything.
|
||||
# config.public_auth = 'ComfyPublicAuthentication'
|
||||
|
||||
# When arriving at /cms-admin you may chose to redirect to arbirtary path,
|
||||
# for example '/cms-admin/users'
|
||||
# config.admin_route_redirect = ''
|
||||
|
||||
# File uploads use Paperclip and can support filesystem or s3 uploads. Override
|
||||
# the upload method and appropriate settings based on Paperclip. For S3 see:
|
||||
# http://rdoc.info/gems/paperclip/2.3.8/Paperclip/Storage/S3, and for
|
||||
# filesystem see: http://rdoc.info/gems/paperclip/2.3.8/Paperclip/Storage/Filesystem
|
||||
# If you are using S3 and HTTPS, pass :s3_protocol => '' to have URLs that use the protocol of the page
|
||||
# config.upload_file_options = {:url => '/system/:class/:id/:attachment/:style/:filename'}
|
||||
|
||||
# Sofa allows you to setup entire site from files. Database is updated with each
|
||||
# request (if necessary). Please note that database entries are destroyed if there's
|
||||
# no corresponding file. Fixtures are disabled by default.
|
||||
# config.enable_fixtures = false
|
||||
|
||||
# Path where fixtures can be located.
|
||||
# config.fixtures_path = File.expand_path('db/cms_fixtures', Rails.root)
|
||||
|
||||
# Importing fixtures into Database
|
||||
# To load fixtures into the database just run this rake task:
|
||||
# local: $ rake comfortable_mexican_sofa:fixtures:import FROM=example.local TO=localhost
|
||||
# Heroku: $ heroku run rake comfortable_mexican_sofa:fixtures:import FROM=example.local TO=yourapp.herokuapp.com
|
||||
# From indicates folder the fixtures are in and to is the Site hostname you have defined in the database.
|
||||
|
||||
# Exporting fixtures into Files
|
||||
# If you need to dump database contents into fixture files run:
|
||||
# local: $ rake comfortable_mexican_sofa:fixtures:export FROM=localhost TO=example.local
|
||||
# Heroku: $ heroku run rake comfortable_mexican_sofa:fixtures:export FROM=yourapp.herokuapp.com TO=example.local
|
||||
# This will create example.local folder and dump all content from example.com Site.
|
||||
|
||||
# Content for Layouts, Pages and Snippets has a revision history. You can revert
|
||||
# a previous version using this system. You can control how many revisions per
|
||||
# object you want to keep. Set it to 0 if you wish to turn this feature off.
|
||||
# config.revisions_limit = 25
|
||||
|
||||
# Locale definitions. If you want to define your own locale merge
|
||||
# {:locale => 'Locale Title'} with this.
|
||||
# config.locales = {:en => 'English', :es => 'Español'}
|
||||
|
||||
# Admin interface will respect the locale of the site being managed. However you can
|
||||
# force it to English by setting this to `:en`
|
||||
# config.admin_locale = nil
|
||||
|
||||
# A class that is included as a sweeper to admin base controller if it's set
|
||||
# config.admin_cache_sweeper = nil
|
||||
|
||||
# By default you cannot have irb code inside your layouts/pages/snippets.
|
||||
# Generally this is to prevent putting something like this:
|
||||
# <% User.delete_all %> but if you really want to allow it...
|
||||
# config.allow_irb = false
|
||||
|
||||
# Whitelist of all helper methods that can be used via {{cms:helper}} tag. By default
|
||||
# all helpers are allowed except `eval`, `send`, `call` and few others. Empty array
|
||||
# will prevent rendering of all helpers.
|
||||
# config.allowed_helpers = nil
|
||||
|
||||
# Whitelist of partials paths that can be used via {{cms:partial}} tag. All partials
|
||||
# are accessible by default. Empty array will prevent rendering of all partials.
|
||||
# config.allowed_partials = nil
|
||||
|
||||
# Site aliases, if you want to have aliases for your site. Good for harmonizing
|
||||
# production env with dev/testing envs.
|
||||
# e.g. config.hostname_aliases = {'host.com' => 'host.inv', 'host_a.com' => ['host.lvh.me', 'host.dev']}
|
||||
# Default is nil (not used)
|
||||
# config.hostname_aliases = nil
|
||||
|
||||
# Reveal partials that can be overwritten in the admin area.
|
||||
# Default is false.
|
||||
# config.reveal_cms_partials = false
|
||||
|
||||
end
|
||||
|
||||
module CmsDeviseAuth
|
||||
def authenticate
|
||||
unless current_member && current_member.has_role?(:admin)
|
||||
redirect_to root_path, :alert => 'Permission denied. Please sign in as an admin user to use the CMS admin area.'
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,6 @@
|
||||
Growstuff::Application.routes.draw do
|
||||
|
||||
|
||||
resources :plant_parts
|
||||
|
||||
devise_for :members, :controllers => { :registrations => "registrations", :passwords => "passwords" }
|
||||
@@ -84,6 +85,8 @@ Growstuff::Application.routes.draw do
|
||||
get '/admin/newsletter' => 'admin#newsletter', :as => :admin_newsletter
|
||||
get '/admin/:action' => 'admin#:action'
|
||||
|
||||
|
||||
# CMS stuff -- must remain LAST
|
||||
comfy_route :cms_admin, :path => '/cms/admin'
|
||||
comfy_route :cms, :path => '/', :sitemap => false
|
||||
|
||||
end
|
||||
|
||||
9
db/migrate/20150124110540_add_properties_to_seeds.rb
Normal file
9
db/migrate/20150124110540_add_properties_to_seeds.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
class AddPropertiesToSeeds < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :seeds, :days_until_maturity_min, :integer
|
||||
add_column :seeds, :days_until_maturity_max, :integer
|
||||
add_column :seeds, :organic, :text, :default => 'unknown'
|
||||
add_column :seeds, :gmo, :text, :default => 'unknown'
|
||||
add_column :seeds, :heirloom, :text, :default => 'unknown'
|
||||
end
|
||||
end
|
||||
6
db/migrate/20150130224814_add_requester_to_crops.rb
Normal file
6
db/migrate/20150130224814_add_requester_to_crops.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
class AddRequesterToCrops < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :crops, :requester_id, :integer
|
||||
add_index :crops, :requester_id
|
||||
end
|
||||
end
|
||||
140
db/migrate/20150201052245_create_cms.rb
Normal file
140
db/migrate/20150201052245_create_cms.rb
Normal file
@@ -0,0 +1,140 @@
|
||||
class CreateCms < ActiveRecord::Migration
|
||||
|
||||
def self.up
|
||||
|
||||
text_limit = case ActiveRecord::Base.connection.adapter_name
|
||||
when 'PostgreSQL'
|
||||
{ }
|
||||
else
|
||||
{ :limit => 16777215 }
|
||||
end
|
||||
|
||||
# -- Sites --------------------------------------------------------------
|
||||
create_table :comfy_cms_sites do |t|
|
||||
t.string :label, :null => false
|
||||
t.string :identifier, :null => false
|
||||
t.string :hostname, :null => false
|
||||
t.string :path
|
||||
t.string :locale, :null => false, :default => 'en'
|
||||
t.boolean :is_mirrored, :null => false, :default => false
|
||||
end
|
||||
add_index :comfy_cms_sites, :hostname
|
||||
add_index :comfy_cms_sites, :is_mirrored
|
||||
|
||||
# -- Layouts ------------------------------------------------------------
|
||||
create_table :comfy_cms_layouts do |t|
|
||||
t.integer :site_id, :null => false
|
||||
t.integer :parent_id
|
||||
t.string :app_layout
|
||||
t.string :label, :null => false
|
||||
t.string :identifier, :null => false
|
||||
t.text :content, text_limit
|
||||
t.text :css, text_limit
|
||||
t.text :js, text_limit
|
||||
t.integer :position, :null => false, :default => 0
|
||||
t.boolean :is_shared, :null => false, :default => false
|
||||
t.timestamps
|
||||
end
|
||||
add_index :comfy_cms_layouts, [:parent_id, :position]
|
||||
add_index :comfy_cms_layouts, [:site_id, :identifier], :unique => true
|
||||
|
||||
# -- Pages --------------------------------------------------------------
|
||||
create_table :comfy_cms_pages do |t|
|
||||
t.integer :site_id, :null => false
|
||||
t.integer :layout_id
|
||||
t.integer :parent_id
|
||||
t.integer :target_page_id
|
||||
t.string :label, :null => false
|
||||
t.string :slug
|
||||
t.string :full_path, :null => false
|
||||
t.text :content_cache, text_limit
|
||||
t.integer :position, :null => false, :default => 0
|
||||
t.integer :children_count, :null => false, :default => 0
|
||||
t.boolean :is_published, :null => false, :default => true
|
||||
t.boolean :is_shared, :null => false, :default => false
|
||||
t.timestamps
|
||||
end
|
||||
add_index :comfy_cms_pages, [:site_id, :full_path]
|
||||
add_index :comfy_cms_pages, [:parent_id, :position]
|
||||
|
||||
# -- Page Blocks --------------------------------------------------------
|
||||
create_table :comfy_cms_blocks do |t|
|
||||
t.string :identifier, :null => false
|
||||
t.text :content, text_limit
|
||||
t.references :blockable, :polymorphic => true
|
||||
t.timestamps
|
||||
end
|
||||
add_index :comfy_cms_blocks, [:identifier]
|
||||
add_index :comfy_cms_blocks, [:blockable_id, :blockable_type]
|
||||
|
||||
# -- Snippets -----------------------------------------------------------
|
||||
create_table :comfy_cms_snippets do |t|
|
||||
t.integer :site_id, :null => false
|
||||
t.string :label, :null => false
|
||||
t.string :identifier, :null => false
|
||||
t.text :content, text_limit
|
||||
t.integer :position, :null => false, :default => 0
|
||||
t.boolean :is_shared, :null => false, :default => false
|
||||
t.timestamps
|
||||
end
|
||||
add_index :comfy_cms_snippets, [:site_id, :identifier], :unique => true
|
||||
add_index :comfy_cms_snippets, [:site_id, :position]
|
||||
|
||||
# -- Files --------------------------------------------------------------
|
||||
create_table :comfy_cms_files do |t|
|
||||
t.integer :site_id, :null => false
|
||||
t.integer :block_id
|
||||
t.string :label, :null => false
|
||||
t.string :file_file_name, :null => false
|
||||
t.string :file_content_type, :null => false
|
||||
t.integer :file_file_size, :null => false
|
||||
t.string :description, :limit => 2048
|
||||
t.integer :position, :null => false, :default => 0
|
||||
t.timestamps
|
||||
end
|
||||
add_index :comfy_cms_files, [:site_id, :label]
|
||||
add_index :comfy_cms_files, [:site_id, :file_file_name]
|
||||
add_index :comfy_cms_files, [:site_id, :position]
|
||||
add_index :comfy_cms_files, [:site_id, :block_id]
|
||||
|
||||
# -- Revisions -----------------------------------------------------------
|
||||
create_table :comfy_cms_revisions, :force => true do |t|
|
||||
t.string :record_type, :null => false
|
||||
t.integer :record_id, :null => false
|
||||
t.text :data, text_limit
|
||||
t.datetime :created_at
|
||||
end
|
||||
add_index :comfy_cms_revisions, [:record_type, :record_id, :created_at],
|
||||
:name => 'index_cms_revisions_on_rtype_and_rid_and_created_at'
|
||||
|
||||
# -- Categories ---------------------------------------------------------
|
||||
create_table :comfy_cms_categories, :force => true do |t|
|
||||
t.integer :site_id, :null => false
|
||||
t.string :label, :null => false
|
||||
t.string :categorized_type, :null => false
|
||||
end
|
||||
add_index :comfy_cms_categories, [:site_id, :categorized_type, :label], :unique => true,
|
||||
:name => 'index_cms_categories_on_site_id_and_cat_type_and_label'
|
||||
|
||||
create_table :comfy_cms_categorizations, :force => true do |t|
|
||||
t.integer :category_id, :null => false
|
||||
t.string :categorized_type, :null => false
|
||||
t.integer :categorized_id, :null => false
|
||||
end
|
||||
add_index :comfy_cms_categorizations, [:category_id, :categorized_type, :categorized_id], :unique => true,
|
||||
:name => 'index_cms_categorizations_on_cat_id_and_catd_type_and_catd_id'
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :comfy_cms_sites
|
||||
drop_table :comfy_cms_layouts
|
||||
drop_table :comfy_cms_pages
|
||||
drop_table :comfy_cms_snippets
|
||||
drop_table :comfy_cms_blocks
|
||||
drop_table :comfy_cms_files
|
||||
drop_table :comfy_cms_revisions
|
||||
drop_table :comfy_cms_categories
|
||||
drop_table :comfy_cms_categorizations
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddApprovalStatusToCrops < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :crops, :approval_status, :string, default: "approved"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddReasonForRejectionToCrops < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :crops, :reason_for_rejection, :text
|
||||
end
|
||||
end
|
||||
5
db/migrate/20150201064502_add_request_notes_to_crops.rb
Normal file
5
db/migrate/20150201064502_add_request_notes_to_crops.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class AddRequestNotesToCrops < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :crops, :request_notes, :text
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddRejectionNotesToCrops < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :crops, :rejection_notes, :text
|
||||
end
|
||||
end
|
||||
239
db/schema.rb
239
db/schema.rb
@@ -11,7 +11,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20150129034206) do
|
||||
ActiveRecord::Schema.define(version: 20150209105410) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
@@ -20,16 +20,16 @@ ActiveRecord::Schema.define(version: 20150129034206) do
|
||||
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
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
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
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
create_table "alternate_names", force: true do |t|
|
||||
@@ -46,33 +46,158 @@ ActiveRecord::Schema.define(version: 20150129034206) do
|
||||
t.string "uid"
|
||||
t.string "token"
|
||||
t.string "secret"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "name"
|
||||
end
|
||||
|
||||
add_index "authentications", ["member_id"], name: "index_authentications_on_member_id", using: :btree
|
||||
|
||||
create_table "comfy_cms_blocks", force: true do |t|
|
||||
t.string "identifier", null: false
|
||||
t.text "content"
|
||||
t.integer "blockable_id"
|
||||
t.string "blockable_type"
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
add_index "comfy_cms_blocks", ["blockable_id", "blockable_type"], name: "index_comfy_cms_blocks_on_blockable_id_and_blockable_type", using: :btree
|
||||
add_index "comfy_cms_blocks", ["identifier"], name: "index_comfy_cms_blocks_on_identifier", using: :btree
|
||||
|
||||
create_table "comfy_cms_categories", force: true do |t|
|
||||
t.integer "site_id", null: false
|
||||
t.string "label", null: false
|
||||
t.string "categorized_type", null: false
|
||||
end
|
||||
|
||||
add_index "comfy_cms_categories", ["site_id", "categorized_type", "label"], name: "index_cms_categories_on_site_id_and_cat_type_and_label", unique: true, using: :btree
|
||||
|
||||
create_table "comfy_cms_categorizations", force: true do |t|
|
||||
t.integer "category_id", null: false
|
||||
t.string "categorized_type", null: false
|
||||
t.integer "categorized_id", null: false
|
||||
end
|
||||
|
||||
add_index "comfy_cms_categorizations", ["category_id", "categorized_type", "categorized_id"], name: "index_cms_categorizations_on_cat_id_and_catd_type_and_catd_id", unique: true, using: :btree
|
||||
|
||||
create_table "comfy_cms_files", force: true do |t|
|
||||
t.integer "site_id", null: false
|
||||
t.integer "block_id"
|
||||
t.string "label", null: false
|
||||
t.string "file_file_name", null: false
|
||||
t.string "file_content_type", null: false
|
||||
t.integer "file_file_size", null: false
|
||||
t.string "description", limit: 2048
|
||||
t.integer "position", default: 0, null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
add_index "comfy_cms_files", ["site_id", "block_id"], name: "index_comfy_cms_files_on_site_id_and_block_id", using: :btree
|
||||
add_index "comfy_cms_files", ["site_id", "file_file_name"], name: "index_comfy_cms_files_on_site_id_and_file_file_name", using: :btree
|
||||
add_index "comfy_cms_files", ["site_id", "label"], name: "index_comfy_cms_files_on_site_id_and_label", using: :btree
|
||||
add_index "comfy_cms_files", ["site_id", "position"], name: "index_comfy_cms_files_on_site_id_and_position", using: :btree
|
||||
|
||||
create_table "comfy_cms_layouts", force: true do |t|
|
||||
t.integer "site_id", null: false
|
||||
t.integer "parent_id"
|
||||
t.string "app_layout"
|
||||
t.string "label", null: false
|
||||
t.string "identifier", null: false
|
||||
t.text "content"
|
||||
t.text "css"
|
||||
t.text "js"
|
||||
t.integer "position", default: 0, null: false
|
||||
t.boolean "is_shared", default: false, null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
add_index "comfy_cms_layouts", ["parent_id", "position"], name: "index_comfy_cms_layouts_on_parent_id_and_position", using: :btree
|
||||
add_index "comfy_cms_layouts", ["site_id", "identifier"], name: "index_comfy_cms_layouts_on_site_id_and_identifier", unique: true, using: :btree
|
||||
|
||||
create_table "comfy_cms_pages", force: true do |t|
|
||||
t.integer "site_id", null: false
|
||||
t.integer "layout_id"
|
||||
t.integer "parent_id"
|
||||
t.integer "target_page_id"
|
||||
t.string "label", null: false
|
||||
t.string "slug"
|
||||
t.string "full_path", null: false
|
||||
t.text "content_cache"
|
||||
t.integer "position", default: 0, null: false
|
||||
t.integer "children_count", default: 0, null: false
|
||||
t.boolean "is_published", default: true, null: false
|
||||
t.boolean "is_shared", default: false, null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
add_index "comfy_cms_pages", ["parent_id", "position"], name: "index_comfy_cms_pages_on_parent_id_and_position", using: :btree
|
||||
add_index "comfy_cms_pages", ["site_id", "full_path"], name: "index_comfy_cms_pages_on_site_id_and_full_path", using: :btree
|
||||
|
||||
create_table "comfy_cms_revisions", force: true do |t|
|
||||
t.string "record_type", null: false
|
||||
t.integer "record_id", null: false
|
||||
t.text "data"
|
||||
t.datetime "created_at"
|
||||
end
|
||||
|
||||
add_index "comfy_cms_revisions", ["record_type", "record_id", "created_at"], name: "index_cms_revisions_on_rtype_and_rid_and_created_at", using: :btree
|
||||
|
||||
create_table "comfy_cms_sites", force: true do |t|
|
||||
t.string "label", null: false
|
||||
t.string "identifier", null: false
|
||||
t.string "hostname", null: false
|
||||
t.string "path"
|
||||
t.string "locale", default: "en", null: false
|
||||
t.boolean "is_mirrored", default: false, null: false
|
||||
end
|
||||
|
||||
add_index "comfy_cms_sites", ["hostname"], name: "index_comfy_cms_sites_on_hostname", using: :btree
|
||||
add_index "comfy_cms_sites", ["is_mirrored"], name: "index_comfy_cms_sites_on_is_mirrored", using: :btree
|
||||
|
||||
create_table "comfy_cms_snippets", force: true do |t|
|
||||
t.integer "site_id", null: false
|
||||
t.string "label", null: false
|
||||
t.string "identifier", null: false
|
||||
t.text "content"
|
||||
t.integer "position", default: 0, null: false
|
||||
t.boolean "is_shared", default: false, null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
add_index "comfy_cms_snippets", ["site_id", "identifier"], name: "index_comfy_cms_snippets_on_site_id_and_identifier", unique: true, using: :btree
|
||||
add_index "comfy_cms_snippets", ["site_id", "position"], name: "index_comfy_cms_snippets_on_site_id_and_position", using: :btree
|
||||
|
||||
create_table "comments", force: true do |t|
|
||||
t.integer "post_id", null: false
|
||||
t.integer "author_id", null: false
|
||||
t.text "body", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
create_table "crops", force: true do |t|
|
||||
t.string "name", null: false
|
||||
t.string "name", null: false
|
||||
t.string "en_wikipedia_url"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "slug"
|
||||
t.integer "parent_id"
|
||||
t.integer "plantings_count", default: 0
|
||||
t.integer "plantings_count", default: 0
|
||||
t.integer "creator_id"
|
||||
t.integer "requester_id"
|
||||
t.string "approval_status", default: "approved"
|
||||
t.text "reason_for_rejection"
|
||||
t.text "request_notes"
|
||||
t.text "rejection_notes"
|
||||
end
|
||||
|
||||
add_index "crops", ["name"], name: "index_crops_on_name", using: :btree
|
||||
add_index "crops", ["requester_id"], name: "index_crops_on_requester_id", using: :btree
|
||||
add_index "crops", ["slug"], name: "index_crops_on_slug", unique: true, using: :btree
|
||||
|
||||
create_table "crops_posts", id: false, force: true do |t|
|
||||
@@ -94,8 +219,8 @@ ActiveRecord::Schema.define(version: 20150129034206) do
|
||||
t.string "name", null: false
|
||||
t.text "description", null: false
|
||||
t.integer "owner_id", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "slug"
|
||||
end
|
||||
|
||||
@@ -105,8 +230,8 @@ ActiveRecord::Schema.define(version: 20150129034206) do
|
||||
t.string "name", null: false
|
||||
t.integer "owner_id"
|
||||
t.string "slug", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.text "description"
|
||||
t.boolean "active", default: true
|
||||
t.string "location"
|
||||
@@ -116,7 +241,7 @@ ActiveRecord::Schema.define(version: 20150129034206) do
|
||||
t.string "area_unit"
|
||||
end
|
||||
|
||||
add_index "gardens", ["owner_id"], name: "index_gardens_on_user_id", using: :btree
|
||||
add_index "gardens", ["owner_id"], name: "index_gardens_on_owner_id", using: :btree
|
||||
add_index "gardens", ["slug"], name: "index_gardens_on_slug", unique: true, using: :btree
|
||||
|
||||
create_table "gardens_photos", id: false, force: true do |t|
|
||||
@@ -133,8 +258,8 @@ ActiveRecord::Schema.define(version: 20150129034206) do
|
||||
t.decimal "quantity"
|
||||
t.string "unit"
|
||||
t.text "description"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "slug"
|
||||
t.decimal "weight_quantity"
|
||||
t.string "weight_unit"
|
||||
@@ -167,8 +292,8 @@ ActiveRecord::Schema.define(version: 20150129034206) do
|
||||
t.integer "failed_attempts", default: 0
|
||||
t.string "unlock_token"
|
||||
t.datetime "locked_at"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "login_name"
|
||||
t.string "slug"
|
||||
t.boolean "tos_agreement"
|
||||
@@ -183,11 +308,11 @@ ActiveRecord::Schema.define(version: 20150129034206) do
|
||||
t.boolean "send_planting_reminder", default: true
|
||||
end
|
||||
|
||||
add_index "members", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
|
||||
add_index "members", ["email"], name: "index_users_on_email", unique: true, using: :btree
|
||||
add_index "members", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
|
||||
add_index "members", ["slug"], name: "index_users_on_slug", unique: true, using: :btree
|
||||
add_index "members", ["unlock_token"], name: "index_users_on_unlock_token", unique: true, using: :btree
|
||||
add_index "members", ["confirmation_token"], name: "index_members_on_confirmation_token", unique: true, using: :btree
|
||||
add_index "members", ["email"], name: "index_members_on_email", unique: true, using: :btree
|
||||
add_index "members", ["reset_password_token"], name: "index_members_on_reset_password_token", unique: true, using: :btree
|
||||
add_index "members", ["slug"], name: "index_members_on_slug", unique: true, using: :btree
|
||||
add_index "members", ["unlock_token"], name: "index_members_on_unlock_token", unique: true, using: :btree
|
||||
|
||||
create_table "members_roles", id: false, force: true do |t|
|
||||
t.integer "member_id"
|
||||
@@ -201,8 +326,8 @@ ActiveRecord::Schema.define(version: 20150129034206) do
|
||||
t.text "body"
|
||||
t.boolean "read", default: false
|
||||
t.integer "post_id"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
create_table "order_items", force: true do |t|
|
||||
@@ -210,13 +335,13 @@ ActiveRecord::Schema.define(version: 20150129034206) do
|
||||
t.integer "product_id"
|
||||
t.integer "price"
|
||||
t.integer "quantity"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
create_table "orders", force: true do |t|
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.datetime "completed_at"
|
||||
t.integer "member_id"
|
||||
t.string "paypal_express_token"
|
||||
@@ -233,8 +358,8 @@ ActiveRecord::Schema.define(version: 20150129034206) do
|
||||
t.integer "owner_id", null: false
|
||||
t.string "thumbnail_url", null: false
|
||||
t.string "fullsize_url", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "title", null: false
|
||||
t.string "license_name", null: false
|
||||
t.string "license_url"
|
||||
@@ -249,8 +374,8 @@ ActiveRecord::Schema.define(version: 20150129034206) do
|
||||
|
||||
create_table "plant_parts", force: true do |t|
|
||||
t.string "name"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "slug"
|
||||
end
|
||||
|
||||
@@ -260,8 +385,8 @@ ActiveRecord::Schema.define(version: 20150129034206) do
|
||||
t.date "planted_at"
|
||||
t.integer "quantity"
|
||||
t.text "description"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "slug"
|
||||
t.string "sunniness"
|
||||
t.string "planted_from"
|
||||
@@ -276,21 +401,22 @@ ActiveRecord::Schema.define(version: 20150129034206) do
|
||||
t.integer "author_id", null: false
|
||||
t.string "subject", null: false
|
||||
t.text "body", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "slug"
|
||||
t.integer "forum_id"
|
||||
t.integer "parent_id"
|
||||
end
|
||||
|
||||
add_index "posts", ["created_at", "author_id"], name: "index_updates_on_created_at_and_user_id", using: :btree
|
||||
add_index "posts", ["slug"], name: "index_updates_on_slug", unique: true, using: :btree
|
||||
add_index "posts", ["created_at", "author_id"], name: "index_posts_on_created_at_and_author_id", using: :btree
|
||||
add_index "posts", ["slug"], name: "index_posts_on_slug", unique: true, using: :btree
|
||||
|
||||
create_table "products", force: true do |t|
|
||||
t.string "name", null: false
|
||||
t.text "description", null: false
|
||||
t.integer "min_price", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.integer "account_type_id"
|
||||
t.integer "paid_months"
|
||||
t.integer "recommended_price"
|
||||
@@ -299,8 +425,8 @@ ActiveRecord::Schema.define(version: 20150129034206) do
|
||||
create_table "roles", force: true do |t|
|
||||
t.string "name", null: false
|
||||
t.text "description"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "slug"
|
||||
end
|
||||
|
||||
@@ -309,21 +435,26 @@ ActiveRecord::Schema.define(version: 20150129034206) do
|
||||
create_table "scientific_names", force: true do |t|
|
||||
t.string "scientific_name", null: false
|
||||
t.integer "crop_id", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.integer "creator_id"
|
||||
end
|
||||
|
||||
create_table "seeds", force: true do |t|
|
||||
t.integer "owner_id", null: false
|
||||
t.integer "crop_id", null: false
|
||||
t.integer "owner_id", null: false
|
||||
t.integer "crop_id", null: false
|
||||
t.text "description"
|
||||
t.integer "quantity"
|
||||
t.date "plant_before"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "tradable_to", default: "nowhere"
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "tradable_to", default: "nowhere"
|
||||
t.string "slug"
|
||||
t.integer "days_until_maturity_min"
|
||||
t.integer "days_until_maturity_max"
|
||||
t.text "organic", default: "unknown"
|
||||
t.text "gmo", default: "unknown"
|
||||
t.text "heirloom", default: "unknown"
|
||||
end
|
||||
|
||||
add_index "seeds", ["slug"], name: "index_seeds_on_slug", unique: true, using: :btree
|
||||
|
||||
@@ -319,6 +319,7 @@ namespace :growstuff do
|
||||
task :populate_si_weight => :environment do
|
||||
Harvest.find_each do |h|
|
||||
h.set_si_weight
|
||||
h.save
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@ describe CropsController do
|
||||
def valid_attributes
|
||||
{
|
||||
:name => "Tomato",
|
||||
:en_wikipedia_url => 'http://en.wikipedia.org/wiki/Tomato'
|
||||
:en_wikipedia_url => 'http://en.wikipedia.org/wiki/Tomato',
|
||||
:approval_status => 'approved'
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ FactoryGirl.define do
|
||||
factory :crop do
|
||||
name "magic bean"
|
||||
en_wikipedia_url "http://en.wikipedia.org/wiki/Magic_bean"
|
||||
approval_status "approved"
|
||||
creator
|
||||
|
||||
factory :tomato do
|
||||
@@ -54,6 +55,21 @@ FactoryGirl.define do
|
||||
name "Swiss chard"
|
||||
end
|
||||
|
||||
#for testing crop request
|
||||
factory :crop_request do
|
||||
name "Ultra berry"
|
||||
en_wikipedia_url ""
|
||||
approval_status "pending"
|
||||
association :requester, factory: :member
|
||||
request_notes "Please approve this even though it's fake."
|
||||
end
|
||||
|
||||
factory :rejected_crop do
|
||||
name "Fail bean"
|
||||
approval_status "rejected"
|
||||
reason_for_rejection "Totally fake"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -8,6 +8,11 @@ FactoryGirl.define do
|
||||
quantity 1
|
||||
plant_before "2013-07-15"
|
||||
tradable_to 'nowhere'
|
||||
organic 'unknown'
|
||||
gmo 'unknown'
|
||||
heirloom 'unknown'
|
||||
days_until_maturity_min nil
|
||||
days_until_maturity_max nil
|
||||
|
||||
factory :tradable_seed do
|
||||
tradable_to "locally"
|
||||
|
||||
37
spec/features/cms_spec.rb
Normal file
37
spec/features/cms_spec.rb
Normal file
@@ -0,0 +1,37 @@
|
||||
require 'spec_helper'
|
||||
|
||||
feature "cms admin" do
|
||||
before(:each) do
|
||||
@member = FactoryGirl.create(:member)
|
||||
@admin_member = FactoryGirl.create(:admin_member)
|
||||
end
|
||||
|
||||
scenario "can't view CMS admin if not signed in" do
|
||||
visit comfy_admin_cms_path
|
||||
current_path.should == root_path
|
||||
page.should have_content("Please sign in as an admin user")
|
||||
end
|
||||
|
||||
scenario "can't view CMS admin if not an admin member" do
|
||||
# sign in as an ordinary member
|
||||
visit root_path
|
||||
click_link 'navbar-signin'
|
||||
fill_in 'Login', :with => @member.email
|
||||
fill_in 'Password', :with => @member.password
|
||||
click_button 'Sign in'
|
||||
visit comfy_admin_cms_path
|
||||
current_path.should == root_path
|
||||
page.should have_content("Please sign in as an admin user")
|
||||
end
|
||||
|
||||
scenario "admin members can view CMS admin area" do
|
||||
visit root_path
|
||||
# now we sign in as an admin member
|
||||
click_link 'navbar-signin'
|
||||
fill_in 'Login', :with => @admin_member.email
|
||||
fill_in 'Password', :with => @admin_member.password
|
||||
click_button 'Sign in'
|
||||
visit comfy_admin_cms_path
|
||||
current_path.should match /#{comfy_admin_cms_path}/ # match any CMS admin page
|
||||
end
|
||||
end
|
||||
19
spec/features/crops/browse_crops_spec.rb
Normal file
19
spec/features/crops/browse_crops_spec.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
require 'rails_helper'
|
||||
|
||||
feature "browse crops" do
|
||||
|
||||
let(:tomato) { FactoryGirl.create(:tomato) }
|
||||
let(:maize) { FactoryGirl.create(:maize) }
|
||||
|
||||
scenario "has a form for sorting by" do
|
||||
visit crops_path
|
||||
expect(page).to have_css "select#sort"
|
||||
end
|
||||
|
||||
scenario "shows a list of crops" do
|
||||
crop1 = tomato
|
||||
visit crops_path
|
||||
expect(page).to have_content crop1.name
|
||||
end
|
||||
|
||||
end
|
||||
22
spec/features/crops/crop_search_spec.rb
Normal file
22
spec/features/crops/crop_search_spec.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
require 'rails_helper'
|
||||
|
||||
feature "crop search" do
|
||||
scenario "search results show the search term in title" do
|
||||
visit root_path
|
||||
within "form#navbar-search" do
|
||||
fill_in "search", with: "tomato"
|
||||
click_button "Search"
|
||||
end
|
||||
expect(page).to have_css "h1", text: "Crops matching \"tomato\""
|
||||
end
|
||||
|
||||
scenario "search page with no search term shows suitable title" do
|
||||
visit crops_search_path
|
||||
expect(page).to have_css "h1", text: "Crop search"
|
||||
end
|
||||
|
||||
scenario "search page has a search form on it" do
|
||||
visit crops_search_path
|
||||
expect(page).to have_css "form#crop-search"
|
||||
end
|
||||
end
|
||||
@@ -5,6 +5,8 @@ feature "crop wranglers" do
|
||||
let!(:crop_wranglers) { FactoryGirl.create_list(:crop_wrangling_member, 3) }
|
||||
let(:wrangler){crop_wranglers.first}
|
||||
let!(:crops) { FactoryGirl.create_list(:crop, 2) }
|
||||
let!(:requested_crop) { FactoryGirl.create(:crop_request) }
|
||||
let!(:rejected_crop) { FactoryGirl.create(:rejected_crop) }
|
||||
|
||||
background do
|
||||
login_as(wrangler)
|
||||
@@ -25,7 +27,7 @@ feature "crop wranglers" do
|
||||
scenario "can see list of crops with extra detail of who created a crop" do
|
||||
visit root_path
|
||||
click_link 'Crop Wrangling'
|
||||
within '.table' do
|
||||
within '#recently-added-crops' do
|
||||
expect(page).to have_content "#{crops.first.creator.login_name}"
|
||||
end
|
||||
end
|
||||
@@ -48,6 +50,24 @@ feature "crop wranglers" do
|
||||
expect(page).to have_content 'Crop was successfully created'
|
||||
expect(page).to have_content 'planticus maximus'
|
||||
end
|
||||
|
||||
scenario "View pending crops" do
|
||||
visit wrangle_crops_path(:approval_status => "pending")
|
||||
within "#pending-crops" do
|
||||
click_link "Ultra berry"
|
||||
end
|
||||
expect(page).to have_content "This crop is currently pending approval."
|
||||
expect(page).to have_content "Please approve this even though it's fake."
|
||||
end
|
||||
|
||||
scenario "View rejected crops" do
|
||||
visit wrangle_crops_path(:approval_status => "rejected")
|
||||
within "#rejected-crops" do
|
||||
click_link "Fail bean"
|
||||
end
|
||||
expect(page).to have_content "This crop was rejected for the following reason: Totally fake"
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
33
spec/features/crops/crop_wrangling_button_spec.rb
Normal file
33
spec/features/crops/crop_wrangling_button_spec.rb
Normal file
@@ -0,0 +1,33 @@
|
||||
require 'rails_helper'
|
||||
|
||||
feature "crop wrangling button" do
|
||||
|
||||
let(:crop_wrangler) { FactoryGirl.create(:crop_wrangling_member) }
|
||||
|
||||
context "crop wrangling button" do
|
||||
|
||||
background do
|
||||
login_as(crop_wrangler)
|
||||
visit crops_path
|
||||
end
|
||||
|
||||
scenario "has a link to crop wrangling page" do
|
||||
expect(page).to have_link "Wrangle Crops", :href => wrangle_crops_path
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
let(:member) { FactoryGirl.create(:member) }
|
||||
|
||||
context "crop wrangling button" do
|
||||
|
||||
background do
|
||||
login_as(member)
|
||||
visit crops_path
|
||||
end
|
||||
|
||||
scenario "has no link to crop wrangling page" do
|
||||
expect(page).to have_no_link "Wrangle Crops", :href => wrangle_crops_path
|
||||
end
|
||||
end
|
||||
end
|
||||
50
spec/features/crops/request_new_crop_spec.rb
Normal file
50
spec/features/crops/request_new_crop_spec.rb
Normal file
@@ -0,0 +1,50 @@
|
||||
require 'rails_helper'
|
||||
|
||||
feature "Requesting a new crop" do
|
||||
|
||||
context "As a regular member" do
|
||||
|
||||
let(:member) { FactoryGirl.create(:member) }
|
||||
let!(:wrangler) { FactoryGirl.create(:crop_wrangling_member) }
|
||||
|
||||
before { login_as member }
|
||||
|
||||
scenario "Submit request" do
|
||||
visit new_crop_path
|
||||
fill_in "Name", with: "Couch potato"
|
||||
fill_in "Comments", with: "Couch potatoes are real for real."
|
||||
click_button "Save"
|
||||
expect(page).to have_content "Crop was successfully requested."
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "As a crop wrangler" do
|
||||
|
||||
let(:wrangler) { FactoryGirl.create(:crop_wrangling_member) }
|
||||
let!(:crop) { FactoryGirl.create(:crop_request) }
|
||||
let!(:already_approved) { FactoryGirl.create(:crop) }
|
||||
|
||||
before { login_as wrangler }
|
||||
|
||||
scenario "Approve a request" do
|
||||
visit edit_crop_path(crop)
|
||||
select "approved", from: "Approval Status"
|
||||
click_button "Save"
|
||||
expect(page).to have_content "En wikipedia url is not a valid English Wikipedia URL"
|
||||
fill_in "Wikipedia URL", with: "http://en.wikipedia.org/wiki/Aung_San_Suu_Kyi"
|
||||
click_button "Save"
|
||||
expect(page).to have_content "Crop was successfully updated."
|
||||
end
|
||||
|
||||
scenario "Rejecting a crop" do
|
||||
visit edit_crop_path(crop)
|
||||
select "rejected", from: "Approval Status"
|
||||
select "not edible", from: "Reason for rejection"
|
||||
click_button "Save"
|
||||
expect(page).to have_content "Crop was successfully updated."
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -2,19 +2,11 @@ require 'rails_helper'
|
||||
|
||||
feature "footer" do
|
||||
|
||||
scenario "has three columns" do
|
||||
scenario "footer is on home page" do
|
||||
visit root_path
|
||||
expect(page).to have_css 'footer #about-growstuff'
|
||||
expect(page).to have_css 'footer #policies'
|
||||
expect(page).to have_css 'footer #contact'
|
||||
expect(page).to have_css 'footer'
|
||||
end
|
||||
|
||||
# NB: not testing specific content in the footer since I'm going to put them
|
||||
# in the CMS and they'll be variable.
|
||||
|
||||
scenario "contact page has Twitter link" do
|
||||
visit root_path
|
||||
click_link 'Contact'
|
||||
page.should have_link '@growstufforg', :href => 'http://twitter.com/growstufforg'
|
||||
end
|
||||
end
|
||||
|
||||
35
spec/features/members_list_spec.rb
Normal file
35
spec/features/members_list_spec.rb
Normal file
@@ -0,0 +1,35 @@
|
||||
require 'rails_helper'
|
||||
|
||||
feature "members list" do
|
||||
|
||||
context "list all members" do
|
||||
let! (:member1) { FactoryGirl.create(:member, :login_name => "Archaeopteryx", :confirmed_at => Time.zone.parse('2013-02-10')) }
|
||||
let! (:member2) { FactoryGirl.create(:member, :login_name => "Zephyrosaurus", :confirmed_at => Time.zone.parse('2014-01-11')) }
|
||||
let! (:member3) { FactoryGirl.create(:member, :login_name => "Testingname", :confirmed_at => Time.zone.parse('2014-05-09')) }
|
||||
|
||||
scenario "default alphabetical sort" do
|
||||
visit members_path
|
||||
expect(page).to have_css "#sort"
|
||||
expect(page).to have_selector "form"
|
||||
click_button('Show')
|
||||
all_links = page.all("#maincontainer p")
|
||||
expect(all_links.first).to have_text member1.login_name
|
||||
expect(all_links.last).to have_text member2.login_name
|
||||
end
|
||||
|
||||
scenario "recently joined sort" do
|
||||
visit members_path
|
||||
expect(page).to have_css "#sort"
|
||||
expect(page).to have_selector "form"
|
||||
select("recently", :from => 'sort')
|
||||
click_button('Show')
|
||||
all_links = page.all("#maincontainer p")
|
||||
expect(all_links.first).to have_text member3.login_name
|
||||
expect(all_links.last).to have_text member1.login_name
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -5,6 +5,12 @@ feature "Planting reminder email", :js => true do
|
||||
let(:member) { FactoryGirl.create(:member) }
|
||||
let(:mail) { Notifier.planting_reminder(member) }
|
||||
|
||||
# Unfortunately, we can't use the default url options for ActionMailer as configured in
|
||||
# test.rb, since this isn't a mailer spec.
|
||||
def self.default_url_options
|
||||
{ host: 'localhost', port: 8080 }
|
||||
end
|
||||
|
||||
scenario "has a greeting" do
|
||||
expect(mail).to have_content "Hello"
|
||||
end
|
||||
@@ -34,10 +40,8 @@ feature "Planting reminder email", :js => true do
|
||||
|
||||
scenario "lists plantings" do
|
||||
expect(mail).to have_content "most recent plantings you've told us about"
|
||||
expect(mail).to have_content @p1.to_s
|
||||
expect(mail).to have_content @p2.to_s
|
||||
# can't test for links to your plantings due to this weirdness:
|
||||
# https://github.com/Skud/growstuff/commit/8e6a57c4429eac88ab934f422ab11bf16b0a7663
|
||||
expect(mail).to have_link @p1.to_s, planting_url(@p1)
|
||||
expect(mail).to have_link @p2.to_s, planting_url(@p2)
|
||||
expect(mail).to have_content "keep your garden records up to date"
|
||||
end
|
||||
end
|
||||
@@ -65,10 +69,8 @@ feature "Planting reminder email", :js => true do
|
||||
|
||||
scenario "lists harvests" do
|
||||
expect(mail).to have_content "the last few things you harvested were"
|
||||
expect(mail).to have_content @h1.to_s
|
||||
expect(mail).to have_content @h2.to_s
|
||||
# can't test for links to your harvests due to this weirdness:
|
||||
# https://github.com/Skud/growstuff/commit/8e6a57c4429eac88ab934f422ab11bf16b0a7663
|
||||
expect(mail).to have_link @h1.to_s, harvest_url(@h1)
|
||||
expect(mail).to have_link @h2.to_s, harvest_url(@h2)
|
||||
expect(mail).to have_content "Harvested anything else lately?"
|
||||
end
|
||||
|
||||
|
||||
@@ -18,12 +18,23 @@ feature "Seeds", :js => true do
|
||||
within "form#new_seed" do
|
||||
fill_in "Quantity:", :with => 42
|
||||
fill_in "Plant before:", :with => "2014-06-15"
|
||||
fill_in "Days until maturity:", :with => 999
|
||||
fill_in "to", :with => 1999
|
||||
select "certified organic", :from => "Organic?"
|
||||
select "non-certified GMO-free", :from => "GMO?"
|
||||
select "heirloom", :from => "Heirloom?"
|
||||
fill_in "Description", :with => "It's killer."
|
||||
select "internationally", :from => "Will trade:"
|
||||
click_button "Save"
|
||||
end
|
||||
|
||||
expect(page).to have_content "Successfully added maize seed to your stash"
|
||||
expect(page).to have_content "Quantity: 42"
|
||||
expect(page).to have_content "Days until maturity: 999–1999"
|
||||
expect(page).to have_content "certified organic"
|
||||
expect(page).to have_content "non-certified GMO-free"
|
||||
expect(page).to have_content "Heirloom? heirloom"
|
||||
expect(page).to have_content "It's killer."
|
||||
end
|
||||
|
||||
scenario "Adding a seed from crop page" do
|
||||
|
||||
@@ -45,5 +45,30 @@ feature "seeds" do
|
||||
click_link 'Delete'
|
||||
current_path.should eq seeds_path
|
||||
end
|
||||
|
||||
scenario "view seeds with max and min days until maturity" do
|
||||
seed = FactoryGirl.create(:seed, :days_until_maturity_min => 5, :days_until_maturity_max => 7)
|
||||
visit seed_path(seed)
|
||||
expect(page).to have_content "Days until maturity: 5–7"
|
||||
end
|
||||
|
||||
scenario "view seeds with only max days until maturity" do
|
||||
seed = FactoryGirl.create(:seed, :days_until_maturity_max => 7)
|
||||
visit seed_path(seed)
|
||||
expect(page).to have_content "Days until maturity: 7"
|
||||
end
|
||||
|
||||
scenario "view seeds with only min days until maturity" do
|
||||
seed = FactoryGirl.create(:seed, :days_until_maturity_min => 5)
|
||||
visit seed_path(seed)
|
||||
expect(page).to have_content "Days until maturity: 5"
|
||||
end
|
||||
|
||||
scenario "view seeds with neither max nor min days until maturity" do
|
||||
seed = FactoryGirl.create(:seed)
|
||||
visit seed_path(seed)
|
||||
expect(page).to have_content "Days until maturity: unknown"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -44,4 +44,84 @@ describe Notifier do
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
describe "new crop request" do
|
||||
let(:member) { FactoryGirl.create(:crop_wrangling_member) }
|
||||
let(:crop) { FactoryGirl.create(:crop_request) }
|
||||
let(:mail) { Notifier.new_crop_request(member, crop) }
|
||||
|
||||
it 'sets the subject correctly' do
|
||||
mail.subject.should == "#{crop.requester.login_name} has requested Ultra berry as a new crop"
|
||||
end
|
||||
|
||||
it 'comes from noreply@growstuff.org' do
|
||||
mail.from.should == ['noreply@growstuff.org']
|
||||
end
|
||||
|
||||
it 'sends the mail to the recipient of the notification' do
|
||||
mail.to.should == [member.email]
|
||||
end
|
||||
|
||||
it 'includes the requested crop URL' do
|
||||
mail.body.encoded.should match crop_url(crop)
|
||||
end
|
||||
end
|
||||
|
||||
describe "crop approved" do
|
||||
let(:member) { FactoryGirl.create(:member) }
|
||||
let(:crop) { FactoryGirl.create(:crop) }
|
||||
let(:mail) { Notifier.crop_request_approved(member, crop) }
|
||||
|
||||
it 'sets the subject correctly' do
|
||||
expect(mail.subject).to eq "Magic bean has been approved"
|
||||
end
|
||||
|
||||
it 'comes from noreply@growstuff.org' do
|
||||
expect(mail.from).to eq ['noreply@growstuff.org']
|
||||
end
|
||||
|
||||
it 'sends the mail to the recipient of the notification' do
|
||||
expect(mail.to).to eq [member.email]
|
||||
end
|
||||
|
||||
it 'includes the approved crop URL' do
|
||||
expect(mail.body.encoded).to match crop_url(crop)
|
||||
end
|
||||
|
||||
it 'includes links to plant, harvest and stash seeds for the new crop' do
|
||||
expect(mail.body.encoded).to match "#{new_planting_url}\\?crop_id=#{crop.id}"
|
||||
expect(mail.body.encoded).to match "#{new_harvest_url}\\?crop_id=#{crop.id}"
|
||||
expect(mail.body.encoded).to match "#{new_seed_url}\\?crop_id=#{crop.id}"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "crop rejected" do
|
||||
let(:member) { FactoryGirl.create(:member) }
|
||||
let(:crop) { FactoryGirl.create(:rejected_crop) }
|
||||
let(:mail) { Notifier.crop_request_rejected(member, crop) }
|
||||
|
||||
it 'sets the subject correctly' do
|
||||
expect(mail.subject).to eq "Fail bean has been rejected"
|
||||
end
|
||||
|
||||
it 'comes from noreply@growstuff.org' do
|
||||
expect(mail.from).to eq ['noreply@growstuff.org']
|
||||
end
|
||||
|
||||
it 'sends the mail to the recipient of the notification' do
|
||||
expect(mail.to).to eq [member.email]
|
||||
end
|
||||
|
||||
it 'includes the rejected crop URL' do
|
||||
expect(mail.body.encoded).to match crop_url(crop)
|
||||
end
|
||||
|
||||
it 'includes the reason for rejection' do
|
||||
expect(mail.body.encoded).to match "Totally fake"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -45,11 +45,14 @@ describe Ability do
|
||||
|
||||
context "standard member" do
|
||||
it "can't manage crops" do
|
||||
@ability.should_not be_able_to(:create, Crop)
|
||||
@ability.should_not be_able_to(:update, @crop)
|
||||
@ability.should_not be_able_to(:destroy, @crop)
|
||||
end
|
||||
|
||||
it "can request crops" do
|
||||
@ability.should be_able_to(:create, Crop)
|
||||
end
|
||||
|
||||
it "can read crops" do
|
||||
@ability.should be_able_to(:read, @crop)
|
||||
end
|
||||
|
||||
@@ -88,6 +88,49 @@ describe Seed do
|
||||
end
|
||||
end
|
||||
|
||||
context 'organic, gmo, heirloom' do
|
||||
it 'all valid organic values should work' do
|
||||
['certified organic', 'non-certified organic',
|
||||
'conventional/non-organic', 'unknown'].each do |t|
|
||||
@seed = FactoryGirl.build(:seed, :organic => t)
|
||||
@seed.should be_valid
|
||||
end
|
||||
end
|
||||
|
||||
it 'all valid GMO values should work' do
|
||||
['certified GMO-free', 'non-certified GMO-free',
|
||||
'GMO', 'unknown'].each do |t|
|
||||
@seed = FactoryGirl.build(:seed, :gmo => t)
|
||||
@seed.should be_valid
|
||||
end
|
||||
end
|
||||
|
||||
it 'all valid heirloom values should work' do
|
||||
%w(heirloom hybrid unknown).each do |t|
|
||||
@seed = FactoryGirl.build(:seed, :heirloom => t)
|
||||
@seed.should be_valid
|
||||
end
|
||||
end
|
||||
|
||||
it 'should refuse invalid organic/GMO/heirloom values' do
|
||||
[:organic, :gmo, :heirloom].each do |field|
|
||||
@seed = FactoryGirl.build(:seed, field => 'not valid')
|
||||
@seed.should_not be_valid
|
||||
@seed.errors[field].should_not be_empty
|
||||
end
|
||||
end
|
||||
|
||||
it 'should not allow nil or blank values' do
|
||||
[:organic, :gmo, :heirloom].each do |field|
|
||||
@seed = FactoryGirl.build(:seed, field => nil)
|
||||
@seed.should_not be_valid
|
||||
@seed = FactoryGirl.build(:seed, field => '')
|
||||
@seed.should_not be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
context 'interesting' do
|
||||
it 'lists interesting seeds' do
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ require 'capybara'
|
||||
require 'capybara/poltergeist'
|
||||
Capybara.javascript_driver = :poltergeist
|
||||
Capybara.app_host = 'http://localhost'
|
||||
Capybara.server_port = 8080
|
||||
Capybara.server_port = 8081
|
||||
|
||||
include Warden::Test::Helpers
|
||||
|
||||
|
||||
@@ -8,24 +8,11 @@ describe "crops/index" do
|
||||
total_entries = 2
|
||||
@tomato = FactoryGirl.create(:tomato)
|
||||
@maize = FactoryGirl.create(:maize)
|
||||
crops = WillPaginate::Collection.create(page, per_page, total_entries) do |pager|
|
||||
assign(:crops, [@tomato, @maize])
|
||||
paginated_crops = WillPaginate::Collection.create(page, per_page, total_entries) do |pager|
|
||||
pager.replace([ @tomato, @maize ])
|
||||
end
|
||||
assign(:crops, crops)
|
||||
end
|
||||
|
||||
it "has a form for sorting by" do
|
||||
render
|
||||
assert_select "form"
|
||||
assert_select "select#sort"
|
||||
assert_select "option[value=alpha]"
|
||||
assert_select "option[value=popular]"
|
||||
end
|
||||
|
||||
it "renders a list of crops" do
|
||||
render
|
||||
assert_select "a", :text => @maize.name
|
||||
assert_select "a", :text => @tomato.name
|
||||
assign(:paginated_crops, paginated_crops)
|
||||
end
|
||||
|
||||
it "shows photos where available" do
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe "crops/search" do
|
||||
before(:each) do
|
||||
controller.stub(:current_user) { nil }
|
||||
end
|
||||
|
||||
context "has results" do
|
||||
|
||||
before :each do
|
||||
@tomato = FactoryGirl.create(:tomato)
|
||||
@roma = FactoryGirl.create(:crop, :name => 'Roma tomato', :parent => @tomato)
|
||||
assign(:search, 'tomato')
|
||||
assign(:all_matches, [@tomato, @roma])
|
||||
render
|
||||
end
|
||||
|
||||
it "shows exact matches" do
|
||||
assert_select "div#all_matches" do
|
||||
assert_select "a[href=#{crop_path(@tomato)}]"
|
||||
end
|
||||
end
|
||||
|
||||
it "shows partial matches" do
|
||||
assert_select "div#all_matches" do
|
||||
assert_select "a[href=#{crop_path(@roma)}]"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "no results" do
|
||||
before :each do
|
||||
assign(:all_matches, [])
|
||||
assign(:search, 'tomato')
|
||||
render
|
||||
end
|
||||
|
||||
it "tells you there are no matches" do
|
||||
rendered.should have_content "No results found"
|
||||
end
|
||||
|
||||
it "links to browse crops" do
|
||||
assert_select "a", :href => crops_path
|
||||
rendered.should have_content "Try browsing our crop database instead"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user