mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-05-25 09:19:15 -04:00
Compare commits
255 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6b83454fe | ||
|
|
d7ef598654 | ||
|
|
bd0da36d63 | ||
|
|
6dedf1b030 | ||
|
|
920ca2948b | ||
|
|
9e53105f43 | ||
|
|
6505254e6c | ||
|
|
ead9a250af | ||
|
|
3a9ec8cf6d | ||
|
|
c76e9ccbf6 | ||
|
|
1b62c5cb97 | ||
|
|
7bca5c8845 | ||
|
|
017df0484c | ||
|
|
d338bab82b | ||
|
|
35f8c126b6 | ||
|
|
eafe90e295 | ||
|
|
7b911034b8 | ||
|
|
798b89db11 | ||
|
|
506e3e550c | ||
|
|
8ab3167a28 | ||
|
|
436e7e3c59 | ||
|
|
9f1091a1c8 | ||
|
|
3b1e855da3 | ||
|
|
d7f8bff17a | ||
|
|
a7caa2fbaa | ||
|
|
b215ef03c7 | ||
|
|
3cbbbc79fb | ||
|
|
eb5e9e926b | ||
|
|
ef2d1eb683 | ||
|
|
965f87393d | ||
|
|
023333b15f | ||
|
|
2241a760c2 | ||
|
|
e4dabd1725 | ||
|
|
1dfac3fb2f | ||
|
|
d0f856d389 | ||
|
|
ac6aa730c1 | ||
|
|
cd12412b46 | ||
|
|
28288c51fe | ||
|
|
70cdab96c4 | ||
|
|
e906d293a2 | ||
|
|
abdc43ef40 | ||
|
|
3ed7edbfbf | ||
|
|
7e9f18e89e | ||
|
|
e09f050088 | ||
|
|
5bfab90b5b | ||
|
|
3289e7b950 | ||
|
|
102763f2ab | ||
|
|
09a78d4661 | ||
|
|
e0d8126514 | ||
|
|
5cceb2c4ff | ||
|
|
66fcad69fa | ||
|
|
2ab73d3df6 | ||
|
|
623dbdd418 | ||
|
|
78c4fc36b1 | ||
|
|
9ccef5471f | ||
|
|
0fc4c3cb4d | ||
|
|
c61f7b8e72 | ||
|
|
b81e034254 | ||
|
|
07f7572b13 | ||
|
|
2697fea249 | ||
|
|
cbb4f9d7ac | ||
|
|
0dbc2e1964 | ||
|
|
319fc12ebb | ||
|
|
2db7bf638b | ||
|
|
497121a221 | ||
|
|
e17a3a2ab1 | ||
|
|
49a5a26f17 | ||
|
|
b66b8263c1 | ||
|
|
c3429cc0f3 | ||
|
|
f7a2ec2054 | ||
|
|
95ecdad024 | ||
|
|
e6cbbb3e3d | ||
|
|
b78ccaa097 | ||
|
|
593d6e7ec1 | ||
|
|
4c6b96e590 | ||
|
|
8631c78f9a | ||
|
|
a69ad34359 | ||
|
|
1099e4c9fe | ||
|
|
3a53f4f8a8 | ||
|
|
076a6cc1df | ||
|
|
b842bff9cb | ||
|
|
80a28085f1 | ||
|
|
0dd8cbccf0 | ||
|
|
abece6473b | ||
|
|
ff1b941690 | ||
|
|
85a6eb6195 | ||
|
|
87bfceb035 | ||
|
|
b95975d632 | ||
|
|
075cdb8272 | ||
|
|
7db4dee61a | ||
|
|
0993917dc6 | ||
|
|
d6cb20e2c4 | ||
|
|
3e65656c7b | ||
|
|
47dc94f820 | ||
|
|
c1870d46a2 | ||
|
|
1988c22626 | ||
|
|
a795d452c6 | ||
|
|
656b0e44d8 | ||
|
|
af39df5e0c | ||
|
|
1f23e1a646 | ||
|
|
ca47127197 | ||
|
|
7ca89908cd | ||
|
|
2c94f61843 | ||
|
|
7326acba81 | ||
|
|
77b4b76f14 | ||
|
|
eca27d18ea | ||
|
|
2f67ffd2f8 | ||
|
|
40d13fadb3 | ||
|
|
3791f4aa6f | ||
|
|
79c60dc7c7 | ||
|
|
5035b65883 | ||
|
|
4211ebec76 | ||
|
|
d02edc3dd1 | ||
|
|
e86200b942 | ||
|
|
776b5450f9 | ||
|
|
c80b42d9bc | ||
|
|
6aa37e6e26 | ||
|
|
089a3d5c24 | ||
|
|
1741567e19 | ||
|
|
de5b16e384 | ||
|
|
a8c203aea0 | ||
|
|
2aa30475e9 | ||
|
|
9a5e15b292 | ||
|
|
bb9695b272 | ||
|
|
d436fd86f8 | ||
|
|
3f393b0937 | ||
|
|
fb271633d9 | ||
|
|
e40fad76fd | ||
|
|
41db12d8d7 | ||
|
|
28d29291a7 | ||
|
|
22e0379769 | ||
|
|
b60bbbcb4e | ||
|
|
0da121a48d | ||
|
|
6b6ffd6e58 | ||
|
|
21dbf87ab9 | ||
|
|
a5fcd9f860 | ||
|
|
2853bf5c70 | ||
|
|
2d55d88db8 | ||
|
|
2567e7cd74 | ||
|
|
e2bac619b7 | ||
|
|
0bdab0d9fc | ||
|
|
e55fe55dc7 | ||
|
|
5a474d523c | ||
|
|
d126392ee2 | ||
|
|
c38dc4661d | ||
|
|
675ac5a03f | ||
|
|
233f9dfd88 | ||
|
|
b414598b07 | ||
|
|
2c4e768a3a | ||
|
|
04d61bd040 | ||
|
|
5a96b7efd6 | ||
|
|
329e147add | ||
|
|
334b5bf63f | ||
|
|
58940a6765 | ||
|
|
2442539e1b | ||
|
|
b56237115e | ||
|
|
038192095d | ||
|
|
9a18b4b62b | ||
|
|
c6f5abc036 | ||
|
|
ad76a04e8f | ||
|
|
0b70be4939 | ||
|
|
da2590791e | ||
|
|
79a7958519 | ||
|
|
f4d452f3bc | ||
|
|
d08e2f09db | ||
|
|
d5cc3f300a | ||
|
|
1982cecc31 | ||
|
|
2f05f1dbdb | ||
|
|
a67a55c599 | ||
|
|
253b5a3f85 | ||
|
|
45aaf31722 | ||
|
|
d0b9917e84 | ||
|
|
32dcacf9ba | ||
|
|
0e0c309c15 | ||
|
|
dabfeeea3c | ||
|
|
20b9996f14 | ||
|
|
1ea5257da4 | ||
|
|
3c360ab1b8 | ||
|
|
b1c60572ef | ||
|
|
14ad572f18 | ||
|
|
1481d01cf8 | ||
|
|
492781ea3d | ||
|
|
542d978d9c | ||
|
|
c893dcd271 | ||
|
|
57552455e3 | ||
|
|
fe69e2f11d | ||
|
|
684a3d2229 | ||
|
|
3d738e1b7c | ||
|
|
a5daa156ab | ||
|
|
018b2b4711 | ||
|
|
d12ec968c4 | ||
|
|
531a0bd9ea | ||
|
|
7950c577e9 | ||
|
|
3bc77f9b09 | ||
|
|
05260c05c2 | ||
|
|
d0ea54237e | ||
|
|
f404d54d02 | ||
|
|
5a2d9eabf4 | ||
|
|
1b936100e7 | ||
|
|
34ea6eb37f | ||
|
|
2c006ec430 | ||
|
|
f541261e43 | ||
|
|
f910fdfa73 | ||
|
|
72f86c4ad0 | ||
|
|
0cb192ce36 | ||
|
|
3aadc5d68f | ||
|
|
1387f381d2 | ||
|
|
7257f2e557 | ||
|
|
025bfdb4b0 | ||
|
|
1a50566328 | ||
|
|
ae914daa0c | ||
|
|
b984475335 | ||
|
|
59f5101858 | ||
|
|
e7d3e4d6dd | ||
|
|
79e1835216 | ||
|
|
e4072fb395 | ||
|
|
666d6dac48 | ||
|
|
ea5b340933 | ||
|
|
534c299383 | ||
|
|
99eb33ccbb | ||
|
|
760e5ca74e | ||
|
|
cd57c9cd34 | ||
|
|
2a184bcb2e | ||
|
|
df63819602 | ||
|
|
628d5b24c1 | ||
|
|
8221d5b441 | ||
|
|
ad7cfdabd0 | ||
|
|
4237dfb269 | ||
|
|
c512b079fa | ||
|
|
b4cd151a03 | ||
|
|
eab958eac4 | ||
|
|
17e94e01d0 | ||
|
|
6451077d1d | ||
|
|
ce265e281a | ||
|
|
f4511c79e6 | ||
|
|
06c7703628 | ||
|
|
619e8590c8 | ||
|
|
88efcf4da6 | ||
|
|
556ba33172 | ||
|
|
9d7b939d42 | ||
|
|
0bea278c5c | ||
|
|
65814f1ef6 | ||
|
|
db0eb3e4da | ||
|
|
323f635b7b | ||
|
|
729dba8e0b | ||
|
|
85c3e74cdd | ||
|
|
f9cec41360 | ||
|
|
19dcf6f3d5 | ||
|
|
1d28c30680 | ||
|
|
9a3c4e69e8 | ||
|
|
56025d3d33 | ||
|
|
59cae7a8ce | ||
|
|
9a9f859b70 | ||
|
|
ba3a1f6298 | ||
|
|
10e6e7c3cb |
42
.travis.yml
42
.travis.yml
@@ -1,12 +1,38 @@
|
||||
---
|
||||
sudo: false
|
||||
language: ruby
|
||||
|
||||
env: GROWSTUFF_SITE_NAME="Growstuff (travis)" RAILS_SECRET_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
|
||||
bundler_args: --without development production staging
|
||||
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.5
|
||||
- 2.1.5
|
||||
before_script:
|
||||
- psql -c 'create database growstuff_test;' -U postgres
|
||||
- psql -c 'create database growstuff_test;' -U postgres
|
||||
script:
|
||||
- bundle exec rake db:migrate --trace
|
||||
- bundle exec rspec spec/
|
||||
- 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
|
||||
|
||||
|
||||
@@ -52,4 +52,6 @@ submit the change with your pull request.
|
||||
- Kevin Yang / [kevieyang](https://github.com/kevieyang)
|
||||
- Justin Hamman / [juzham](https://github.com/juzham)
|
||||
- Rocky Jaiswal / [rocky-jaiswal](https://github.com/rocky-jaiswal)
|
||||
- Robert Landreaux / [robertlandreaux](https://github.com/robertlandreaux)
|
||||
- Savant Krishna / [sksavant](https://github.com/sksavant)
|
||||
|
||||
|
||||
15
Gemfile
15
Gemfile
@@ -31,6 +31,9 @@ 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
|
||||
|
||||
# vendored activemerchant for testing- needed for bogus paypal
|
||||
# gateway monkeypatch
|
||||
@@ -76,6 +79,12 @@ 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
|
||||
@@ -83,6 +92,7 @@ group :production, :staging do
|
||||
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
|
||||
@@ -93,6 +103,7 @@ group :development do
|
||||
gem 'better_errors'
|
||||
gem 'binding_of_caller'
|
||||
gem 'letter_opener'
|
||||
gem 'quiet_assets'
|
||||
end
|
||||
|
||||
group :development, :test do
|
||||
@@ -109,3 +120,7 @@ group :development, :test do
|
||||
gem 'poltergeist', '~> 1.5.1' # for headless JS testing
|
||||
gem 'i18n-tasks' # adds tests for finding missing and unused translations
|
||||
end
|
||||
|
||||
group :travis do
|
||||
gem 'heroku-api'
|
||||
end
|
||||
|
||||
72
Gemfile.lock
72
Gemfile.lock
@@ -33,6 +33,8 @@ GEM
|
||||
activesupport (= 4.1.9)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
active_link_to (1.0.2)
|
||||
actionpack
|
||||
activemodel (4.1.9)
|
||||
activesupport (= 4.1.9)
|
||||
builder (~> 3.1)
|
||||
@@ -48,6 +50,9 @@ GEM
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.3.6)
|
||||
arel (5.0.1.20140414130214)
|
||||
autoprefixer-rails (5.1.1)
|
||||
execjs
|
||||
json
|
||||
bcrypt (3.1.9)
|
||||
better_errors (2.0.0)
|
||||
coderay (>= 1.0.0)
|
||||
@@ -56,8 +61,13 @@ 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-sass (3.3.3)
|
||||
autoprefixer-rails (>= 5.0.0.1)
|
||||
sass (>= 3.2.19)
|
||||
bootstrap_form (2.2.0)
|
||||
builder (3.2.2)
|
||||
byebug (3.5.1)
|
||||
columnize (~> 0.8)
|
||||
@@ -73,7 +83,13 @@ GEM
|
||||
capybara-email (2.4.0)
|
||||
capybara (~> 2.4)
|
||||
mail
|
||||
climate_control (0.0.3)
|
||||
activesupport (>= 3.0)
|
||||
cliver (0.3.2)
|
||||
cocaine (0.5.5)
|
||||
climate_control (>= 0.0.3, < 1.0)
|
||||
codemirror-rails (4.8)
|
||||
railties (>= 3.0, < 5)
|
||||
coderay (1.1.0)
|
||||
coffee-rails (4.1.0)
|
||||
coffee-script (>= 2.2.0)
|
||||
@@ -83,6 +99,21 @@ GEM
|
||||
execjs
|
||||
coffee-script-source (1.8.0)
|
||||
columnize (0.9.0)
|
||||
comfortable_mexican_sofa (1.12.7)
|
||||
active_link_to (>= 1.0.0)
|
||||
bootstrap-sass (>= 3.2.0)
|
||||
bootstrap_form (>= 2.2.0)
|
||||
codemirror-rails (>= 3.0.0)
|
||||
coffee-rails (>= 3.1.0)
|
||||
haml-rails (>= 0.3.0)
|
||||
jquery-rails (>= 3.0.0)
|
||||
jquery-ui-rails (>= 5.0.0)
|
||||
kramdown (>= 1.0.0)
|
||||
paperclip (>= 4.0.0)
|
||||
plupload-rails (>= 1.2.1)
|
||||
rails (>= 4.0.0, < 5)
|
||||
rails-i18n (>= 4.0.0)
|
||||
sass-rails (>= 4.0.3)
|
||||
commonjs (0.2.7)
|
||||
coveralls (0.7.1)
|
||||
multi_json (~> 1.3)
|
||||
@@ -109,13 +140,29 @@ GEM
|
||||
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.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)
|
||||
figaro (1.0.0)
|
||||
thor (~> 0.14)
|
||||
flickraw (0.9.8)
|
||||
@@ -136,6 +183,9 @@ GEM
|
||||
html2haml (>= 1.0.1)
|
||||
railties (>= 4.0.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)
|
||||
@@ -167,6 +217,7 @@ GEM
|
||||
sprockets-rails
|
||||
json (1.8.2)
|
||||
kgio (2.9.2)
|
||||
kramdown (1.5.0)
|
||||
launchy (2.4.3)
|
||||
addressable (~> 2.3)
|
||||
leaflet-markercluster-rails (0.7.0)
|
||||
@@ -191,6 +242,7 @@ GEM
|
||||
minitest (5.5.1)
|
||||
multi_json (1.10.1)
|
||||
multi_xml (0.5.5)
|
||||
multipart-post (2.0.0)
|
||||
netrc (0.10.0)
|
||||
newrelic_rpm (3.9.8.273)
|
||||
nokogiri (1.6.5)
|
||||
@@ -208,7 +260,14 @@ GEM
|
||||
multi_json (~> 1.3)
|
||||
omniauth-oauth (~> 1.0)
|
||||
orm_adapter (0.5.0)
|
||||
paperclip (4.2.1)
|
||||
activemodel (>= 3.0.0)
|
||||
activesupport (>= 3.0.0)
|
||||
cocaine (~> 0.5.3)
|
||||
mime-types
|
||||
pg (0.17.1)
|
||||
plupload-rails (1.2.1)
|
||||
rails (>= 3.1)
|
||||
poltergeist (1.5.1)
|
||||
capybara (~> 2.1)
|
||||
cliver (~> 0.3.1)
|
||||
@@ -218,6 +277,8 @@ GEM
|
||||
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.2)
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
@@ -231,6 +292,9 @@ GEM
|
||||
bundler (>= 1.3.0, < 2.0)
|
||||
railties (= 4.1.9)
|
||||
sprockets-rails (~> 2.0)
|
||||
rails-i18n (4.0.3)
|
||||
i18n (~> 0.6)
|
||||
railties (~> 4.0)
|
||||
rails_12factor (0.0.3)
|
||||
rails_serve_static_assets
|
||||
rails_stdout_logging
|
||||
@@ -269,6 +333,7 @@ GEM
|
||||
rspec-mocks (~> 3.1.0)
|
||||
rspec-support (~> 3.1.0)
|
||||
rspec-support (3.1.2)
|
||||
ruby-units (1.4.5)
|
||||
ruby_parser (3.1.3)
|
||||
sexp_processor (~> 4.1)
|
||||
sass (3.2.19)
|
||||
@@ -335,6 +400,7 @@ DEPENDENCIES
|
||||
better_errors
|
||||
binding_of_caller
|
||||
bluecloth
|
||||
bonsai-elasticsearch-rails
|
||||
bootstrap-datepicker-rails
|
||||
bundler (>= 1.1.5)
|
||||
byebug
|
||||
@@ -342,11 +408,14 @@ DEPENDENCIES
|
||||
capybara
|
||||
capybara-email
|
||||
coffee-rails (~> 4.1.0)
|
||||
comfortable_mexican_sofa (~> 1.12.0)
|
||||
coveralls
|
||||
csv_shaper
|
||||
dalli
|
||||
database_cleaner (~> 1.3.0)
|
||||
devise (~> 3.4.1)
|
||||
elasticsearch-model
|
||||
elasticsearch-rails
|
||||
factory_girl_rails (~> 4.5.0)
|
||||
figaro
|
||||
flickraw
|
||||
@@ -356,6 +425,7 @@ DEPENDENCIES
|
||||
gravatar-ultimate
|
||||
haml
|
||||
haml-rails
|
||||
heroku-api
|
||||
i18n-tasks
|
||||
jquery-rails
|
||||
jquery-ui-rails (~> 5.0.2)
|
||||
@@ -375,11 +445,13 @@ DEPENDENCIES
|
||||
pg
|
||||
poltergeist (~> 1.5.1)
|
||||
pry
|
||||
quiet_assets
|
||||
rails (= 4.1.9)
|
||||
rails_12factor
|
||||
rake (>= 10.0.0)
|
||||
rspec-activemodel-mocks
|
||||
rspec-rails (~> 3.1.0)
|
||||
ruby-units
|
||||
sass-rails (~> 4.0.4)
|
||||
therubyracer (~> 0.12)
|
||||
uglifier (~> 2.5.3)
|
||||
|
||||
1
app/assets/javascripts/comfy/admin/cms/custom.js.coffee
Normal file
1
app/assets/javascripts/comfy/admin/cms/custom.js.coffee
Normal file
@@ -0,0 +1 @@
|
||||
# Custom JS for the admin area
|
||||
1
app/assets/stylesheets/comfy/admin/cms/custom.sass
Normal file
1
app/assets/stylesheets/comfy/admin/cms/custom.sass
Normal file
@@ -0,0 +1 @@
|
||||
// custom CSS for admin area
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
@import "twitter/bootstrap/bootstrap";
|
||||
@import "custom_bootstrap/variables";
|
||||
// this padding needs to be done before the responsive stuff is imported
|
||||
@@ -168,16 +166,31 @@ p.stats {
|
||||
|
||||
.crop-thumbnail {
|
||||
height: 220px;
|
||||
.scientific-name small, .crop-name a {
|
||||
.cropinfo {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1em;
|
||||
padding-bottom: 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;
|
||||
}
|
||||
@@ -201,8 +214,10 @@ li.crop-hierarchy {
|
||||
margin: 40px 0px 0px 0px !important;
|
||||
}
|
||||
|
||||
// footer
|
||||
|
||||
footer {
|
||||
#contact, #about-growstuff, #policies {
|
||||
#footer1, #footer2, #footer3 {
|
||||
text-align: left;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 2em;
|
||||
@@ -229,6 +244,17 @@ footer {
|
||||
}
|
||||
}
|
||||
|
||||
// ensure footer is pushed to bottom of browser window
|
||||
|
||||
#maincontainer {
|
||||
min-height: 80%;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
.crop-image, .member-image {
|
||||
width: 100%;
|
||||
@@ -257,6 +283,12 @@ footer {
|
||||
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) {
|
||||
@@ -274,3 +306,13 @@ footer {
|
||||
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%);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require 'will_paginate/array'
|
||||
|
||||
class CropsController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :hierarchy, :search, :show]
|
||||
load_and_authorize_resource
|
||||
@@ -9,10 +11,12 @@ class CropsController < ApplicationController
|
||||
@sort = params[:sort]
|
||||
if @sort == 'alpha'
|
||||
# alphabetical order
|
||||
@crops = Crop.includes(:scientific_names, {:plantings => :photos}).paginate(:page => params[:page])
|
||||
@crops = Crop.includes(:scientific_names, {:plantings => :photos})
|
||||
@paginated_crops = @crops.approved.paginate(:page => params[:page])
|
||||
else
|
||||
# default to sorting by popularity
|
||||
@crops = Crop.popular.includes(:scientific_names, {:plantings => :photos}).paginate(:page => params[:page])
|
||||
@crops = Crop.popular.includes(:scientific_names, {:plantings => :photos})
|
||||
@paginated_crops = @crops.approved.paginate(:page => params[:page])
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
@@ -32,7 +36,18 @@ class CropsController < ApplicationController
|
||||
|
||||
# GET /crops/wrangle
|
||||
def wrangle
|
||||
@crops = Crop.recent.paginate(:page => params[:page])
|
||||
@approval_status = params[:approval_status]
|
||||
case @approval_status
|
||||
when "pending"
|
||||
@crops = Crop.pending_approval
|
||||
when "rejected"
|
||||
@crops = Crop.rejected
|
||||
else
|
||||
@crops = Crop.recent
|
||||
end
|
||||
|
||||
@crops = @crops.paginate(:page => params[:page])
|
||||
|
||||
@crop_wranglers = Role.crop_wranglers
|
||||
respond_to do |format|
|
||||
format.html
|
||||
@@ -49,18 +64,13 @@ class CropsController < ApplicationController
|
||||
|
||||
# GET /crops/search
|
||||
def search
|
||||
@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 = @partial_matches.reject{ |r| @exact_match && r.eql?(@exact_match) }
|
||||
|
||||
@fuzzy = Crop.search(params[:term])
|
||||
@term = params[:term]
|
||||
@matches = Crop.search(@term)
|
||||
@paginated_matches = @matches.paginate(:page => params[:page])
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json { render :json => @fuzzy }
|
||||
format.json { render :json => @matches }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -96,17 +106,35 @@ class CropsController < ApplicationController
|
||||
# GET /crops/1/edit
|
||||
def edit
|
||||
@crop = Crop.find(params[:id])
|
||||
|
||||
(3 - @crop.scientific_names.length).times do
|
||||
@crop.scientific_names.build
|
||||
end
|
||||
end
|
||||
|
||||
# POST /crops
|
||||
# POST /crops.json
|
||||
def create
|
||||
params[:crop][:creator_id] = current_member.id
|
||||
@crop = Crop.new(crop_params)
|
||||
|
||||
if current_member.has_role? :crop_wrangler
|
||||
@crop.creator = current_member
|
||||
success_msg = "Crop was successfully created."
|
||||
else
|
||||
@crop.requester = current_member
|
||||
@crop.approval_status = "pending"
|
||||
success_msg = "Crop was successfully requested."
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
if @crop.save
|
||||
format.html { redirect_to @crop, notice: 'Crop was successfully created.' }
|
||||
unless current_member.has_role? :crop_wrangler
|
||||
Role.crop_wranglers.each do |w|
|
||||
Notifier.new_crop_request(w, @crop).deliver!
|
||||
end
|
||||
end
|
||||
|
||||
format.html { redirect_to @crop, notice: success_msg }
|
||||
format.json { render json: @crop, status: :created, location: @crop }
|
||||
else
|
||||
format.html { render action: "new" }
|
||||
@@ -120,8 +148,18 @@ class CropsController < ApplicationController
|
||||
def update
|
||||
@crop = Crop.find(params[:id])
|
||||
|
||||
previous_status = @crop.approval_status
|
||||
|
||||
@crop.creator = current_member if previous_status == "pending"
|
||||
|
||||
respond_to do |format|
|
||||
if @crop.update(crop_params)
|
||||
if previous_status == "pending"
|
||||
requester = @crop.requester
|
||||
new_status = @crop.approval_status
|
||||
Notifier.crop_request_approved(requester, @crop).deliver! if new_status == "approved"
|
||||
Notifier.crop_request_rejected(requester, @crop).deliver! if new_status == "rejected"
|
||||
end
|
||||
format.html { redirect_to @crop, notice: 'Crop was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
@@ -146,6 +184,6 @@ class CropsController < ApplicationController
|
||||
private
|
||||
|
||||
def crop_params
|
||||
params.require(:crop).permit(:en_wikipedia_url, :name, :parent_id, :creator_id, :scientific_names_attributes)
|
||||
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
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
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])
|
||||
|
||||
if @follow.save
|
||||
|
||||
@@ -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.name}-" : @crop ? "#{@crop.name}-" : nil)
|
||||
specifics = (@owner ? "#{@owner.login_name}-" : @crop ? "#{@crop.name}-" : nil)
|
||||
@filename = "Growstuff-#{specifics}Harvests-#{Time.zone.now.to_s(:number)}.csv"
|
||||
render :csv => @harvests
|
||||
end
|
||||
@@ -107,6 +107,6 @@ class HarvestsController < ApplicationController
|
||||
|
||||
def harvest_params
|
||||
params.require(:harvest).permit(:crop_id, :harvested_at, :description, :owner_id,
|
||||
:quantity, :unit, :weight_quantity, :weight_unit, :plant_part_id, :slug)
|
||||
:quantity, :unit, :weight_quantity, :weight_unit, :plant_part_id, :slug, :si_weight)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,7 +6,12 @@ class MembersController < ApplicationController
|
||||
after_action :expire_cache_fragments, :only => :create
|
||||
|
||||
def index
|
||||
@members = Member.confirmed.paginate(:page => params[:page])
|
||||
@sort = params[:sort]
|
||||
if @sort == 'recently_joined'
|
||||
@members = Member.confirmed.recently_joined.paginate(:page => params[:page])
|
||||
else
|
||||
@members = Member.confirmed.paginate(:page => params[:page])
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.haml
|
||||
|
||||
@@ -65,7 +65,7 @@ class PhotosController < ApplicationController
|
||||
|
||||
# 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"]
|
||||
valid_models = ["planting", "harvest", "garden"]
|
||||
if params[:type]
|
||||
if valid_models.include?(params[:type])
|
||||
if params[:id]
|
||||
|
||||
@@ -110,7 +110,9 @@ class SeedsController < ApplicationController
|
||||
private
|
||||
|
||||
def seed_params
|
||||
params.require(:seed).permit(:owner_id, :crop_id, :description, :quantity, :plant_before,
|
||||
:tradable_to, :slug)
|
||||
params.require(:seed).permit(
|
||||
:owner_id, :crop_id, :description, :quantity, :plant_before,
|
||||
:days_until_maturity_min, :days_until_maturity_max, :organic, :gmo,
|
||||
:heirloom, :tradable_to, :slug)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -19,4 +19,4 @@ module AutoSuggestHelper
|
||||
}.html_safe
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,4 +22,19 @@ class Notifier < ActionMailer::Base
|
||||
end
|
||||
end
|
||||
|
||||
def new_crop_request(member, request)
|
||||
@member, @request = member, request
|
||||
mail(:to => @member.email, :subject => "#{@request.requester.login_name} has requested #{@request.name} as a new crop")
|
||||
end
|
||||
|
||||
def crop_request_approved(member, crop)
|
||||
@member, @crop = member, crop
|
||||
mail(:to => @member.email, :subject => "#{crop.name.capitalize} has been approved")
|
||||
end
|
||||
|
||||
def crop_request_rejected(member, crop)
|
||||
@member, @crop = member, crop
|
||||
mail(:to => @member.email, :subject => "#{crop.name.capitalize} has been rejected")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -21,7 +21,24 @@ class Ability
|
||||
cannot :read, Account
|
||||
cannot :read, AccountType
|
||||
|
||||
# nobody should be able to view unapproved crops unless they
|
||||
# are wranglers or admins
|
||||
cannot :read, Crop
|
||||
can :read, Crop, :approval_status => "approved"
|
||||
# scientific names should only be viewable if associated crop is approved
|
||||
cannot :read, ScientificName
|
||||
can :read, ScientificName do |sn|
|
||||
sn.crop.approved?
|
||||
end
|
||||
# ... same for alternate names
|
||||
cannot :read, AlternateName
|
||||
can :read, AlternateName do |an|
|
||||
an.crop.approved?
|
||||
end
|
||||
|
||||
if member
|
||||
# members can see even rejected or pending crops if they requested it
|
||||
can :read, Crop, :requester_id => member.id
|
||||
|
||||
# managing your own user settings
|
||||
can :update, Member, :id => member.id
|
||||
@@ -45,6 +62,9 @@ class Ability
|
||||
can :manage, AlternateName
|
||||
end
|
||||
|
||||
# any member can create a crop provisionally
|
||||
can :create, Crop
|
||||
|
||||
# can create & destroy their own authentications against other sites.
|
||||
can :create, Authentication
|
||||
can :destroy, Authentication, :member_id => member.id
|
||||
@@ -94,9 +114,11 @@ class Ability
|
||||
cannot :destroy, OrderItem, :order => { :member_id => member.id, :completed_at => nil }
|
||||
|
||||
# following/unfollowing permissions
|
||||
can :create, Follow do |f|
|
||||
!member.already_following?(f.followed) && f.followed_id != member.id
|
||||
end
|
||||
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
|
||||
|
||||
if member.has_role? :admin
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class AlternateName < ActiveRecord::Base
|
||||
after_commit { |an| an.crop.__elasticsearch__.index_document if an.crop && ENV['GROWSTUFF_ELASTICSEARCH'] == "true" }
|
||||
belongs_to :crop
|
||||
belongs_to :creator, :class_name => 'Member'
|
||||
end
|
||||
|
||||
@@ -2,18 +2,19 @@ class Crop < ActiveRecord::Base
|
||||
extend FriendlyId
|
||||
friendly_id :name, use: [:slugged, :finders]
|
||||
|
||||
has_many :scientific_names
|
||||
has_many :scientific_names, after_add: :update_index, after_remove: :update_index
|
||||
accepts_nested_attributes_for :scientific_names,
|
||||
:allow_destroy => true,
|
||||
:reject_if => :all_blank
|
||||
|
||||
has_many :alternate_names
|
||||
has_many :alternate_names, after_add: :update_index, after_remove: :update_index
|
||||
has_many :plantings
|
||||
has_many :photos, :through => :plantings
|
||||
has_many :seeds
|
||||
has_many :harvests
|
||||
has_many :plant_parts, -> { uniq }, :through => :harvests
|
||||
belongs_to :creator, :class_name => 'Member'
|
||||
belongs_to :requester, :class_name => 'Member'
|
||||
|
||||
belongs_to :parent, :class_name => 'Crop'
|
||||
has_many :varieties, :class_name => 'Crop', :foreign_key => 'parent_id'
|
||||
@@ -21,16 +22,94 @@ class Crop < ActiveRecord::Base
|
||||
before_destroy {|crop| crop.posts.clear}
|
||||
|
||||
default_scope { order("lower(name) asc") }
|
||||
scope :recent, -> { reorder("created_at desc") }
|
||||
scope :toplevel, -> { where(:parent_id => nil) }
|
||||
scope :popular, -> { reorder("plantings_count desc, lower(name) asc") }
|
||||
scope :randomized, -> { reorder('random()') } # ok on sqlite and psql, but not on mysql
|
||||
scope :recent, -> { where(:approval_status => "approved").reorder("created_at desc") }
|
||||
scope :toplevel, -> { where(:approval_status => "approved", :parent_id => nil) }
|
||||
scope :popular, -> { where(:approval_status => "approved").reorder("plantings_count desc, lower(name) asc") }
|
||||
scope :randomized, -> { where(:approval_status => "approved").reorder('random()') } # ok on sqlite and psql, but not on mysql
|
||||
scope :pending_approval, -> { where(:approval_status => "pending") }
|
||||
scope :approved, -> { where(:approval_status => "approved") }
|
||||
scope :rejected, -> { where(:approval_status => "rejected") }
|
||||
|
||||
## Wikipedia urls are only necessary when approving a crop
|
||||
validates :en_wikipedia_url,
|
||||
:format => {
|
||||
:with => /\Ahttps?:\/\/en\.wikipedia\.org\/wiki/,
|
||||
:message => 'is not a valid English Wikipedia URL'
|
||||
}
|
||||
},
|
||||
:if => :approved?
|
||||
|
||||
## Reasons are only necessary when rejecting
|
||||
validates :reason_for_rejection, :presence => true, :if => :rejected?
|
||||
|
||||
## This validation addresses a race condition
|
||||
validate :approval_status_cannot_be_changed_again
|
||||
|
||||
validate :must_be_rejected_if_rejected_reasons_present
|
||||
|
||||
validate :must_have_meaningful_reason_for_rejection
|
||||
|
||||
####################################
|
||||
# Elastic search configuration
|
||||
include Elasticsearch::Model
|
||||
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 don’t 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
|
||||
@@ -103,6 +182,26 @@ class Crop < ActiveRecord::Base
|
||||
return true
|
||||
end
|
||||
|
||||
def pending?
|
||||
approval_status == "pending"
|
||||
end
|
||||
|
||||
def approved?
|
||||
approval_status == "approved"
|
||||
end
|
||||
|
||||
def rejected?
|
||||
approval_status == "rejected"
|
||||
end
|
||||
|
||||
def approval_statuses
|
||||
[ 'rejected', 'pending', 'approved' ]
|
||||
end
|
||||
|
||||
def reasons_for_rejection
|
||||
[ "already in database", "not edible", "not enough information", "other" ]
|
||||
end
|
||||
|
||||
# Crop.interesting
|
||||
# returns a list of interesting crops, for use on the homepage etc
|
||||
def Crop.interesting
|
||||
@@ -200,11 +299,76 @@ 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)
|
||||
where("name ILIKE ?", "%#{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
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -7,6 +7,17 @@ class Garden < ActiveRecord::Base
|
||||
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
|
||||
@@ -23,7 +34,9 @@ class Garden < ActiveRecord::Base
|
||||
}
|
||||
|
||||
validates :area,
|
||||
:numericality => { :only_integer => false, :greater_than_or_equal_to => 0 },
|
||||
:numericality => {
|
||||
:only_integer => false,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
|
||||
AREA_UNITS_VALUES = {
|
||||
@@ -83,4 +96,8 @@ class Garden < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def default_photo
|
||||
return photos.first
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -20,10 +20,14 @@ class Harvest < ActiveRecord::Base
|
||||
|
||||
default_scope { order('created_at DESC') }
|
||||
|
||||
validates :crop, :approved => true
|
||||
|
||||
validates :crop, :presence => {:message => "must be present and exist in our database"}
|
||||
|
||||
validates :quantity,
|
||||
:numericality => { :only_integer => false },
|
||||
:numericality => {
|
||||
:only_integer => false,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
|
||||
UNITS_VALUES = {
|
||||
@@ -59,6 +63,17 @@ 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
|
||||
|
||||
@@ -32,6 +32,7 @@ class Member < ActiveRecord::Base
|
||||
scope :confirmed, -> { where('confirmed_at IS NOT NULL') }
|
||||
scope :located, -> { where("location <> '' and latitude IS NOT NULL and longitude IS NOT NULL") }
|
||||
scope :recently_signed_in, -> { reorder('updated_at DESC') }
|
||||
scope :recently_joined, -> { reorder("confirmed_at desc") }
|
||||
scope :wants_newsletter, -> { where(:newsletter => true) }
|
||||
|
||||
has_many :follows, :class_name => "Follow", :foreign_key => "follower_id"
|
||||
|
||||
@@ -3,16 +3,18 @@ class Photo < ActiveRecord::Base
|
||||
|
||||
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") }
|
||||
|
||||
# remove photos that aren't used by anything
|
||||
def destroy_if_unused
|
||||
unless plantings.size > 0 or harvests.size > 0
|
||||
unless plantings.size > 0 or harvests.size > 0 or gardens.size > 0
|
||||
self.destroy
|
||||
end
|
||||
end
|
||||
|
||||
@@ -30,10 +30,14 @@ class Planting < ActiveRecord::Base
|
||||
|
||||
default_scope { order("created_at desc") }
|
||||
|
||||
validates :crop_id, :presence => {:message => "must be present and exist in our database"}
|
||||
validates :crop, :approved => true
|
||||
|
||||
validates :crop, :presence => {:message => "must be present and exist in our database"}
|
||||
|
||||
validates :quantity,
|
||||
:numericality => { :only_integer => true },
|
||||
:numericality => {
|
||||
:only_integer => true,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
|
||||
SUNNINESS_VALUES = %w(sun semi-shade shade)
|
||||
|
||||
@@ -2,8 +2,11 @@ class Product < ActiveRecord::Base
|
||||
has_and_belongs_to_many :orders
|
||||
belongs_to :account_type
|
||||
|
||||
validates :paid_months, :numericality => { :only_integer => true,
|
||||
:allow_nil => true }
|
||||
validates :paid_months,
|
||||
:numericality => {
|
||||
:only_integer => true,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
|
||||
def to_s
|
||||
name
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class ScientificName < ActiveRecord::Base
|
||||
after_commit { |sn| sn.crop.__elasticsearch__.index_document if sn.crop && ENV['GROWSTUFF_ELASTICSEARCH'] == "true" }
|
||||
belongs_to :crop
|
||||
belongs_to :creator, :class_name => 'Member'
|
||||
end
|
||||
|
||||
@@ -7,9 +7,23 @@ class Seed < ActiveRecord::Base
|
||||
|
||||
default_scope { order("created_at desc") }
|
||||
|
||||
validates :crop, :approved => true
|
||||
|
||||
validates :crop, :presence => {:message => "must be present and exist in our database"}
|
||||
validates :quantity,
|
||||
:numericality => { :only_integer => true },
|
||||
:numericality => {
|
||||
:only_integer => true,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
validates :days_until_maturity_min,
|
||||
:numericality => {
|
||||
:only_integer => true,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
validates :days_until_maturity_max,
|
||||
:numericality => {
|
||||
:only_integer => true,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
|
||||
scope :tradable, -> { where("tradable_to != 'nowhere'") }
|
||||
@@ -20,6 +34,32 @@ class Seed < ActiveRecord::Base
|
||||
:allow_nil => false,
|
||||
:allow_blank => false
|
||||
|
||||
ORGANIC_VALUES = [
|
||||
'certified organic',
|
||||
'non-certified organic',
|
||||
'conventional/non-organic',
|
||||
'unknown']
|
||||
validates :organic, :inclusion => { :in => ORGANIC_VALUES,
|
||||
:message => "You must say whether the seeds are organic or not, or that you don't know" },
|
||||
:allow_nil => false,
|
||||
:allow_blank => false
|
||||
|
||||
GMO_VALUES = [
|
||||
'certified GMO-free',
|
||||
'non-certified GMO-free',
|
||||
'GMO',
|
||||
'unknown']
|
||||
validates :gmo, :inclusion => { :in => GMO_VALUES,
|
||||
:message => "You must say whether the seeds are genetically modified or not, or that you don't know" },
|
||||
:allow_nil => false,
|
||||
:allow_blank => false
|
||||
|
||||
HEIRLOOM_VALUES = %w(heirloom hybrid unknown)
|
||||
validates :heirloom, :inclusion => { :in => HEIRLOOM_VALUES,
|
||||
:message => "You must say whether the seeds are heirloom, hybrid, or unknown" },
|
||||
:allow_nil => false,
|
||||
:allow_blank => false
|
||||
|
||||
def tradable?
|
||||
if self.tradable_to == 'nowhere'
|
||||
return false
|
||||
|
||||
7
app/validators/approved_validator.rb
Normal file
7
app/validators/approved_validator.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
class ApprovedValidator < ActiveModel::EachValidator
|
||||
def validate_each(record, attribute, value)
|
||||
unless record.crop.try(:approved?)
|
||||
record.errors[attribute] << (options[:message] || 'must be approved')
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -8,6 +8,7 @@
|
||||
%li= link_to "Roles", roles_path
|
||||
%li= link_to "Forums", forums_path
|
||||
%li= link_to "Newsletter subscribers", admin_newsletter_path
|
||||
%li= link_to "CMS", comfy_admin_cms_path
|
||||
|
||||
%h2 Orders
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
%p#notice= notice
|
||||
|
||||
= render :partial => 'crops/approval_status_message', :locals => { :crop => @alternate_name.crop }
|
||||
|
||||
%p
|
||||
%b Alternate name:
|
||||
= @alternate_name.name
|
||||
|
||||
11
app/views/crops/_approval_status_message.html.haml
Normal file
11
app/views/crops/_approval_status_message.html.haml
Normal file
@@ -0,0 +1,11 @@
|
||||
- if crop.pending?
|
||||
.alert.alert-danger
|
||||
%b This crop is currently pending approval.
|
||||
%p This crop was requested by #{crop.requester} #{time_ago_in_words(crop.created_at)} ago.
|
||||
- unless crop.request_notes.blank?
|
||||
%p
|
||||
Request notes: #{crop.request_notes}
|
||||
|
||||
- if crop.rejected?
|
||||
.alert.alert-danger
|
||||
%b This crop was rejected for the following reason: #{crop.rejection_explanation}
|
||||
@@ -10,7 +10,8 @@
|
||||
= render :partial => 'members/location', :locals => { :member => seed.owner }
|
||||
%p
|
||||
= link_to "View all #{crop.name} seeds", seeds_by_crop_path(crop)
|
||||
- if current_member
|
||||
= link_to "List achiote seeds to trade", new_seed_path(:crop_id => crop.id)
|
||||
- else
|
||||
= render :partial => 'shared/signin_signup', :locals => { :to => 'list your seeds to trade' }
|
||||
- if crop.approved?
|
||||
- if current_member
|
||||
%p= link_to "List #{crop.name} seeds to trade", new_seed_path(:crop_id => crop.id)
|
||||
- else
|
||||
= render :partial => 'shared/signin_signup', :locals => { :to => 'list your seeds to trade' }
|
||||
|
||||
@@ -6,30 +6,47 @@
|
||||
- @crop.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
||||
%p
|
||||
%span.help-block
|
||||
For detailed crop wrangling guidelines, please consult the
|
||||
=link_to "crop wrangling guide", "http://wiki.growstuff.org/index.php/Crop_wrangling"
|
||||
on the Growstuff wiki.
|
||||
-# Handy link to crop wrangling policy/style guide, shown to wranglers only
|
||||
- if can? :wrangle, @crop
|
||||
%p
|
||||
%span.help-block
|
||||
For detailed crop wrangling guidelines, please consult the
|
||||
=link_to "crop wrangling guide", "http://wiki.growstuff.org/index.php/Crop_wrangling"
|
||||
on the Growstuff wiki.
|
||||
|
||||
-# Everyone (wranglers and requesters) sees the basic info section
|
||||
%h2 Basic information
|
||||
|
||||
.form-group
|
||||
= f.label :name, :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_field :name, :class => 'form-control'
|
||||
%span.help-block Name in US English; singular; capitalize proper nouns only.
|
||||
%span.help-block
|
||||
The common name for the crop, in English (required).
|
||||
- if can? :wrangle, @crop
|
||||
Wranglers: please ensure this is singular, and capitalize
|
||||
proper nouns only.
|
||||
|
||||
.form-group
|
||||
= f.label :en_wikipedia_url, 'Wikipedia URL', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_field :en_wikipedia_url, :class => 'form-control'
|
||||
%span.help-block Link to this crop's page on the English language Wikipedia.
|
||||
.form-group
|
||||
= f.label :parent_id, 'Parent crop', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= collection_select(:crop, :parent_id, Crop.all, :id, :name, {:include_blank => true}, :class => 'form-control')
|
||||
%span.help-block Optional. For setting up crop hierarchies for varieties etc.
|
||||
%p
|
||||
%span.help-block
|
||||
You may enter up to 3 scientific names for a crop. Most crops will have only one.
|
||||
%span.help-block
|
||||
Link to the crop's page on the English language Wikipedia (required).
|
||||
|
||||
-# Only crop wranglers see the crop hierarchy (for now)
|
||||
- if can? :wrangle, @crop
|
||||
.form-group
|
||||
= f.label :parent_id, 'Parent crop', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= collection_select(:crop, :parent_id, Crop.all, :id, :name, {:include_blank => true}, :class => 'form-control')
|
||||
%span.help-block Optional. For setting up crop hierarchies for varieties etc.
|
||||
|
||||
|
||||
-# Everyone (wranglers and requesters) gets to add scientific names
|
||||
%h2 Scientific names
|
||||
%p You may enter up to 3 scientific names for a crop. Most crops will have only one.
|
||||
|
||||
= f.fields_for :scientific_names do |sn|
|
||||
.form-group
|
||||
= sn.label :scientific_name, "Scientific name", :class => 'control-label col-md-2'
|
||||
@@ -40,6 +57,42 @@
|
||||
%label.checkbox
|
||||
= sn.check_box :_destroy
|
||||
= sn.label :_destroy, "Delete"
|
||||
|
||||
-# This is used for comments from crop requesters. We need to show it
|
||||
-# to everyone, but we don't include it on new crops from wranglers.
|
||||
|
||||
- if (can? :wrangle, @crop and @crop.requester) or (cannot? :wrangle, @crop and @crop.new_record?)
|
||||
%h2 Crop request notes
|
||||
.form-group
|
||||
= f.label :request_notes, 'Comments', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_area :request_notes, :rows => 3, :class => 'form-control'
|
||||
|
||||
-# A final explanation of what's going to happen next, for crop requesters
|
||||
- unless can? :wrangle, @crop
|
||||
%p When you submit this form, your suggestion will be sent to our team of #{link_to 'volunteer crop wranglers', 'http://talk.growstuff.org/c/crop-wrangling'} for review. We'll let you know the outcome as soon as we can.
|
||||
|
||||
-# Now, for crop wranglers, let's have approval/rejection at the bottom of the page
|
||||
- if can? :wrangle, @crop and @crop.requester
|
||||
%h2 Approve or reject pending crops
|
||||
.form-group
|
||||
= f.label :approval_status, 'Approval status', :class=> 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.select(:approval_status, @crop.approval_statuses, {}, {:class => 'form-control'})
|
||||
|
||||
.form-group
|
||||
= f.label :reason_for_rejection, 'Reason for rejection', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.select(:reason_for_rejection, @crop.reasons_for_rejection, {:include_blank => true}, {:class => 'form-control'})
|
||||
|
||||
.form-group
|
||||
= f.label :rejection_notes, 'Rejection notes', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_area :rejection_notes, :rows => 3, :class => 'form-control'
|
||||
%span.help-block
|
||||
Please provide additional notes why this crop request was rejected if the above reasons do not apply.
|
||||
|
||||
|
||||
.form-group
|
||||
.form-actions.col-md-offset-2.col-md-8
|
||||
= f.submit 'Save', :class => 'btn btn-primary'
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
ago.
|
||||
%p
|
||||
= link_to "View all #{crop.name} harvests", harvests_by_crop_path(crop)
|
||||
- if current_member
|
||||
%p
|
||||
= link_to "Harvest #{crop.name}", new_harvest_path(:crop_id => crop.id)
|
||||
- else
|
||||
= render :partial => 'shared/signin_signup', :locals => { :to => "track your #{crop.name} harvests" }
|
||||
- if crop.approved?
|
||||
- if current_member
|
||||
%p= link_to "Harvest #{crop.name}", new_harvest_path(:crop_id => crop.id)
|
||||
- else
|
||||
= render :partial => 'shared/signin_signup', :locals => { :to => "track your #{crop.name} harvests" }
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.well
|
||||
.row
|
||||
.col-md-4
|
||||
= link_to image_tag((crop.default_photo ? crop.default_photo.thumbnail_url : 'placeholder_150.png'), :alt => '', :class => 'img-rounded crop-image'), crop
|
||||
= link_to image_tag((crop.default_photo ? crop.default_photo.thumbnail_url : 'placeholder_150.png'), :alt => '', :class => 'img crop-image'), crop
|
||||
.col-md-8
|
||||
%h3{:style => 'padding-top: 0px; margin-top: 0px'}
|
||||
= link_to crop, crop
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
ago.
|
||||
%p
|
||||
= link_to "View all #{crop.name} plantings", plantings_by_crop_path(crop)
|
||||
- if current_member
|
||||
%p
|
||||
= link_to "Plant #{crop.name}", new_planting_path(:crop_id => crop.id)
|
||||
- else
|
||||
= render :partial => 'shared/signin_signup', :locals => { :to => "track your #{crop.name} plantings" }
|
||||
- if crop.approved?
|
||||
- if current_member
|
||||
%p= link_to "Plant #{crop.name}", new_planting_path(:crop_id => crop.id)
|
||||
- else
|
||||
= render :partial => 'shared/signin_signup', :locals => { :to => "track your #{crop.name} plantings" }
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
.thumbnail.crop-thumbnail
|
||||
- if crop
|
||||
= link_to image_tag((crop.default_photo ? crop.default_photo.thumbnail_url : 'placeholder_150.png'), :alt => crop.name, :class => 'img'), crop
|
||||
%p.crop-name
|
||||
= link_to crop.name, crop
|
||||
- if crop.scientific_names.count > 0
|
||||
%br/
|
||||
%i.scientific-name
|
||||
%small
|
||||
.thumbnail
|
||||
.crop-thumbnail
|
||||
- if crop
|
||||
= link_to image_tag((crop.default_photo ? crop.default_photo.thumbnail_url : 'placeholder_150.png'), :alt => crop.name, :class => 'img'), crop
|
||||
.cropinfo
|
||||
.cropname
|
||||
= link_to crop.name, crop
|
||||
- if crop.scientific_names.count > 0
|
||||
.scientificname
|
||||
= crop.scientific_names.first.scientific_name
|
||||
%br/
|
||||
%small
|
||||
.plantingcount
|
||||
Planted
|
||||
= pluralize(crop.plantings.size, "time")
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
- content_for :title, "Editing crop"
|
||||
- content_for :title, "Edit crop: #{@crop.name}"
|
||||
|
||||
%p
|
||||
Added by
|
||||
= @crop.creator
|
||||
= distance_of_time_in_words(@crop.created_at, Time.zone.now)
|
||||
ago.
|
||||
- if @crop.approval_status == "approved"
|
||||
- if @crop.requester
|
||||
%p Requested by #{link_to @crop.requester, @crop.requester} #{distance_of_time_in_words(@crop.created_at, Time.zone.now)} ago.
|
||||
%p Approved by #{link_to @crop.creator, @crop.creator}.
|
||||
- else
|
||||
%p Added by #{link_to @crop.creator, @crop.creator} #{distance_of_time_in_words(@crop.created_at, Time.zone.now)} ago.
|
||||
- elsif @crop.approval_status == "pending"
|
||||
.alert.alert-danger
|
||||
%p Requested by #{link_to @crop.requester, @crop.requester} #{distance_of_time_in_words(@crop.created_at, Time.zone.now)} ago.
|
||||
%p Status: #{@crop.approval_status}.
|
||||
- elsif @crop.approval_status == "rejected"
|
||||
.alert.alert-danger
|
||||
%p Requested by #{link_to @crop.requester, @crop.requester} #{distance_of_time_in_words(@crop.created_at, Time.zone.now)} ago.
|
||||
%p Status: #{@crop.approval_status} by #{link_to @crop.creator, @crop.creator}.
|
||||
|
||||
= render 'form'
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
- content_for :title, "Crops"
|
||||
- content_for :title, "Browse crops"
|
||||
- content_for :subtitle, "#{@crops.size} total"
|
||||
|
||||
- if can? :wrangle, Crop
|
||||
= link_to 'Wrangle Crops', wrangle_crops_path, :class => 'btn btn-primary'
|
||||
%p
|
||||
#{ENV['GROWSTUFF_SITE_NAME']} tracks who's growing what, where.
|
||||
View any crop page to see which of our members have planted it and find
|
||||
@@ -12,11 +15,10 @@
|
||||
= submit_tag "Show", :class => 'btn btn-primary'
|
||||
|
||||
%div.pagination
|
||||
= page_entries_info @crops, :model => "crops"
|
||||
= will_paginate @crops
|
||||
= will_paginate @paginated_crops
|
||||
|
||||
.row
|
||||
- @crops.each do |crop|
|
||||
- @paginated_crops.each do |crop|
|
||||
.col-md-2.six-across
|
||||
= render :partial => "thumbnail", :locals => { :crop => crop }
|
||||
|
||||
@@ -25,8 +27,7 @@
|
||||
= link_to 'New Crop', new_crop_path, {:class => 'btn btn-primary'}
|
||||
|
||||
%div.pagination
|
||||
= page_entries_info @crops, :model => "crops"
|
||||
= will_paginate @crops
|
||||
= will_paginate @paginated_crops
|
||||
|
||||
|
||||
%ul.list-inline
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
- content_for :title, "New crop"
|
||||
- content_for :title, (can?(:wrangle, @crop) ? "New crop" : "Suggest a crop")
|
||||
|
||||
- unless can? :wrangler, @crop
|
||||
|
||||
%p Thanks for taking the time to suggest a crop! Our crop database is managed by volunteers, and we appreciate your help. Here are some things to consider when suggesting a new crop:
|
||||
|
||||
%ul
|
||||
%li First, you might want to #{link_to 'search our crops', crops_search_path} to make sure we don't have it already, perhaps under an alternate name.
|
||||
|
||||
%li The Growstuff database only contains edible crops. In future we hope to support other crops, but for now, if your suggestion is not edible we won't be able to add it.
|
||||
|
||||
%li At this time, we are only adding crops which have a Wikipedia page. If you want to add a specific variety of crop that doesn't have its own Wikipedia entry, please use the more general form of the crop instead and put the name of your variety in the notes/description.
|
||||
|
||||
= render 'form'
|
||||
|
||||
@@ -1,26 +1,35 @@
|
||||
- content_for :title, "Crops matching #{@search}"
|
||||
- if @term
|
||||
- content_for :title, "Crops matching \"#{@term}\""
|
||||
- if @matches
|
||||
- content_for :subtitle, "#{@matches.size} total"
|
||||
- else
|
||||
- content_for :title, "Crop search"
|
||||
|
||||
- unless (@exact_match || @partial_matches.length > 0)
|
||||
%div
|
||||
= form_tag crops_search_path, :method => :get, :id => 'crop-search', :class => 'form-inline' do
|
||||
.form-group
|
||||
= label_tag :term, "Search crops:", :class => 'sr-only'
|
||||
= text_field_tag 'term', nil, :class => 'search-query input-medium form-control', :placeholder => 'Search crops', :value => @term
|
||||
= submit_tag "Search", :class => 'btn btn-primary'
|
||||
|
||||
- if @matches.empty?
|
||||
%h2 No results found
|
||||
%p
|
||||
Sorry, we couldn't find any crops that matched your search for "#{@search}".
|
||||
Sorry, we couldn't find any crops that matched your search for "#{@term}".
|
||||
%p
|
||||
Try
|
||||
= link_to "browsing our crop database", crops_path
|
||||
instead.
|
||||
|
||||
- if @exact_match
|
||||
%div#exact_match
|
||||
%h2 Exact match
|
||||
- else
|
||||
%div.pagination
|
||||
= will_paginate @paginated_matches
|
||||
|
||||
%div#paginated_matches
|
||||
.row
|
||||
.col-md-2.six-across
|
||||
= render :partial => "thumbnail", :locals => { :crop => @exact_match }
|
||||
|
||||
- if ! @partial_matches.empty?
|
||||
%div#partial_matches
|
||||
%h2 Partial matches
|
||||
.row
|
||||
- @partial_matches.each do |c|
|
||||
- @paginated_matches.each do |c|
|
||||
.col-md-2.six-across
|
||||
= render :partial => "thumbnail", :locals => { :crop => c }
|
||||
|
||||
%div.pagination
|
||||
= will_paginate @paginated_matches
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
- content_for :title, @crop.name
|
||||
- content_for :subtitle, @crop.default_scientific_name
|
||||
- content_for :buttonbar do
|
||||
- if can? :create, Planting
|
||||
= link_to "Plant this", new_planting_path(:crop_id => @crop.id), :class => 'btn btn-default'
|
||||
|
||||
- if can? :create, Harvest
|
||||
= link_to "Harvest this", new_harvest_path(:crop_id => @crop.id), :class => 'btn btn-default'
|
||||
= render :partial => 'approval_status_message', :locals => { :crop => @crop }
|
||||
|
||||
- if can? :create, Seed
|
||||
= link_to 'Add seeds to stash', new_seed_path(:params => { :crop_id => @crop.id }), :class => 'btn btn-default'
|
||||
- if @crop.approved?
|
||||
- content_for :buttonbar do
|
||||
- if can? :create, Planting
|
||||
= link_to "Plant this", new_planting_path(:crop_id => @crop.id), :class => 'btn btn-default'
|
||||
|
||||
- if can? :create, Harvest
|
||||
= link_to "Harvest this", new_harvest_path(:crop_id => @crop.id), :class => 'btn btn-default'
|
||||
|
||||
- if can? :create, Seed
|
||||
= link_to 'Add seeds to stash', new_seed_path(:params => { :crop_id => @crop.id }), :class => 'btn btn-default'
|
||||
|
||||
|
||||
.row
|
||||
|
||||
@@ -15,28 +15,50 @@
|
||||
%li.crop_wrangler
|
||||
= link_to crop_wrangler.login_name, crop_wrangler
|
||||
|
||||
%h2 Recently added crops
|
||||
.tabbable
|
||||
%ul.nav.nav-tabs
|
||||
%li{:class => @approval_status.blank? ? 'active' : ''}
|
||||
= link_to "Recently added", wrangle_crops_path
|
||||
%li{:class => @approval_status == "pending" ? 'active' : ''}
|
||||
= link_to "Pending approval", wrangle_crops_path(:approval_status => "pending")
|
||||
%li{:class => @approval_status == "rejected" ? 'active' : ''}
|
||||
= link_to "Rejected", wrangle_crops_path(:approval_status => "rejected")
|
||||
|
||||
%h2
|
||||
- if @approval_status == "pending"
|
||||
Requested Crops
|
||||
- elsif @approval_status == "rejected"
|
||||
Rejected Crops
|
||||
- else
|
||||
Recently added crops
|
||||
|
||||
|
||||
%div.pagination
|
||||
= page_entries_info @crops, :model => "crops"
|
||||
= will_paginate @crops
|
||||
|
||||
%table.table.table-striped
|
||||
%table{:class => "table table-striped", :id => @approval_status.blank? ? 'recently-added-crops' : "#{@approval_status}-crops"}
|
||||
%tr
|
||||
%th System name
|
||||
%th English Wikipedia URL
|
||||
%th Scientific names
|
||||
%th Added by
|
||||
%th Requested by
|
||||
- if @approval_status == "rejected"
|
||||
%th Rejected by
|
||||
- if @approval_status != "rejected" && @approval_status != "pending"
|
||||
%th Added by
|
||||
%th When
|
||||
- @crops.each do |c|
|
||||
%tr
|
||||
%td= link_to c.name, c
|
||||
%td= link_to c.name, edit_crop_path(c)
|
||||
%td= link_to c.en_wikipedia_url, c.en_wikipedia_url
|
||||
%td
|
||||
- c.scientific_names.each do |s|
|
||||
= link_to s.scientific_name, s
|
||||
%br/
|
||||
%td= link_to c.creator, c.creator
|
||||
%td= c.requester.present? ? (link_to c.requester, c.requester) : "N/A"
|
||||
- unless @approval_status == "pending"
|
||||
%td= c.creator.present? ? (link_to c.creator, c.creator) : "N/A"
|
||||
%td
|
||||
= distance_of_time_in_words(c.created_at, Time.zone.now)
|
||||
ago.
|
||||
@@ -44,3 +66,5 @@
|
||||
%div.pagination
|
||||
= page_entries_info @crops, :model => "crops"
|
||||
= will_paginate @crops
|
||||
|
||||
|
||||
|
||||
@@ -29,6 +29,21 @@
|
||||
:growstuff_markdown
|
||||
#{strip_tags @garden.description}
|
||||
|
||||
- if @garden.photos.count > 0 or (can? :edit, @garden and can? :create, Photo)
|
||||
.row
|
||||
%h2 Photos
|
||||
|
||||
%ul.thumbnails
|
||||
- @garden.photos.each do |p|
|
||||
.col-md-2.six-across
|
||||
= render :partial => 'photos/thumbnail', :locals => { :photo => p }
|
||||
- if can? :create, Photo and can? :edit, @garden
|
||||
.col-md-2
|
||||
.thumbnail(style='height: 220px')
|
||||
%p{:style => 'text-align: center; padding-top: 50px'}
|
||||
= link_to "Add photo", new_photo_path(:type => "garden", :id => @garden.id), :class => 'btn btn-primary'
|
||||
|
||||
|
||||
%h3
|
||||
What's planted here?
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
= collection_select(:harvest, :plant_part_id, PlantPart.all, :id, :name, { :selected => @harvest.plant_part_id }, { :class => 'form-control' })
|
||||
%span.help-block.col-md-8
|
||||
Can't find what you're looking for?
|
||||
= link_to "Request new crops.", Growstuff::Application.config.new_crops_request_link
|
||||
= link_to "Request new crops.", new_crop_path
|
||||
|
||||
.form-group
|
||||
= f.label :harvested_at, 'When?', :class => 'control-label col-md-2'
|
||||
|
||||
@@ -10,6 +10,7 @@ csv.headers :id,
|
||||
:unit,
|
||||
:weight_quantity,
|
||||
:weight_unit,
|
||||
:si_weight,
|
||||
:date_harvested,
|
||||
:description,
|
||||
:date_added,
|
||||
@@ -31,7 +32,7 @@ csv.headers :id,
|
||||
csv.cell :plant_part_id, h.plant_part ? h.plant_part.id : ''
|
||||
csv.cell :plant_part_name, h.plant_part ? h.plant_part.to_s : ''
|
||||
|
||||
csv.cells :quantity, :unit, :weight_quantity, :weight_unit
|
||||
csv.cells :quantity, :unit, :weight_quantity, :weight_unit, :si_weight
|
||||
|
||||
csv.cell :date_harvested, h.created_at.to_s(:db)
|
||||
|
||||
|
||||
@@ -1,29 +1,9 @@
|
||||
.navbar.navbar-default.navbar-bottom
|
||||
.container
|
||||
.row
|
||||
.col-md-4#about-growstuff
|
||||
%ul
|
||||
%li= link_to t('about'), "http://wiki.growstuff.org/index.php/About%20Growstuff"
|
||||
%li= link_to t('our_values'), "http://wiki.growstuff.org/index.php/Values"
|
||||
%li= link_to t('open_source'), "https://github.com/Growstuff/growstuff"
|
||||
%li= link_to t('growstuff_team'), "http://wiki.growstuff.org/index.php/Team"
|
||||
%li= link_to t('get_involved'), "http://wiki.growstuff.org/index.php/Get_involved"
|
||||
.col-md-4#policies
|
||||
%ul
|
||||
%li= link_to t('terms_of_service'), url_for(:controller => '/policy', :action => 'tos')
|
||||
%li= link_to t('privacy_policy'), url_for(:controller => '/policy', :action => 'privacy')
|
||||
%li= link_to t('data_use_policy'), url_for(:controller => '/policy', :action => 'api')
|
||||
%li= link_to t('community_guidelines'), url_for(:controller => '/policy', :action => 'community')
|
||||
.col-md-4#contact
|
||||
%ul
|
||||
%li= link_to t('support_'), url_for(:controller => '/support')
|
||||
%li= link_to t('contact'), url_for(:controller => '/about', :action => 'contact')
|
||||
%p
|
||||
= link_to('http://twitter.com/growstufforg', :target => "_blank") do
|
||||
= image_tag("twitter_32.png", :alt => 'Twitter: @growstufforg')
|
||||
|
||||
= link_to('https://www.facebook.com/Growstufforg', :target => "_blank") do
|
||||
= image_tag("facebook_32.png", :alt => 'Facebook')
|
||||
|
||||
= link_to('http://blog.growstuff.org/', :target => "_blank") do
|
||||
= image_tag("blog_32.png", :alt => 'Growstuff Blog')
|
||||
.col-md-4#footer1
|
||||
!= cms_snippet_content(:footer1)
|
||||
.col-md-4#footer2
|
||||
!= cms_snippet_content(:footer2)
|
||||
.col-md-4#footer3
|
||||
!= cms_snippet_content(:footer3)
|
||||
|
||||
@@ -65,12 +65,14 @@
|
||||
%li= link_to "Sign out", destroy_member_session_path, :method => :delete
|
||||
|
||||
- else
|
||||
%li= link_to 'Sign in', new_member_session_path
|
||||
%li= link_to 'Sign up', new_member_registration_path
|
||||
%li= link_to 'Sign in', new_member_session_path, :id => 'navbar-signin'
|
||||
%li= link_to 'Sign up', new_member_registration_path, :id => 'navbar-signup'
|
||||
|
||||
= form_tag crops_search_path, :method => :get, :class => 'navbar-form pull-right' do
|
||||
= form_tag crops_search_path, :method => :get, :id => 'navbar-search', :class => 'navbar-form pull-right' do
|
||||
.input
|
||||
= text_field_tag 'search', nil, :class => 'search-query input-medium form-control', :placeholder => 'Search crops'
|
||||
= label_tag :term, "Search crop database:", :class => 'sr-only'
|
||||
= text_field_tag 'term', nil, :class => 'search-query input-medium form-control', :placeholder => 'Search crops'
|
||||
= submit_tag "Search", :class => 'btn sr-only'
|
||||
|
||||
- # anchor tag for accessibility link to skip the navigation menu
|
||||
%a{:name => 'skipnav'}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
= render :partial => "layouts/header"
|
||||
= render :partial => "layouts/crowdfunding"
|
||||
|
||||
.container
|
||||
#maincontainer.container
|
||||
.row
|
||||
.col-md-12
|
||||
- if content_for?(:title)
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
:size => defined?(size) ? size : 150, |
|
||||
:default => :identicon }), |
|
||||
:alt => '', |
|
||||
:class => 'img-rounded img-responsive avatar' ), |
|
||||
:class => 'img img-responsive avatar' ), |
|
||||
member_path(member)
|
||||
|
||||
@@ -19,6 +19,20 @@
|
||||
:growstuff_markdown
|
||||
#{ strip_tags g.description }
|
||||
|
||||
- if g.photos.count > 0 or (can? :edit, g and can? :create, Photo)
|
||||
.row
|
||||
%h2 Photos
|
||||
|
||||
%ul.thumbnails
|
||||
- g.photos.each do |p|
|
||||
.col-md-2.six-across
|
||||
= render :partial => 'photos/thumbnail', :locals => { :photo => p }
|
||||
- if can? :create, Photo and can? :edit, g
|
||||
.col-md-2
|
||||
.thumbnail(style='height: 220px')
|
||||
%p{:style => 'text-align: center; padding-top: 50px'}
|
||||
= link_to "Add photo", new_photo_path(:type => "garden", :id => g.id), :class => 'btn btn-primary'
|
||||
|
||||
%h3 What's planted here?
|
||||
- g.featured_plantings.each do |p|
|
||||
= render :partial => "plantings/thumbnail", :locals => { :planting => p, :hide_description => true }
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
= content_for :title, "#{ENV['GROWSTUFF_SITE_NAME']} members"
|
||||
|
||||
= form_tag(members_path, :method => :get, :class => 'form-inline', :role => 'form') do
|
||||
.form-group
|
||||
= label_tag :sort, "Sort by:", :class => 'sr-only'
|
||||
= select_tag "sort", options_for_select({"Sort alphabetically" => 'alpha', "Sort by recently joined" => "recently_joined"}, @sort || 'alpha'), :class => 'form-control'
|
||||
= submit_tag "Show", :class => 'btn btn-primary'
|
||||
|
||||
%div.pagination
|
||||
= page_entries_info @members, :model => "members"
|
||||
= will_paginate @members
|
||||
|
||||
@@ -5,14 +5,16 @@
|
||||
= link_to 'Edit profile', edit_member_registration_path, :class => 'btn btn-default'
|
||||
- if @member == current_member && !@member.is_paid?
|
||||
= link_to "Upgrade account", shop_path, :class => 'btn btn-default'
|
||||
-if can? :create, Notification and current_member != @member
|
||||
=link_to 'Send message', new_notification_path(:recipient_id => @member.id), :class => 'btn btn-default'
|
||||
- if can? :create, Follow and current_member != @member
|
||||
- if !current_member.already_following?(@member)
|
||||
- if can? :create, Notification and current_member != @member
|
||||
= link_to 'Send message', new_notification_path(:recipient_id => @member.id), :class => 'btn btn-default'
|
||||
|
||||
- if current_member and current_member != @member # must be logged in, can't follow yourself
|
||||
- follow = current_member.get_follow(@member)
|
||||
- if !follow and can? :create, Follow # not already following
|
||||
= link_to 'Follow', follows_path(:followed_id => @member.id), :method => :post, :class => 'btn btn-default'
|
||||
- else
|
||||
- follow = current_member.get_follow(@member)
|
||||
- if follow and can? :destroy, follow # already following
|
||||
= link_to 'Unfollow', follow_path(follow), :method => :delete, :class => 'btn btn-default'
|
||||
|
||||
- content_for :member_rss_login_name, @member.login_name
|
||||
- content_for :member_rss_slug, @member.slug
|
||||
|
||||
|
||||
6
app/views/notifier/_signature.html.haml
Normal file
6
app/views/notifier/_signature.html.haml
Normal file
@@ -0,0 +1,6 @@
|
||||
- site_name = ENV['GROWSTUFF_SITE_NAME']
|
||||
|
||||
%p
|
||||
The #{site_name} team.
|
||||
%br/
|
||||
=link_to root_url, root_url
|
||||
16
app/views/notifier/crop_request_approved.html.haml
Normal file
16
app/views/notifier/crop_request_approved.html.haml
Normal file
@@ -0,0 +1,16 @@
|
||||
- site_name = ENV['GROWSTUFF_SITE_NAME']
|
||||
%p Hello #{@member.login_name},
|
||||
|
||||
%p
|
||||
Your request for the new crop: #{link_to @crop.name, crop_url(@crop)} has been approved! Thank you for helping us make our listing ever more complete!
|
||||
|
||||
%ul
|
||||
%li
|
||||
= link_to "Plant #{@crop.name}", new_planting_url(crop_id: @crop.id)
|
||||
%li
|
||||
= link_to "Harvest #{@crop.name}", new_harvest_url(crop_id: @crop.id)
|
||||
%li
|
||||
= link_to "Stash seeds for #{@crop.name}", new_seed_url(crop_id: @crop.id)
|
||||
|
||||
= render :partial => 'signature'
|
||||
|
||||
7
app/views/notifier/crop_request_rejected.html.haml
Normal file
7
app/views/notifier/crop_request_rejected.html.haml
Normal file
@@ -0,0 +1,7 @@
|
||||
- site_name = ENV['GROWSTUFF_SITE_NAME']
|
||||
%p Hello #{@member.login_name},
|
||||
|
||||
%p
|
||||
We're sorry, but your request for the new crop: #{link_to @crop.name, crop_url(@crop)} has been rejected for the following reason: #{@crop.rejection_explanation}
|
||||
|
||||
= render :partial => 'signature'
|
||||
22
app/views/notifier/new_crop_request.html.haml
Normal file
22
app/views/notifier/new_crop_request.html.haml
Normal file
@@ -0,0 +1,22 @@
|
||||
- site_name = ENV['GROWSTUFF_SITE_NAME']
|
||||
|
||||
%p Hello #{@member.login_name},
|
||||
|
||||
%p
|
||||
#{@request.requester.login_name} has requested a new crop on #{site_name}.
|
||||
|
||||
%ul
|
||||
%li Name: #{@request.name}
|
||||
%li Wikipedia URL: #{@request.en_wikipedia_url.present? ? @request.en_wikipedia_url : "not specified"}
|
||||
%li Notes: #{@request.request_notes}
|
||||
|
||||
%p
|
||||
As a crop wrangler, you can #{link_to "approve or reject this request", edit_crop_url(@request)}.
|
||||
|
||||
%p
|
||||
Or, discuss this and other crop wrangling issues in our #{link_to "crop wrangling forum", "http://talk.growstuff.org/c/crop-wrangling"}.
|
||||
|
||||
%p
|
||||
Thanks for your help!
|
||||
|
||||
= render :partial => 'signature'
|
||||
@@ -20,7 +20,4 @@
|
||||
%br/
|
||||
= link_to "Turn off these notifications", edit_member_registration_url
|
||||
|
||||
%p
|
||||
The #{site_name} team.
|
||||
%br/
|
||||
=link_to root_url, root_url
|
||||
= render :partial => 'signature'
|
||||
@@ -59,10 +59,7 @@
|
||||
%h2
|
||||
See you soon on #{site_name}!
|
||||
|
||||
%p
|
||||
The #{site_name} team.
|
||||
%br/
|
||||
=link_to root_url, root_url
|
||||
= render :partial => 'signature'
|
||||
|
||||
%hr/
|
||||
%p
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
- @photos.each do |p|
|
||||
.col-md-2.six-across
|
||||
.thumbnail(style='height: 220px')
|
||||
= link_to image_tag(p.thumbnail_url, :alt => p.title, :class => 'img-rounded'), p
|
||||
= link_to image_tag(p.thumbnail_url, :alt => p.title, :class => 'img'), p
|
||||
%p
|
||||
= link_to p.title, p
|
||||
by
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
- @photos.each do |p|
|
||||
.col-md-2.six-across
|
||||
.thumbnail(style='height: 220px')
|
||||
= link_to image_tag(FlickRaw.url_q(p), :alt => '', :class => 'img-rounded'), photos_path(:photo => { :flickr_photo_id => p.id }, :type => @type, :id => @id), :method => :post
|
||||
= link_to image_tag(FlickRaw.url_q(p), :alt => '', :class => 'img'), photos_path(:photo => { :flickr_photo_id => p.id }, :type => @type, :id => @id), :method => :post
|
||||
%p
|
||||
=p.title
|
||||
|
||||
|
||||
@@ -27,5 +27,5 @@
|
||||
|
||||
.row
|
||||
.col-md-12
|
||||
%p= image_tag(@photo.fullsize_url, :alt => @photo.title, :class => 'img-rounded')
|
||||
%p= image_tag(@photo.fullsize_url, :alt => @photo.title, :class => 'img')
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
= auto_suggest @planting, :crop, :class => 'form-control', :default => @crop
|
||||
%span.help-inline
|
||||
Can't find what you're looking for?
|
||||
= link_to "Request new crops.", Growstuff::Application.config.new_crops_request_link
|
||||
= link_to "Request new crops.", new_crop_path
|
||||
.form-group
|
||||
= f.label :garden_id, 'Where did you plant it?', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.well
|
||||
.row
|
||||
.col-md-3
|
||||
= link_to image_tag((planting.default_photo ? planting.default_photo.thumbnail_url : 'placeholder_150.png'), :alt => '', :class => 'img-rounded'), planting
|
||||
= link_to image_tag((planting.default_photo ? planting.default_photo.thumbnail_url : 'placeholder_150.png'), :alt => '', :class => 'img'), planting
|
||||
|
||||
.col-md-9
|
||||
%h4
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
= content_for :title, @post.subject
|
||||
|
||||
- unless current_member
|
||||
.alert.alert-info
|
||||
= link_to @post.author.login_name, member_path(@post.author)
|
||||
is using
|
||||
= link_to ENV["GROWSTUFF_SITE_NAME"], root_path
|
||||
to discuss #{ @post.subject } with a community of food gardeners worldwide.
|
||||
We have advice on growing
|
||||
= succeed "," do
|
||||
= link_to 'hundreds of different crops', crops_url
|
||||
and a community from all around the world.
|
||||
|
||||
= render :partial => "shared/signin_signup",
|
||||
:locals => { :to => "or to start using #{ENV["GROWSTUFF_SITE_NAME"]} to track what you're planting and harvesting" }
|
||||
|
||||
= render :partial => "single", :locals => { :post => @post, :subject => false, :hide_comments => true }
|
||||
|
||||
- if can? :edit, @post or can? :destroy, @post
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
%p#notice= notice
|
||||
|
||||
= render :partial => 'crops/approval_status_message', :locals => { :crop => @scientific_name.crop }
|
||||
|
||||
%p
|
||||
%b Scientific name:
|
||||
= @scientific_name.scientific_name
|
||||
|
||||
9
app/views/seeds/_days_until_maturity.html.haml
Normal file
9
app/views/seeds/_days_until_maturity.html.haml
Normal file
@@ -0,0 +1,9 @@
|
||||
- if seed.days_until_maturity_min.blank?
|
||||
- if seed.days_until_maturity_max.blank?
|
||||
unknown
|
||||
- else
|
||||
= seed.days_until_maturity_max.to_s
|
||||
- elsif seed.days_until_maturity_max.blank?
|
||||
= seed.days_until_maturity_min.to_s
|
||||
- else
|
||||
!= "#{seed.days_until_maturity_min}–#{seed.days_until_maturity_max}"
|
||||
@@ -12,7 +12,7 @@
|
||||
= auto_suggest @seed, :crop, :class => 'form-control', :default => @crop
|
||||
%span.help-inline
|
||||
Can't find what you're looking for?
|
||||
= link_to "Request new crops.", Growstuff::Application.config.new_crops_request_link
|
||||
= link_to "Request new crops.", new_crop_path
|
||||
.form-group
|
||||
= f.label :quantity, 'Quantity:', :class => 'control-label col-md-2'
|
||||
.col-md-2
|
||||
@@ -21,6 +21,29 @@
|
||||
= f.label :plant_before, 'Plant before:', :class => 'control-label col-md-2'
|
||||
.col-md-2
|
||||
= f.text_field :plant_before, :class => 'add-datepicker form-control', :value => @seed.plant_before ? @seed.plant_before.to_s(:ymd) : ''
|
||||
.form-group
|
||||
= f.label :days_until_maturity_min, 'Days until maturity:', :class => 'control-label col-md-2'
|
||||
%fieldset
|
||||
.col-md-2
|
||||
= f.number_field :days_until_maturity_min, :class => 'form-control'
|
||||
.col-md-1
|
||||
= f.label :days_until_maturity_max, 'to', :class => 'control-label'
|
||||
.col-md-2
|
||||
= f.number_field :days_until_maturity_max, :class => 'form-control'
|
||||
.col-md-1
|
||||
= f.label :dummy, 'days', :class => 'control-label'
|
||||
.form-group
|
||||
= f.label :organic, 'Organic?', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.select(:organic, Seed::ORGANIC_VALUES, {}, :class => 'form-control', :default => 'unknown')
|
||||
.form-group
|
||||
= f.label :gmo, 'GMO?', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.select(:gmo, Seed::GMO_VALUES, {}, :class => 'form-control', :default => 'unknown')
|
||||
.form-group
|
||||
= f.label :heirloom, 'Heirloom?', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.select(:heirloom, Seed::HEIRLOOM_VALUES, {}, :class => 'form-control', :default => 'unknown')
|
||||
.form-group
|
||||
= f.label :description, 'Description:', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
|
||||
@@ -13,6 +13,18 @@
|
||||
%p
|
||||
%b Plant before:
|
||||
= @seed.plant_before.to_s
|
||||
%p
|
||||
%b Days until maturity:
|
||||
= render :partial => 'days_until_maturity', :locals => { :seed => @seed }
|
||||
%p
|
||||
%b Organic?
|
||||
= @seed.organic
|
||||
%p
|
||||
%b GMO?
|
||||
= @seed.gmo
|
||||
%p
|
||||
%b Heirloom?
|
||||
= @seed.heirloom
|
||||
%p
|
||||
%b Will trade:
|
||||
= @seed.tradable_to
|
||||
|
||||
@@ -62,6 +62,13 @@ GROWSTUFF_PAYPAL_USERNAME: "dummy"
|
||||
GROWSTUFF_PAYPAL_PASSWORD: "dummy"
|
||||
GROWSTUFF_PAYPAL_SIGNATURE: "dummy"
|
||||
|
||||
# Elasticsearch is used for flexible search and it requires another component
|
||||
# to be installed. To make it easy for people who don't need to test this feature
|
||||
# it's been turned off for test and development environment as a default.
|
||||
# If you want to test this functionality, install elasticsearch and
|
||||
# set this flag to "true".
|
||||
GROWSTUFF_ELASTICSEARCH: "false"
|
||||
|
||||
##############################################################################
|
||||
# Other environments
|
||||
# You can override the above for staging, production, etc.
|
||||
@@ -78,6 +85,8 @@ test:
|
||||
|
||||
staging:
|
||||
GROWSTUFF_SITE_NAME: Growstuff (staging)
|
||||
GROWSTUFF_ELASTICSEARCH: "true"
|
||||
|
||||
production:
|
||||
GROWSTUFF_SITE_NAME: Growstuff
|
||||
GROWSTUFF_ELASTICSEARCH: "true"
|
||||
|
||||
@@ -44,7 +44,6 @@ Growstuff::Application.configure do
|
||||
# config.action_view.raise_on_missing_translations = true
|
||||
|
||||
# Growstuff config
|
||||
config.new_crops_request_link = "http://example.com/not-a-real-url"
|
||||
config.action_mailer.default_url_options = { :host => 'localhost:8080' }
|
||||
|
||||
config.action_mailer.delivery_method = :letter_opener
|
||||
|
||||
@@ -67,7 +67,6 @@ Growstuff::Application.configure do
|
||||
config.active_record.dump_schema_after_migration = false
|
||||
|
||||
# Growstuff configuration
|
||||
config.new_crops_request_link = "http://growstuff.org/posts/skud-20130319-requests-for-new-crops"
|
||||
config.action_mailer.default_url_options = { :host => 'growstuff.org' }
|
||||
|
||||
config.action_mailer.smtp_settings = {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
Growstuff::Application.configure do
|
||||
# Settings specified here will take precedence over those in config/application.rb
|
||||
|
||||
config.action_controller.action_on_unpermitted_parameters = :raise
|
||||
|
||||
# Eager load code on boot. This eager loads most of Rails and
|
||||
# your application in memory, allowing both threaded web servers
|
||||
# and those relying on copy on write to perform better.
|
||||
@@ -67,7 +69,6 @@ Growstuff::Application.configure do
|
||||
config.active_record.dump_schema_after_migration = false
|
||||
|
||||
# Growstuff configuration
|
||||
config.new_crops_request_link = "http://example.com/not-a-real-url"
|
||||
config.action_mailer.default_url_options = { :host => 'staging.growstuff.org' }
|
||||
|
||||
config.action_mailer.smtp_settings = {
|
||||
|
||||
@@ -40,7 +40,6 @@ Growstuff::Application.configure do
|
||||
# config.action_view.raise_on_missing_translations = true
|
||||
|
||||
# Growstuff config
|
||||
config.new_crops_request_link = "http://example.com/not-a-real-url"
|
||||
config.action_mailer.default_url_options = { :host => 'localhost:8080' }
|
||||
|
||||
Growstuff::Application.configure do
|
||||
|
||||
103
config/initializers/comfortable_mexican_sofa.rb
Normal file
103
config/initializers/comfortable_mexican_sofa.rb
Normal file
@@ -0,0 +1,103 @@
|
||||
# encoding: utf-8
|
||||
|
||||
ComfortableMexicanSofa.configure do |config|
|
||||
# Title of the admin area
|
||||
# config.cms_title = 'ComfortableMexicanSofa CMS Engine'
|
||||
|
||||
# Controller that is inherited from CmsAdmin::BaseController
|
||||
# config.base_controller = 'ApplicationController'
|
||||
|
||||
# Module responsible for authentication. You can replace it with your own.
|
||||
# It simply needs to have #authenticate method. See http_auth.rb for reference.
|
||||
config.admin_auth = 'CmsDeviseAuth'
|
||||
|
||||
# Module responsible for authorization on admin side. It should have #authorize
|
||||
# method that returns true or false based on params and loaded instance
|
||||
# variables available for a given controller.
|
||||
# config.admin_authorization = 'ComfyAdminAuthorization'
|
||||
|
||||
# Module responsible for public authentication. Similar to the above. You also
|
||||
# will have access to @cms_site, @cms_layout, @cms_page so you can use them in
|
||||
# your logic. Default module doesn't do anything.
|
||||
# config.public_auth = 'ComfyPublicAuthentication'
|
||||
|
||||
# When arriving at /cms-admin you may chose to redirect to arbirtary path,
|
||||
# for example '/cms-admin/users'
|
||||
# config.admin_route_redirect = ''
|
||||
|
||||
# File uploads use Paperclip and can support filesystem or s3 uploads. Override
|
||||
# the upload method and appropriate settings based on Paperclip. For S3 see:
|
||||
# http://rdoc.info/gems/paperclip/2.3.8/Paperclip/Storage/S3, and for
|
||||
# filesystem see: http://rdoc.info/gems/paperclip/2.3.8/Paperclip/Storage/Filesystem
|
||||
# If you are using S3 and HTTPS, pass :s3_protocol => '' to have URLs that use the protocol of the page
|
||||
# config.upload_file_options = {:url => '/system/:class/:id/:attachment/:style/:filename'}
|
||||
|
||||
# Sofa allows you to setup entire site from files. Database is updated with each
|
||||
# request (if necessary). Please note that database entries are destroyed if there's
|
||||
# no corresponding file. Fixtures are disabled by default.
|
||||
# config.enable_fixtures = false
|
||||
|
||||
# Path where fixtures can be located.
|
||||
# config.fixtures_path = File.expand_path('db/cms_fixtures', Rails.root)
|
||||
|
||||
# Importing fixtures into Database
|
||||
# To load fixtures into the database just run this rake task:
|
||||
# local: $ rake comfortable_mexican_sofa:fixtures:import FROM=example.local TO=localhost
|
||||
# Heroku: $ heroku run rake comfortable_mexican_sofa:fixtures:import FROM=example.local TO=yourapp.herokuapp.com
|
||||
# From indicates folder the fixtures are in and to is the Site hostname you have defined in the database.
|
||||
|
||||
# Exporting fixtures into Files
|
||||
# If you need to dump database contents into fixture files run:
|
||||
# local: $ rake comfortable_mexican_sofa:fixtures:export FROM=localhost TO=example.local
|
||||
# Heroku: $ heroku run rake comfortable_mexican_sofa:fixtures:export FROM=yourapp.herokuapp.com TO=example.local
|
||||
# This will create example.local folder and dump all content from example.com Site.
|
||||
|
||||
# Content for Layouts, Pages and Snippets has a revision history. You can revert
|
||||
# a previous version using this system. You can control how many revisions per
|
||||
# object you want to keep. Set it to 0 if you wish to turn this feature off.
|
||||
# config.revisions_limit = 25
|
||||
|
||||
# Locale definitions. If you want to define your own locale merge
|
||||
# {:locale => 'Locale Title'} with this.
|
||||
# config.locales = {:en => 'English', :es => 'Español'}
|
||||
|
||||
# Admin interface will respect the locale of the site being managed. However you can
|
||||
# force it to English by setting this to `:en`
|
||||
# config.admin_locale = nil
|
||||
|
||||
# A class that is included as a sweeper to admin base controller if it's set
|
||||
# config.admin_cache_sweeper = nil
|
||||
|
||||
# By default you cannot have irb code inside your layouts/pages/snippets.
|
||||
# Generally this is to prevent putting something like this:
|
||||
# <% User.delete_all %> but if you really want to allow it...
|
||||
# config.allow_irb = false
|
||||
|
||||
# Whitelist of all helper methods that can be used via {{cms:helper}} tag. By default
|
||||
# all helpers are allowed except `eval`, `send`, `call` and few others. Empty array
|
||||
# will prevent rendering of all helpers.
|
||||
# config.allowed_helpers = nil
|
||||
|
||||
# Whitelist of partials paths that can be used via {{cms:partial}} tag. All partials
|
||||
# are accessible by default. Empty array will prevent rendering of all partials.
|
||||
# config.allowed_partials = nil
|
||||
|
||||
# Site aliases, if you want to have aliases for your site. Good for harmonizing
|
||||
# production env with dev/testing envs.
|
||||
# e.g. config.hostname_aliases = {'host.com' => 'host.inv', 'host_a.com' => ['host.lvh.me', 'host.dev']}
|
||||
# Default is nil (not used)
|
||||
# config.hostname_aliases = nil
|
||||
|
||||
# Reveal partials that can be overwritten in the admin area.
|
||||
# Default is false.
|
||||
# config.reveal_cms_partials = false
|
||||
|
||||
end
|
||||
|
||||
module CmsDeviseAuth
|
||||
def authenticate
|
||||
unless current_member && current_member.has_role?(:admin)
|
||||
redirect_to root_path, :alert => 'Permission denied. Please sign in as an admin user to use the CMS admin area.'
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,6 @@
|
||||
Growstuff::Application.routes.draw do
|
||||
|
||||
|
||||
resources :plant_parts
|
||||
|
||||
devise_for :members, :controllers => { :registrations => "registrations", :passwords => "passwords" }
|
||||
@@ -78,12 +79,14 @@ Growstuff::Application.routes.draw do
|
||||
get '/shop' => 'shop#index'
|
||||
get '/shop/:action' => 'shop#:action'
|
||||
|
||||
comfy_route :cms_admin, :path => '/admin/cms'
|
||||
get '/admin/orders' => 'admin/orders#index'
|
||||
get '/admin/orders/:action' => 'admin/orders#:action'
|
||||
get '/admin' => 'admin#index'
|
||||
get '/admin/newsletter' => 'admin#newsletter', :as => :admin_newsletter
|
||||
get '/admin/:action' => 'admin#:action'
|
||||
|
||||
|
||||
# CMS stuff -- must remain LAST
|
||||
comfy_route :cms, :path => '/', :sitemap => false
|
||||
|
||||
end
|
||||
|
||||
9
db/migrate/20150124110540_add_properties_to_seeds.rb
Normal file
9
db/migrate/20150124110540_add_properties_to_seeds.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
class AddPropertiesToSeeds < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :seeds, :days_until_maturity_min, :integer
|
||||
add_column :seeds, :days_until_maturity_max, :integer
|
||||
add_column :seeds, :organic, :text, :default => 'unknown'
|
||||
add_column :seeds, :gmo, :text, :default => 'unknown'
|
||||
add_column :seeds, :heirloom, :text, :default => 'unknown'
|
||||
end
|
||||
end
|
||||
9
db/migrate/20150127043022_add_gardens_photos_table.rb
Normal file
9
db/migrate/20150127043022_add_gardens_photos_table.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
class AddGardensPhotosTable < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :gardens_photos, :id => false do |t|
|
||||
t.integer :photo_id
|
||||
t.integer :garden_id
|
||||
end
|
||||
add_index(:gardens_photos, [:garden_id, :photo_id])
|
||||
end
|
||||
end
|
||||
5
db/migrate/20150129034206_add_si_weight_to_harvest.rb
Normal file
5
db/migrate/20150129034206_add_si_weight_to_harvest.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class AddSiWeightToHarvest < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :harvests, :si_weight, :float
|
||||
end
|
||||
end
|
||||
6
db/migrate/20150130224814_add_requester_to_crops.rb
Normal file
6
db/migrate/20150130224814_add_requester_to_crops.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
class AddRequesterToCrops < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :crops, :requester_id, :integer
|
||||
add_index :crops, :requester_id
|
||||
end
|
||||
end
|
||||
140
db/migrate/20150201052245_create_cms.rb
Normal file
140
db/migrate/20150201052245_create_cms.rb
Normal file
@@ -0,0 +1,140 @@
|
||||
class CreateCms < ActiveRecord::Migration
|
||||
|
||||
def self.up
|
||||
|
||||
text_limit = case ActiveRecord::Base.connection.adapter_name
|
||||
when 'PostgreSQL'
|
||||
{ }
|
||||
else
|
||||
{ :limit => 16777215 }
|
||||
end
|
||||
|
||||
# -- Sites --------------------------------------------------------------
|
||||
create_table :comfy_cms_sites do |t|
|
||||
t.string :label, :null => false
|
||||
t.string :identifier, :null => false
|
||||
t.string :hostname, :null => false
|
||||
t.string :path
|
||||
t.string :locale, :null => false, :default => 'en'
|
||||
t.boolean :is_mirrored, :null => false, :default => false
|
||||
end
|
||||
add_index :comfy_cms_sites, :hostname
|
||||
add_index :comfy_cms_sites, :is_mirrored
|
||||
|
||||
# -- Layouts ------------------------------------------------------------
|
||||
create_table :comfy_cms_layouts do |t|
|
||||
t.integer :site_id, :null => false
|
||||
t.integer :parent_id
|
||||
t.string :app_layout
|
||||
t.string :label, :null => false
|
||||
t.string :identifier, :null => false
|
||||
t.text :content, text_limit
|
||||
t.text :css, text_limit
|
||||
t.text :js, text_limit
|
||||
t.integer :position, :null => false, :default => 0
|
||||
t.boolean :is_shared, :null => false, :default => false
|
||||
t.timestamps
|
||||
end
|
||||
add_index :comfy_cms_layouts, [:parent_id, :position]
|
||||
add_index :comfy_cms_layouts, [:site_id, :identifier], :unique => true
|
||||
|
||||
# -- Pages --------------------------------------------------------------
|
||||
create_table :comfy_cms_pages do |t|
|
||||
t.integer :site_id, :null => false
|
||||
t.integer :layout_id
|
||||
t.integer :parent_id
|
||||
t.integer :target_page_id
|
||||
t.string :label, :null => false
|
||||
t.string :slug
|
||||
t.string :full_path, :null => false
|
||||
t.text :content_cache, text_limit
|
||||
t.integer :position, :null => false, :default => 0
|
||||
t.integer :children_count, :null => false, :default => 0
|
||||
t.boolean :is_published, :null => false, :default => true
|
||||
t.boolean :is_shared, :null => false, :default => false
|
||||
t.timestamps
|
||||
end
|
||||
add_index :comfy_cms_pages, [:site_id, :full_path]
|
||||
add_index :comfy_cms_pages, [:parent_id, :position]
|
||||
|
||||
# -- Page Blocks --------------------------------------------------------
|
||||
create_table :comfy_cms_blocks do |t|
|
||||
t.string :identifier, :null => false
|
||||
t.text :content, text_limit
|
||||
t.references :blockable, :polymorphic => true
|
||||
t.timestamps
|
||||
end
|
||||
add_index :comfy_cms_blocks, [:identifier]
|
||||
add_index :comfy_cms_blocks, [:blockable_id, :blockable_type]
|
||||
|
||||
# -- Snippets -----------------------------------------------------------
|
||||
create_table :comfy_cms_snippets do |t|
|
||||
t.integer :site_id, :null => false
|
||||
t.string :label, :null => false
|
||||
t.string :identifier, :null => false
|
||||
t.text :content, text_limit
|
||||
t.integer :position, :null => false, :default => 0
|
||||
t.boolean :is_shared, :null => false, :default => false
|
||||
t.timestamps
|
||||
end
|
||||
add_index :comfy_cms_snippets, [:site_id, :identifier], :unique => true
|
||||
add_index :comfy_cms_snippets, [:site_id, :position]
|
||||
|
||||
# -- Files --------------------------------------------------------------
|
||||
create_table :comfy_cms_files do |t|
|
||||
t.integer :site_id, :null => false
|
||||
t.integer :block_id
|
||||
t.string :label, :null => false
|
||||
t.string :file_file_name, :null => false
|
||||
t.string :file_content_type, :null => false
|
||||
t.integer :file_file_size, :null => false
|
||||
t.string :description, :limit => 2048
|
||||
t.integer :position, :null => false, :default => 0
|
||||
t.timestamps
|
||||
end
|
||||
add_index :comfy_cms_files, [:site_id, :label]
|
||||
add_index :comfy_cms_files, [:site_id, :file_file_name]
|
||||
add_index :comfy_cms_files, [:site_id, :position]
|
||||
add_index :comfy_cms_files, [:site_id, :block_id]
|
||||
|
||||
# -- Revisions -----------------------------------------------------------
|
||||
create_table :comfy_cms_revisions, :force => true do |t|
|
||||
t.string :record_type, :null => false
|
||||
t.integer :record_id, :null => false
|
||||
t.text :data, text_limit
|
||||
t.datetime :created_at
|
||||
end
|
||||
add_index :comfy_cms_revisions, [:record_type, :record_id, :created_at],
|
||||
:name => 'index_cms_revisions_on_rtype_and_rid_and_created_at'
|
||||
|
||||
# -- Categories ---------------------------------------------------------
|
||||
create_table :comfy_cms_categories, :force => true do |t|
|
||||
t.integer :site_id, :null => false
|
||||
t.string :label, :null => false
|
||||
t.string :categorized_type, :null => false
|
||||
end
|
||||
add_index :comfy_cms_categories, [:site_id, :categorized_type, :label], :unique => true,
|
||||
:name => 'index_cms_categories_on_site_id_and_cat_type_and_label'
|
||||
|
||||
create_table :comfy_cms_categorizations, :force => true do |t|
|
||||
t.integer :category_id, :null => false
|
||||
t.string :categorized_type, :null => false
|
||||
t.integer :categorized_id, :null => false
|
||||
end
|
||||
add_index :comfy_cms_categorizations, [:category_id, :categorized_type, :categorized_id], :unique => true,
|
||||
:name => 'index_cms_categorizations_on_cat_id_and_catd_type_and_catd_id'
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :comfy_cms_sites
|
||||
drop_table :comfy_cms_layouts
|
||||
drop_table :comfy_cms_pages
|
||||
drop_table :comfy_cms_snippets
|
||||
drop_table :comfy_cms_blocks
|
||||
drop_table :comfy_cms_files
|
||||
drop_table :comfy_cms_revisions
|
||||
drop_table :comfy_cms_categories
|
||||
drop_table :comfy_cms_categorizations
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddApprovalStatusToCrops < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :crops, :approval_status, :string, default: "approved"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddReasonForRejectionToCrops < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :crops, :reason_for_rejection, :text
|
||||
end
|
||||
end
|
||||
5
db/migrate/20150201064502_add_request_notes_to_crops.rb
Normal file
5
db/migrate/20150201064502_add_request_notes_to_crops.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class AddRequestNotesToCrops < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :crops, :request_notes, :text
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddRejectionNotesToCrops < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :crops, :rejection_notes, :text
|
||||
end
|
||||
end
|
||||
151
db/schema.rb
151
db/schema.rb
@@ -11,7 +11,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20141119130555) do
|
||||
ActiveRecord::Schema.define(version: 20150209105410) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
@@ -53,6 +53,125 @@ ActiveRecord::Schema.define(version: 20141119130555) do
|
||||
|
||||
add_index "authentications", ["member_id"], name: "index_authentications_on_member_id", using: :btree
|
||||
|
||||
create_table "comfy_cms_blocks", force: true do |t|
|
||||
t.string "identifier", null: false
|
||||
t.text "content"
|
||||
t.integer "blockable_id"
|
||||
t.string "blockable_type"
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
add_index "comfy_cms_blocks", ["blockable_id", "blockable_type"], name: "index_comfy_cms_blocks_on_blockable_id_and_blockable_type", using: :btree
|
||||
add_index "comfy_cms_blocks", ["identifier"], name: "index_comfy_cms_blocks_on_identifier", using: :btree
|
||||
|
||||
create_table "comfy_cms_categories", force: true do |t|
|
||||
t.integer "site_id", null: false
|
||||
t.string "label", null: false
|
||||
t.string "categorized_type", null: false
|
||||
end
|
||||
|
||||
add_index "comfy_cms_categories", ["site_id", "categorized_type", "label"], name: "index_cms_categories_on_site_id_and_cat_type_and_label", unique: true, using: :btree
|
||||
|
||||
create_table "comfy_cms_categorizations", force: true do |t|
|
||||
t.integer "category_id", null: false
|
||||
t.string "categorized_type", null: false
|
||||
t.integer "categorized_id", null: false
|
||||
end
|
||||
|
||||
add_index "comfy_cms_categorizations", ["category_id", "categorized_type", "categorized_id"], name: "index_cms_categorizations_on_cat_id_and_catd_type_and_catd_id", unique: true, using: :btree
|
||||
|
||||
create_table "comfy_cms_files", force: true do |t|
|
||||
t.integer "site_id", null: false
|
||||
t.integer "block_id"
|
||||
t.string "label", null: false
|
||||
t.string "file_file_name", null: false
|
||||
t.string "file_content_type", null: false
|
||||
t.integer "file_file_size", null: false
|
||||
t.string "description", limit: 2048
|
||||
t.integer "position", default: 0, null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
add_index "comfy_cms_files", ["site_id", "block_id"], name: "index_comfy_cms_files_on_site_id_and_block_id", using: :btree
|
||||
add_index "comfy_cms_files", ["site_id", "file_file_name"], name: "index_comfy_cms_files_on_site_id_and_file_file_name", using: :btree
|
||||
add_index "comfy_cms_files", ["site_id", "label"], name: "index_comfy_cms_files_on_site_id_and_label", using: :btree
|
||||
add_index "comfy_cms_files", ["site_id", "position"], name: "index_comfy_cms_files_on_site_id_and_position", using: :btree
|
||||
|
||||
create_table "comfy_cms_layouts", force: true do |t|
|
||||
t.integer "site_id", null: false
|
||||
t.integer "parent_id"
|
||||
t.string "app_layout"
|
||||
t.string "label", null: false
|
||||
t.string "identifier", null: false
|
||||
t.text "content"
|
||||
t.text "css"
|
||||
t.text "js"
|
||||
t.integer "position", default: 0, null: false
|
||||
t.boolean "is_shared", default: false, null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
add_index "comfy_cms_layouts", ["parent_id", "position"], name: "index_comfy_cms_layouts_on_parent_id_and_position", using: :btree
|
||||
add_index "comfy_cms_layouts", ["site_id", "identifier"], name: "index_comfy_cms_layouts_on_site_id_and_identifier", unique: true, using: :btree
|
||||
|
||||
create_table "comfy_cms_pages", force: true do |t|
|
||||
t.integer "site_id", null: false
|
||||
t.integer "layout_id"
|
||||
t.integer "parent_id"
|
||||
t.integer "target_page_id"
|
||||
t.string "label", null: false
|
||||
t.string "slug"
|
||||
t.string "full_path", null: false
|
||||
t.text "content_cache"
|
||||
t.integer "position", default: 0, null: false
|
||||
t.integer "children_count", default: 0, null: false
|
||||
t.boolean "is_published", default: true, null: false
|
||||
t.boolean "is_shared", default: false, null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
add_index "comfy_cms_pages", ["parent_id", "position"], name: "index_comfy_cms_pages_on_parent_id_and_position", using: :btree
|
||||
add_index "comfy_cms_pages", ["site_id", "full_path"], name: "index_comfy_cms_pages_on_site_id_and_full_path", using: :btree
|
||||
|
||||
create_table "comfy_cms_revisions", force: true do |t|
|
||||
t.string "record_type", null: false
|
||||
t.integer "record_id", null: false
|
||||
t.text "data"
|
||||
t.datetime "created_at"
|
||||
end
|
||||
|
||||
add_index "comfy_cms_revisions", ["record_type", "record_id", "created_at"], name: "index_cms_revisions_on_rtype_and_rid_and_created_at", using: :btree
|
||||
|
||||
create_table "comfy_cms_sites", force: true do |t|
|
||||
t.string "label", null: false
|
||||
t.string "identifier", null: false
|
||||
t.string "hostname", null: false
|
||||
t.string "path"
|
||||
t.string "locale", default: "en", null: false
|
||||
t.boolean "is_mirrored", default: false, null: false
|
||||
end
|
||||
|
||||
add_index "comfy_cms_sites", ["hostname"], name: "index_comfy_cms_sites_on_hostname", using: :btree
|
||||
add_index "comfy_cms_sites", ["is_mirrored"], name: "index_comfy_cms_sites_on_is_mirrored", using: :btree
|
||||
|
||||
create_table "comfy_cms_snippets", force: true do |t|
|
||||
t.integer "site_id", null: false
|
||||
t.string "label", null: false
|
||||
t.string "identifier", null: false
|
||||
t.text "content"
|
||||
t.integer "position", default: 0, null: false
|
||||
t.boolean "is_shared", default: false, null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
add_index "comfy_cms_snippets", ["site_id", "identifier"], name: "index_comfy_cms_snippets_on_site_id_and_identifier", unique: true, using: :btree
|
||||
add_index "comfy_cms_snippets", ["site_id", "position"], name: "index_comfy_cms_snippets_on_site_id_and_position", using: :btree
|
||||
|
||||
create_table "comments", force: true do |t|
|
||||
t.integer "post_id", null: false
|
||||
t.integer "author_id", null: false
|
||||
@@ -62,17 +181,23 @@ ActiveRecord::Schema.define(version: 20141119130555) do
|
||||
end
|
||||
|
||||
create_table "crops", force: true do |t|
|
||||
t.string "name", null: false
|
||||
t.string "name", null: false
|
||||
t.string "en_wikipedia_url"
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "slug"
|
||||
t.integer "parent_id"
|
||||
t.integer "plantings_count", default: 0
|
||||
t.integer "plantings_count", default: 0
|
||||
t.integer "creator_id"
|
||||
t.integer "requester_id"
|
||||
t.string "approval_status", default: "approved"
|
||||
t.text "reason_for_rejection"
|
||||
t.text "request_notes"
|
||||
t.text "rejection_notes"
|
||||
end
|
||||
|
||||
add_index "crops", ["name"], name: "index_crops_on_name", using: :btree
|
||||
add_index "crops", ["requester_id"], name: "index_crops_on_requester_id", using: :btree
|
||||
add_index "crops", ["slug"], name: "index_crops_on_slug", unique: true, using: :btree
|
||||
|
||||
create_table "crops_posts", id: false, force: true do |t|
|
||||
@@ -119,6 +244,13 @@ ActiveRecord::Schema.define(version: 20141119130555) do
|
||||
add_index "gardens", ["owner_id"], name: "index_gardens_on_owner_id", using: :btree
|
||||
add_index "gardens", ["slug"], name: "index_gardens_on_slug", unique: true, using: :btree
|
||||
|
||||
create_table "gardens_photos", id: false, force: true do |t|
|
||||
t.integer "photo_id"
|
||||
t.integer "garden_id"
|
||||
end
|
||||
|
||||
add_index "gardens_photos", ["garden_id", "photo_id"], name: "index_gardens_photos_on_garden_id_and_photo_id", using: :btree
|
||||
|
||||
create_table "harvests", force: true do |t|
|
||||
t.integer "crop_id", null: false
|
||||
t.integer "owner_id", null: false
|
||||
@@ -132,6 +264,7 @@ ActiveRecord::Schema.define(version: 20141119130555) do
|
||||
t.decimal "weight_quantity"
|
||||
t.string "weight_unit"
|
||||
t.integer "plant_part_id"
|
||||
t.float "si_weight"
|
||||
end
|
||||
|
||||
create_table "harvests_photos", id: false, force: true do |t|
|
||||
@@ -272,6 +405,7 @@ ActiveRecord::Schema.define(version: 20141119130555) do
|
||||
t.datetime "updated_at"
|
||||
t.string "slug"
|
||||
t.integer "forum_id"
|
||||
t.integer "parent_id"
|
||||
end
|
||||
|
||||
add_index "posts", ["created_at", "author_id"], name: "index_posts_on_created_at_and_author_id", using: :btree
|
||||
@@ -307,15 +441,20 @@ ActiveRecord::Schema.define(version: 20141119130555) do
|
||||
end
|
||||
|
||||
create_table "seeds", force: true do |t|
|
||||
t.integer "owner_id", null: false
|
||||
t.integer "crop_id", null: false
|
||||
t.integer "owner_id", null: false
|
||||
t.integer "crop_id", null: false
|
||||
t.text "description"
|
||||
t.integer "quantity"
|
||||
t.date "plant_before"
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "tradable_to", default: "nowhere"
|
||||
t.string "tradable_to", default: "nowhere"
|
||||
t.string "slug"
|
||||
t.integer "days_until_maturity_min"
|
||||
t.integer "days_until_maturity_max"
|
||||
t.text "organic", default: "unknown"
|
||||
t.text "gmo", default: "unknown"
|
||||
t.text "heirloom", default: "unknown"
|
||||
end
|
||||
|
||||
add_index "seeds", ["slug"], name: "index_seeds_on_slug", unique: true, using: :btree
|
||||
|
||||
@@ -314,6 +314,20 @@ namespace :growstuff do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc "January 2015: fill in si_weight column"
|
||||
task :populate_si_weight => :environment do
|
||||
Harvest.find_each do |h|
|
||||
h.set_si_weight
|
||||
h.save
|
||||
end
|
||||
end
|
||||
|
||||
desc "January 2015: build Elasticsearch index"
|
||||
task :elasticsearch_create_index => :environment do
|
||||
Crop.__elasticsearch__.create_index! force: true
|
||||
Crop.import
|
||||
end
|
||||
end # end oneoff section
|
||||
|
||||
end
|
||||
|
||||
@@ -18,3 +18,9 @@ rake growstuff:import_crops file=db/seeds/crops-15-squashes.csv
|
||||
|
||||
echo "2014-12-01 - load alternate names for crops"
|
||||
rake growstuff:oneoff:add_alternate_names
|
||||
|
||||
echo "2015-01-28 - populate the harvest si_weight field"
|
||||
rake growstuff:oneoff:populate_si_weight
|
||||
|
||||
echo "2015-01-30 - build Elasticsearch index"
|
||||
rake growstuff:oneoff:elasticsearch_create_index
|
||||
|
||||
25
script/heroku_maintenance.rb
Executable file
25
script/heroku_maintenance.rb
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'heroku-api'
|
||||
require 'yaml'
|
||||
|
||||
heroku = Heroku::API.new(:api_key => ENV['HEROKU_API_KEY'])
|
||||
branch = ENV['TRAVIS_BRANCH']
|
||||
travis_config = YAML.load_file('.travis.yml')
|
||||
if travis_config['deploy']['app'].has_key? branch
|
||||
app = travis_config['deploy']['app'][branch]
|
||||
else
|
||||
abort "No Heroku app found for branch #{branch}"
|
||||
end
|
||||
|
||||
case ARGV[0]
|
||||
when "on"
|
||||
maintenance_state = 1
|
||||
when "off"
|
||||
maintenance_state = 0
|
||||
else
|
||||
abort "usage: #{$0} (on|off)"
|
||||
end
|
||||
|
||||
puts "Turning #{maintenance_state} maintenance mode on app #{app}"
|
||||
heroku.post_app_maintenance(app, maintenance_state)
|
||||
@@ -7,7 +7,8 @@ describe CropsController do
|
||||
def valid_attributes
|
||||
{
|
||||
:name => "Tomato",
|
||||
:en_wikipedia_url => 'http://en.wikipedia.org/wiki/Tomato'
|
||||
:en_wikipedia_url => 'http://en.wikipedia.org/wiki/Tomato',
|
||||
:approval_status => 'approved'
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -37,6 +37,11 @@ describe HarvestsController do
|
||||
assigns(:crop).should eq @maize
|
||||
assigns(:harvests).should eq [@harvest2]
|
||||
end
|
||||
|
||||
it "generates a csv" do
|
||||
get :index, {:format => "csv"}
|
||||
response.status.should eq 200
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET show" do
|
||||
|
||||
@@ -48,6 +48,12 @@ describe PhotosController do
|
||||
assigns(:type).should eq "harvest"
|
||||
end
|
||||
|
||||
it "assigns a garden id" do
|
||||
get :new, { :type => "garden", :id => 5 }
|
||||
assigns(:id).should eq "5"
|
||||
assigns(:type).should eq "garden"
|
||||
end
|
||||
|
||||
it "assigns the current set as @current_set" do
|
||||
get :new, { :set => 'foo' }
|
||||
assigns(:current_set).should eq "foo"
|
||||
|
||||
@@ -3,6 +3,7 @@ FactoryGirl.define do
|
||||
factory :crop do
|
||||
name "magic bean"
|
||||
en_wikipedia_url "http://en.wikipedia.org/wiki/Magic_bean"
|
||||
approval_status "approved"
|
||||
creator
|
||||
|
||||
factory :tomato do
|
||||
@@ -54,6 +55,21 @@ FactoryGirl.define do
|
||||
name "Swiss chard"
|
||||
end
|
||||
|
||||
#for testing crop request
|
||||
factory :crop_request do
|
||||
name "Ultra berry"
|
||||
en_wikipedia_url ""
|
||||
approval_status "pending"
|
||||
association :requester, factory: :member
|
||||
request_notes "Please approve this even though it's fake."
|
||||
end
|
||||
|
||||
factory :rejected_crop do
|
||||
name "Fail bean"
|
||||
approval_status "rejected"
|
||||
reason_for_rejection "Totally fake"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -8,6 +8,11 @@ FactoryGirl.define do
|
||||
quantity 1
|
||||
plant_before "2013-07-15"
|
||||
tradable_to 'nowhere'
|
||||
organic 'unknown'
|
||||
gmo 'unknown'
|
||||
heirloom 'unknown'
|
||||
days_until_maturity_min nil
|
||||
days_until_maturity_max nil
|
||||
|
||||
factory :tradable_seed do
|
||||
tradable_to "locally"
|
||||
|
||||
42
spec/features/admin/products.rb
Normal file
42
spec/features/admin/products.rb
Normal file
@@ -0,0 +1,42 @@
|
||||
require 'rails_helper'
|
||||
|
||||
feature "products" do
|
||||
context "admin user" do
|
||||
let(:member) { FactoryGirl.create(:admin_member) }
|
||||
background do
|
||||
login_as(member)
|
||||
end
|
||||
|
||||
scenario "navigating to product admin" do
|
||||
visit admin_path
|
||||
click_link "Products"
|
||||
current_path.should eq products_path
|
||||
end
|
||||
|
||||
scenario "adding a product" do
|
||||
visit products_path
|
||||
click_link "New Product"
|
||||
current_path.should eq new_product_path
|
||||
fill_in 'Name', :with => 'Special offer'
|
||||
# note that failing to fill in a mandatory field has a messy error. This is not a priority defect but should be raised at some point.
|
||||
fill_in 'Minimum price', :with => '150'
|
||||
click_button 'Save'
|
||||
current_path.should eq product_path(Product.last)
|
||||
page.should have_content 'Product was successfully created'
|
||||
end
|
||||
|
||||
scenario 'editing product' do
|
||||
p = FactoryGirl.create(:product)
|
||||
visit product_path(p)
|
||||
click_link 'Edit'
|
||||
fill_in 'Name', :with => 'Something else'
|
||||
click_button 'Save'
|
||||
current_path.should eq product_path(p)
|
||||
page.should have_content 'Product was successfully updated'
|
||||
page.should have_content 'Something else'
|
||||
end
|
||||
|
||||
scenario 'deleting product'
|
||||
# this isn't possible. Should it be?
|
||||
end
|
||||
end
|
||||
37
spec/features/cms_spec.rb
Normal file
37
spec/features/cms_spec.rb
Normal file
@@ -0,0 +1,37 @@
|
||||
require 'spec_helper'
|
||||
|
||||
feature "cms admin" do
|
||||
before(:each) do
|
||||
@member = FactoryGirl.create(:member)
|
||||
@admin_member = FactoryGirl.create(:admin_member)
|
||||
end
|
||||
|
||||
scenario "can't view CMS admin if not signed in" do
|
||||
visit comfy_admin_cms_path
|
||||
current_path.should == root_path
|
||||
page.should have_content("Please sign in as an admin user")
|
||||
end
|
||||
|
||||
scenario "can't view CMS admin if not an admin member" do
|
||||
# sign in as an ordinary member
|
||||
visit root_path
|
||||
click_link 'navbar-signin'
|
||||
fill_in 'Login', :with => @member.email
|
||||
fill_in 'Password', :with => @member.password
|
||||
click_button 'Sign in'
|
||||
visit comfy_admin_cms_path
|
||||
current_path.should == root_path
|
||||
page.should have_content("Please sign in as an admin user")
|
||||
end
|
||||
|
||||
scenario "admin members can view CMS admin area" do
|
||||
visit root_path
|
||||
# now we sign in as an admin member
|
||||
click_link 'navbar-signin'
|
||||
fill_in 'Login', :with => @admin_member.email
|
||||
fill_in 'Password', :with => @admin_member.password
|
||||
click_button 'Sign in'
|
||||
visit comfy_admin_cms_path
|
||||
current_path.should match /#{comfy_admin_cms_path}/ # match any CMS admin page
|
||||
end
|
||||
end
|
||||
@@ -1,24 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
feature "crop wranglers" do
|
||||
context "signed in member" do
|
||||
let!(:crop_wranglers) { FactoryGirl.create_list(:crop_wrangling_member, 3) }
|
||||
let(:member){crop_wranglers.first}
|
||||
|
||||
background do
|
||||
login_as(member)
|
||||
end
|
||||
|
||||
scenario "crop wranglers are listed on the crop wrangler page" do
|
||||
visit root_path
|
||||
click_link 'Crop Wrangling'
|
||||
|
||||
within '.crop_wranglers' do
|
||||
expect(page).to have_content 'Crop Wranglers:'
|
||||
crop_wranglers.each do |crop_wrangler|
|
||||
page.should have_link crop_wrangler.login_name, :href => member_path(crop_wrangler)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -69,6 +69,18 @@ feature "Alternate names" do
|
||||
expect(page).to have_content alternate_eggplant.crop.name
|
||||
end
|
||||
|
||||
context "When alternate name is rejected" do
|
||||
let(:rejected_crop) { FactoryGirl.create(:rejected_crop) }
|
||||
let(:pending_alt_name) { FactoryGirl.create(:alternate_name, :crop => rejected_crop) }
|
||||
|
||||
scenario "Displays crop rejection message" do
|
||||
visit alternate_name_path(pending_alt_name)
|
||||
expect(page).to have_content "This crop was rejected for the following reason: Totally fake"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
30
spec/features/crops/browse_crops_spec.rb
Normal file
30
spec/features/crops/browse_crops_spec.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
require 'rails_helper'
|
||||
|
||||
feature "browse crops" do
|
||||
|
||||
let(:tomato) { FactoryGirl.create(:tomato) }
|
||||
let(:maize) { FactoryGirl.create(:maize) }
|
||||
let(:pending_crop) { FactoryGirl.create(:crop_request) }
|
||||
let(:rejected_crop) { FactoryGirl.create(:rejected_crop) }
|
||||
|
||||
scenario "has a form for sorting by" do
|
||||
visit crops_path
|
||||
expect(page).to have_css "select#sort"
|
||||
end
|
||||
|
||||
scenario "shows a list of crops" do
|
||||
crop1 = tomato
|
||||
visit crops_path
|
||||
expect(page).to have_content crop1.name
|
||||
end
|
||||
|
||||
scenario "pending crops are not listed" do
|
||||
visit crops_path
|
||||
expect(page).not_to have_content pending_crop.name
|
||||
end
|
||||
|
||||
scenario "rejected crops are not listed" do
|
||||
visit crops_path
|
||||
expect(page).not_to have_content rejected_crop.name
|
||||
end
|
||||
end
|
||||
22
spec/features/crops/crop_search_spec.rb
Normal file
22
spec/features/crops/crop_search_spec.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
require 'rails_helper'
|
||||
|
||||
feature "crop search" do
|
||||
scenario "search results show the search term in title" do
|
||||
visit root_path
|
||||
within "form#navbar-search" do
|
||||
fill_in "term", with: "tomato"
|
||||
click_button "Search"
|
||||
end
|
||||
expect(page).to have_css "h1", text: "Crops matching \"tomato\""
|
||||
end
|
||||
|
||||
scenario "search page with no search term shows suitable title" do
|
||||
visit crops_search_path
|
||||
expect(page).to have_css "h1", text: "Crop search"
|
||||
end
|
||||
|
||||
scenario "search page has a search form on it" do
|
||||
visit crops_search_path
|
||||
expect(page).to have_css "form#crop-search"
|
||||
end
|
||||
end
|
||||
82
spec/features/crops/crop_wranglers_spec.rb
Normal file
82
spec/features/crops/crop_wranglers_spec.rb
Normal file
@@ -0,0 +1,82 @@
|
||||
require 'rails_helper'
|
||||
|
||||
feature "crop wranglers" do
|
||||
context "signed in wrangler" do
|
||||
let!(:crop_wranglers) { FactoryGirl.create_list(:crop_wrangling_member, 3) }
|
||||
let(:wrangler){crop_wranglers.first}
|
||||
let!(:crops) { FactoryGirl.create_list(:crop, 2) }
|
||||
let!(:requested_crop) { FactoryGirl.create(:crop_request) }
|
||||
let!(:rejected_crop) { FactoryGirl.create(:rejected_crop) }
|
||||
|
||||
background do
|
||||
login_as(wrangler)
|
||||
end
|
||||
|
||||
scenario "sees crop wranglers listed on the crop wrangler page" do
|
||||
visit root_path
|
||||
click_link 'Crop Wrangling'
|
||||
|
||||
within '.crop_wranglers' do
|
||||
expect(page).to have_content 'Crop Wranglers:'
|
||||
crop_wranglers.each do |crop_wrangler|
|
||||
page.should have_link crop_wrangler.login_name, :href => member_path(crop_wrangler)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
scenario "can see list of crops with extra detail of who created a crop" do
|
||||
visit root_path
|
||||
click_link 'Crop Wrangling'
|
||||
within '#recently-added-crops' do
|
||||
expect(page).to have_content "#{crops.first.creator.login_name}"
|
||||
end
|
||||
end
|
||||
|
||||
scenario "visiting a crop can see wrangler links" do
|
||||
visit crop_path(crops.first)
|
||||
expect(page).to have_content 'You are a CROP WRANGLER'
|
||||
expect(page).to have_link 'Edit crop'
|
||||
expect(page).to have_link 'Delete crop'
|
||||
end
|
||||
|
||||
scenario "can create a new crop" do
|
||||
visit root_path
|
||||
click_link 'Crop Wrangling'
|
||||
click_link 'Add Crop'
|
||||
fill_in 'Name', with: "aubergine"
|
||||
fill_in 'Wikipedia URL', with: "http://en.wikipedia.org/wiki/Maize"
|
||||
fill_in 'crop_scientific_names_attributes_0_scientific_name', with: "planticus maximus"
|
||||
click_on 'Save'
|
||||
expect(page).to have_content 'Crop was successfully created'
|
||||
expect(page).to have_content 'planticus maximus'
|
||||
end
|
||||
|
||||
scenario "View pending crops" do
|
||||
visit crop_path(requested_crop)
|
||||
expect(page).to have_content "This crop is currently pending approval."
|
||||
expect(page).to have_content "Please approve this even though it's fake."
|
||||
end
|
||||
|
||||
scenario "View rejected crops" do
|
||||
visit crop_path(rejected_crop)
|
||||
expect(page).to have_content "This crop was rejected for the following reason: Totally fake"
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
context "signed in non-wrangler" do
|
||||
let!(:crop_wranglers) { FactoryGirl.create_list(:crop_wrangling_member, 3) }
|
||||
let(:member) { FactoryGirl.create(:member) }
|
||||
|
||||
background do
|
||||
login_as(member)
|
||||
end
|
||||
|
||||
scenario "can't see wrangling page" do
|
||||
visit root_path
|
||||
expect(page).not_to have_link "Crop Wrangling"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user