Compare commits

..

7 Commits

Author SHA1 Message Date
Skud
d066801943 Merge pull request #616 from pmackay/api_v1
Api v1 - initial start
2015-01-13 10:50:01 +11:00
Paul Mackay
016feaf8bf #582: Change license checks to expect() syntax. 2015-01-12 10:25:00 +00:00
Paul Mackay
1b5d2bb898 #582: Fixes for review comments. 2015-01-11 19:25:40 +00:00
Paul Mackay
a3e3abecf3 Merge api-v1-jbuilder into new API branch. 2015-01-07 08:41:26 +00:00
Paul Mackay
223a06ef8b Some crop API tweaks, basic LD stuff. 2014-11-07 07:03:27 +00:00
Paul Mackay
f3eb5774ef Add spec test for 2 crop API calls, including license. 2014-11-03 16:45:37 +00:00
Paul Mackay
800a1f10e3 First commit for v1 API implementation based on jbuilder. 2014-11-03 15:03:59 +00:00
504 changed files with 3658 additions and 9720 deletions

2
.gitignore vendored
View File

@@ -13,5 +13,3 @@ Pathogen:
custom_plan.rb
zeus.json
.bundle
config/application.yml
.idea/**

1
.rspec
View File

@@ -1,2 +1 @@
--color
--require spec_helper

View File

@@ -1 +1 @@
2.1.8
2.1.5

View File

@@ -1,38 +1,12 @@
sudo: false
---
language: ruby
cache: bundler
env:
matrix:
- GROWSTUFF_SITE_NAME="Growstuff (travis)" RAILS_SECRET_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' GROWSTUFF_ELASTICSEARCH='true'
- GROWSTUFF_SITE_NAME="Growstuff (travis)" RAILS_SECRET_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' GROWSTUFF_ELASTICSEARCH='false'
global:
secure: "Z5TpM2jEX4UCvNePnk/LwltQX48U2u9BRc+Iypr1x9QW2o228QJhPIOH39a8RMUrepGnkQIq9q3ZRUn98RfrJz1yThtlNFL3NmzdQ57gKgjGwfpa0e4Dwj/ZJqV2D84tDGjvdVYLP7zzaYZxQcwk/cgNpzKf/jq97HLNP7CYuf4="
bundler_args: "--without development production staging"
rvm:
- 2.1.8
before_script:
- psql -c 'create database growstuff_test;' -U postgres
script:
- bundle exec rake db:migrate --trace
- bundle exec rspec spec/
services:
- elasticsearch
before_deploy:
- bundle exec script/heroku_maintenance.rb on
deploy:
provider: heroku
api_key:
secure: WrQxf0fEKkCdXrjcejurobOnNNz3he4dDwjBbToXbQTQNDObPp7NetJrLsfM8FiUFEeOuvhIHHiDQtMvY720zGGAGxDptvgFS+0QHCUqoTRZA/yFfUmHlG2jROXTzk5uVK0AE4k6Ion5kX8+mM0EnMT/7u+MTFiukrJctSiEXfg=
on:
repo: Growstuff/growstuff
app:
dev: growstuff-staging
travis_deploy: tranquil-basin-3130
travis_containers: tranquil-basin-3130
run:
- "rake db:migrate"
- "script/deploy-tasks.sh"
- restart
after_deploy:
- bundle exec script/heroku_maintenance.rb off
env: GROWSTUFF_SITE_NAME="Growstuff (travis)" RAILS_SECRET_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
bundler_args: --without development production staging
rvm:
- 2.1.5
before_script:
- psql -c 'create database growstuff_test;' -U postgres
script:
- bundle exec rake db:migrate --trace
- bundle exec rspec spec/

View File

@@ -11,12 +11,10 @@ submit the change with your pull request.
- Alex Bayley / [Skud](https://github.com/Skud)
- Cesy / [cesy](https://github.com/cesy)
- Miles Gould / [pozorvlak](https://github.com/pozorvlak)
- Taylor Griffin / [tygriffin](https://github.com/tygriffin)
- Mackenzie Morgan / [maco](https://github.com/maco)
- Joseph Caudle / [jcaudle](https://github.com/jcaudle)
## Contributors
- Joseph Caudle / [jcaudle](https://github.com/jcaudle)
- Ricky Amianym / [amianym](https://github.com/amianym)
- Juliet Kemp / [julietk](https://github.com/julietk)
- Federico Mena Quintero / [federicomenaquintero](https://github.com/federicomenaquintero)
@@ -24,6 +22,7 @@ submit the change with your pull request.
- Maia Sauren / [sauramaia](https://github.com/sauramaia)
- Norman Ancajas / [nbancajas](https://github.com/nbancajas)
- Jonathan "Duke" Leto / [leto](https://github.com/leto)
- Mackenzie Morgan / [maco](https://github.com/maco)
- Amy Hendrix / [sabreuse](https://github.com/sabreuse)
- CephLPod / [cephLpod](https://github.com/cephLpod/)
- Gemma Mason / [gemmaellen](https://github.com/gemmaellen)
@@ -41,6 +40,7 @@ submit the change with your pull request.
- Marty Hines / [martyhines](https://github.com/martyhines)
- Amelia Greenhall / [ameliagreenhall](https://github.com/ameliagreenhall)
- Barb Natali / [barbnatali](https://github.com/barbnatali)
- Taylor Griffin / [tygriffin](https://github.com/tygriffin)
- Marlena Compton / [Marlena](https://github.com/marlena)
- Elizabeth A. Kari / [catfriend](https://github.com/catfriend)
- Cheri Allen / [cherimarie](https://github.com/cherimarie)
@@ -51,18 +51,4 @@ submit the change with your pull request.
- Yoong Kang Lim / [yoongkang](https://github.com/yoongkang)
- 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)
- Jake Yesbeck / [yez](https://github.com/yez)
- Mauricio Gonzalez / [mauricio-gonzalez](https://github.com/mauricio-gonzalez)
- Andrey Bazhutkin / [andrba](https://github.com/andrba)
- Gabriel Sandoval / [gabrielsandoval](https://github.com/gabrielsandoval)
- Cjay Billones / [CjayBillones](https://github.com/CjayBillones)
- Katy Ereira / [maccath](https://github.com/maccath)
- Gabrielle DeWitt / [gabrielle27](https://github.com/gabrielle27)
- Manmeet Singh / [manmeetsingh](https://github.com/manmeetsingh)
- Jym Paul Carandang / [jacarandang](https://github.com/jacarandang)
- Anthony Atkinson / [sha1sum](https://github.com/sha1sum)
- Terence Conquest / [twconquest](https://github.com/twconquest)
- Daniel O'Connor / [CloCkWeRX](https://github.com/CloCkWeRX)

168
Gemfile
View File

@@ -1,58 +1,92 @@
source 'https://rubygems.org'
ruby '2.1.8'
gem 'rails', '~> 4.1.11'
ruby "2.1.5"
gem 'bundler', '>=1.1.5'
gem 'sass-rails', '~> 4.0.4'
gem 'coffee-rails', '~> 4.1.0'
gem 'rails', '3.2.13'
gem 'rack', '~>1.4.5'
gem 'json', '~>1.7.7'
gem 'haml'
# Another CSS preprocessor, used for Bootstrap overrides
gem 'less', '~>2.5.0'
gem 'less-rails', '~> 2.5.0'
# CSS framework
gem 'less-rails-bootstrap', '~> 3.2.0'
gem 'uglifier', '~> 2.7.2' # JavaScript compressor
gem 'jquery-rails'
gem 'jquery-ui-rails', '~> 5.0.2'
gem 'js-routes' # provides access to Rails routes in Javascript
gem 'flickraw'
gem 'leaflet-rails'
gem 'leaflet-markercluster-rails'
gem 'unicorn' # http server
gem 'unicorn' # http server
gem 'pg'
gem 'figaro' # for handling config via ENV variables
gem 'cancancan', '~> 1.9' # for checking member privileges
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
gem 'figaro' # for handling config via ENV variables
gem 'kaminari' # pagination
gem 'bootstrap-kaminari-views' # bootstrap views for kaminari
gem 'cancan' # for checking member privileges
gem 'gibbon' # for Mailchimp newsletter subscriptions
gem 'csv_shaper' # CSV export
# vendored activemerchant for testing- needed for bogus paypal
# gateway monkeypatch
gem 'activemerchant', '1.33.0',
:path => 'vendor/gems/activemerchant-1.33.0',
:path => 'vendor/gems/activemerchant-1.33.0',
:require => 'active_merchant'
gem 'active_utils', '1.0.5',
:path => 'vendor/gems/active_utils-1.0.5'
:path => 'vendor/gems/active_utils-1.0.5'
# less-rails depends on a JavaScript engine; we use therubyracer.
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# long term, we'll probably want node.js for performance, but this will do
# for now as it's easier for new people to install
gem 'therubyracer', '~> 0.12', :platforms => :ruby
# libv8 is needed by therubyracer and is a bit finicky
gem 'libv8', '3.16.14.7'
group :production, :staging do
gem 'newrelic_rpm'
gem 'dalli'
gem 'memcachier'
gem 'rails_12factor' # supresses heroku plugin injection
end
# Gems used only for assets and not required
# in production environments by default.
group :assets do
# CSS preprocessor, used for app/assets/stylesheets/application.css
gem 'sass-rails', '~> 3.2.3'
# CoffeeScript is a Python-like language that compiles to JavaScript
gem 'coffee-rails', '~> 3.2.1'
# less-rails depends on a JavaScript engine; we use therubyracer.
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# long term, we'll probably want node.js for performance, but this will do
# for now as it's easier for new people to install
gem "therubyracer", "~> 0.12", :platforms => :ruby
# libv8 is needed by therubyracer and is a bit finicky
gem 'libv8', '3.16.14.7'
# Another CSS preprocessor, used for Bootstrap overrides
gem "less", '~>2.5.0'
gem "less-rails", '~> 2.5.0'
# CSS framework
gem "less-rails-bootstrap", '~> 3.2.0'
gem 'uglifier', '>= 1.0.3' # JavaScript compressor
gem 'compass-rails', '~> 1.0.3' # Yet Another CSS framework
end
gem 'jquery-rails'
gem 'jquery-ui-rails'
gem 'js-routes' # provides access to Rails routes in Javascript
gem 'flickraw'
# To use ActiveModel has_secure_password
# gem 'bcrypt-ruby', '~> 3.0.0'
# To use Jbuilder templates for JSON
gem 'jbuilder'
# Use unicorn as the app server
# gem 'unicorn'
group :development do
# A debugger and irb alternative. Pry doesn't play nice
# with unicorn, so start a Webrick server when debugging
# with Pry
gem 'pry'
gem 'better_errors'
gem 'binding_of_caller'
gem 'letter_opener'
end
# Markdown formatting for updates etc
gem 'bluecloth'
@@ -61,10 +95,10 @@ gem 'bluecloth'
gem 'will_paginate', '~> 3.0'
# user signup/login/etc
gem 'devise', '~> 3.5.0'
gem 'devise', '~> 3.2.0'
# nicely formatted URLs
gem 'friendly_id', '~> 5.0.4'
gem 'friendly_id', '~> 4.0.10'
# gravatars
gem 'gravatar-ultimate'
@@ -82,51 +116,19 @@ gem 'omniauth'
gem 'omniauth-twitter'
gem 'omniauth-flickr', '>= 0.0.15'
# client for Elasticsearch. Elasticsearch is a flexible
# and powerful, distributed, real-time search and analytics engine.
# An example of the use in the project is fuzzy crop search.
gem "elasticsearch-model"
gem "elasticsearch-rails"
gem 'rake', '>= 10.0.0'
group :production, :staging do
gem 'newrelic_rpm'
gem 'dalli'
gem 'memcachier'
gem 'rails_12factor' # supresses heroku plugin injection
gem 'bonsai-elasticsearch-rails' # Integration with Bonsa-Elasticsearch on heroku
end
group :development do
# A debugger and irb alternative. Pry doesn't play nice
# with unicorn, so start a Webrick server when debugging
# with Pry
gem 'pry'
gem 'better_errors'
gem 'binding_of_caller'
gem 'letter_opener'
gem 'quiet_assets'
gem 'guard'
gem 'guard-rspec'
end
group :development, :test do
gem 'haml-rails' # HTML templating language
gem 'rspec-rails', '~> 3.4.0' # unit testing framework
gem 'rspec-activemodel-mocks'
gem 'byebug' # debugging
gem 'database_cleaner', '~> 1.5.0'
gem 'webrat' # provides HTML matchers for view tests
gem 'factory_girl_rails', '~> 4.5.0' # for creating test data
gem 'coveralls', require: false # coverage analysis
gem 'capybara' # integration tests
gem 'capybara-email' # integration tests for email
gem 'poltergeist', '~> 1.6' # for headless JS testing
gem 'i18n-tasks' # adds tests for finding missing and unused translations
gem 'selenium-webdriver'
end
group :travis do
gem 'heroku-api'
gem 'byebug' # debugging
gem 'haml-rails' # HTML templating language
gem 'rspec-rails', '~> 2.12.1' # unit testing framework
gem 'database_cleaner', '~> 1.3.0'
gem 'webrat' # provides HTML matchers for view tests
gem 'factory_girl_rails', '~> 4.0' # for creating test data
gem 'coveralls', require: false # coverage analysis
gem 'capybara' # integration tests
gem 'capybara-email' # integration tests for email
gem 'poltergeist', '~> 1.5.1' # for headless JS testing
gem 'i18n-tasks' # adds tests for finding missing and unused translations
gem 'json_spec' # extra ways to test JSON data
end

View File

@@ -20,40 +20,36 @@ PATH
GEM
remote: https://rubygems.org/
specs:
actionmailer (4.1.15)
actionpack (= 4.1.15)
actionview (= 4.1.15)
mail (~> 2.5, >= 2.5.4)
actionpack (4.1.15)
actionview (= 4.1.15)
activesupport (= 4.1.15)
rack (~> 1.5.2)
rack-test (~> 0.6.2)
actionview (4.1.15)
activesupport (= 4.1.15)
builder (~> 3.1)
actionmailer (3.2.13)
actionpack (= 3.2.13)
mail (~> 2.5.3)
actionpack (3.2.13)
activemodel (= 3.2.13)
activesupport (= 3.2.13)
builder (~> 3.0.0)
erubis (~> 2.7.0)
active_link_to (1.0.2)
actionpack
activemodel (4.1.15)
activesupport (= 4.1.15)
builder (~> 3.1)
activerecord (4.1.15)
activemodel (= 4.1.15)
activesupport (= 4.1.15)
arel (~> 5.0.0)
activesupport (4.1.15)
i18n (~> 0.6, >= 0.6.9)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
thread_safe (~> 0.1)
tzinfo (~> 1.1)
journey (~> 1.0.4)
rack (~> 1.4.5)
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.2.1)
activemodel (3.2.13)
activesupport (= 3.2.13)
builder (~> 3.0.0)
activerecord (3.2.13)
activemodel (= 3.2.13)
activesupport (= 3.2.13)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activeresource (3.2.13)
activemodel (= 3.2.13)
activesupport (= 3.2.13)
activesupport (3.2.13)
i18n (= 0.6.1)
multi_json (~> 1.0)
addressable (2.3.6)
arel (5.0.1.20140414130214)
autoprefixer-rails (5.1.1)
execjs
json
bcrypt (3.1.11)
arel (3.0.3)
bcrypt (3.1.9)
better_errors (2.0.0)
coderay (>= 1.0.0)
erubis (>= 2.6.6)
@@ -61,22 +57,14 @@ GEM
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
bluecloth (2.2.0)
bonsai-elasticsearch-rails (0.0.4)
bootstrap-datepicker-rails (1.3.0.2)
railties (>= 3.0)
bootstrap-kaminari-views (0.0.5)
kaminari (>= 0.13)
rails (>= 3.1)
bootstrap-sass (3.3.3)
autoprefixer-rails (>= 5.0.0.1)
sass (>= 3.2.19)
bootstrap_form (2.2.0)
builder (3.2.2)
builder (3.0.4)
byebug (3.5.1)
columnize (~> 0.8)
debugger-linecache (~> 1.2)
slop (~> 3.6)
cancancan (1.9.2)
cancan (1.6.10)
capybara (2.4.4)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
@@ -86,40 +74,24 @@ GEM
capybara-email (2.4.0)
capybara (~> 2.4)
mail
childprocess (0.5.6)
ffi (~> 1.0, >= 1.0.11)
climate_control (0.0.3)
activesupport (>= 3.0)
chunky_png (1.3.3)
cliver (0.3.2)
cocaine (0.5.7)
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-rails (3.2.2)
coffee-script (>= 2.2.0)
railties (>= 4.0.0, < 5.0)
railties (~> 3.2.0)
coffee-script (2.3.0)
coffee-script-source
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)
columnize (0.8.9)
commonjs (0.2.7)
compass (0.12.7)
chunky_png (~> 1.2)
fssm (>= 0.2.7)
sass (~> 3.2.19)
compass-rails (1.0.3)
compass (>= 0.12.2, < 0.14)
coveralls (0.7.1)
multi_json (~> 1.3)
rest-client
@@ -129,100 +101,54 @@ GEM
csv_shaper (1.1.1)
activesupport (>= 3.0.0)
dalli (2.7.2)
database_cleaner (1.5.0)
database_cleaner (1.3.0)
debug_inspector (0.0.2)
debugger-linecache (1.2.0)
devise (3.5.6)
devise (3.2.4)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 3.2.6, < 5)
responders
thread_safe (~> 0.1)
warden (~> 1.2.3)
diff-lcs (1.2.5)
diff-lcs (1.1.3)
docile (1.1.5)
domain_name (0.5.24)
unf (>= 0.0.5, < 1.0.0)
easy_translate (0.5.0)
json
thread
thread_safe
elasticsearch (1.0.6)
elasticsearch-api (= 1.0.6)
elasticsearch-transport (= 1.0.6)
elasticsearch-api (1.0.6)
multi_json
elasticsearch-model (0.1.6)
activesupport (> 3)
elasticsearch (> 0.4)
hashie
elasticsearch-rails (0.1.6)
elasticsearch-transport (1.0.6)
faraday
multi_json
erubis (2.7.0)
excon (0.43.0)
execjs (2.6.0)
execjs (2.2.2)
factory_girl (4.5.0)
activesupport (>= 3.0.0)
factory_girl_rails (4.5.0)
factory_girl (~> 4.5.0)
railties (>= 3.0.0)
faraday (0.9.1)
multipart-post (>= 1.2, < 3)
ffi (1.9.10)
figaro (1.0.0)
thor (~> 0.14)
flickraw (0.9.8)
formatador (0.2.5)
friendly_id (5.0.4)
activerecord (>= 4.0.0)
friendly_id (4.0.10.1)
activerecord (>= 3.0, < 4.0)
fssm (0.2.10)
gibbon (1.1.4)
httparty
multi_json (>= 1.3.4)
gravatar-ultimate (2.0.0)
activesupport (>= 2.3.14)
rack
guard (2.12.8)
formatador (>= 0.2.4)
listen (>= 2.7, <= 4.0)
lumberjack (~> 1.0)
nenv (~> 0.1)
notiffany (~> 0.0)
pry (>= 0.9.12)
shellany (~> 0.0)
thor (>= 0.18.1)
guard-compat (1.2.1)
guard-rspec (4.6.2)
guard (~> 2.1)
guard-compat (~> 1.1)
rspec (>= 2.99.0, < 4.0)
haml (4.1.0.beta.1)
haml (4.0.5)
tilt
haml-rails (0.6.0)
actionpack (>= 4.0.1)
activesupport (>= 4.0.1)
haml (>= 3.1, < 5.0)
html2haml (>= 1.0.1)
railties (>= 4.0.1)
haml-rails (0.4)
actionpack (>= 3.1, < 4.1)
activesupport (>= 3.1, < 4.1)
haml (>= 3.1, < 4.1)
railties (>= 3.1, < 4.1)
hashie (3.3.2)
heroku-api (0.3.22)
excon (~> 0.38)
multi_json (~> 1.8)
highline (1.6.21)
hike (1.2.3)
hpricot (0.8.6)
html2haml (1.0.1)
erubis (~> 2.7.0)
haml (>= 4.0.0.rc.1)
hpricot (~> 0.8.6)
ruby_parser (~> 3.1.1)
http-cookie (1.0.2)
domain_name (~> 0.5)
httparty (0.13.3)
json (~> 1.8)
httparty (0.11.0)
multi_json (~> 1.0)
multi_xml (>= 0.5.2)
i18n (0.7.0)
i18n (0.6.1)
i18n-tasks (0.7.8)
activesupport
easy_translate (>= 0.5.0)
@@ -232,20 +158,23 @@ GEM
slop (>= 3.5.0)
term-ansicolor
terminal-table
jquery-rails (3.1.3)
jbuilder (2.2.6)
activesupport (>= 3.0.0, < 5)
multi_json (~> 1.2)
journey (1.0.4)
jquery-rails (3.1.2)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
jquery-ui-rails (5.0.3)
railties (>= 3.2.16)
jquery-ui-rails (4.1.2)
railties (>= 3.1.0)
js-routes (0.9.9)
railties (>= 3.2)
sprockets-rails
json (1.8.3)
kaminari (0.16.3)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
json (1.7.7)
json_spec (1.1.4)
multi_json (~> 1.0)
rspec (>= 2.0, < 4.0)
kgio (2.9.2)
kramdown (1.5.0)
launchy (2.4.3)
addressable (~> 2.3)
leaflet-markercluster-rails (0.7.0)
@@ -258,32 +187,22 @@ GEM
less (~> 2.5.0)
less-rails-bootstrap (3.2.0)
less-rails (~> 2.5.0)
letter_opener (1.3.0)
letter_opener (1.2.0)
launchy (~> 2.2)
libv8 (3.16.14.7)
listen (3.0.2)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
lumberjack (1.0.9)
mail (2.6.4)
mime-types (>= 1.16, < 4)
mail (2.5.4)
mime-types (~> 1.16)
treetop (~> 1.4.8)
memcachier (0.0.2)
method_source (0.8.2)
mime-types (2.99.1)
mimemagic (0.3.0)
mini_portile2 (2.0.0)
minitest (5.8.4)
multi_json (1.11.2)
mime-types (1.25.1)
mini_portile (0.6.1)
multi_json (1.10.1)
multi_xml (0.5.5)
multipart-post (2.0.0)
nenv (0.2.0)
netrc (0.10.3)
newrelic_rpm (3.9.8.273)
nokogiri (1.6.7.2)
mini_portile2 (~> 2.0.0.rc2)
notiffany (0.0.6)
nenv (~> 0.1)
shellany (~> 0.0)
netrc (0.8.0)
newrelic_rpm (3.9.7.266)
nokogiri (1.6.5)
mini_portile (~> 0.6.0)
oauth (0.4.7)
omniauth (1.2.2)
hashie (>= 1.2, < 4)
@@ -297,121 +216,85 @@ GEM
multi_json (~> 1.3)
omniauth-oauth (~> 1.0)
orm_adapter (0.5.0)
paperclip (4.3.0)
activemodel (>= 3.2.0)
activesupport (>= 3.2.0)
cocaine (~> 0.5.5)
mime-types
mimemagic (= 0.3.0)
pg (0.17.1)
plupload-rails (1.2.1)
rails (>= 3.1)
poltergeist (1.6.0)
poltergeist (1.5.1)
capybara (~> 2.1)
cliver (~> 0.3.1)
multi_json (~> 1.0)
websocket-driver (>= 0.2.0)
polyglot (0.3.5)
pry (0.10.1)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
quiet_assets (1.1.0)
railties (>= 3.1, < 5.0)
rack (1.5.5)
rack-test (0.6.3)
rack (1.4.5)
rack-cache (1.2)
rack (>= 0.4)
rack-ssl (1.3.4)
rack
rack-test (0.6.2)
rack (>= 1.0)
rails (4.1.15)
actionmailer (= 4.1.15)
actionpack (= 4.1.15)
actionview (= 4.1.15)
activemodel (= 4.1.15)
activerecord (= 4.1.15)
activesupport (= 4.1.15)
bundler (>= 1.3.0, < 2.0)
railties (= 4.1.15)
sprockets-rails (~> 2.0)
rails-i18n (4.0.3)
i18n (~> 0.6)
railties (~> 4.0)
rails (3.2.13)
actionmailer (= 3.2.13)
actionpack (= 3.2.13)
activerecord (= 3.2.13)
activeresource (= 3.2.13)
activesupport (= 3.2.13)
bundler (~> 1.0)
railties (= 3.2.13)
rails_12factor (0.0.3)
rails_serve_static_assets
rails_stdout_logging
rails_serve_static_assets (0.0.2)
rails_stdout_logging (0.0.3)
railties (4.1.15)
actionpack (= 4.1.15)
activesupport (= 4.1.15)
railties (3.2.13)
actionpack (= 3.2.13)
activesupport (= 3.2.13)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rdoc (~> 3.4)
thor (>= 0.14.6, < 2.0)
raindrops (0.13.0)
rake (11.1.2)
rb-fsevent (0.9.5)
rb-inotify (0.9.5)
ffi (>= 0.5.0)
rake (10.4.0)
rdoc (3.12.2)
json (~> 1.4)
ref (1.0.5)
responders (1.1.2)
railties (>= 3.2, < 4.2)
rest-client (1.8.0)
http-cookie (>= 1.0.2, < 2.0)
rest-client (1.7.2)
mime-types (>= 1.16, < 3.0)
netrc (~> 0.7)
rspec (3.4.0)
rspec-core (~> 3.4.0)
rspec-expectations (~> 3.4.0)
rspec-mocks (~> 3.4.0)
rspec-activemodel-mocks (1.0.1)
activemodel (>= 3.0)
rspec (2.12.0)
rspec-core (~> 2.12.0)
rspec-expectations (~> 2.12.0)
rspec-mocks (~> 2.12.0)
rspec-core (2.12.2)
rspec-expectations (2.12.1)
diff-lcs (~> 1.1.3)
rspec-mocks (2.12.2)
rspec-rails (2.12.2)
actionpack (>= 3.0)
activesupport (>= 3.0)
rspec-mocks (>= 2.99, < 4.0)
rspec-core (3.4.4)
rspec-support (~> 3.4.0)
rspec-expectations (3.4.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0)
rspec-mocks (3.4.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0)
rspec-rails (3.4.2)
actionpack (>= 3.0, < 4.3)
activesupport (>= 3.0, < 4.3)
railties (>= 3.0, < 4.3)
rspec-core (~> 3.4.0)
rspec-expectations (~> 3.4.0)
rspec-mocks (~> 3.4.0)
rspec-support (~> 3.4.0)
rspec-support (3.4.1)
ruby-units (1.4.5)
ruby_parser (3.1.3)
sexp_processor (~> 4.1)
rubyzip (1.1.7)
railties (>= 3.0)
rspec-core (~> 2.12.0)
rspec-expectations (~> 2.12.0)
rspec-mocks (~> 2.12.0)
sass (3.2.19)
sass-rails (4.0.5)
railties (>= 4.0.0, < 5.0)
sass (~> 3.2.2)
sprockets (~> 2.8, < 3.0)
sprockets-rails (~> 2.0)
selenium-webdriver (2.47.1)
childprocess (~> 0.5)
multi_json (~> 1.0)
rubyzip (~> 1.0)
websocket (~> 1.0)
sexp_processor (4.4.4)
shellany (0.0.1)
sass-rails (3.2.6)
railties (~> 3.2.0)
sass (>= 3.1.10)
tilt (~> 1.3)
simplecov (0.9.1)
docile (~> 1.1.0)
multi_json (~> 1.0)
simplecov-html (~> 0.8.0)
simplecov-html (0.8.0)
slop (3.6.0)
sprockets (2.12.4)
sprockets (2.2.3)
hike (~> 1.2)
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
sprockets-rails (2.3.3)
actionpack (>= 3.0)
activesupport (>= 3.0)
sprockets (>= 2.8, < 4.0)
sprockets-rails (0.0.1)
sprockets (>= 1.0.2)
term-ansicolor (1.3.0)
tins (~> 1.0)
terminal-table (1.4.5)
@@ -420,31 +303,27 @@ GEM
ref
thor (0.19.1)
thread (0.1.4)
thread_safe (0.3.5)
thread_safe (0.3.4)
tilt (1.4.1)
tins (1.3.3)
tzinfo (1.2.2)
thread_safe (~> 0.1)
uglifier (2.7.2)
treetop (1.4.15)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.42)
uglifier (2.2.1)
execjs (>= 0.3.0)
json (>= 1.8.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.1)
multi_json (~> 1.0, >= 1.0.2)
unicorn (4.8.3)
kgio (~> 2.6)
rack
raindrops (~> 0.7)
warden (1.2.6)
warden (1.2.3)
rack (>= 1.0)
webrat (0.7.3)
nokogiri (>= 1.2.0)
rack (>= 1.0)
rack-test (>= 0.5.3)
websocket (1.2.2)
websocket-driver (0.5.4)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
websocket-driver (0.4.0)
will_paginate (3.0.7)
xpath (2.0.0)
nokogiri (~> 1.3)
@@ -458,40 +337,35 @@ DEPENDENCIES
better_errors
binding_of_caller
bluecloth
bonsai-elasticsearch-rails
bootstrap-datepicker-rails
bootstrap-kaminari-views
bundler (>= 1.1.5)
byebug
cancancan (~> 1.9)
cancan
capybara
capybara-email
coffee-rails (~> 4.1.0)
comfortable_mexican_sofa (~> 1.12.0)
coffee-rails (~> 3.2.1)
compass-rails (~> 1.0.3)
coveralls
csv_shaper
dalli
database_cleaner (~> 1.5.0)
devise (~> 3.5.0)
elasticsearch-model
elasticsearch-rails
factory_girl_rails (~> 4.5.0)
database_cleaner (~> 1.3.0)
devise (~> 3.2.0)
factory_girl_rails (~> 4.0)
figaro
flickraw
friendly_id (~> 5.0.4)
friendly_id (~> 4.0.10)
geocoder!
gibbon
gravatar-ultimate
guard
guard-rspec
haml
haml-rails
heroku-api
i18n-tasks
jbuilder
jquery-rails
jquery-ui-rails (~> 5.0.2)
jquery-ui-rails
js-routes
kaminari
json (~> 1.7.7)
json_spec
leaflet-markercluster-rails
leaflet-rails
less (~> 2.5.0)
@@ -505,22 +379,16 @@ DEPENDENCIES
omniauth-flickr (>= 0.0.15)
omniauth-twitter
pg
poltergeist (~> 1.6)
poltergeist (~> 1.5.1)
pry
quiet_assets
rails (~> 4.1.11)
rack (~> 1.4.5)
rails (= 3.2.13)
rails_12factor
rake (>= 10.0.0)
rspec-activemodel-mocks
rspec-rails (~> 3.4.0)
ruby-units
sass-rails (~> 4.0.4)
selenium-webdriver
rspec-rails (~> 2.12.1)
sass-rails (~> 3.2.3)
therubyracer (~> 0.12)
uglifier (~> 2.7.2)
uglifier (>= 1.0.3)
unicorn
webrat
will_paginate (~> 3.0)
BUNDLED WITH
1.11.2

View File

@@ -1,13 +0,0 @@
guard :rspec,
cmd: 'bundle exec rspec --format documentation',
failed_mode: :keep do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/libs/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
# Rails example
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
watch('config/routes.rb') { "spec/routing" }
end

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 613 B

After

Width:  |  Height:  |  Size: 572 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -15,7 +15,7 @@
//= require js-routes
//= require jquery
//= require jquery_ujs
//= require jquery-ui/autocomplete
//= require jquery.ui.autocomplete
//= require twitter/bootstrap
//= require_tree .
//= require bootstrap-datepicker

View File

@@ -1 +0,0 @@
# Custom JS for the admin area

View File

@@ -47,7 +47,3 @@ function showCropMap(cropmap) {
cropmap.addLayer(markers);
}
$('.btn.toggle.crop-hierarchy').click(function () {
$('.toggle.crop-hierarchy').toggleClass('hide');
});

View File

@@ -9,11 +9,11 @@ jQuery ->
finished = $('#planting_finished_at')
if @checked
if previousValue.length
date = previousValue
date = previousValue
finished.val(date)
else
finished.trigger('focus')
else
previousValue = finished.val()
finished.val('')
)
)

View File

@@ -4,42 +4,3 @@
jQuery ->
$('.add-datepicker').datepicker('format' : 'yyyy-mm-dd')
$('#add-sci_name-row').css("display", "inline-block")
$('#remove-sci_name-row').css("display", "inline-block")
$("#add-alt_name-row").css("display", "inline-block")
$("#remove-alt_name-row").css("display", "inline-block")
$ ->
sci_template = "<div id='sci_template[INDEX]' class='template col-md-12'><div class='col-md-2'><label>Scientific name INDEX:</label></div><div class='col-md-8'><input name='sci_name[INDEX]' class='form-control', id='sci_name[INDEX]')'></input><span class='help-block'>Scientific name of crop.</span></div><div class='col-md-2'></div></div>"
sci_index = $('#scientific_names .template').length + 1
$('#add-sci_name-row').click ->
compiled_input = $(sci_template.split("INDEX").join(sci_index))
$('#scientific_names').append(compiled_input)
sci_index = sci_index + 1
$('#remove-sci_name-row').click ->
if (sci_index > 2)
sci_index = sci_index - 1
tmp = 'sci_template[' + sci_index + ']'
element = document.getElementById(tmp)
element.remove()
alt_template = "<div id='alt_template[INDEX]' class='template col-md-12'><div class='col-md-2'><label>Alternate name INDEX:</label></div><div class='col-md-8'><input name='alt_name[INDEX]' class='form-control', id='alt_name[INDEX]')'></input><span class='help-block'>Alternate name of crop.</span></div><div class='col-md-2'></div></div>"
alt_index = $('#alternate_names .template').length + 1
$('#add-alt_name-row').click ->
compiled_input = $(alt_template.split("INDEX").join(alt_index))
$('#alternate_names').append(compiled_input)
alt_index = alt_index + 1
$('#remove-alt_name-row').click ->
if (alt_index > 2)
alt_index = alt_index - 1
tmp = 'alt_template[' + alt_index + ']'
element = document.getElementById(tmp)
console.log("%s",tmp)
element.remove()

View File

@@ -3,7 +3,7 @@
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
* the top of the compiled file, but it's generally better to create a new file per style scope.
*= require_self
*= require jquery-ui/autocomplete
*= require jquery.ui.autocomplete
*= require bootstrap-datepicker
*= require leaflet
*= require leaflet.markercluster

View File

@@ -1 +0,0 @@
// custom CSS for admin area

View File

@@ -1,10 +1,3 @@
.leaflet-popup-content-wrapper, .leaflet-popup-tip {
border: none;
}
.thumbnail{
background: #fff !important;
border: solid 1px whitesmoke;
}
.thumbnail .crop-thumbnail .cropinfo{
padding-top: 14px;
}

View File

@@ -97,25 +97,12 @@ p.stats {
font-weight: bold;
}
.member-cards {
display: flex;
flex: none;
flex-wrap: wrap;
justify-content: space-between;
.homepage-members {
height: 100px;
}
.member-thumbnail {
padding: .25em;
div {
width: 5em;
display: inline-block;
vertical-align: top;
}
div ~ div {
width: 15em;
padding-left: 1em;
}
.homepage-members:nth-child(odd) {
margin-left: 0px;
}
#placesmap, #cropmap {
@@ -135,7 +122,7 @@ p.stats {
color: @brown;
}
.photo-thumbnail {
.crop-thumbnail {
position:relative;
padding:0;
img {
@@ -162,55 +149,33 @@ p.stats {
}
}
.member-thumbnail {
img {
height: 85px;
width: 85px;
max-width: 85px
}
}
.thumbnail {
border: none;
text-align: center;
margin-bottom: 1.5em;
.member-thumbnail {
text-align: left;
img {
height: 85px;
width: 85px;
max-width: 85px
}
.scientific-name small, .crop-name a {
display: inline-block;
max-width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1em;
padding-bottom: 2px;
}
.crop-thumbnail {
height: 220px;
.cropinfo {
display: inline-block;
max-width: 100%;
white-space: nowrap;
line-height: 1em;
padding-bottom: 2px;
.crop-name a {
padding-top: 2px;
}
.cropname {
overflow: hidden;
text-overflow: ellipsis;
}
.scientificname {
font-size: small;
font-style: italic;
overflow: hidden;
text-overflow: ellipsis;
}
.plantingcount {
font-size: small;
}
}
.crop-name a {
padding-top: 2px;
}
.scientific-name small {
margin-bottom: -2px;
}
.scientific-name small {
margin-bottom: -2px;
}
}
@@ -227,48 +192,31 @@ li.crop-hierarchy {
margin: 40px 0px 0px 0px !important;
}
// footer
footer {
#footer1, #footer2, #footer3 {
text-align: left;
padding-top: 1em;
padding-bottom: 2em;
ul {
list-style-type: none;
list-style-position: outside;
padding-left: 0px;
margin-left: 0px;
}
a {
color: @navbar-default-link-color;
text-decoration: none;
}
a:hover {
color: @navbar-default-link-hover-color;
}
a:active {
color: @navbar-default-link-active-color;
}
}
.navbar-bottom.navbar {
border-radius: 0;
// navbar centering
footer .navbar .nav {
float: none;
display: inline-block;
*display: inline;
/* ie7 fix */
*zoom: 1;
/* hasLayout ie7 trigger */
vertical-align: top;
> li {
float: none;
display: inline-block;
*display: inline;
/* ie7 fix */
*zoom: 1;
/* hasLayout ie7 trigger */
vertical-align: top;
}
}
// ensure footer is pushed to bottom of browser window
#maincontainer {
min-height: 80%;
.navbar-bottom.navbar {
text-align: center;
border-radius: 0;
}
html, body {
height: 100%;
}
//
.crop-image, .member-image {
width: 100%;
height: 100%;
@@ -296,12 +244,6 @@ html, body {
text-decoration: underline;
}
.alert {
a {
font-weight: 800;
}
}
// Overrides applying only to mobile view. This must be at the end of the overrides file.
@media only screen and (max-width: 767px) {
@@ -319,42 +261,3 @@ html, body {
display: block;
}
}
/* override "info" alert boxes to be green, not blue, on Growstuff */
@state-info-text: darken(@green, 10%);
@state-info-bg: lighten(@green, 50%);
/* and set "success" to be the same, as it was just very slightly
* different because the default bootstrap green is slightly different
* from ours */
@state-success-text: darken(@green, 10%);
@state-success-bg: lighten(@green, 50%);
.hide {
display: none;
}
#add-sci_name-row, #remove-sci_name-row, #add-alt_name-row, #remove-alt_name-row{
display: none;
}
.panel-footer{
height: 6em;
}
#gardens_panel_body{
height: 20em;
}
.form-group.required .control-label:before {
content: "* ";
color: red;
}
.margin-bottom {
margin-bottom: 1em;
}
.red {
color: red;
}

View File

@@ -36,7 +36,7 @@ class AccountTypesController < ApplicationController
# POST /account_types
def create
@account_type = AccountType.new(account_type_params)
@account_type = AccountType.new(params[:account_type])
respond_to do |format|
if @account_type.save
@@ -52,7 +52,7 @@ class AccountTypesController < ApplicationController
@account_type = AccountType.find(params[:id])
respond_to do |format|
if @account_type.update(account_type_params)
if @account_type.update_attributes(params[:account_type])
format.html { redirect_to @account_type, notice: 'Account type was successfully updated.' }
else
format.html { render action: "edit" }
@@ -69,10 +69,4 @@ class AccountTypesController < ApplicationController
format.html { redirect_to account_types_url, notice: 'Account type was successfully deleted.' }
end
end
private
def account_type_params
params.require(:account_type).permit(:is_paid, :is_permanent_paid, :name)
end
end

View File

@@ -30,7 +30,7 @@ class AccountsController < ApplicationController
@account = Account.find(params[:id])
respond_to do |format|
if @account.update(params[:account])
if @account.update_attributes(params[:account])
format.html { redirect_to @account, notice: 'Account detail was successfully updated.' }
else
format.html { render action: "edit" }
@@ -38,10 +38,4 @@ class AccountsController < ApplicationController
end
end
private
def account_params
params.require(:account).permit(:account_type_id, :member_id, :paid_until)
end
end

View File

@@ -45,7 +45,7 @@ class AlternateNamesController < ApplicationController
# POST /alternate_names.json
def create
params[:alternate_name][:creator_id] = current_member.id
@alternate_name = AlternateName.new(alternate_name_params)
@alternate_name = AlternateName.new(params[:alternate_name])
respond_to do |format|
if @alternate_name.save
@@ -64,7 +64,7 @@ class AlternateNamesController < ApplicationController
@alternate_name = AlternateName.find(params[:id])
respond_to do |format|
if @alternate_name.update(alternate_name_params)
if @alternate_name.update_attributes(params[:alternate_name])
format.html { redirect_to @alternate_name.crop, notice: 'Alternate name was successfully updated.' }
format.json { head :no_content }
else
@@ -88,10 +88,4 @@ class AlternateNamesController < ApplicationController
format.json { head :no_content }
end
end
private
def alternate_name_params
params.require(:alternate_name).permit(:crop_id, :name, :creator_id)
end
end

View File

@@ -0,0 +1,13 @@
class Api::V1::CropsController < ApplicationController
# GET /api/v1/crops
def index
@crops = Crop.all
end
# GET /crops/1
def show
@crop = Crop.find(params[:id])
end
end

View File

@@ -22,10 +22,6 @@ class ApplicationController < ActionController::Base
stored_location_for(:member) || root_path
end
def after_sign_out_path_for(resource_or_scope)
request.referrer
end
# tweak CanCan defaults because we don't have a "current_user" method
# this means that we use current_user in specs but current_member everywhere
# else in the code.
@@ -37,7 +33,7 @@ class ApplicationController < ActionController::Base
rescue_from CanCan::AccessDenied do |exception|
redirect_to request.referer || root_url, :alert => exception.message
end
def set_locale
I18n.locale = params[:locale] || extract_locale_from_subdomain || I18n.default_locale
end
@@ -47,36 +43,4 @@ class ApplicationController < ActionController::Base
I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
end
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) do |member|
member.permit(:login_name, :email, :password, :password_confirmation,
:remember_me, :login,
# terms of service
:tos_agreement,
# profile stuff
:bio, :location, :latitude, :longitude,
# email settings
:show_email, :newsletter, :send_notification_email, :send_planting_reminder
)
end
devise_parameter_sanitizer.for(:account_update) do |member|
member.permit(:login_name, :email, :password, :password_confirmation,
:remember_me, :login,
# terms of service
:tos_agreement,
# profile stuff
:bio, :location, :latitude, :longitude,
# email settings
:show_email, :newsletter, :send_notification_email, :send_planting_reminder,
#update password
:current_password
)
end
end
end

View File

@@ -2,6 +2,15 @@ class AuthenticationsController < ApplicationController
before_filter :authenticate_member!
load_and_authorize_resource
# GET /authentications
def index
@authentications = current_member.authentications if member_signed_in?
respond_to do |format|
format.html # index.html.erb
end
end
# POST /authentications
def create
auth = request.env['omniauth.auth']
@@ -18,17 +27,12 @@ class AuthenticationsController < ApplicationController
name = auth['info']['name']
end
@authentication = current_member.authentications
.create_with(
:name => name,
:token => auth['credentials']['token'],
:secret => auth['credentials']['secret']
)
.find_or_create_by(
@authentication = current_member.authentications.find_or_create_by_provider_and_uid(
:provider => auth['provider'],
:uid => auth['uid'],
:name => name)
:name => name,
:token => auth['credentials']['token'],
:secret => auth['credentials']['secret'])
flash[:notice] = "Authentication successful."
else
flash[:notice] = "Authentication failed."

View File

@@ -2,6 +2,8 @@ class CommentsController < ApplicationController
before_filter :authenticate_member!, :except => [:index, :show]
load_and_authorize_resource
cache_sweeper :comment_sweeper
# GET /comments
# GET /comments.json
def index
@@ -53,11 +55,11 @@ class CommentsController < ApplicationController
# POST /comments.json
def create
params[:comment][:author_id] = current_member.id
@comment = Comment.new(comment_params)
@comment = Comment.new(params[:comment])
respond_to do |format|
if @comment.save
format.html { redirect_to @comment.post, notice: "Comment was successfully created." }
format.html { redirect_to @comment.post, notice: 'Comment was successfully created.' }
format.json { render json: @comment, status: :created, location: @comment }
else
format.html { render action: "new" }
@@ -77,7 +79,7 @@ class CommentsController < ApplicationController
params[:comment].delete("author_id")
respond_to do |format|
if @comment.update(comment_params)
if @comment.update_attributes(params[:comment])
format.html { redirect_to @comment.post, notice: 'Comment was successfully updated.' }
format.json { head :no_content }
else
@@ -99,10 +101,4 @@ class CommentsController < ApplicationController
format.json { head :no_content }
end
end
private
def comment_params
params.require(:comment).permit(:author_id, :body, :post_id)
end
end

View File

@@ -1,22 +1,20 @@
require 'will_paginate/array'
class CropsController < ApplicationController
before_filter :authenticate_member!, :except => [:index, :hierarchy, :search, :show]
load_and_authorize_resource
skip_authorize_resource :only => [:hierarchy, :search]
cache_sweeper :crop_sweeper
# GET /crops
# GET /crops.json
def index
@sort = params[:sort]
if @sort == 'alpha'
# alphabetical order
@crops = Crop.includes(:scientific_names, {:plantings => :photos})
@paginated_crops = @crops.approved.paginate(:page => params[:page])
@crops = Crop.includes(:scientific_names, {:plantings => :photos}).paginate(:page => params[:page])
else
# default to sorting by popularity
@crops = Crop.popular.includes(:scientific_names, {:plantings => :photos})
@paginated_crops = @crops.approved.paginate(:page => params[:page])
@crops = Crop.popular.includes(:scientific_names, {:plantings => :photos}).paginate(:page => params[:page])
end
respond_to do |format|
@@ -36,18 +34,7 @@ class CropsController < ApplicationController
# GET /crops/wrangle
def wrangle
@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])
@crops = Crop.recent.paginate(:page => params[:page])
@crop_wranglers = Role.crop_wranglers
respond_to do |format|
format.html
@@ -64,13 +51,18 @@ class CropsController < ApplicationController
# GET /crops/search
def search
@term = params[:term]
@matches = Crop.search(@term)
@paginated_matches = @matches.paginate(:page => params[:page])
@search = params[:search]
@exact_match = Crop.find_by_name(params[:search])
@partial_matches = Crop.search(params[:search])
# exclude exact match from partial match list
@partial_matches.reject!{ |r| @exact_match && r.eql?(@exact_match) }
@fuzzy = Crop.search(params[:term])
respond_to do |format|
format.html
format.json { render :json => @matches }
format.json { render :json => @fuzzy }
end
end
@@ -94,9 +86,9 @@ class CropsController < ApplicationController
# GET /crops/new.json
def new
@crop = Crop.new
@crop.alternate_names.build
@crop.scientific_names.build
3.times do
@crop.scientific_names.build
end
respond_to do |format|
format.html # new.html.haml
format.json { render json: @crop }
@@ -106,41 +98,17 @@ class CropsController < ApplicationController
# GET /crops/1/edit
def edit
@crop = Crop.find(params[:id])
@crop.alternate_names.build if @crop.alternate_names.blank?
@crop.scientific_names.build if @crop.scientific_names.blank?
end
# POST /crops
# POST /crops.json
def create
@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
params[:crop][:creator_id] = current_member.id
@crop = Crop.new(params[:crop])
respond_to do |format|
if @crop.save
params[:alt_name].each do |index, value|
@crop.alternate_names.create(name: value, creator_id: current_member.id)
end
params[:sci_name].each do |index, value|
@crop.scientific_names.create(scientific_name: value, creator_id: current_member.id)
end
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.html { redirect_to @crop, notice: 'Crop was successfully created.' }
format.json { render json: @crop, status: :created, location: @crop }
else
format.html { render action: "new" }
@@ -154,35 +122,8 @@ 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 !params[:alt_name].nil?
@crop.alternate_names.each do |alt_name|
alt_name.destroy
end
params[:alt_name].each do |index, value|
alt_name = @crop.alternate_names.create(name: value, creator_id: current_member.id)
end
@crop.scientific_names.each do |sci_name|
sci_name.destroy
end
params[:sci_name].each do |index, value|
sci_name = @crop.scientific_names.create(scientific_name: value, creator_id: current_member.id)
end
end
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
if @crop.update_attributes(params[:crop])
format.html { redirect_to @crop, notice: 'Crop was successfully updated.' }
format.json { head :no_content }
else
@@ -203,10 +144,4 @@ class CropsController < ApplicationController
format.json { head :no_content }
end
end
private
def crop_params
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

View File

@@ -1,12 +1,8 @@
class FollowsController < ApplicationController
before_filter :authenticate_member!
load_and_authorize_resource
skip_load_resource :only => :create
# POST /follows
def create
@follow = current_member.follows.build(:followed_id => follow_params[:followed_id])
@follow = current_member.follows.build(:followed_id => params[:followed_id])
if @follow.save
flash[:notice] = "Followed #{ @follow.followed.login_name }"
@@ -19,17 +15,11 @@ class FollowsController < ApplicationController
# DELETE /follows/1
def destroy
@follow = current_member.follows.find(follow_params[:id])
@follow = current_member.follows.find(params[:id])
unfollowed_name = @follow.followed.login_name
@follow.destroy
flash[:notice] = "Unfollowed #{ unfollowed_name }"
redirect_to root_path
end
private
def follow_params
params.permit(:id, :followed_id, :follower_id, :authenticity_token, :_method)
end
end

View File

@@ -1,6 +1,8 @@
class ForumsController < ApplicationController
load_and_authorize_resource
cache_sweeper :forum_sweeper
# GET /forums
# GET /forums.json
def index
@@ -42,7 +44,7 @@ class ForumsController < ApplicationController
# POST /forums
# POST /forums.json
def create
@forum = Forum.new(forum_params)
@forum = Forum.new(params[:forum])
respond_to do |format|
if @forum.save
@@ -61,7 +63,7 @@ class ForumsController < ApplicationController
@forum = Forum.find(params[:id])
respond_to do |format|
if @forum.update(forum_params)
if @forum.update_attributes(params[:forum])
format.html { redirect_to @forum, notice: 'Forum was successfully updated.' }
format.json { head :no_content }
else
@@ -82,10 +84,4 @@ class ForumsController < ApplicationController
format.json { head :no_content }
end
end
private
def forum_params
params.require(:forum).permit(:description, :name, :owner_id, :slug)
end
end

View File

@@ -1,7 +1,8 @@
class GardensController < ApplicationController
before_filter :authenticate_member!, :except => [:index, :show]
load_and_authorize_resource
cache_sweeper :garden_sweeper
# GET /gardens
# GET /gardens.json
@@ -49,13 +50,12 @@ class GardensController < ApplicationController
# POST /gardens.json
def create
params[:garden][:owner_id] = current_member.id
@garden = Garden.new(garden_params)
@garden = Garden.new(params[:garden])
respond_to do |format|
if @garden.save
format.html { redirect_to @garden, notice: 'Garden was successfully created.' }
format.json { render json: @garden, status: :created, location: @garden }
expire_fragment("homepage_stats")
else
format.html { render action: "new" }
format.json { render json: @garden.errors, status: :unprocessable_entity }
@@ -69,7 +69,7 @@ class GardensController < ApplicationController
@garden = Garden.find(params[:id])
respond_to do |format|
if @garden.update(garden_params)
if @garden.update_attributes(params[:garden])
format.html { redirect_to @garden, notice: 'Garden was successfully updated.' }
format.json { head :no_content }
else
@@ -84,18 +84,10 @@ class GardensController < ApplicationController
def destroy
@garden = Garden.find(params[:id])
@garden.destroy
expire_fragment("homepage_stats")
respond_to do |format|
format.html { redirect_to gardens_by_owner_path(:owner => @garden.owner), notice: 'Garden was successfully deleted.' }
format.json { head :no_content }
end
end
private
def garden_params
params.require(:garden).permit(:name, :slug, :owner_id, :description, :active,
:location, :latitude, :longitude, :area, :area_unit)
end
end

View File

@@ -20,7 +20,7 @@ class HarvestsController < ApplicationController
format.html { @harvests = @harvests.paginate(:page => params[:page]) }
format.json { render json: @harvests }
format.csv do
specifics = (@owner ? "#{@owner.login_name}-" : @crop ? "#{@crop.name}-" : nil)
specifics = (@owner ? "#{@owner.name}-" : @crop ? "#{@crop.name}-" : nil)
@filename = "Growstuff-#{specifics}Harvests-#{Time.zone.now.to_s(:number)}.csv"
render :csv => @harvests
end
@@ -62,7 +62,7 @@ class HarvestsController < ApplicationController
def create
params[:harvest][:owner_id] = current_member.id
params[:harvested_at] = parse_date(params[:harvested_at])
@harvest = Harvest.new(harvest_params)
@harvest = Harvest.new(params[:harvest])
respond_to do |format|
if @harvest.save
@@ -81,7 +81,7 @@ class HarvestsController < ApplicationController
@harvest = Harvest.find(params[:id])
respond_to do |format|
if @harvest.update(harvest_params)
if @harvest.update_attributes(params[:harvest])
format.html { redirect_to @harvest, notice: 'Harvest was successfully updated.' }
format.json { head :no_content }
else
@@ -102,11 +102,4 @@ class HarvestsController < ApplicationController
format.json { head :no_content }
end
end
private
def harvest_params
params.require(:harvest).permit(:crop_id, :harvested_at, :description, :owner_id,
:quantity, :unit, :weight_quantity, :weight_unit, :plant_part_id, :slug, :si_weight)
end
end

View File

@@ -1,17 +1,12 @@
class MembersController < ApplicationController
load_and_authorize_resource
skip_authorize_resource :only => [:nearby, :unsubscribe]
cache_sweeper :member_sweeper
after_action :expire_cache_fragments, :only => :create
skip_authorize_resource :only => :nearby
def index
@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
@members = Member.confirmed.paginate(:page => params[:page])
respond_to do |format|
format.html # index.html.haml
@@ -49,31 +44,4 @@ class MembersController < ApplicationController
@followers = @member.followers.paginate(:page => params[:page])
end
EMAIL_TYPE_STRING = {
send_notification_email: "direct message notifications",
send_planting_reminder: "planting reminders"
}
def unsubscribe
begin
verifier = ActiveSupport::MessageVerifier.new(ENV['RAILS_SECRET_TOKEN'])
decrypted_message = verifier.verify(params[:message])
@member = Member.find(decrypted_message[:member_id])
@type = decrypted_message[:type]
@member.update(@type => false)
flash.now[:notice] = "You have been unsubscribed from #{EMAIL_TYPE_STRING[@type]} emails."
rescue ActiveSupport::MessageVerifier::InvalidSignature
flash.now[:alert] = "We're sorry, there was an error updating your settings."
end
end
private
def expire_cache_fragments
expire_fragment("homepage_stats")
end
end

View File

@@ -5,7 +5,7 @@ class NotificationsController < ApplicationController
# GET /notifications
def index
@notifications = Notification.where(recipient_id: current_member).page(params[:page])
@notifications = Notification.find_all_by_recipient_id(current_member)
respond_to do |format|
format.html # index.html.erb
@@ -36,21 +36,6 @@ class NotificationsController < ApplicationController
end
end
# GET /notifications/1/reply
def reply
@notification = Notification.new
@sender_notification = Notification.find(params[:id])
@recipient = @sender_notification.sender
@subject = @sender_notification.subject =~ /^Re: / ?
@sender_notification.subject :
"Re: " + @sender_notification.subject
respond_to do |format|
format.html # reply.html.haml
end
end
# DELETE /notifications/1
def destroy
@notification = Notification.find(params[:id])
@@ -64,7 +49,7 @@ class NotificationsController < ApplicationController
# POST /notifications
def create
params[:notification][:sender_id] = current_member.id
@notification = Notification.new(notification_params)
@notification = Notification.new(params[:notification])
@recipient = Member.find_by_id(params[:notification][:recipient_id])
respond_to do |format|
@@ -75,10 +60,4 @@ class NotificationsController < ApplicationController
end
end
end
private
def notification_params
params.require(:notification).permit(:sender_id, :recipient_id, :subject, :body, :post_id, :read)
end
end

View File

@@ -7,7 +7,7 @@ class OrderItemsController < ApplicationController
if params[:order_item][:price]
params[:order_item][:price] = params[:order_item][:price].to_f * 100 # convert to cents
end
@order_item = OrderItem.new(order_item_params)
@order_item = OrderItem.new(params[:order_item])
@order_item.order = current_member.current_order || Order.create(:member_id => current_member.id)
respond_to do |format|
@@ -20,10 +20,4 @@ class OrderItemsController < ApplicationController
end
end
end
private
def order_item_params
params.require(:order_item).permit(:order_id, :price, :product_id, :quantity)
end
end

View File

@@ -4,7 +4,7 @@ class OrdersController < ApplicationController
# GET /orders
def index
@orders = Order.where(member_id: current_member.id)
@orders = Order.find_all_by_member_id(current_member.id)
respond_to do |format|
format.html # index.html.erb

View File

@@ -1,6 +1,8 @@
class PhotosController < ApplicationController
before_filter :authenticate_member!, :except => [:index, :show]
load_and_authorize_resource
cache_sweeper :photo_sweeper
# GET /photos
# GET /photos.json
@@ -59,13 +61,13 @@ class PhotosController < ApplicationController
# POST /photos.json
def create
@photo = Photo.find_by_flickr_photo_id(params[:photo][:flickr_photo_id]) ||
Photo.new(photo_params)
Photo.new(params[:photo])
@photo.owner_id = current_member.id
@photo.set_flickr_metadata
# several models can have photos. we need to know what model and the id
# for the entry to attach the photo to
valid_models = ["planting", "harvest", "garden"]
valid_models = ["planting", "harvest"]
if params[:type]
if valid_models.include?(params[:type])
if params[:id]
@@ -109,7 +111,7 @@ class PhotosController < ApplicationController
@photo = Photo.find(params[:id])
respond_to do |format|
if @photo.update(photo_params)
if @photo.update_attributes(params[:photo])
format.html { redirect_to @photo, notice: 'Photo was successfully updated.' }
format.json { head :no_content }
else
@@ -124,18 +126,10 @@ class PhotosController < ApplicationController
def destroy
@photo = Photo.find(params[:id])
@photo.destroy
flash[:alert] = "Photo successfully deleted."
respond_to do |format|
format.html { redirect_to photos_url }
format.json { head :no_content }
end
end
private
def photo_params
params.require(:photo).permit(:flickr_photo_id, :owner_id, :title, :license_name,
:license_url, :thumbnail_url, :fullsize_url, :link_url)
end
end

View File

@@ -21,17 +21,9 @@ class PlacesController < ApplicationController
end
def search
if params[:new_place].empty?
respond_to do |format|
format.html do
redirect_to places_path, alert: 'Please enter a valid location'
end
end
else
respond_to do |format|
format.html do
redirect_to place_path(params[:new_place])
end
respond_to do |format|
format.html do
redirect_to place_path(params[:new_place])
end
end
end

View File

@@ -42,7 +42,7 @@ class PlantPartsController < ApplicationController
# POST /plant_parts
# POST /plant_parts.json
def create
@plant_part = PlantPart.new(plant_part_params)
@plant_part = PlantPart.new(params[:plant_part])
respond_to do |format|
if @plant_part.save
@@ -61,7 +61,7 @@ class PlantPartsController < ApplicationController
@plant_part = PlantPart.find(params[:id])
respond_to do |format|
if @plant_part.update(plant_part_params)
if @plant_part.update_attributes(params[:plant_part])
format.html { redirect_to @plant_part, notice: 'Plant part was successfully updated.' }
format.json { head :no_content }
else
@@ -82,10 +82,4 @@ class PlantPartsController < ApplicationController
format.json { head :no_content }
end
end
private
def plant_part_params
params.require(:plant_part).permit(:name, :slug)
end
end

View File

@@ -1,6 +1,9 @@
class PlantingsController < ApplicationController
before_filter :authenticate_member!, :except => [:index, :show]
load_and_authorize_resource
cache_sweeper :planting_sweeper
# GET /plantings
# GET /plantings.json
@@ -20,7 +23,7 @@ class PlantingsController < ApplicationController
format.json { render json: @plantings }
format.rss { render :layout => false } #index.rss.builder
format.csv do
specifics = (@owner ? "#{@owner.login_name}-" : @crop ? "#{@crop.name}-" : nil)
specifics = (@owner ? "#{@owner.name}-" : @crop ? "#{@crop.name}-" : nil)
@filename = "Growstuff-#{specifics}Plantings-#{Time.zone.now.to_s(:number)}.csv"
render :csv => @plantings
end
@@ -30,7 +33,7 @@ class PlantingsController < ApplicationController
# GET /plantings/1
# GET /plantings/1.json
def show
@planting = Planting.includes(:owner, :crop, :garden, :photos).friendly.find(params[:id])
@planting = Planting.includes(:owner, :crop, :garden, :photos).find(params[:id])
respond_to do |format|
format.html # show.html.erb
@@ -65,16 +68,14 @@ class PlantingsController < ApplicationController
# POST /plantings
# POST /plantings.json
def create
params[:planting][:owner_id] = current_member.id
params[:planted_at] = parse_date(params[:planted_at])
@planting = Planting.new(planting_params)
@planting.owner = current_member
@planting = Planting.new(params[:planting])
respond_to do |format|
if @planting.save
@planting.update_attribute(:days_before_maturity, update_days_before_maturity(@planting, planting_params[:crop_id]))
format.html { redirect_to @planting, notice: 'Planting was successfully created.' }
format.json { render json: @planting, status: :created, location: @planting }
expire_fragment("homepage_stats")
else
format.html { render action: "new" }
format.json { render json: @planting.errors, status: :unprocessable_entity }
@@ -89,8 +90,7 @@ class PlantingsController < ApplicationController
params[:planted_at] = parse_date(params[:planted_at])
respond_to do |format|
if @planting.update(planting_params)
@planting.update_attribute(:days_before_maturity, update_days_before_maturity(@planting, planting_params[:crop_id]))
if @planting.update_attributes(params[:planting])
format.html { redirect_to @planting, notice: 'Planting was successfully updated.' }
format.json { head :no_content }
else
@@ -106,27 +106,10 @@ class PlantingsController < ApplicationController
@planting = Planting.find(params[:id])
@garden = @planting.garden
@planting.destroy
expire_fragment("homepage_stats")
respond_to do |format|
format.html { redirect_to @garden }
format.json { head :no_content }
end
end
private
def planting_params
params.require(:planting).permit(:crop_id, :description, :garden_id, :planted_at,
:quantity, :sunniness, :planted_from, :owner_id, :finished,
:finished_at)
end
def update_days_before_maturity(planting, crop_id)
if planting.finished_at.nil?
planting.calculate_days_before_maturity(planting, crop_id)
else
(planting.finished_at - planting.planted_at).to_i
end
end
end

View File

@@ -2,6 +2,8 @@ class PostsController < ApplicationController
before_filter :authenticate_member!, :except => [:index, :show]
load_and_authorize_resource
cache_sweeper :post_sweeper
# GET /posts
# GET /posts.json
@@ -56,7 +58,7 @@ class PostsController < ApplicationController
# POST /posts.json
def create
params[:post][:author_id] = current_member.id
@post = Post.new(post_params)
@post = Post.new(params[:post])
respond_to do |format|
if @post.save
@@ -75,7 +77,7 @@ class PostsController < ApplicationController
@post = Post.find(params[:id])
respond_to do |format|
if @post.update(post_params)
if @post.update_attributes(params[:post])
format.html { redirect_to @post, notice: 'Post was successfully updated.' }
format.json { head :no_content }
else
@@ -96,10 +98,4 @@ class PostsController < ApplicationController
format.json { head :no_content }
end
end
private
def post_params
params.require(:post).permit(:body, :subject, :author_id, :forum_id)
end
end

View File

@@ -36,7 +36,7 @@ class ProductsController < ApplicationController
# POST /products
def create
@product = Product.new(product_params)
@product = Product.new(params[:product])
respond_to do |format|
if @product.save
@@ -52,7 +52,7 @@ class ProductsController < ApplicationController
@product = Product.find(params[:id])
respond_to do |format|
if @product.update(product_params)
if @product.update_attributes(params[:product])
format.html { redirect_to @product, notice: 'Product was successfully updated.' }
else
format.html { render action: "edit" }
@@ -69,11 +69,4 @@ class ProductsController < ApplicationController
format.html { redirect_to products_url }
end
end
private
def product_params
params.require(:product).permit(:description, :min_price, :recommended_price, :name,
:account_type_id, :paid_months)
end
end

View File

@@ -1,5 +1,7 @@
class RegistrationsController < Devise::RegistrationsController
cache_sweeper :member_sweeper
def edit
@twitter_auth = current_member.auth('twitter')
@flickr_auth = current_member.auth('flickr')

View File

@@ -1,20 +0,0 @@
class RobotsController < ApplicationController
DEFAULT_FILENAME = 'config/robots.txt'.freeze
def robots
filename = if subdomain && subdomain != 'www'
"config/robots.#{ subdomain }.txt"
end
file_to_render = File.exists?(filename.to_s) ? filename : DEFAULT_FILENAME
render file: file_to_render, layout: false, content_type: 'text/plain'
end
private
def subdomain
request.subdomain.present? ? request.subdomain : nil
end
end

View File

@@ -36,7 +36,7 @@ class RolesController < ApplicationController
# POST /roles
def create
@role = Role.new(role_params)
@role = Role.new(params[:role])
respond_to do |format|
if @role.save
@@ -52,7 +52,7 @@ class RolesController < ApplicationController
@role = Role.find(params[:id])
respond_to do |format|
if @role.update(role_params)
if @role.update_attributes(params[:role])
format.html { redirect_to @role, notice: 'Role was successfully updated.' }
else
format.html { render action: "edit" }
@@ -69,10 +69,4 @@ class RolesController < ApplicationController
format.html { redirect_to roles_url }
end
end
private
def role_params
params.require(:role).permit(:description, :name, :members, :slug)
end
end

View File

@@ -2,6 +2,8 @@ class ScientificNamesController < ApplicationController
before_filter :authenticate_member!, :except => [:index, :show]
load_and_authorize_resource
cache_sweeper :scientific_name_sweeper
# GET /scientific_names
# GET /scientific_names.json
def index
@@ -45,7 +47,7 @@ class ScientificNamesController < ApplicationController
# POST /scientific_names.json
def create
params[:scientific_name][:creator_id] = current_member.id
@scientific_name = ScientificName.new(scientific_name_params)
@scientific_name = ScientificName.new(params[:scientific_name])
respond_to do |format|
if @scientific_name.save
@@ -64,7 +66,7 @@ class ScientificNamesController < ApplicationController
@scientific_name = ScientificName.find(params[:id])
respond_to do |format|
if @scientific_name.update(scientific_name_params)
if @scientific_name.update_attributes(params[:scientific_name])
format.html { redirect_to @scientific_name.crop, notice: 'Scientific name was successfully updated.' }
format.json { head :no_content }
else
@@ -88,10 +90,4 @@ class ScientificNamesController < ApplicationController
format.json { head :no_content }
end
end
private
def scientific_name_params
params.require(:scientific_name).permit(:crop_id, :scientific_name, :creator_id)
end
end

View File

@@ -2,6 +2,8 @@ class SeedsController < ApplicationController
before_filter :authenticate_member!, :except => [:index, :show]
load_and_authorize_resource
cache_sweeper :seed_sweeper
# GET /seeds
# GET /seeds.json
def index
@@ -66,7 +68,7 @@ class SeedsController < ApplicationController
# POST /seeds.json
def create
params[:seed][:owner_id] = current_member.id
@seed = Seed.new(seed_params)
@seed = Seed.new(params[:seed])
respond_to do |format|
if @seed.save
@@ -85,7 +87,7 @@ class SeedsController < ApplicationController
@seed = Seed.find(params[:id])
respond_to do |format|
if @seed.update(seed_params)
if @seed.update_attributes(params[:seed])
format.html { redirect_to @seed, notice: 'Seed was successfully updated.' }
format.json { head :no_content }
else
@@ -106,13 +108,4 @@ class SeedsController < ApplicationController
format.json { head :no_content }
end
end
private
def seed_params
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

View File

@@ -24,31 +24,5 @@ module ApplicationHelper
:target => "_blank"
end
# Produces a cache key for uniquely identifying cached fragments.
def cache_key_for(klass, identifier="all")
count = klass.count
max_updated_at = klass.maximum(:updated_at).try(:utc).try(:to_s, :number)
"#{klass.name.downcase.pluralize}/#{identifier}-#{count}-#{max_updated_at}"
end
def required_field_help_text
asterisk = content_tag :span, '*', class: ['red']
text = content_tag :em, 'denotes a required field'
content_tag :div, asterisk + ' '.html_safe + text, class: ['margin-bottom']
end
#
# Returns an image uri for a given member.
#
# Falls back to Gravatar
#
def avatar_uri(member, size = 150)
return member.preferred_avatar_uri if member.preferred_avatar_uri.present?
Gravatar.new(member.email).image_url({
:size => size,
:default => :identicon
})
end
end

View File

@@ -19,4 +19,4 @@ module AutoSuggestHelper
}.html_safe
end
end
end

View File

@@ -1,17 +0,0 @@
module CropsHelper
def display_seed_availability(member, crop)
total_quantity = 0
member.seeds.each do |seed|
if seed.crop.name == crop.name
total_quantity = total_quantity + seed.quantity
end
end
if (total_quantity != 0)
"You have #{pluralize(total_quantity, "seed")} of this crop."
else
"You don't have any seeds of this crop."
end
end
end

View File

@@ -1,25 +0,0 @@
module GardensHelper
def display_garden_description(garden)
if garden.description.nil?
"no description provided."
else
truncate(garden.description, length: 130, separator: ' ', omission: '... ') { link_to "Read more", garden_path(garden) }
end
end
def display_garden_plantings(plantings)
if plantings.blank?
"None"
else
output = ""
plantings.first(2).each do |planting|
output += "<li>"
output += planting.quantity.nil? ? "0 " : "#{planting.quantity} "
output += link_to planting.crop.name, planting.crop
output += ", planted on #{planting.planted_at}</li>"
end
output.html_safe
end
end
end

View File

@@ -37,12 +37,4 @@ module HarvestsHelper
end
end
def display_harvest_description(harvest)
if harvest.description.empty?
"No description provided."
else
truncate(harvest.description, length: 130, separator: ' ', omission: '... ') { link_to "Read more", harvest_path(harvest) }
end
end
end

View File

@@ -5,7 +5,12 @@ module NotificationsHelper
new_comment_url(:post_id => notification.post.id)
else
# by default, reply link sends a PM in return
reply_notification_url(notification)
new_notification_url(
:recipient_id => notification.sender.id,
:subject => notification.subject =~ /^Re: / ?
notification.subject :
"Re: " + notification.subject
)
end
end
end

View File

@@ -1,45 +0,0 @@
module PlantingsHelper
def display_days_before_maturity(planting)
if planting.finished?
0
elsif !planting.finished_at.nil?
((p = planting.finished_at - DateTime.now).to_i) <= 0 ? 0 : p.to_i
elsif planting.days_before_maturity.nil?
"unknown"
else
((p = (planting.planted_at + planting.days_before_maturity) - DateTime.now).to_i <= 0) ? 0 : p.to_i
end
end
def display_finished(planting)
if !planting.finished_at.nil?
planting.finished_at
elsif planting.finished
"Yes (no date specified)"
else
"(no date specified)"
end
end
def display_planted_from(planting)
!planting.planted_from.blank? ? planting.planted_from : "not specified"
end
def display_planting_quantity(planting)
!planting.quantity.blank? ? planting.quantity : "not specified"
end
def display_planting(planting)
if planting.quantity.to_i > 0 && planting.planted_from.present?
return "#{planting.owner} planted #{pluralize(planting.quantity, planting.planted_from)}."
elsif planting.quantity.to_i > 0
return "#{planting.owner} planted #{pluralize(planting.quantity, 'unit')}."
elsif planting.planted_from.present?
return "#{planting.owner} planted #{planting.planted_from.pluralize}."
else
return "#{planting.owner}."
end
end
end

View File

@@ -1,11 +0,0 @@
module SeedsHelper
def display_seed_description(seed)
if seed.description.nil?
"no description provided."
else
truncate(seed.description, length: 130, separator: ' ', omission: '... ') { link_to "Read more", seed_path(seed) }
end
end
end

View File

@@ -2,21 +2,10 @@ class Notifier < ActionMailer::Base
include NotificationsHelper
default from: "Growstuff <noreply@growstuff.org>"
def verifier()
if ENV['RAILS_SECRET_TOKEN']
return ActiveSupport::MessageVerifier.new(ENV['RAILS_SECRET_TOKEN'])
else
raise "RAILS_SECRET_TOKEN environment variable not set - have you created config/application.yml?"
end
end
def notify(notification)
@notification = notification
@reply_link = reply_link(@notification)
# Encrypting
@signed_message = verifier.generate ({ member_id: @notification.recipient.id, type: :send_notification_email })
mail(:to => @notification.recipient.email,
:subject => @notification.subject)
end
@@ -24,11 +13,8 @@ class Notifier < ActionMailer::Base
def planting_reminder(member)
@member = member
@plantings = @member.plantings.first(5)
@harvests = @member.harvests.first(5)
# Encrypting
@signed_message = verifier.generate ({ member_id: @member.id, type: :send_planting_reminder })
@plantings = @member.plantings.reorder.last(5)
@harvests = @member.harvests.reorder.last(5)
if @member.send_planting_reminder
mail(:to => @member.email,
@@ -36,19 +22,4 @@ 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

View File

@@ -21,24 +21,7 @@ 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
@@ -46,7 +29,6 @@ class Ability
# can read/delete notifications that were sent to them
can :read, Notification, :recipient_id => member.id
can :destroy, Notification, :recipient_id => member.id
can :reply, Notification, :recipient_id => member.id
# can send a private message to anyone but themselves
# note: sadly, we can't test for this from the view, but it works
# for the model/controller
@@ -63,9 +45,6 @@ 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
@@ -115,11 +94,9 @@ class Ability
cannot :destroy, OrderItem, :order => { :member_id => member.id, :completed_at => nil }
# following/unfollowing permissions
can :create, Follow
cannot :create, Follow, :followed_id => member.id # can't follow yourself
can :destroy, Follow
cannot :destroy, Follow, :followed_id => member.id # can't unfollow yourself
can :create, Follow do |f|
!member.already_following?(f.followed) && f.followed_id != member.id
end
if member.has_role? :admin

View File

@@ -1,4 +1,5 @@
class Account < ActiveRecord::Base
attr_accessible :account_type_id, :member_id, :paid_until
belongs_to :member
belongs_to :account_type
@@ -8,7 +9,7 @@ class Account < ActiveRecord::Base
before_create do |account|
unless account.account_type
account.account_type = AccountType.find_or_create_by(name:
account.account_type = AccountType.find_or_create_by_name(
Growstuff::Application.config.default_account_type
)
end

View File

@@ -1,4 +1,5 @@
class AccountType < ActiveRecord::Base
attr_accessible :is_paid, :is_permanent_paid, :name
has_many :products
def to_s

View File

@@ -1,5 +1,5 @@
class AlternateName < ActiveRecord::Base
after_commit { |an| an.crop.__elasticsearch__.index_document if an.crop && ENV['GROWSTUFF_ELASTICSEARCH'] == "true" }
attr_accessible :crop_id, :name, :creator_id
belongs_to :crop
belongs_to :creator, :class_name => 'Member'
end

View File

@@ -1,3 +1,4 @@
class Authentication < ActiveRecord::Base
belongs_to :member
attr_accessible :provider, :uid, :token, :secret, :name
end

View File

@@ -1,9 +1,10 @@
class Comment < ActiveRecord::Base
attr_accessible :author_id, :body, :post_id
belongs_to :author, :class_name => 'Member'
belongs_to :post
default_scope { order("created_at DESC") }
scope :post_order, -> { reorder("created_at ASC") } # for display on post page
default_scope order("created_at DESC")
scope :post_order, reorder("created_at ASC") # for display on post page
after_create do
recipient = self.post.author.id

View File

@@ -0,0 +1,15 @@
class CommentSweeper < ActionController::Caching::Sweeper
observe Comment
def after_create(comment)
expire_fragment('recent_posts')
end
def after_update(comment)
end
def after_destroy(comment)
expire_fragment('recent_posts')
end
end

View File

@@ -1,122 +1,45 @@
class Crop < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: [:slugged, :finders]
friendly_id :name, use: :slugged
attr_accessible :en_wikipedia_url, :name, :parent_id, :creator_id, :scientific_names_attributes
has_many :scientific_names, after_add: :update_index, after_remove: :update_index
has_many :scientific_names
accepts_nested_attributes_for :scientific_names,
:allow_destroy => true,
:reject_if => :all_blank
has_many :alternate_names, after_add: :update_index, after_remove: :update_index, dependent: :destroy
has_many :alternate_names
has_many :plantings
has_many :photos, :through => :plantings
has_many :seeds
has_many :harvests
has_many :plant_parts, -> { uniq }, :through => :harvests
has_many :plant_parts, :through => :harvests, :uniq => :true
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'
has_and_belongs_to_many :posts
before_destroy {|crop| crop.posts.clear}
default_scope { order("lower(name) asc") }
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 :approved, -> { where(:approval_status => "approved") }
scope :rejected, -> { where(:approval_status => "rejected") }
## Wikipedia urls are only necessary when approving a crop
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
validates :en_wikipedia_url,
:format => {
:with => /\Ahttps?:\/\/en\.wikipedia\.org\/wiki/,
:with => /^https?:\/\/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
include Elasticsearch::Model::Callbacks
# In order to avoid clashing between different environments,
# use Rails.env as a part of index name (eg. development_growstuff)
index_name [Rails.env, "growstuff"].join('_')
settings index: { number_of_shards: 1 },
analysis: {
tokenizer: {
gs_edgeNGram_tokenizer: {
type: "edgeNGram", # edgeNGram: NGram match from the start of a token
min_gram: 3,
max_gram: 10,
# token_chars: Elasticsearch will split on characters
# that dont belong to any of these classes
token_chars: [ "letter", "digit" ]
}
},
analyzer: {
gs_edgeNGram_analyzer: {
tokenizer: "gs_edgeNGram_tokenizer",
filter: ["lowercase"]
}
},
} do
mappings dynamic: 'false' do
indexes :id, type: 'long'
indexes :name, type: 'string', analyzer: 'gs_edgeNGram_analyzer'
indexes :approval_status, type: 'string'
indexes :scientific_names do
indexes :scientific_name,
type: 'string',
analyzer: 'gs_edgeNGram_analyzer',
# Disabling field-length norm (norm). If the norm option is turned on(by default),
# higher weigh would be given for shorter fields, which in our case is irrelevant.
norms: { enabled: false }
end
indexes :alternate_names do
indexes :name, type: 'string', analyzer: 'gs_edgeNGram_analyzer'
end
end
end
def as_indexed_json(options={})
self.as_json(
only: [:id, :name, :approval_status],
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)
if ENV["GROWSTUFF_ELASTICSEARCH"] == "true"
__elasticsearch__.index_document
end
end
# End Elasticsearch section
}
def to_s
return name
end
def default_scientific_name
if scientific_names.size > 0
if scientific_names.count > 0
return scientific_names.first.scientific_name
else
return nil
@@ -177,38 +100,18 @@ class Crop < ActiveRecord::Base
def interesting?
min_plantings = 3 # needs this many plantings to be interesting
min_photos = 3 # needs this many photos to be interesting
return false unless photos.size >= min_photos
return false unless photos.count >= min_photos
return false unless plantings_count >= min_plantings
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
howmany = 12 # max number to find
interesting_crops = Array.new
Crop.includes(:photos).randomized.each do |c|
break if interesting_crops.size == howmany
Crop.randomized.each do |c|
break if interesting_crops.length == howmany
next unless c.interesting?
interesting_crops.push(c)
end
@@ -229,7 +132,7 @@ class Crop < ActiveRecord::Base
cropbot = Member.find_by_login_name('cropbot')
raise "cropbot account not found: run rake db:seed" unless cropbot
crop = Crop.find_or_create_by(name: name)
crop = Crop.find_or_create_by_name(name)
crop.update_attributes(
:en_wikipedia_url => en_wikipedia_url,
:creator_id => cropbot.id
@@ -299,76 +202,11 @@ class Crop < ActiveRecord::Base
end
end
def rejection_explanation
if reason_for_rejection == "other"
return rejection_notes
else
return reason_for_rejection
end
end
# Crop.search(string)
# searches for crops whose names match the string given
# just uses SQL LIKE for now, but can be made fancier later
def self.search(query)
if ENV['GROWSTUFF_ELASTICSEARCH'] == "true"
search_str = query.nil? ? "" : query.downcase
response = __elasticsearch__.search( {
# Finds documents which match any field, but uses the _score from
# the best field insead of adding up _score from each field.
query: {
multi_match: {
query: "#{search_str}",
analyzer: "standard",
fields: ["name", "scientific_names.scientific_name", "alternate_names.name"]
}
},
filter: {
term: {approval_status: "approved"}
},
size: 50
}
)
return response.records.to_a
else
# if we don't have elasticsearch, just do a basic SQL query.
# also, make sure it's an actual array not an activerecord
# collection, so it matches what we get from elasticsearch and we can
# manipulate it in the same ways (eg. deleting elements without deleting
# the whole record from the db)
matches = Crop.approved.where("name ILIKE ?", "%#{query}%").to_a
# we want to make sure that exact matches come first, even if not
# using elasticsearch (eg. in development)
exact_match = Crop.approved.find_by_name(query)
if exact_match
matches.delete(exact_match)
matches.unshift(exact_match)
end
return matches
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
where("name ILIKE ?", "%#{query}%")
end
end

View File

@@ -0,0 +1,21 @@
class CropSweeper < ActionController::Caching::Sweeper
observe Crop
def after_create(crop)
expire_fragment('homepage_stats')
expire_fragment('recent_crops')
expire_fragment('full_crop_hierarchy')
end
def after_update(crop)
expire_fragment("crop_image_#{crop.id}")
end
def after_destroy(crop)
expire_fragment('homepage_stats')
expire_fragment('recent_crops')
expire_fragment('full_crop_hierarchy')
end
end

View File

@@ -1,4 +1,5 @@
class Follow < ActiveRecord::Base
attr_accessible :followed_id, :follower_id
belongs_to :follower, class_name: "Member"
belongs_to :followed, class_name: "Member"
validates :follower_id, uniqueness: { :scope => :followed_id }

View File

@@ -1,7 +1,7 @@
class Forum < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: [:slugged, :finders]
friendly_id :name, use: :slugged
attr_accessible :description, :name, :owner_id, :slug
has_many :posts
belongs_to :owner, :class_name => "Member"

View File

@@ -0,0 +1,16 @@
class ForumSweeper < ActionController::Caching::Sweeper
observe Forum
def after_create(forum)
expire_fragment('homepage_forums')
end
def after_update(forum)
expire_fragment('homepage_forums')
end
def after_destroy(forum)
expire_fragment('homepage_forums')
end
end

View File

@@ -1,46 +1,32 @@
class Garden < ActiveRecord::Base
include Geocodable
extend FriendlyId
friendly_id :garden_slug, use: [:slugged, :finders]
friendly_id :garden_slug, use: :slugged
attr_accessible :name, :slug, :owner_id, :description, :active,
:location, :latitude, :longitude, :area, :area_unit
belongs_to :owner, :class_name => 'Member', :foreign_key => 'owner_id'
has_many :plantings, -> { order(created_at: :desc) }, :dependent => :destroy
has_many :plantings, :order => 'created_at DESC', :dependent => :destroy
has_many :crops, :through => :plantings
has_and_belongs_to_many :photos
before_destroy do |garden|
photolist = garden.photos.to_a # save a temp copy of the photo list
garden.photos.clear # clear relationship b/w garden and photo
photolist.each do |photo|
photo.destroy_if_unused
end
end
# set up geocoding
geocoded_by :location
after_validation :geocode
after_validation :empty_unwanted_geocodes
after_save :mark_inactive_garden_plantings_as_finished
default_scope { order("lower(name) asc") }
scope :active, -> { where(:active => true) }
scope :inactive, -> { where(:active => false) }
validates :location,
:length => { :maximum => 255 }
default_scope order("lower(name) asc")
scope :active, where(:active => true)
scope :inactive, where(:active => false)
validates :name,
:format => {
:with => /\S/
},
:length => { :maximum => 255 }
}
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 = {
@@ -100,8 +86,4 @@ class Garden < ActiveRecord::Base
end
end
def default_photo
return photos.first
end
end

View File

@@ -0,0 +1,16 @@
class GardenSweeper < ActionController::Caching::Sweeper
observe Garden
def after_create(garden)
expire_fragment('homepage_stats')
expire_fragment('interesting_members') if garden.owner.interesting?
expire_fragment("member_thumbnail_#{garden.owner.id}")
end
def after_destroy(garden)
expire_fragment('homepage_stats')
expire_fragment('interesting_members') if garden.owner.interesting?
expire_fragment("member_thumbnail_#{garden.owner.id}")
end
end

View File

@@ -1,7 +1,10 @@
class Harvest < ActiveRecord::Base
include ActionView::Helpers::NumberHelper
extend FriendlyId
friendly_id :harvest_slug, use: [:slugged, :finders]
friendly_id :harvest_slug, use: :slugged
attr_accessible :crop_id, :harvested_at, :description, :owner_id,
:quantity, :unit, :weight_quantity, :weight_unit, :plant_part_id, :slug
belongs_to :crop
belongs_to :owner, :class_name => 'Member'
@@ -18,18 +21,12 @@ class Harvest < ActiveRecord::Base
end
end
default_scope { order('created_at DESC') }
validates :crop, :approved => true
default_scope order('created_at DESC')
validates :crop, :presence => {:message => "must be present and exist in our database"}
validates :plant_part, :presence => {:message => "must be present and exist in our database"}
validates :quantity,
:numericality => {
:only_integer => false,
:greater_than_or_equal_to => 0 },
:numericality => { :only_integer => false },
:allow_nil => true
UNITS_VALUES = {
@@ -65,17 +62,6 @@ class Harvest < ActiveRecord::Base
after_validation :cleanup_quantities
before_save :set_si_weight
# we're storing the harvest weight in kilograms in the db too
# to make data manipulation easier
def set_si_weight
if self.weight_unit != nil
weight_string = "#{self.weight_quantity} #{self.weight_unit}"
self.si_weight = Unit(weight_string).convert_to("kg").to_s("%0.3f").delete(" kg").to_f
end
end
def cleanup_quantities
if quantity == 0
self.quantity = nil

View File

@@ -2,7 +2,7 @@ class Member < ActiveRecord::Base
include Geocodable
extend FriendlyId
friendly_id :login_name, use: [:slugged, :finders]
friendly_id :login_name, use: :slugged
has_many :posts, :foreign_key => 'author_id'
has_many :comments, :foreign_key => 'author_id'
@@ -27,20 +27,18 @@ class Member < ActiveRecord::Base
has_many :photos
default_scope { order("lower(login_name) asc") }
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"
has_many :followed, :through => :follows
has_many :inverse_follows, :class_name => "Follow", :foreign_key => "followed_id"
has_many :followers, :through => :inverse_follows, :source => :follower
default_scope order("lower(login_name) asc")
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 :wants_newsletter, where(:newsletter => true)
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
@@ -48,6 +46,16 @@ class Member < ActiveRecord::Base
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :lockable, :timeoutable
# Setup accessible (or protected) attributes for your model
attr_accessible :login_name, :email, :password, :password_confirmation,
:remember_me, :login,
# terms of service
:tos_agreement,
# profile stuff
:bio, :location, :latitude, :longitude,
# email settings
:show_email, :newsletter, :send_notification_email, :send_planting_reminder
# set up geocoding
geocoded_by :location
after_validation :geocode
@@ -72,7 +80,7 @@ class Member < ActiveRecord::Base
:message => "name is reserved"
},
:format => {
:with => /\A\w+\z/,
:with => /^\w+$/,
:message => "may only include letters, numbers, or underscores"
},
:uniqueness => {
@@ -85,7 +93,7 @@ class Member < ActiveRecord::Base
# and an account record (for paid accounts etc)
# we use find_or_create to avoid accidentally creating a second one,
# which can happen sometimes especially with FactoryGirl associations
after_create {|member| Account.find_or_create_by(:member_id => member.id) }
after_create {|member| Account.find_or_create_by_member_id(:member_id => member.id) }
after_save :update_newsletter_subscription
@@ -202,7 +210,7 @@ class Member < ActiveRecord::Base
howmany = 12 # max number to find
interesting_members = Array.new
Member.confirmed.located.recently_signed_in.each do |m|
break if interesting_members.size == howmany
break if interesting_members.length == howmany
if m.interesting?
interesting_members.push(m)
end
@@ -235,19 +243,17 @@ class Member < ActiveRecord::Base
end
end
def newsletter_subscribe(testing=false)
return true if (Rails.env.test? && !testing)
def newsletter_subscribe
gb = Gibbon::API.new
res = gb.lists.subscribe({
:id => Gibbon::API.api_key,
:email => { :email => email },
:merge_vars => { :login_name => login_name },
:double_optin => false # they already confirmed their email with us
:double_optin => false # they alredy confirmed their email with us
})
end
def newsletter_unsubscribe(testing=false)
return true if (Rails.env.test? && !testing)
def newsletter_unsubscribe
gb = Gibbon::API.new
res = gb.lists.unsubscribe({
:id => ENV['GROWSTUFF_MAILCHIMP_NEWSLETTER_ID'],

View File

@@ -0,0 +1,23 @@
class MemberSweeper < ActionController::Caching::Sweeper
observe Member
def after_create(member)
expire_fragment('homepage_stats')
end
def after_update(member)
expire_fragment('interesting_members') if member.interesting?
expire_fragment("interesting_seeds") if member.seeds.tradable.present?
expire_fragment("member_thumbnail_#{member.id}")
if member.plantings.present?
member.plantings.each do |p|
expire_fragment("plantings_listitem_#{p.id}") if p.interesting?
end
expire_fragment('interesting_plantings')
end
end
end

View File

@@ -1,18 +1,19 @@
class Notification < ActiveRecord::Base
attr_accessible :sender_id, :recipient_id,
:subject, :body, :post_id, :read
belongs_to :sender, :class_name => 'Member'
belongs_to :recipient, :class_name => 'Member'
belongs_to :post
validates :subject, :length => { :maximum => 255 }
default_scope { order('created_at DESC') }
scope :unread, -> { where(:read => false) }
default_scope order('created_at DESC')
scope :unread, where(:read => false)
before_create :replace_blank_subject
after_create :send_email
def self.unread_count
self.unread.size
self.unread.count
end
def replace_blank_subject

View File

@@ -1,9 +1,10 @@
class Order < ActiveRecord::Base
attr_accessible :member_id, :completed_at, :referral_code
belongs_to :member
has_many :order_items, :dependent => :destroy
default_scope { order('created_at DESC') }
default_scope order('created_at DESC')
validates :referral_code, :format => {
:with => /\A[a-zA-Z0-9 ]*\z/,

View File

@@ -1,4 +1,6 @@
class OrderItem < ActiveRecord::Base
attr_accessible :order_id, :price, :product_id, :quantity
belongs_to :order
belongs_to :product

View File

@@ -1,20 +1,20 @@
class Photo < ActiveRecord::Base
attr_accessible :flickr_photo_id, :owner_id, :title, :license_name,
:license_url, :thumbnail_url, :fullsize_url, :link_url
belongs_to :owner, :class_name => 'Member'
has_and_belongs_to_many :plantings
has_and_belongs_to_many :harvests
has_and_belongs_to_many :gardens
before_destroy do |photo|
photo.plantings.clear
photo.harvests.clear
photo.gardens.clear
end
default_scope { order("created_at desc") }
default_scope order("created_at desc")
# remove photos that aren't used by anything
def destroy_if_unused
unless plantings.size > 0 or harvests.size > 0 or gardens.size > 0
unless plantings.size > 0 or harvests.size > 0
self.destroy
end
end

View File

@@ -0,0 +1,13 @@
class PhotoSweeper < ActionController::Caching::Sweeper
observe Photo
def after_create(photo)
expire_fragment('interesting_plantings')
end
def after_destroy(photo)
expire_fragment('interesting_plantings')
end
end

View File

@@ -1,9 +1,11 @@
class PlantPart < ActiveRecord::Base
extend FriendlyId
friendly_id :name, :use => [:slugged, :finders]
friendly_id :name, :use => :slugged
has_many :harvests
has_many :crops, -> { uniq }, :through => :harvests
has_many :crops, :through => :harvests, :uniq => true
attr_accessible :name, :slug
def to_s
return name

View File

@@ -1,6 +1,10 @@
class Planting < ActiveRecord::Base
extend FriendlyId
friendly_id :planting_slug, use: [:slugged, :finders]
friendly_id :planting_slug, use: :slugged
attr_accessible :crop_id, :description, :garden_id, :planted_at,
:quantity, :sunniness, :planted_from, :owner_id, :finished,
:finished_at
belongs_to :garden
belongs_to :owner, :class_name => 'Member', :counter_cache => true
@@ -17,9 +21,9 @@ class Planting < ActiveRecord::Base
end
end
default_scope { order("created_at desc") }
scope :finished, -> { where(:finished => true) }
scope :current, -> { where(:finished => false) }
default_scope order("created_at desc")
scope :finished, where(:finished => true)
scope :current, where(:finished => false)
delegate :name,
:en_wikipedia_url,
@@ -28,16 +32,12 @@ class Planting < ActiveRecord::Base
:to => :crop,
:prefix => true
default_scope { order("created_at desc") }
default_scope order("created_at desc")
validates :crop, :approved => true
validates :crop, :presence => {:message => "must be present and exist in our database"}
validates :crop_id, :presence => {:message => "must be present and exist in our database"}
validates :quantity,
:numericality => {
:only_integer => true,
:greater_than_or_equal_to => 0 },
:numericality => { :only_integer => true },
:allow_nil => true
SUNNINESS_VALUES = %w(sun semi-shade shade)
@@ -94,41 +94,6 @@ class Planting < ActiveRecord::Base
return photos.present?
end
def calculate_days_before_maturity(planting, crop)
p_crop = Planting.where(:crop_id => crop).where.not(:id => planting)
differences = p_crop.collect do |p|
if p.finished and !p.finished_at.nil?
(p.finished_at - p.planted_at).to_i
end
end
if differences.compact.empty?
nil
else
differences.compact.sum/differences.compact.size
end
end
def planted?(current_date = Date.today)
planted_at.present? && current_date.to_date >= planted_at
end
def percentage_grown(current_date = Date.today)
return nil unless days_before_maturity && planted?(current_date)
days = (current_date.to_date - planted_at.to_date).to_i
return 0 if current_date < planted_at
return 100 if days > days_before_maturity
percent = (days/days_before_maturity*100).to_i
if percent >= 100
percent = 100
end
percent
end
# return a list of interesting plantings, for the homepage etc.
# we can't do this via a scope (as far as we know) so sadly we have to
# do it this way.
@@ -136,8 +101,8 @@ class Planting < ActiveRecord::Base
interesting_plantings = Array.new
seen_owners = Hash.new(false) # keep track of which owners we've seen already
Planting.includes(:photos).each do |p|
break if interesting_plantings.size == howmany # got enough yet?
Planting.all.each do |p|
break if interesting_plantings.count == howmany # got enough yet?
if require_photo
next unless p.photos.present? # skip those without photos, if required
end

View File

@@ -0,0 +1,25 @@
class PlantingSweeper < ActionController::Caching::Sweeper
observe Planting
def after_create(planting)
expire_fragment('homepage_stats')
expire_fragment("member_thumbnail_#{planting.owner.id}")
expire_fragment("interesting_members") if planting.owner.interesting?
expire_fragment("crop_image_#{planting.crop.id}")
end
def after_update(planting)
expire_fragment("planting_listitem_#{planting.id}")
expire_fragment("planting_image_#{planting.id}")
expire_fragment("interesting_plantings")
end
def after_destroy(planting)
expire_fragment('homepage_stats')
expire_fragment("crop_image_#{planting.crop.id}")
expire_fragment('interesting_plantings') if planting.interesting?
expire_fragment("interesting_members") if planting.owner.interesting?
end
end

View File

@@ -1,6 +1,7 @@
class Post < ActiveRecord::Base
extend FriendlyId
friendly_id :author_date_subject, use: [:slugged, :finders]
friendly_id :author_date_subject, use: :slugged
attr_accessible :body, :subject, :author_id, :forum_id
belongs_to :author, :class_name => 'Member'
belongs_to :forum
has_many :comments, :dependent => :destroy
@@ -10,40 +11,12 @@ class Post < ActiveRecord::Base
# also has_many notifications, but kinda meaningless to get at them
# from this direction, so we won't set up an association for now.
after_create do
recipients = Array.new
sender = self.author.id
self.body.scan(Haml::Filters::GrowstuffMarkdown::MEMBER_REGEX) do |m|
# find member case-insensitively and add to list of recipients
member = Member.where('lower(login_name) = ?', $1.downcase).first
recipients << member if member and not recipients.include?(member)
end
self.body.scan(Haml::Filters::GrowstuffMarkdown::MEMBER_AT_REGEX) do |m|
# find member case-insensitively and add to list of recipients
member = Member.where('lower(login_name) = ?', $1[1..-1].downcase).first
recipients << member if member and not recipients.include?(member)
end
# don't send notifications to yourself
recipients.map{ |r| r.id }.each do |recipient|
if recipient != sender
Notification.create(
:recipient_id => recipient,
:sender_id => sender,
:subject => "#{self.author} mentioned you in their post #{self.subject}",
:body => self.body,
)
end
end
end
default_scope { order("created_at desc") }
default_scope order("created_at desc")
validates :subject,
:format => {
:with => /\S/
},
:length => { :maximum => 255 }
}
def author_date_subject
# slugs are created before created_at is set
@@ -52,7 +25,7 @@ class Post < ActiveRecord::Base
end
def comment_count
self.comments.size
self.comments.count
end
# return the timestamp of the most recent activity on this post

View File

@@ -0,0 +1,16 @@
class PostSweeper < ActionController::Caching::Sweeper
observe Post
def after_create(post)
expire_fragment('recent_posts')
end
def after_update(post)
expire_fragment('recent_posts')
end
def after_destroy(post)
expire_fragment('recent_posts')
end
end

View File

@@ -1,12 +1,12 @@
class Product < ActiveRecord::Base
attr_accessible :description, :min_price, :recommended_price, :name,
:account_type_id, :paid_months
has_and_belongs_to_many :orders
belongs_to :account_type
validates :paid_months,
:numericality => {
:only_integer => true,
:greater_than_or_equal_to => 0 },
:allow_nil => true
validates :paid_months, :numericality => { :only_integer => true,
:allow_nil => true }
def to_s
name

View File

@@ -1,7 +1,7 @@
class Role < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: [:slugged, :finders]
friendly_id :name, use: :slugged
attr_accessible :description, :name, :members, :slug
has_and_belongs_to_many :members
class << self

View File

@@ -1,5 +1,5 @@
class ScientificName < ActiveRecord::Base
after_commit { |sn| sn.crop.__elasticsearch__.index_document if sn.crop && ENV['GROWSTUFF_ELASTICSEARCH'] == "true" }
attr_accessible :crop_id, :scientific_name, :creator_id
belongs_to :crop
belongs_to :creator, :class_name => 'Member'
end

View File

@@ -0,0 +1,17 @@
class ScientificNameSweeper < ActionController::Caching::Sweeper
observe ScientificName
def after_create(scientific_name)
expire_fragment("crop_image_#{scientific_name.crop.id}")
end
def after_update(scientific_name)
expire_fragment("crop_image_#{scientific_name.crop.id}")
end
def after_destroy(scientific_name)
expire_fragment("crop_image_#{scientific_name.crop.id}")
end
end

View File

@@ -1,32 +1,21 @@
class Seed < ActiveRecord::Base
extend FriendlyId
friendly_id :seed_slug, use: [:slugged, :finders]
friendly_id :seed_slug, use: :slugged
attr_accessible :owner_id, :crop_id, :description, :quantity, :plant_before,
:tradable_to, :slug
belongs_to :crop
belongs_to :owner, :class_name => 'Member', :foreign_key => 'owner_id'
default_scope { order("created_at desc") }
validates :crop, :approved => true
default_scope order("created_at desc")
validates :crop, :presence => {:message => "must be present and exist in our database"}
validates :quantity,
: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 },
:numericality => { :only_integer => true },
:allow_nil => true
scope :tradable, -> { where("tradable_to != 'nowhere'") }
scope :tradable, where("tradable_to != 'nowhere'")
TRADABLE_TO_VALUES = %w(nowhere locally nationally internationally)
validates :tradable_to, :inclusion => { :in => TRADABLE_TO_VALUES,
@@ -34,32 +23,6 @@ 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
@@ -82,7 +45,7 @@ class Seed < ActiveRecord::Base
interesting_seeds = Array.new
Seed.tradable.each do |s|
break if interesting_seeds.size == howmany
break if interesting_seeds.length == howmany
if s.interesting?
interesting_seeds.push(s)
end

View File

@@ -0,0 +1,26 @@
class SeedSweeper < ActionController::Caching::Sweeper
observe Seed
def after_create(seed)
if seed.tradable? && seed.interesting?
expire_fragment('interesting_seeds')
end
expire_fragment('interesting_members') if seed.owner.interesting?
expire_fragment("member_thumbnail_#{seed.owner.id}")
end
def after_update(seed)
expire_fragment('interesting_seeds')
end
def after_destroy(seed)
if seed.tradable? && seed.interesting?
expire_fragment('interesting_seeds')
end
expire_fragment('interesting_members') if seed.owner.interesting?
expire_fragment("member_thumbnail_#{seed.owner.id}")
end
end

View File

@@ -1,7 +0,0 @@
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

View File

@@ -1,7 +1,7 @@
= form_for @account_type do |f|
- if @account_type.errors.any?
#error_explanation
%h2= "#{pluralize(@account_type.errors.size, "error")} prohibited this account_type from being saved:"
%h2= "#{pluralize(@account_type.errors.count, "error")} prohibited this account_type from being saved:"
%ul
- @account_type.errors.full_messages.each do |msg|
%li= msg

View File

@@ -1,7 +1,7 @@
= form_for @account do |f|
- if @account.errors.any?
#error_explanation
%h2= "#{pluralize(@account.errors.size, "error")} prohibited this account from being saved:"
%h2= "#{pluralize(@account.errors.count, "error")} prohibited this account from being saved:"
%ul
- @account.errors.full_messages.each do |msg|
%li= msg

View File

@@ -8,7 +8,6 @@
%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

View File

@@ -5,7 +5,7 @@
- unless @orders.empty?
%h2
Found
= pluralize(@orders.size, "result")
= pluralize(@orders.count, "result")
%table.table.table-striped
%tr
@@ -28,7 +28,7 @@
%td
= order.referral_code
%td
- if order.order_items.size > 0
- if order.order_items.count > 0
- order.order_items.each do |o|
= o.quantity
x

View File

@@ -1,7 +1,7 @@
= form_for @alternate_name, :html => {:class => 'form-horizontal', :role => "form"} do |f|
- if @alternate_name.errors.any?
#error_explanation
%h2= "#{pluralize(@alternate_name.errors.size, "error")} prohibited this alternate_name from being saved:"
%h2= "#{pluralize(@alternate_name.errors.count, "error")} prohibited this alternate_name from being saved:"
%ul
- @alternate_name.errors.full_messages.each do |msg|
%li= msg
@@ -16,12 +16,10 @@
= f.label :crop_id, :class => 'control-label col-md-2'
.col-md-8
= collection_select(:alternate_name, :crop_id, Crop.all, :id, :name, { :selected => @alternate_name.crop_id || @crop.id }, :class => 'form-control')
.form-group
= f.label :name, :class => 'control-label col-md-2'
.col-md-8
= f.text_field :name, :class => 'form-control'
.form-group
.form-actions.col-md-offset-2.col-md-8
= f.submit 'Save', :class => 'btn btn-primary'
= f.submit 'Save', :class => 'btn btn-primary'

View File

@@ -1,7 +1,5 @@
%p#notice= notice
= render :partial => 'crops/approval_status_message', :locals => { :crop => @alternate_name.crop }
%p
%b Alternate name:
= @alternate_name.name
@@ -11,5 +9,5 @@
- if can? :edit, @alternate_name
= link_to 'Edit', edit_alternate_name_path(@alternate_name), :class => 'btn btn-default btn-xs'
\|
\|
= link_to 'Back', alternate_names_path

View File

@@ -0,0 +1,11 @@
json.version '1.0.0'
json.license do
json.name "Creative Commons Attribution ShareAlike 3.0 Unported"
json.short_name "CC-BY-SA"
json.url "http://creativecommons.org/licenses/by-sa/3.0/"
json.credit "Growstuff"
json.link "http://growstuff.org/"
json.easy_link '<a href="http://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a> <a href="http://growstuff.org/">Growstuff</a>'
end

View File

@@ -0,0 +1,7 @@
json.data @crops do |crop|
json.(crop, :id, :name)
json.set! '@id', url_for(:only_path => false) + '/' + crop.id.to_s
json.url crop_url(crop)
end
json.partial! 'api/v1/_partials/base'

Some files were not shown because too many files have changed in this diff Show More