mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-05-18 21:56:55 -04:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -1 +1 @@
|
||||
2.1.1
|
||||
2.1.2
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
---
|
||||
language: ruby
|
||||
env: GROWSTUFF_SITE_NAME="Growstuff (travis)"
|
||||
bundler_args: --without development assets production staging
|
||||
|
||||
env: GROWSTUFF_SITE_NAME="Growstuff (travis)" RAILS_SECRET_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
|
||||
bundler_args: --without development production staging
|
||||
rvm:
|
||||
- 2.1.1
|
||||
- 2.1.2
|
||||
before_script:
|
||||
- psql -c 'create database growstuff_test;' -U postgres
|
||||
script:
|
||||
|
||||
@@ -1,35 +1,13 @@
|
||||
Thanks for contributing to Growstuff! We have different contribution
|
||||
guidelines depending on whether your change is a small one (a one-line
|
||||
bugfix or similar) or a larger one.
|
||||
Thanks for contributing to Growstuff!
|
||||
|
||||
## Small changes
|
||||
Please include the following information in your pull request:
|
||||
|
||||
Send us a pull request! We will get one of our pairs of coders to
|
||||
review it.
|
||||
|
||||
If you are interested in becoming a more regular contributor or working
|
||||
on larger features, please see the [Growstuff
|
||||
wiki](http://wiki.growstuff.org/) for more information.
|
||||
|
||||
## Larger changes
|
||||
|
||||
Growstuff does pair programming (two coders working together) for all
|
||||
new features and other significant changes. This means that if you
|
||||
submit a pull request and weren't working in a pair, we're unlikely to
|
||||
merge it into the project as-is.
|
||||
|
||||
If you would like to work on any larger change, we would appreciate it
|
||||
if you would get in touch with us, preferably via our [mailing
|
||||
list](http://lists.growstuff.org/mailman/listinfo/discuss), and talk to
|
||||
us about it first. We'll try and hook you up with a partner so you can
|
||||
work as a pair, either in person or remotely depending on where you are.
|
||||
The [Growstuff wiki](http://wiki.growstuff.org/) has lots more
|
||||
information on our dev process, to get you started if you would like to
|
||||
join us.
|
||||
|
||||
If you submit a larger change without working in a pair, we will treat
|
||||
your work as an experimental "spike" and get one of our pairs of
|
||||
programmers to look over it and maybe use what you've done as the basis
|
||||
for re-implementing it using our processes. **We'd much rather work
|
||||
with you, so please talk to us first!**
|
||||
* A link to the [Pivotal Tracker](http://tracker.growstuff.org/) task to which it relates.
|
||||
* Your code should follow our [Coding style guide](http://wiki.growstuff.org/index.php/Coding_style_guide)
|
||||
* Make sure you have automated tests for your work, where possible.
|
||||
* Add your name (and that of your pair partner, if any) to [CONTRIBUTORS.md](CONTRIBUTORS.md).
|
||||
|
||||
If you would like to discuss your work before submitting a pull request,
|
||||
please join any of our [Discussion
|
||||
forums](http://wiki.growstuff.org/index.php/Discussion_forums), where
|
||||
our dev team will be happy to help you.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
This is a list of contributors to Growstuff's codebase. We maintain
|
||||
this list because we work in pairs, but Github only knows about the
|
||||
this list because we often work in pairs, but Github only knows about the
|
||||
person who actually does the commits. This gives credit to both members
|
||||
of the pair.
|
||||
|
||||
@@ -42,3 +42,10 @@ submit the change with your pull request.
|
||||
- Amelia Greenhall / [ameliagreenhall](https://github.com/ameliagreenhall)
|
||||
- Barb Natali / [barbnatali](https://github.com/barbnatali)
|
||||
- Taylor Griffin / [tygriffin](https://github.com/tygriffin)
|
||||
- Marlena Compton / [Marlena](https://github.com/marlena)
|
||||
- Elizabeth A. Kari / [catfriend](https://github.com/catfriend)
|
||||
- Cheri Allen / [cherimarie](https://github.com/cherimarie)
|
||||
- Maki Sugita / [macckii](https:://github.com/macckii)
|
||||
- Shiho Takagi / [oshiho3](https://github.com/oshiho3)
|
||||
- Emma Winston / [emmawinston](https://github.com/emmawinston)
|
||||
- Kevin Rio / [krio](https://github.com/krio)
|
||||
|
||||
28
Gemfile
28
Gemfile
@@ -1,6 +1,6 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
ruby "2.1.1"
|
||||
ruby "2.1.2"
|
||||
|
||||
gem 'bundler', '>=1.1.5'
|
||||
|
||||
@@ -54,12 +54,10 @@ group :assets do
|
||||
gem 'libv8', '3.16.14.3'
|
||||
|
||||
# Another CSS preprocessor, used for Bootstrap overrides
|
||||
gem "less", '~>2.3.2'
|
||||
gem "less-rails", '~> 2.3.3'
|
||||
gem "less", '~>2.5.0'
|
||||
gem "less-rails", '~> 2.5.0'
|
||||
# CSS framework
|
||||
gem "twitter-bootstrap-rails",
|
||||
:git => 'https://github.com/seyhunak/twitter-bootstrap-rails.git',
|
||||
:ref => '2c7c52'
|
||||
gem "less-rails-bootstrap", '~> 3.2.0'
|
||||
|
||||
gem 'uglifier', '>= 1.0.3' # JavaScript compressor
|
||||
|
||||
@@ -67,6 +65,8 @@ group :assets do
|
||||
end
|
||||
|
||||
gem 'jquery-rails'
|
||||
gem 'jquery-ui-rails'
|
||||
gem 'js-routes' # provides access to Rails routes in Javascript
|
||||
gem 'flickraw'
|
||||
|
||||
# To use ActiveModel has_secure_password
|
||||
@@ -78,11 +78,11 @@ gem 'flickraw'
|
||||
# Use unicorn as the app server
|
||||
# gem 'unicorn'
|
||||
|
||||
# To use debugger
|
||||
group :development do
|
||||
# Installation of the debugger gem fails on Travis CI,
|
||||
# so we don't use it in the test environment
|
||||
gem 'debugger'
|
||||
# A debugger and irb alternative. Pry doesn't play nice
|
||||
# with unicorn, so start a Webrick server when debugging
|
||||
# with Pry
|
||||
gem 'pry'
|
||||
gem 'better_errors'
|
||||
gem 'binding_of_caller'
|
||||
gem 'letter_opener'
|
||||
@@ -95,7 +95,7 @@ gem 'bluecloth'
|
||||
gem 'will_paginate', '~> 3.0'
|
||||
|
||||
# user signup/login/etc
|
||||
gem 'devise', '~> 3.0.0'
|
||||
gem 'devise', '~> 3.2.0'
|
||||
|
||||
# nicely formatted URLs
|
||||
gem 'friendly_id', '~> 4.0.10'
|
||||
@@ -119,9 +119,15 @@ gem 'omniauth-flickr', '>= 0.0.15'
|
||||
gem 'rake', '>= 10.0.0'
|
||||
|
||||
group :development, :test do
|
||||
gem 'byebug' # debugging
|
||||
gem 'haml-rails' # HTML templating language
|
||||
gem 'rspec-rails', '~> 2.12.1' # unit testing framework
|
||||
gem 'database_cleaner', '~> 1.3.0'
|
||||
gem 'webrat' # provides HTML matchers for view tests
|
||||
gem 'factory_girl_rails', '~> 4.0' # for creating test data
|
||||
gem 'coveralls', require: false # coverage analysis
|
||||
gem 'capybara' # integration tests
|
||||
gem 'capybara-email' # integration tests for email
|
||||
gem 'poltergeist', '~> 1.5.1' # for headless JS testing
|
||||
gem 'i18n-tasks' # adds tests for finding missing and unused translations
|
||||
end
|
||||
|
||||
190
Gemfile.lock
190
Gemfile.lock
@@ -5,17 +5,6 @@ GIT
|
||||
specs:
|
||||
geocoder (1.1.8)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/seyhunak/twitter-bootstrap-rails.git
|
||||
revision: 2c7c527c354d9068ce49346d4fd8389328d32ce6
|
||||
ref: 2c7c52
|
||||
specs:
|
||||
twitter-bootstrap-rails (2.2.7)
|
||||
actionpack (>= 3.1)
|
||||
execjs
|
||||
rails (>= 3.1)
|
||||
railties (>= 3.1)
|
||||
|
||||
PATH
|
||||
remote: vendor/gems/active_utils-1.0.5
|
||||
specs:
|
||||
@@ -60,74 +49,87 @@ GEM
|
||||
multi_json (~> 1.0)
|
||||
addressable (2.3.6)
|
||||
arel (3.0.3)
|
||||
bcrypt (3.1.7)
|
||||
bcrypt-ruby (3.1.5)
|
||||
bcrypt (>= 3.1.3)
|
||||
better_errors (1.1.0)
|
||||
bcrypt (3.1.9)
|
||||
better_errors (2.0.0)
|
||||
coderay (>= 1.0.0)
|
||||
erubis (>= 2.6.6)
|
||||
rack (>= 0.9.0)
|
||||
binding_of_caller (0.7.2)
|
||||
debug_inspector (>= 0.0.1)
|
||||
bluecloth (2.2.0)
|
||||
bootstrap-datepicker-rails (1.3.0.2)
|
||||
railties (>= 3.0)
|
||||
builder (3.0.4)
|
||||
byebug (3.5.1)
|
||||
columnize (~> 0.8)
|
||||
debugger-linecache (~> 1.2)
|
||||
slop (~> 3.6)
|
||||
cancan (1.6.10)
|
||||
chunky_png (1.3.1)
|
||||
capybara (2.4.4)
|
||||
mime-types (>= 1.16)
|
||||
nokogiri (>= 1.3.3)
|
||||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
xpath (~> 2.0)
|
||||
capybara-email (2.4.0)
|
||||
capybara (~> 2.4)
|
||||
mail
|
||||
chunky_png (1.3.3)
|
||||
cliver (0.3.2)
|
||||
coderay (1.1.0)
|
||||
coffee-rails (3.2.2)
|
||||
coffee-script (>= 2.2.0)
|
||||
railties (~> 3.2.0)
|
||||
coffee-script (2.2.0)
|
||||
coffee-script (2.3.0)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.7.0)
|
||||
coffee-script-source (1.8.0)
|
||||
columnize (0.8.9)
|
||||
commonjs (0.2.7)
|
||||
compass (0.12.6)
|
||||
compass (0.12.7)
|
||||
chunky_png (~> 1.2)
|
||||
fssm (>= 0.2.7)
|
||||
sass (~> 3.2.19)
|
||||
compass-rails (1.0.3)
|
||||
compass (>= 0.12.2, < 0.14)
|
||||
coveralls (0.7.0)
|
||||
coveralls (0.7.1)
|
||||
multi_json (~> 1.3)
|
||||
rest-client
|
||||
simplecov (>= 0.7)
|
||||
term-ansicolor
|
||||
thor
|
||||
csv_shaper (1.0.0)
|
||||
csv_shaper (1.1.1)
|
||||
activesupport (>= 3.0.0)
|
||||
dalli (2.7.0)
|
||||
dalli (2.7.2)
|
||||
database_cleaner (1.3.0)
|
||||
debug_inspector (0.0.2)
|
||||
debugger (1.6.6)
|
||||
columnize (>= 0.3.1)
|
||||
debugger-linecache (~> 1.2.0)
|
||||
debugger-ruby_core_source (~> 1.3.2)
|
||||
debugger-linecache (1.2.0)
|
||||
debugger-ruby_core_source (1.3.2)
|
||||
devise (3.0.4)
|
||||
bcrypt-ruby (~> 3.0)
|
||||
devise (3.2.4)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 3.2.6, < 5)
|
||||
thread_safe (~> 0.1)
|
||||
warden (~> 1.2.3)
|
||||
diff-lcs (1.1.3)
|
||||
docile (1.1.3)
|
||||
docile (1.1.5)
|
||||
easy_translate (0.5.0)
|
||||
json
|
||||
thread
|
||||
thread_safe
|
||||
erubis (2.7.0)
|
||||
execjs (2.0.2)
|
||||
factory_girl (4.4.0)
|
||||
execjs (2.2.2)
|
||||
factory_girl (4.5.0)
|
||||
activesupport (>= 3.0.0)
|
||||
factory_girl_rails (4.4.1)
|
||||
factory_girl (~> 4.4.0)
|
||||
factory_girl_rails (4.5.0)
|
||||
factory_girl (~> 4.5.0)
|
||||
railties (>= 3.0.0)
|
||||
figaro (0.7.0)
|
||||
bundler (~> 1.0)
|
||||
rails (>= 3, < 5)
|
||||
figaro (1.0.0)
|
||||
thor (~> 0.14)
|
||||
flickraw (0.9.8)
|
||||
friendly_id (4.0.10.1)
|
||||
activerecord (>= 3.0, < 4.0)
|
||||
fssm (0.2.10)
|
||||
gibbon (1.1.2)
|
||||
gibbon (1.1.4)
|
||||
httparty
|
||||
multi_json (>= 1.3.4)
|
||||
gravatar-ultimate (2.0.0)
|
||||
@@ -140,28 +142,45 @@ GEM
|
||||
activesupport (>= 3.1, < 4.1)
|
||||
haml (>= 3.1, < 4.1)
|
||||
railties (>= 3.1, < 4.1)
|
||||
hashie (2.1.1)
|
||||
hashie (3.3.1)
|
||||
highline (1.6.21)
|
||||
hike (1.2.3)
|
||||
httparty (0.11.0)
|
||||
multi_json (~> 1.0)
|
||||
multi_xml (>= 0.5.2)
|
||||
i18n (0.6.1)
|
||||
i18n-tasks (0.7.8)
|
||||
activesupport
|
||||
easy_translate (>= 0.5.0)
|
||||
erubis
|
||||
highline
|
||||
i18n
|
||||
slop (>= 3.5.0)
|
||||
term-ansicolor
|
||||
terminal-table
|
||||
journey (1.0.4)
|
||||
jquery-rails (3.1.0)
|
||||
jquery-rails (3.1.2)
|
||||
railties (>= 3.0, < 5.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
jquery-ui-rails (4.1.2)
|
||||
railties (>= 3.1.0)
|
||||
js-routes (0.9.9)
|
||||
railties (>= 3.2)
|
||||
sprockets-rails
|
||||
json (1.7.7)
|
||||
kgio (2.9.2)
|
||||
launchy (2.4.2)
|
||||
launchy (2.4.3)
|
||||
addressable (~> 2.3)
|
||||
leaflet-markercluster-rails (0.7.0)
|
||||
railties (>= 3.1)
|
||||
leaflet-rails (0.7.2)
|
||||
less (2.3.3)
|
||||
commonjs (~> 0.2.6)
|
||||
less-rails (2.3.3)
|
||||
leaflet-rails (0.7.4)
|
||||
less (2.5.1)
|
||||
commonjs (~> 0.2.7)
|
||||
less-rails (2.5.0)
|
||||
actionpack (>= 3.1)
|
||||
less (~> 2.3.1)
|
||||
less (~> 2.5.0)
|
||||
less-rails-bootstrap (3.2.0)
|
||||
less-rails (~> 2.5.0)
|
||||
letter_opener (1.2.0)
|
||||
launchy (~> 2.2)
|
||||
libv8 (3.16.14.3)
|
||||
@@ -169,28 +188,39 @@ GEM
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
memcachier (0.0.2)
|
||||
method_source (0.8.2)
|
||||
mime-types (1.25.1)
|
||||
mini_portile (0.5.3)
|
||||
multi_json (1.9.3)
|
||||
mini_portile (0.6.1)
|
||||
multi_json (1.10.1)
|
||||
multi_xml (0.5.5)
|
||||
newrelic_rpm (3.8.0.218)
|
||||
nokogiri (1.6.1)
|
||||
mini_portile (~> 0.5.0)
|
||||
netrc (0.8.0)
|
||||
newrelic_rpm (3.9.6.257)
|
||||
nokogiri (1.6.4.1)
|
||||
mini_portile (~> 0.6.0)
|
||||
oauth (0.4.7)
|
||||
omniauth (1.2.1)
|
||||
hashie (>= 1.2, < 3)
|
||||
omniauth (1.2.2)
|
||||
hashie (>= 1.2, < 4)
|
||||
rack (~> 1.0)
|
||||
omniauth-flickr (0.0.15)
|
||||
omniauth-oauth (~> 1.0)
|
||||
omniauth-oauth (1.0.1)
|
||||
oauth
|
||||
omniauth (~> 1.0)
|
||||
omniauth-twitter (1.0.1)
|
||||
omniauth-twitter (1.1.0)
|
||||
multi_json (~> 1.3)
|
||||
omniauth-oauth (~> 1.0)
|
||||
orm_adapter (0.5.0)
|
||||
pg (0.17.1)
|
||||
polyglot (0.3.4)
|
||||
poltergeist (1.5.1)
|
||||
capybara (~> 2.1)
|
||||
cliver (~> 0.3.1)
|
||||
multi_json (~> 1.0)
|
||||
websocket-driver (>= 0.2.0)
|
||||
polyglot (0.3.5)
|
||||
pry (0.10.1)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.8.1)
|
||||
slop (~> 3.4)
|
||||
rack (1.4.5)
|
||||
rack-cache (1.2)
|
||||
rack (>= 0.4)
|
||||
@@ -206,7 +236,7 @@ GEM
|
||||
activesupport (= 3.2.13)
|
||||
bundler (~> 1.0)
|
||||
railties (= 3.2.13)
|
||||
rails_12factor (0.0.2)
|
||||
rails_12factor (0.0.3)
|
||||
rails_serve_static_assets
|
||||
rails_stdout_logging
|
||||
rails_serve_static_assets (0.0.2)
|
||||
@@ -219,12 +249,13 @@ GEM
|
||||
rdoc (~> 3.4)
|
||||
thor (>= 0.14.6, < 2.0)
|
||||
raindrops (0.13.0)
|
||||
rake (10.3.1)
|
||||
rake (10.3.2)
|
||||
rdoc (3.12.2)
|
||||
json (~> 1.4)
|
||||
ref (1.0.5)
|
||||
rest-client (1.6.7)
|
||||
mime-types (>= 1.16)
|
||||
rest-client (1.7.2)
|
||||
mime-types (>= 1.16, < 3.0)
|
||||
netrc (~> 0.7)
|
||||
rspec-core (2.12.2)
|
||||
rspec-expectations (2.12.1)
|
||||
diff-lcs (~> 1.1.3)
|
||||
@@ -241,32 +272,38 @@ GEM
|
||||
railties (~> 3.2.0)
|
||||
sass (>= 3.1.10)
|
||||
tilt (~> 1.3)
|
||||
simplecov (0.8.2)
|
||||
simplecov (0.9.1)
|
||||
docile (~> 1.1.0)
|
||||
multi_json
|
||||
multi_json (~> 1.0)
|
||||
simplecov-html (~> 0.8.0)
|
||||
simplecov-html (0.8.0)
|
||||
sprockets (2.2.2)
|
||||
slop (3.6.0)
|
||||
sprockets (2.2.3)
|
||||
hike (~> 1.2)
|
||||
multi_json (~> 1.0)
|
||||
rack (~> 1.0)
|
||||
tilt (~> 1.1, != 1.3.0)
|
||||
sprockets-rails (0.0.1)
|
||||
sprockets (>= 1.0.2)
|
||||
term-ansicolor (1.3.0)
|
||||
tins (~> 1.0)
|
||||
terminal-table (1.4.5)
|
||||
therubyracer (0.12.1)
|
||||
libv8 (~> 3.16.14.0)
|
||||
ref
|
||||
thor (0.19.1)
|
||||
thread (0.1.4)
|
||||
thread_safe (0.3.4)
|
||||
tilt (1.4.1)
|
||||
tins (1.1.0)
|
||||
tins (1.3.3)
|
||||
treetop (1.4.15)
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
tzinfo (0.3.39)
|
||||
tzinfo (0.3.42)
|
||||
uglifier (2.2.1)
|
||||
execjs (>= 0.3.0)
|
||||
multi_json (~> 1.0, >= 1.0.2)
|
||||
unicorn (4.8.2)
|
||||
unicorn (4.8.3)
|
||||
kgio (~> 2.6)
|
||||
rack
|
||||
raindrops (~> 0.7)
|
||||
@@ -276,7 +313,10 @@ GEM
|
||||
nokogiri (>= 1.2.0)
|
||||
rack (>= 1.0)
|
||||
rack-test (>= 0.5.3)
|
||||
will_paginate (3.0.5)
|
||||
websocket-driver (0.4.0)
|
||||
will_paginate (3.0.7)
|
||||
xpath (2.0.0)
|
||||
nokogiri (~> 1.3)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
@@ -289,14 +329,17 @@ DEPENDENCIES
|
||||
bluecloth
|
||||
bootstrap-datepicker-rails
|
||||
bundler (>= 1.1.5)
|
||||
byebug
|
||||
cancan
|
||||
capybara
|
||||
capybara-email
|
||||
coffee-rails (~> 3.2.1)
|
||||
compass-rails (~> 1.0.3)
|
||||
coveralls
|
||||
csv_shaper
|
||||
dalli
|
||||
debugger
|
||||
devise (~> 3.0.0)
|
||||
database_cleaner (~> 1.3.0)
|
||||
devise (~> 3.2.0)
|
||||
factory_girl_rails (~> 4.0)
|
||||
figaro
|
||||
flickraw
|
||||
@@ -306,12 +349,16 @@ DEPENDENCIES
|
||||
gravatar-ultimate
|
||||
haml
|
||||
haml-rails
|
||||
i18n-tasks
|
||||
jquery-rails
|
||||
jquery-ui-rails
|
||||
js-routes
|
||||
json (~> 1.7.7)
|
||||
leaflet-markercluster-rails
|
||||
leaflet-rails
|
||||
less (~> 2.3.2)
|
||||
less-rails (~> 2.3.3)
|
||||
less (~> 2.5.0)
|
||||
less-rails (~> 2.5.0)
|
||||
less-rails-bootstrap (~> 3.2.0)
|
||||
letter_opener
|
||||
libv8 (= 3.16.14.3)
|
||||
memcachier
|
||||
@@ -320,6 +367,8 @@ DEPENDENCIES
|
||||
omniauth-flickr (>= 0.0.15)
|
||||
omniauth-twitter
|
||||
pg
|
||||
poltergeist (~> 1.5.1)
|
||||
pry
|
||||
rack (~> 1.4.5)
|
||||
rails (= 3.2.13)
|
||||
rails_12factor
|
||||
@@ -327,7 +376,6 @@ DEPENDENCIES
|
||||
rspec-rails (~> 2.12.1)
|
||||
sass-rails (~> 3.2.3)
|
||||
therubyracer (~> 0.12)
|
||||
twitter-bootstrap-rails!
|
||||
uglifier (>= 1.0.3)
|
||||
unicorn
|
||||
webrat
|
||||
|
||||
31
README.md
31
README.md
@@ -5,9 +5,32 @@
|
||||
|
||||
Welcome to the Growstuff project.
|
||||
|
||||
Growstuff is an open source/open data project to create a website for food gardeners.
|
||||
Growstuff is an open source/open data project to create a website for
|
||||
food gardeners. We crowdsource information on what our members are
|
||||
growing and harvesting, aggregate it, and make it available as open data
|
||||
via our API.
|
||||
|
||||
You can find most of our documentation at: http://wiki.growstuff.org/
|
||||
Growstuff was founded in 2012 and has been built by dozens of
|
||||
[contributors](CONTRIBUTORS.md). We are an inclusive, welcoming project, and
|
||||
encourage participation from people of all backgrounds and skill levels.
|
||||
|
||||
Our development uses Extreme Programming practices, which means we pair on all our work. Feel free to fork our code and explore, but if you would like to contribute back to us,
|
||||
please read http://wiki.growstuff.org/index.php/Development_process_overview before sending us a pull request.
|
||||
## Important links
|
||||
|
||||
* [Wiki](http://wiki.growstuff.org/) (general documentation)
|
||||
* [Task tracker](http://tracker.growstuff.org/) (recent and upcoming work, bugs, etc)
|
||||
* [Discussion forums](http://wiki.growstuff.org/index.php/Discussion_forums) (mailing lists, IRC, etc)
|
||||
|
||||
## For developers
|
||||
|
||||
* Start by looking at our [task tracker](http://tracker.growstuff.org/) for something to work on.
|
||||
* Drop in to one of our [discussion forums](http://wiki.growstuff.org/index.php/Discussion_forums) to talk to our team about it.
|
||||
* To set up your development environment, see [Getting started](http://wiki.growstuff.org/index.php/Development/Getting_Started).
|
||||
* We encourage [pair programming](http://wiki.growstuff.org/index.php/Pairing), especially for newer developers.
|
||||
* You may also be interested in our [API](http://wiki.growstuff.org/index.php/API).
|
||||
|
||||
## Contact
|
||||
|
||||
For more information about this project, contact [info@growstuff.org](mailto:info@growstuff.org).
|
||||
|
||||
You can also contact us on [Twitter](http://twitter.com/growstufforg/) or
|
||||
[Facebook](https://www.facebook.com/pages/Growstuff/1531133417099494).
|
||||
|
||||
BIN
app/assets/images/favicon.ico
Normal file
BIN
app/assets/images/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 920 B |
BIN
app/assets/images/growstuff-apple-touch-icon-precomposed.png
Normal file
BIN
app/assets/images/growstuff-apple-touch-icon-precomposed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
BIN
app/assets/images/growstuff-brand.png
Normal file
BIN
app/assets/images/growstuff-brand.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.1 KiB |
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
38
app/assets/javascripts/append_date.js.coffee
Normal file
38
app/assets/javascripts/append_date.js.coffee
Normal file
@@ -0,0 +1,38 @@
|
||||
# Displays datepicker to finished at date
|
||||
# when marking a planting finished using a
|
||||
# button. The button must have class 'append-date'.
|
||||
|
||||
jQuery ->
|
||||
|
||||
el = $('.append-date')
|
||||
|
||||
el.datepicker({'format': 'yyyy-mm-dd'})
|
||||
|
||||
href = el.attr('href')
|
||||
|
||||
originalText = el.text()
|
||||
|
||||
el.click (e) ->
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
|
||||
$(this).text('Confirm without date')
|
||||
|
||||
$(this).bind('click.confirm', (e) ->
|
||||
link = $("<a href='#{href}' data-method='put'></a>")
|
||||
$('body').append(link)
|
||||
$(link).click()
|
||||
)
|
||||
|
||||
$(this).blur (e) ->
|
||||
$(this).text(originalText)
|
||||
$(this).unbind('click.confirm')
|
||||
|
||||
|
||||
el.one 'changeDate', ->
|
||||
date = $(this).datepicker('getDate')
|
||||
url = "#{href}&planting[finished_at]=#{date}"
|
||||
|
||||
link = $("<a href='#{url}' data-method='put'></a>")
|
||||
$('body').append(link)
|
||||
$(link).click()
|
||||
@@ -12,8 +12,11 @@
|
||||
//
|
||||
//= require leaflet
|
||||
//= require leaflet.markercluster
|
||||
//= require js-routes
|
||||
//= require jquery
|
||||
//= require jquery_ujs
|
||||
//= require jquery.ui.autocomplete
|
||||
//= require twitter/bootstrap
|
||||
//= require_tree .
|
||||
//= require bootstrap-datepicker
|
||||
|
||||
|
||||
37
app/assets/javascripts/auto_suggest.js.coffee
Normal file
37
app/assets/javascripts/auto_suggest.js.coffee
Normal file
@@ -0,0 +1,37 @@
|
||||
# Uses JQuery's autocomplete to make a suggestion in lieu of a
|
||||
# preposterously long select dropdown. To implement add code to
|
||||
# the view like this:
|
||||
#
|
||||
# = auto_suggest @resource, :auto_suggest_source
|
||||
#
|
||||
# You must also add a search method to the resource's controller.
|
||||
|
||||
jQuery ->
|
||||
|
||||
if el = $( '.auto-suggest' )
|
||||
|
||||
id = $( '.auto-suggest-id' )
|
||||
|
||||
el.autocomplete
|
||||
minLength: 1,
|
||||
source: el.attr( 'data-source-url' ),
|
||||
focus: ( event, ui ) ->
|
||||
el.val( ui.item.name )
|
||||
id.val( ui.item.id )
|
||||
false
|
||||
select: ( event, ui ) ->
|
||||
el.val( ui.item.name )
|
||||
id.val( ui.item.id )
|
||||
false
|
||||
response: ( event, ui ) ->
|
||||
id.val( "" )
|
||||
for item in ui.content
|
||||
if item.name == el.val()
|
||||
id.val( item.id )
|
||||
|
||||
if el.data( 'uiAutocomplete' )
|
||||
el.data( 'uiAutocomplete' )._renderItem = ( ul, item ) ->
|
||||
$( '<li></li>' )
|
||||
.data( 'item.autocomplete', item )
|
||||
.append( "<a>#{item.name}</a>" )
|
||||
.appendTo( ul )
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,5 +1,5 @@
|
||||
if (document.getElementById("cropmap") !== null) {
|
||||
mapbox_map_id = "<%= Growstuff::Application.config.mapbox_map_id %>";
|
||||
mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>";
|
||||
mapbox_base_url = "https://c.tiles.mapbox.com/v3/" + mapbox_map_id + "/{z}/{x}/{y}.png";
|
||||
|
||||
L.Icon.Default.imagePath = '/assets'
|
||||
|
||||
19
app/assets/javascripts/finish_planting.js.coffee
Normal file
19
app/assets/javascripts/finish_planting.js.coffee
Normal file
@@ -0,0 +1,19 @@
|
||||
# Clears the finished at date field when
|
||||
# a planting is marked unfinished, and
|
||||
# repopulates the field with a cached value
|
||||
# marking unfinished is undone.
|
||||
|
||||
jQuery ->
|
||||
previousValue = ''
|
||||
$('#planting_finished').on('click', ->
|
||||
finished = $('#planting_finished_at')
|
||||
if @checked
|
||||
if previousValue.length
|
||||
date = previousValue
|
||||
finished.val(date)
|
||||
else
|
||||
finished.trigger('focus')
|
||||
else
|
||||
previousValue = finished.val()
|
||||
finished.val('')
|
||||
)
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
28
app/assets/javascripts/members.js.erb
Normal file
28
app/assets/javascripts/members.js.erb
Normal file
@@ -0,0 +1,28 @@
|
||||
if (document.getElementById("membermap") !== null) {
|
||||
mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>";
|
||||
mapbox_base_url = "https://c.tiles.mapbox.com/v3/" + mapbox_map_id + "/{z}/{x}/{y}.png";
|
||||
|
||||
L.Icon.Default.imagePath = '/assets'
|
||||
|
||||
|
||||
$.getJSON(location.pathname + '.json', function(member) {
|
||||
if (member.latitude && member.longitude) {
|
||||
membermap = L.map('membermap').setView([member.latitude, member.longitude], 4);
|
||||
|
||||
L.tileLayer(mapbox_base_url, {
|
||||
attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors under <a href="http://www.openstreetmap.org/copyright">ODbL</a> | Map imagery © <a href="http://mapbox.com">Mapbox</a>',
|
||||
maxZoom: 18
|
||||
}).addTo(membermap);
|
||||
marker = new L.Marker(new L.LatLng(member.latitude, member.longitude));
|
||||
|
||||
member_url = "/members/" + member.slug;
|
||||
member_link = "<a href='" + member_url + "'>" + member.login_name + "</a>";
|
||||
|
||||
where = "<p><i>" + member.location + "</i></p>";
|
||||
|
||||
marker.bindPopup(member_link + where).openPopup();
|
||||
marker.addTo(membermap);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,9 +1,9 @@
|
||||
if (document.getElementById("placesmap") !== null) {
|
||||
places_base_path = "/places";
|
||||
mapbox_map_id = "<%= Growstuff::Application.config.mapbox_map_id %>";
|
||||
mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>";
|
||||
mapbox_base_url = "https://c.tiles.mapbox.com/v3/" + mapbox_map_id + "/{z}/{x}/{y}.png";
|
||||
nominatim_base_url = 'http://nominatim.openstreetmap.org/search/';
|
||||
nominatim_user_agent_email = "<%= Growstuff::Application.config.user_agent_email %>";
|
||||
nominatim_user_agent_email = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.user_agent_email %>";
|
||||
|
||||
L.Icon.Default.imagePath = '/assets'
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -3,11 +3,12 @@
|
||||
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
|
||||
* the top of the compiled file, but it's generally better to create a new file per style scope.
|
||||
*= require_self
|
||||
*= require jquery.ui.autocomplete
|
||||
*= require bootstrap-datepicker
|
||||
*= require leaflet
|
||||
*= require leaflet.markercluster
|
||||
*= require leaflet.markercluster.default
|
||||
*= require custom_bootstrap/custom_bootstrap
|
||||
*= require overrides.css
|
||||
*= require_tree .
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
// !!! AUTOMATICALLY GENERATED FILE. DO NOT MODIFY !!!
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
// Core variables and mixins
|
||||
@import "twitter/bootstrap/variables.less";
|
||||
@import "custom_bootstrap/variables.less"; // Modify this for custom colors, font-sizes, etc
|
||||
@import "twitter/bootstrap/mixins.less";
|
||||
@import "custom_bootstrap/mixins.less"; // Modify this for custom mixins
|
||||
|
||||
// Reset and dependencies
|
||||
@import "twitter/bootstrap/normalize.less";
|
||||
@import "twitter/bootstrap/print.less";
|
||||
@import "twitter/bootstrap/glyphicons.less";
|
||||
|
||||
// Core CSS
|
||||
@import "twitter/bootstrap/scaffolding.less";
|
||||
@import "twitter/bootstrap/type.less";
|
||||
@import "twitter/bootstrap/code.less";
|
||||
@import "twitter/bootstrap/grid.less";
|
||||
@import "twitter/bootstrap/tables.less";
|
||||
@import "twitter/bootstrap/forms.less";
|
||||
@import "twitter/bootstrap/buttons.less";
|
||||
|
||||
// Components
|
||||
@import "twitter/bootstrap/component-animations.less";
|
||||
@import "twitter/bootstrap/dropdowns.less";
|
||||
@import "twitter/bootstrap/button-groups.less";
|
||||
@import "twitter/bootstrap/input-groups.less";
|
||||
@import "twitter/bootstrap/navs.less";
|
||||
@import "twitter/bootstrap/navbar.less";
|
||||
@import "twitter/bootstrap/breadcrumbs.less";
|
||||
@import "twitter/bootstrap/pagination.less";
|
||||
@import "twitter/bootstrap/pager.less";
|
||||
@import "twitter/bootstrap/labels.less";
|
||||
@import "twitter/bootstrap/badges.less";
|
||||
@import "twitter/bootstrap/jumbotron.less";
|
||||
@import "twitter/bootstrap/thumbnails.less";
|
||||
@import "twitter/bootstrap/alerts.less";
|
||||
@import "twitter/bootstrap/progress-bars.less";
|
||||
@import "twitter/bootstrap/media.less";
|
||||
@import "twitter/bootstrap/list-group.less";
|
||||
@import "twitter/bootstrap/panels.less";
|
||||
@import "twitter/bootstrap/responsive-embed.less";
|
||||
@import "twitter/bootstrap/wells.less";
|
||||
@import "twitter/bootstrap/close.less";
|
||||
|
||||
// Components w/ JavaScript
|
||||
@import "twitter/bootstrap/modals.less";
|
||||
@import "twitter/bootstrap/tooltip.less";
|
||||
@import "twitter/bootstrap/popovers.less";
|
||||
@import "twitter/bootstrap/carousel.less";
|
||||
|
||||
// Utility classes
|
||||
@import "twitter/bootstrap/utilities.less";
|
||||
@import "twitter/bootstrap/responsive-utilities.less";
|
||||
1
app/assets/stylesheets/custom_bootstrap/mixins.less
Normal file
1
app/assets/stylesheets/custom_bootstrap/mixins.less
Normal file
@@ -0,0 +1 @@
|
||||
// Use this file to override Twitter Bootstrap mixins or define own mixins.
|
||||
54
app/assets/stylesheets/custom_bootstrap/variables.less
Normal file
54
app/assets/stylesheets/custom_bootstrap/variables.less
Normal file
@@ -0,0 +1,54 @@
|
||||
// Use this file to override Twitter Bootstrap variables or define own variables.
|
||||
|
||||
// Import original variables so they can be used in overrides
|
||||
@import "twitter/bootstrap/variables.less";
|
||||
|
||||
// Base colours
|
||||
|
||||
@beige: #f3f1ee;
|
||||
@brown: #413f3b;
|
||||
|
||||
@green: #5f8e43;
|
||||
@blue: #2f4365;
|
||||
@red: #8e4d43;
|
||||
@orange: #b2685c;
|
||||
@yellow: #b2935c;
|
||||
|
||||
@body-bg: @beige;
|
||||
@text-color: @brown;
|
||||
@link-color: @green;
|
||||
|
||||
@brand-primary: @green;
|
||||
|
||||
@font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
@font-family-serif: Georgia, "Times New Roman", Times, serif;
|
||||
@font-family-mono: Monaco, Menlo, Consolas, "Courier New", monospace;
|
||||
|
||||
@font-size-base: 14px;
|
||||
@font-family-base: @font-family-sans-serif;
|
||||
@line-height-base: 1.5;
|
||||
@alt-font-family: @font-family-serif;
|
||||
|
||||
@headings-font-family: @font-family-sans-serif;
|
||||
@headings-font-weight: bold; // instead of browser default, bold
|
||||
@headings-color: inherit; // empty to use BS default, @textColor
|
||||
|
||||
// Hero unit
|
||||
@jumbotron-bg: darken(@body-bg, 10%);
|
||||
|
||||
// Nav bar
|
||||
@navbar-default-bg: @brown;
|
||||
@navbar-default-bg-highlight: @brown;
|
||||
@navbar-default-color: @beige;
|
||||
@navbar-default-link-color: darken(@beige, 20%);
|
||||
@navbar-default-link-hover-color: @beige;
|
||||
@navbar-default-link-active-color: @beige;
|
||||
@navbar-default-brand-color: lighten(@green, 20%);
|
||||
|
||||
// Top nav collapse threshold
|
||||
@grid-float-breakpoint: @screen-md-min;
|
||||
|
||||
@dropdown-bg: lighten(@beige, 10%);
|
||||
@dropdown-link-color: @brown;
|
||||
@dropdown-link-hover-color: @brown;
|
||||
@dropdown-link-hover-bg: lighten(@green, 50%);
|
||||
@@ -1,11 +1,14 @@
|
||||
@import "twitter/bootstrap/bootstrap";
|
||||
|
||||
@import "custom_bootstrap/variables";
|
||||
// this padding needs to be done before the responsive stuff is imported
|
||||
body {
|
||||
padding-top: @navbarHeight + 15px;
|
||||
// modifying this for our promotional banner. can be replaced after if
|
||||
// needed.
|
||||
// padding-top: @navbar-height + 15px;
|
||||
padding-top: @navbar-height;
|
||||
}
|
||||
|
||||
@import "twitter/bootstrap/responsive";
|
||||
//@import "twitter/bootstrap/responsive";
|
||||
|
||||
// Set the correct sprite paths
|
||||
@iconSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings");
|
||||
@@ -19,72 +22,13 @@ body {
|
||||
@fontAwesomeSvgPath: asset-url("fontawesome-webfont.svg#fontawesomeregular");
|
||||
|
||||
// Font Awesome
|
||||
@import "fontawesome/font-awesome";
|
||||
//@import "fontawesome/font-awesome";
|
||||
|
||||
// Glyphicons
|
||||
//@import "twitter/bootstrap/sprites.less";
|
||||
|
||||
// Your custom LESS stylesheets goes here
|
||||
//
|
||||
// Since bootstrap was imported above you have access to its mixins which
|
||||
// you may use and inherit here
|
||||
//
|
||||
// If you'd like to override bootstrap's own variables, you can do so here as well
|
||||
// See http://twitter.github.com/bootstrap/customize.html#variables for their names and documentation
|
||||
//
|
||||
// Example:
|
||||
// @linkColor: #ff0000;
|
||||
|
||||
// Base colours
|
||||
|
||||
@beige: #f3f1ee;
|
||||
@brown: #413f3b;
|
||||
|
||||
@green: #5f8e43;
|
||||
@blue: #2f4365;
|
||||
@red: #8e4d43;
|
||||
@orange: #b2685c;
|
||||
@yellow: #b2935c;
|
||||
|
||||
@bodyBackground: @beige;
|
||||
@textColor: @brown;
|
||||
|
||||
@linkColor: @green;
|
||||
|
||||
// Typography (with help from bootswatch.com's "readable" theme)
|
||||
@import url('//fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic');
|
||||
@sansFontFamily: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
@serifFontFamily: Georgia, "Times New Roman", Times, serif;
|
||||
@monoFontFamily: Monaco, Menlo, Consolas, "Courier New", monospace;
|
||||
|
||||
@baseFontSize: 15px;
|
||||
@baseFontFamily: @serifFontFamily;
|
||||
@baseLineHeight: @baseFontSize * 1.5;
|
||||
@altFontFamily: @sansFontFamily;
|
||||
|
||||
@headingsFontFamily: "Lora", Georgia, "Times New Roman", Times, serif;
|
||||
@headingsFontWeight: bold; // instead of browser default, bold
|
||||
@headingsColor: inherit; // empty to use BS default, @textColor
|
||||
|
||||
// Hero unit
|
||||
@heroUnitBackground: darken(@bodyBackground, 10%);
|
||||
|
||||
// Nav bar
|
||||
@navbarBackground: @brown;
|
||||
@navbarBackgroundHighlight: @brown;
|
||||
@navbarText: @beige;
|
||||
@navbarLinkColor: darken(@beige, 20%);
|
||||
@navbarLinkColorHover: @beige;
|
||||
@navbarLinkColorActive: @beige;
|
||||
@navbarBrandColor: lighten(@green, 20%);
|
||||
|
||||
@dropdownBackground: lighten(@beige, 10%);
|
||||
@dropdownLinkColor: @brown;
|
||||
@dropdownLinkColorHover: @brown;
|
||||
@dropdownLinkBackgroundHover: lighten(@green, 50%);
|
||||
|
||||
|
||||
ul.inline > li.first {
|
||||
.list-inline > li.first {
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
@@ -92,6 +36,17 @@ h2 {
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
/*
|
||||
#subtitle {
|
||||
color: lighten(@brown, 30%);
|
||||
margin-top: 0px;
|
||||
padding-top: 0px;
|
||||
padding-left: 1em;
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
}
|
||||
*/
|
||||
|
||||
h3 {
|
||||
font-size: 120%;
|
||||
}
|
||||
@@ -117,24 +72,24 @@ h3 {
|
||||
}
|
||||
|
||||
// let's condense the hero unit a little
|
||||
.hero-unit {
|
||||
.jumbotron {
|
||||
padding-top: 30px;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
// info under the main heading on homepage
|
||||
.hero-unit .info {
|
||||
.jumbotron .info {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
// signup widget on homepage
|
||||
.hero-unit .signup {
|
||||
.jumbotron .signup {
|
||||
background-color: lighten(@green, 40%);
|
||||
border: 1px solid lighten(@green, 20%);
|
||||
border-radius: 6px;
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
line-height: 110%;
|
||||
line-height: 200%;
|
||||
}
|
||||
|
||||
// stats shown on homepage. eg. "999 members..."
|
||||
@@ -154,6 +109,10 @@ p.stats {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
#membermap {
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
.member-location {
|
||||
font-size: small;
|
||||
font-style: italic;
|
||||
@@ -190,13 +149,25 @@ p.stats {
|
||||
}
|
||||
}
|
||||
|
||||
.member-thumbnail {
|
||||
img {
|
||||
height: 85px;
|
||||
width: 85px;
|
||||
max-width: 85px
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
li.crop-hierarchy {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
.navbar-inner {
|
||||
border-width: 0px !important;
|
||||
.navbar-brand {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.navbar-bottom {
|
||||
@@ -223,24 +194,36 @@ footer .navbar .nav {
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-inner {
|
||||
border: none !important;
|
||||
background-image: none !important;
|
||||
-webkit-border-radius: 0px;
|
||||
-moz-border-radius: 0px;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.navbar-bottom .navbar-inner {
|
||||
.navbar-bottom.navbar {
|
||||
text-align: center;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.navbar-bottom .container{
|
||||
padding: 20px 0px 40px 0px;
|
||||
.crop-image, .member-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.navbar-search{
|
||||
padding-bottom: 7px;
|
||||
// Autosuggest
|
||||
|
||||
.ui-autocomplete {
|
||||
z-index: @zindex-tooltip;
|
||||
}
|
||||
|
||||
// Crowdfunding campaign, Sep-Oct 2014
|
||||
|
||||
.crowdfunding-banner {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
background-color: lighten(@green, 30%);
|
||||
margin-top: 0px;
|
||||
margin-bottom: 5px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.crowdfunding-banner a {
|
||||
color: @brown;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
// Overrides applying only to mobile view. This must be at the end of the overrides file.
|
||||
@@ -64,7 +64,7 @@ class AccountTypesController < ApplicationController
|
||||
@account_type.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to account_types_url }
|
||||
format.html { redirect_to account_types_url, notice: 'Account type was successfully deleted.' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
90
app/controllers/alternate_names_controller.rb
Normal file
90
app/controllers/alternate_names_controller.rb
Normal file
@@ -0,0 +1,90 @@
|
||||
class AlternateNamesController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /alternate_names
|
||||
# GET /alternate_names.json
|
||||
def index
|
||||
@alternate_names = AlternateName.all
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.haml
|
||||
format.json { render json: @alternate_names }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /alternate_names/1
|
||||
# GET /alternate_names/1.json
|
||||
def show
|
||||
@alternate_name = AlternateName.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.haml
|
||||
format.json { render json: @alternate_name }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /alternate_names/new
|
||||
# GET /alternate_names/new.json
|
||||
def new
|
||||
@alternate_name = AlternateName.new
|
||||
@crop = Crop.find_by_id(params[:crop_id]) || Crop.new
|
||||
|
||||
respond_to do |format|
|
||||
format.html # new.html.haml
|
||||
format.json { render json: @alternate_name }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /alternate_names/1/edit
|
||||
def edit
|
||||
@alternate_name = AlternateName.find(params[:id])
|
||||
end
|
||||
|
||||
# POST /alternate_names
|
||||
# POST /alternate_names.json
|
||||
def create
|
||||
params[:alternate_name][:creator_id] = current_member.id
|
||||
@alternate_name = AlternateName.new(params[:alternate_name])
|
||||
|
||||
respond_to do |format|
|
||||
if @alternate_name.save
|
||||
format.html { redirect_to @alternate_name.crop, notice: 'Alternate name was successfully created.' }
|
||||
format.json { render json: @alternate_name, status: :created, location: @alternate_name }
|
||||
else
|
||||
format.html { render action: "new" }
|
||||
format.json { render json: @alternate_name.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# PUT /alternate_names/1
|
||||
# PUT /alternate_names/1.json
|
||||
def update
|
||||
@alternate_name = AlternateName.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @alternate_name.update_attributes(params[:alternate_name])
|
||||
format.html { redirect_to @alternate_name.crop, notice: 'Alternate name was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
format.html { render action: "edit" }
|
||||
format.json { render json: @alternate_name.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /alternate_names/1
|
||||
# DELETE /alternate_names/1.json
|
||||
def destroy
|
||||
@alternate_name = AlternateName.find(params[:id])
|
||||
@crop = @alternate_name.crop
|
||||
@alternate_name.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
redirect_to @crop, notice: 'Alternate name was successfully deleted.'
|
||||
}
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4,17 +4,22 @@ class ApplicationController < ActionController::Base
|
||||
include ApplicationHelper
|
||||
|
||||
after_filter :store_location
|
||||
before_filter :set_locale
|
||||
|
||||
def store_location
|
||||
# store last url - this is needed for post-login redirect to whatever the user last visited.
|
||||
if (request.fullpath != new_member_session_path && \
|
||||
!request.xhr?) # don't store ajax calls
|
||||
session[:previous_url] = request.fullpath
|
||||
if (request.path != "/members/sign_in" &&
|
||||
request.path != "/members/sign_up" &&
|
||||
request.path != "/members/password/new" &&
|
||||
request.path != "/members/password/edit" &&
|
||||
request.path != "/members/confirmation" &&
|
||||
request.path != "/members/sign_out" &&
|
||||
!request.xhr?)
|
||||
store_location_for(:member, request.fullpath)
|
||||
end
|
||||
end
|
||||
|
||||
def after_sign_in_path_for(resource)
|
||||
session[:previous_url] || root_path
|
||||
stored_location_for(:member) || root_path
|
||||
end
|
||||
|
||||
# tweak CanCan defaults because we don't have a "current_user" method
|
||||
@@ -28,5 +33,14 @@ class ApplicationController < ActionController::Base
|
||||
rescue_from CanCan::AccessDenied do |exception|
|
||||
redirect_to request.referer || root_url, :alert => exception.message
|
||||
end
|
||||
|
||||
def set_locale
|
||||
I18n.locale = params[:locale] || extract_locale_from_subdomain || I18n.default_locale
|
||||
end
|
||||
|
||||
def extract_locale_from_subdomain
|
||||
parsed_locale = request.subdomains.first
|
||||
I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -34,7 +34,7 @@ class CropsController < ApplicationController
|
||||
# GET /crops/wrangle
|
||||
def wrangle
|
||||
@crops = Crop.recent.paginate(:page => params[:page])
|
||||
|
||||
@crop_wranglers = Role.crop_wranglers
|
||||
respond_to do |format|
|
||||
format.html
|
||||
end
|
||||
@@ -57,8 +57,11 @@ class CropsController < ApplicationController
|
||||
# exclude exact match from partial match list
|
||||
@partial_matches.reject!{ |r| @exact_match && r.eql?(@exact_match) }
|
||||
|
||||
@fuzzy = Crop.search(params[:term])
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json { render :json => @fuzzy }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -66,6 +69,7 @@ class CropsController < ApplicationController
|
||||
# GET /crops/1.json
|
||||
def show
|
||||
@crop = Crop.includes(:scientific_names, {:plantings => :photos}).find(params[:id])
|
||||
@posts = @crop.posts.paginate(:page => params[:page])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.haml
|
||||
@@ -101,7 +105,6 @@ class CropsController < ApplicationController
|
||||
params[:crop][:creator_id] = current_member.id
|
||||
@crop = Crop.new(params[:crop])
|
||||
|
||||
|
||||
respond_to do |format|
|
||||
if @crop.save
|
||||
format.html { redirect_to @crop, notice: 'Crop was successfully created.' }
|
||||
|
||||
@@ -80,7 +80,7 @@ class ForumsController < ApplicationController
|
||||
@forum.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to forums_url }
|
||||
format.html { redirect_to forums_url, notice: 'Forum was successfully deleted' }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -85,7 +85,7 @@ class GardensController < ApplicationController
|
||||
@garden.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to @garden.owner, notice: 'Garden was successfully deleted.' }
|
||||
format.html { redirect_to gardens_by_owner_path(:owner => @garden.owner), notice: 'Garden was successfully deleted.' }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,23 +6,21 @@ class HarvestsController < ApplicationController
|
||||
# GET /harvests.json
|
||||
def index
|
||||
@owner = Member.find_by_slug(params[:owner])
|
||||
@crop = Crop.find_by_slug(params[:crop])
|
||||
if @owner
|
||||
@harvests = @owner.harvests.includes(:owner, :crop).paginate(:page => params[:page])
|
||||
@harvests = @owner.harvests.includes(:owner, :crop)
|
||||
elsif @crop
|
||||
@harvests = @crop.harvests.includes(:owner, :crop)
|
||||
else
|
||||
@harvests = Harvest.includes(:owner, :crop).paginate(:page => params[:page])
|
||||
@harvests = Harvest.includes(:owner, :crop)
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
format.html { @harvests = @harvests.paginate(:page => params[:page]) }
|
||||
format.json { render json: @harvests }
|
||||
format.csv do
|
||||
if @owner
|
||||
@filename = "Growstuff-#{@owner}-Harvests-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@harvests = @owner.harvests.includes(:owner, :crop)
|
||||
else
|
||||
@filename = "Growstuff-Harvests-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@harvests = Harvest.includes(:owner, :crop)
|
||||
end
|
||||
specifics = (@owner ? "#{@owner.name}-" : @crop ? "#{@crop.name}-" : nil)
|
||||
@filename = "Growstuff-#{specifics}Harvests-#{Time.zone.now.to_s(:number)}.csv"
|
||||
render :csv => @harvests
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,6 +10,7 @@ class MembersController < ApplicationController
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.haml
|
||||
format.json { render :json => @members.to_json(:only => [:id, :login_name, :slug, :bio, :created_at, :location, :latitude, :longitude]) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -25,6 +26,7 @@ class MembersController < ApplicationController
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.haml
|
||||
format.json { render :json => @member.to_json(:only => [:id, :login_name, :bio, :created_at, :slug, :location, :latitude, :longitude]) }
|
||||
format.rss { render(
|
||||
:layout => false,
|
||||
:locals => { :member => @member }
|
||||
|
||||
@@ -29,7 +29,8 @@ class PhotosController < ApplicationController
|
||||
# GET /photos/new.json
|
||||
def new
|
||||
@photo = Photo.new
|
||||
@planting_id = params[:planting_id]
|
||||
@type = params[:type]
|
||||
@id = params[:id]
|
||||
|
||||
page = params[:page] || 1
|
||||
|
||||
@@ -63,19 +64,35 @@ class PhotosController < ApplicationController
|
||||
@photo.owner_id = current_member.id
|
||||
@photo.set_flickr_metadata
|
||||
|
||||
if params[:planting_id]
|
||||
planting = Planting.find_by_id(params[:planting_id])
|
||||
if planting
|
||||
if planting.owner.id == current_member.id
|
||||
@photo.plantings << planting unless @photo.plantings.include?(planting)
|
||||
# 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"]
|
||||
if params[:type]
|
||||
if valid_models.include?(params[:type])
|
||||
if params[:id]
|
||||
item = params[:type].camelcase.constantize.find_by_id(params[:id])
|
||||
if item
|
||||
if item.owner.id == current_member.id
|
||||
# This syntax is weird, so just know that it means this:
|
||||
# @photo.harvests << item unless @photo.harvests.include?(item)
|
||||
# but with the correct many-to-many relationship automatically referenced
|
||||
(@photo.send "#{params[:type]}s") << item unless (@photo.send "#{params[:type]}s").include?(item)
|
||||
else
|
||||
flash[:alert] = "You must own both the #{params[:type]} and the photo."
|
||||
end
|
||||
else
|
||||
flash[:alert] = "Couldn't find #{params[:type]} to connect to photo."
|
||||
end
|
||||
else
|
||||
flash[:alert] = "You must own both the planting and the photo."
|
||||
flash[:alert] = "Missing id parameter"
|
||||
end
|
||||
else
|
||||
flash[:alert] = "Couldn't find planting to connect to photo."
|
||||
flash[:alert] = "Cannot attach photos to #{params[:type]}"
|
||||
end
|
||||
else
|
||||
flash[:alert] = "Missing type parameter"
|
||||
end
|
||||
|
||||
|
||||
respond_to do |format|
|
||||
if @photo.save
|
||||
format.html { redirect_to @photo, notice: 'Photo was successfully added.' }
|
||||
|
||||
@@ -7,24 +7,22 @@ class PlantingsController < ApplicationController
|
||||
# GET /plantings.json
|
||||
def index
|
||||
@owner = Member.find_by_slug(params[:owner])
|
||||
@crop = Crop.find_by_slug(params[:crop])
|
||||
if @owner
|
||||
@plantings = @owner.plantings.includes(:owner, :crop, :garden).paginate(:page => params[:page])
|
||||
elsif @crop
|
||||
@plantings = @crop.plantings.includes(:owner, :crop, :garden).paginate(:page => params[:page])
|
||||
else
|
||||
@plantings = Planting.includes(:owner, :crop, :garden).paginate(:page => params[:page])
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
format.html { @plantings = @plantings.paginate(:page => params[:page]) }
|
||||
format.json { render json: @plantings }
|
||||
format.rss { render :layout => false } #index.rss.builder
|
||||
format.csv do
|
||||
if @owner
|
||||
@filename = "Growstuff-#{@owner}-Plantings-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@plantings = @owner.plantings.includes(:owner, :crop, :garden)
|
||||
else
|
||||
@filename = "Growstuff-Plantings-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@plantings = Planting.includes(:owner, :crop, :garden)
|
||||
end
|
||||
specifics = (@owner ? "#{@owner.name}-" : @crop ? "#{@crop.name}-" : nil)
|
||||
@filename = "Growstuff-#{specifics}Plantings-#{Time.zone.now.to_s(:number)}.csv"
|
||||
render :csv => @plantings
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,26 +10,37 @@ class RegistrationsController < Devise::RegistrationsController
|
||||
|
||||
# we need this subclassed method so that Devise doesn't force people to
|
||||
# change their password every time they want to edit their settings.
|
||||
# we also check that they give their current password to change their password.
|
||||
# Code copied from
|
||||
# https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-account-without-providing-a-password
|
||||
|
||||
def update
|
||||
# required for settings form to submit when password is left blank
|
||||
if params[:member][:password].blank?
|
||||
params[:member].delete("password")
|
||||
params[:member].delete("password_confirmation")
|
||||
params[:member].delete("current_password")
|
||||
end
|
||||
|
||||
@member = Member.find(current_member.id)
|
||||
|
||||
if @member.update_attributes(params[:member])
|
||||
successfully_updated = if needs_password?(@member, params)
|
||||
@member.update_with_password(devise_parameter_sanitizer.sanitize(:account_update))
|
||||
else
|
||||
# remove the virtual current_password attribute
|
||||
# update_without_password doesn't know how to ignore it
|
||||
params[:member].delete(:current_password)
|
||||
@member.update_without_password(devise_parameter_sanitizer.sanitize(:account_update))
|
||||
end
|
||||
|
||||
if successfully_updated
|
||||
set_flash_message :notice, :updated
|
||||
# Sign in the member bypassing validation in case his password changed
|
||||
# Sign in the member bypassing validation in case their password changed
|
||||
sign_in @member, :bypass => true
|
||||
redirect_to edit_member_registration_path
|
||||
else
|
||||
render "edit"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
# check if we need the current password to update fields
|
||||
def needs_password?(member, params)
|
||||
params[:member][:password].present? ||
|
||||
params[:member][:password_confirmation].present?
|
||||
end
|
||||
|
||||
@@ -83,7 +83,9 @@ class ScientificNamesController < ApplicationController
|
||||
@scientific_name.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to @crop }
|
||||
format.html {
|
||||
redirect_to @crop, notice: 'Scientific name was successfully deleted.'
|
||||
}
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,8 +7,11 @@ class SeedsController < ApplicationController
|
||||
# GET /seeds.json
|
||||
def index
|
||||
@owner = Member.find_by_slug(params[:owner])
|
||||
@crop = Crop.find_by_slug(params[:crop])
|
||||
if @owner
|
||||
@seeds = @owner.seeds.includes(:owner, :crop).paginate(:page => params[:page])
|
||||
elsif @crop
|
||||
@seeds = @crop.seeds.includes(:owner, :crop).paginate(:page => params[:page])
|
||||
else
|
||||
@seeds = Seed.includes(:owner, :crop).paginate(:page => params[:page])
|
||||
end
|
||||
|
||||
22
app/helpers/auto_suggest_helper.rb
Normal file
22
app/helpers/auto_suggest_helper.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
module AutoSuggestHelper
|
||||
|
||||
def auto_suggest(resource, source, options={})
|
||||
if options[:default] && !options[:default].new_record?
|
||||
default = options[:default]
|
||||
default_id = options[:default].try(:id)
|
||||
else
|
||||
default = resource.send(source)
|
||||
default_id = default.try(:id)
|
||||
end
|
||||
|
||||
resource = resource.class.name.downcase
|
||||
source_path = Rails.application.routes.url_helpers.send("#{source}s_search_path")
|
||||
|
||||
%Q{
|
||||
<input id="#{source}" class="auto-suggest #{options[:class]}" type="text" value="#{default}" data-source-url="#{source_path}", placeholder="e.g. lettuce">
|
||||
<noscript class="text-warning">Warning: Javascript must be available to search and match crops</noscript>
|
||||
<input id="#{resource}_#{source}_id" class="auto-suggest-id" type="hidden" name="#{resource}[#{source}_id]" value="#{default_id}">
|
||||
}.html_safe
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,2 +0,0 @@
|
||||
module PlantPartsHelper
|
||||
end
|
||||
@@ -1,2 +0,0 @@
|
||||
module SeedsHelper
|
||||
end
|
||||
@@ -9,4 +9,17 @@ class Notifier < ActionMailer::Base
|
||||
mail(:to => @notification.recipient.email,
|
||||
:subject => @notification.subject)
|
||||
end
|
||||
|
||||
def planting_reminder(member)
|
||||
@member = member
|
||||
|
||||
@plantings = @member.plantings.reorder.last(5)
|
||||
@harvests = @member.harvests.reorder.last(5)
|
||||
|
||||
if @member.send_planting_reminder
|
||||
mail(:to => @member.email,
|
||||
:subject => "What have you planted lately?")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -40,6 +40,7 @@ class Ability
|
||||
can :wrangle, Crop
|
||||
can :manage, Crop
|
||||
can :manage, ScientificName
|
||||
can :manage, AlternateName
|
||||
end
|
||||
|
||||
# can create & destroy their own authentications against other sites.
|
||||
|
||||
5
app/models/alternate_name.rb
Normal file
5
app/models/alternate_name.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class AlternateName < ActiveRecord::Base
|
||||
attr_accessible :crop_id, :name, :creator_id
|
||||
belongs_to :crop
|
||||
belongs_to :creator, :class_name => 'Member'
|
||||
end
|
||||
@@ -8,6 +8,7 @@ class Crop < ActiveRecord::Base
|
||||
:allow_destroy => true,
|
||||
:reject_if => :all_blank
|
||||
|
||||
has_many :alternate_names
|
||||
has_many :plantings
|
||||
has_many :photos, :through => :plantings
|
||||
has_many :seeds
|
||||
@@ -17,6 +18,9 @@ class Crop < ActiveRecord::Base
|
||||
|
||||
belongs_to :parent, :class_name => 'Crop'
|
||||
has_many :varieties, :class_name => 'Crop', :foreign_key => 'parent_id'
|
||||
has_and_belongs_to_many :posts
|
||||
before_destroy {|crop| crop.posts.clear}
|
||||
|
||||
|
||||
default_scope order("lower(name) asc")
|
||||
scope :recent, reorder("created_at desc")
|
||||
@@ -30,11 +34,6 @@ class Crop < ActiveRecord::Base
|
||||
:message => 'is not a valid English Wikipedia URL'
|
||||
}
|
||||
|
||||
def Crop.random
|
||||
@crop = Crop.offset(rand(Crop.count)).first
|
||||
return @crop
|
||||
end
|
||||
|
||||
def to_s
|
||||
return name
|
||||
end
|
||||
@@ -127,52 +126,55 @@ class Crop < ActiveRecord::Base
|
||||
# - en_wikipedia_url (required)
|
||||
# - parent (name, optional)
|
||||
|
||||
def Crop.create_from_csv(row, definitely_new=false)
|
||||
def Crop.create_from_csv(row)
|
||||
name,scientific_name,en_wikipedia_url,parent = row
|
||||
|
||||
@cropbot = Member.find_by_login_name('cropbot')
|
||||
raise "cropbot account not found: run rake db:seed" unless @cropbot
|
||||
cropbot = Member.find_by_login_name('cropbot')
|
||||
raise "cropbot account not found: run rake db:seed" unless cropbot
|
||||
|
||||
crop = Crop.find_or_create_by_name(name)
|
||||
crop.update_attributes(
|
||||
:en_wikipedia_url => en_wikipedia_url,
|
||||
:creator_id => cropbot.id
|
||||
)
|
||||
|
||||
if definitely_new then
|
||||
@crop = Crop.create(
|
||||
:name => name,
|
||||
:en_wikipedia_url => en_wikipedia_url,
|
||||
:creator_id => @cropbot.id
|
||||
)
|
||||
else
|
||||
@crop = Crop.find_or_create_by_name(name)
|
||||
@crop.update_attributes(
|
||||
:en_wikipedia_url => en_wikipedia_url,
|
||||
:creator_id => @cropbot.id
|
||||
)
|
||||
end
|
||||
if parent
|
||||
@parent = Crop.find_by_name(parent)
|
||||
if @parent
|
||||
@crop.update_attributes(:parent_id => @parent.id)
|
||||
parent = Crop.find_by_name(parent)
|
||||
if parent
|
||||
crop.update_attributes(:parent_id => parent.id)
|
||||
else
|
||||
logger.warn("Warning: parent crop #{parent} not found")
|
||||
end
|
||||
end
|
||||
|
||||
unless @crop.scientific_names.exists?(:scientific_name => scientific_name)
|
||||
@sn = ''
|
||||
if scientific_name
|
||||
@sn = scientific_name
|
||||
elsif @crop.parent
|
||||
@sn = @crop.parent.scientific_names.first.scientific_name
|
||||
end
|
||||
crop.add_scientific_name_from_csv(scientific_name)
|
||||
|
||||
if @sn
|
||||
@crop.scientific_names.create(
|
||||
:scientific_name => @sn,
|
||||
:creator_id => @cropbot.id
|
||||
)
|
||||
else
|
||||
logger.warn("Warning: no scientific name (not even on parent crop) for #{@crop}")
|
||||
end
|
||||
end
|
||||
|
||||
def add_scientific_name_from_csv(scientific_name)
|
||||
name_to_add = nil
|
||||
if ! scientific_name.blank? # i.e. we actually passed one in, which isn't a given
|
||||
name_to_add = scientific_name
|
||||
elsif parent && parent.default_scientific_name
|
||||
name_to_add = parent.default_scientific_name
|
||||
else
|
||||
logger.warn("Warning: no scientific name (not even on parent crop) for #{self}")
|
||||
end
|
||||
|
||||
if name_to_add
|
||||
if scientific_names.exists?(:scientific_name => name_to_add)
|
||||
logger.warn("Warning: skipping duplicate scientific name #{name_to_add} for #{self}")
|
||||
else
|
||||
cropbot = Member.find_by_login_name('cropbot')
|
||||
raise "cropbot account not found: run rake db:seed" unless cropbot
|
||||
|
||||
scientific_names.create(
|
||||
:scientific_name => name_to_add,
|
||||
:creator_id => cropbot.id
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Crop.search(string)
|
||||
|
||||
@@ -14,6 +14,7 @@ class Garden < ActiveRecord::Base
|
||||
geocoded_by :location
|
||||
after_validation :geocode
|
||||
after_validation :empty_unwanted_geocodes
|
||||
after_save :mark_inactive_garden_plantings_as_finished
|
||||
|
||||
default_scope order("lower(name) asc")
|
||||
scope :active, where(:active => true)
|
||||
@@ -74,4 +75,15 @@ class Garden < ActiveRecord::Base
|
||||
name
|
||||
end
|
||||
|
||||
# When you mark a garden as inactive, all the plantings in it should be
|
||||
# marked as finished. This automates that.
|
||||
def mark_inactive_garden_plantings_as_finished
|
||||
if (active == false)
|
||||
plantings.current.each do |p|
|
||||
p.finished = true
|
||||
p.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class Harvest < ActiveRecord::Base
|
||||
include ActionView::Helpers::NumberHelper
|
||||
extend FriendlyId
|
||||
friendly_id :harvest_slug, use: :slugged
|
||||
|
||||
@@ -9,8 +10,21 @@ class Harvest < ActiveRecord::Base
|
||||
belongs_to :owner, :class_name => 'Member'
|
||||
belongs_to :plant_part
|
||||
|
||||
has_and_belongs_to_many :photos
|
||||
|
||||
before_destroy do |harvest|
|
||||
photolist = harvest.photos.to_a # save a temp copy of the photo list
|
||||
harvest.photos.clear # clear relationship b/w harvest and photo
|
||||
|
||||
photolist.each do |photo|
|
||||
photo.destroy_if_unused
|
||||
end
|
||||
end
|
||||
|
||||
default_scope order('created_at DESC')
|
||||
|
||||
validates :crop, :presence => {:message => "must be present and exist in our database"}
|
||||
|
||||
validates :quantity,
|
||||
:numericality => { :only_integer => false },
|
||||
:allow_nil => true
|
||||
@@ -70,4 +84,41 @@ class Harvest < ActiveRecord::Base
|
||||
"#{owner.login_name}-#{crop}".downcase.gsub(' ', '-')
|
||||
end
|
||||
|
||||
# stringify as "beet in Skud's backyard" or similar
|
||||
def to_s
|
||||
# 50 individual apples, weighing 3lb
|
||||
# 2 buckets of apricots, weighing 10kg
|
||||
string = ''
|
||||
if self.quantity
|
||||
string += "#{number_to_human(self.quantity.to_s, :strip_insignificant_zeros => true)} "
|
||||
if self.unit == 'individual'
|
||||
string += 'individual '
|
||||
else
|
||||
if self.quantity == 1
|
||||
string += "#{self.unit} of "
|
||||
else
|
||||
string += "#{self.unit.pluralize} of "
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.unit != 'individual' # buckets of apricot*s*
|
||||
string += "#{self.crop.name.pluralize}"
|
||||
elsif self.quantity == 1
|
||||
string += "#{self.crop.name}"
|
||||
else
|
||||
string += "#{self.crop.name.pluralize}"
|
||||
end
|
||||
|
||||
if self.weight_quantity
|
||||
string += " weighing #{number_to_human(self.weight_quantity, :strip_insignificant_zeros => true)} #{self.weight_unit}"
|
||||
end
|
||||
|
||||
return string
|
||||
end
|
||||
|
||||
def default_photo
|
||||
return photos.first
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -42,8 +42,13 @@ class Member < ActiveRecord::Base
|
||||
|
||||
# Setup accessible (or protected) attributes for your model
|
||||
attr_accessible :login_name, :email, :password, :password_confirmation,
|
||||
:remember_me, :login, :tos_agreement, :show_email, :newsletter,
|
||||
:location, :latitude, :longitude, :send_notification_email, :bio
|
||||
:remember_me, :login,
|
||||
# terms of service
|
||||
:tos_agreement,
|
||||
# profile stuff
|
||||
:bio, :location, :latitude, :longitude,
|
||||
# email settings
|
||||
:show_email, :newsletter, :send_notification_email, :send_planting_reminder
|
||||
|
||||
# set up geocoding
|
||||
geocoded_by :location
|
||||
|
||||
@@ -4,10 +4,21 @@ class Photo < ActiveRecord::Base
|
||||
belongs_to :owner, :class_name => 'Member'
|
||||
|
||||
has_and_belongs_to_many :plantings
|
||||
before_destroy {|photo| photo.plantings.clear}
|
||||
has_and_belongs_to_many :harvests
|
||||
before_destroy do |photo|
|
||||
photo.plantings.clear
|
||||
photo.harvests.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
|
||||
self.destroy
|
||||
end
|
||||
end
|
||||
|
||||
# This is split into a side-effect free method and a side-effecting method
|
||||
# for easier stubbing and testing.
|
||||
def flickr_metadata
|
||||
|
||||
@@ -3,16 +3,27 @@ class Planting < ActiveRecord::Base
|
||||
friendly_id :planting_slug, use: :slugged
|
||||
|
||||
attr_accessible :crop_id, :description, :garden_id, :planted_at,
|
||||
:quantity, :sunniness, :planted_from, :owner_id
|
||||
:quantity, :sunniness, :planted_from, :owner_id, :finished,
|
||||
:finished_at
|
||||
|
||||
belongs_to :garden
|
||||
belongs_to :owner, :class_name => 'Member', :counter_cache => true
|
||||
belongs_to :crop, :counter_cache => true
|
||||
|
||||
has_and_belongs_to_many :photos
|
||||
before_destroy {|planting| planting.photos.clear}
|
||||
|
||||
before_destroy do |planting|
|
||||
photolist = planting.photos.to_a # save a temp copy of the photo list
|
||||
planting.photos.clear # clear relationship b/w planting and photo
|
||||
|
||||
photolist.each do |photo|
|
||||
photo.destroy_if_unused
|
||||
end
|
||||
end
|
||||
|
||||
default_scope order("created_at desc")
|
||||
scope :finished, where(:finished => true)
|
||||
scope :current, where(:finished => false)
|
||||
|
||||
delegate :name,
|
||||
:en_wikipedia_url,
|
||||
@@ -23,6 +34,8 @@ class Planting < ActiveRecord::Base
|
||||
|
||||
default_scope order("created_at desc")
|
||||
|
||||
validates :crop_id, :presence => {:message => "must be present and exist in our database"}
|
||||
|
||||
validates :quantity,
|
||||
:numericality => { :only_integer => true },
|
||||
:allow_nil => true
|
||||
@@ -51,6 +64,14 @@ class Planting < ActiveRecord::Base
|
||||
:allow_nil => true,
|
||||
:allow_blank => true
|
||||
|
||||
validate :finished_must_be_after_planted
|
||||
|
||||
# check that any finished_at date occurs after planted_at
|
||||
def finished_must_be_after_planted
|
||||
return unless planted_at and finished_at # only check if we have both
|
||||
errors.add(:finished_at, "must be after the planting date") unless planted_at < finished_at
|
||||
end
|
||||
|
||||
def planting_slug
|
||||
"#{owner.login_name}-#{garden}-#{crop}".downcase.gsub(' ', '-')
|
||||
end
|
||||
@@ -76,15 +97,15 @@ class Planting < ActiveRecord::Base
|
||||
# return a list of interesting plantings, for the homepage etc.
|
||||
# we can't do this via a scope (as far as we know) so sadly we have to
|
||||
# do it this way.
|
||||
def Planting.interesting
|
||||
howmany = 12 # max amount to collect
|
||||
|
||||
def Planting.interesting(howmany=12, require_photo=true)
|
||||
interesting_plantings = Array.new
|
||||
seen_owners = Hash.new(false) # keep track of which owners we've seen already
|
||||
|
||||
Planting.all.each do |p|
|
||||
break if interesting_plantings.count == howmany # got enough yet?
|
||||
next unless p.interesting? # skip those that don't have photos
|
||||
if require_photo
|
||||
next unless p.photos.present? # skip those without photos, if required
|
||||
end
|
||||
next if seen_owners[p.owner] # skip if we already have one from this owner
|
||||
seen_owners[p.owner] = true # we've seen this owner
|
||||
interesting_plantings.push(p)
|
||||
|
||||
@@ -5,6 +5,9 @@ class Post < ActiveRecord::Base
|
||||
belongs_to :author, :class_name => 'Member'
|
||||
belongs_to :forum
|
||||
has_many :comments, :dependent => :destroy
|
||||
has_and_belongs_to_many :crops
|
||||
before_destroy {|post| post.crops.clear}
|
||||
after_save :update_crops_posts_association
|
||||
# also has_many notifications, but kinda meaningless to get at them
|
||||
# from this direction, so we won't set up an association for now.
|
||||
|
||||
@@ -39,4 +42,15 @@ class Post < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def update_crops_posts_association
|
||||
self.crops.destroy_all
|
||||
# look for crops mentioned in the post. eg. [tomato](crop)
|
||||
self.body.scan(Haml::Filters::GrowstuffMarkdown::CROP_REGEX) do |m|
|
||||
# find crop case-insensitively
|
||||
crop = Crop.where('lower(name) = ?', $1.downcase).first
|
||||
# create association
|
||||
self.crops << crop if crop and not self.crops.include?(crop)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,4 +3,13 @@ class Role < ActiveRecord::Base
|
||||
friendly_id :name, use: :slugged
|
||||
attr_accessible :description, :name, :members, :slug
|
||||
has_and_belongs_to_many :members
|
||||
|
||||
class << self
|
||||
[:crop_wranglers, :admins].each do |method|
|
||||
define_method method do
|
||||
slug = method.to_s.singularize.dasherize
|
||||
Role.where(slug: slug).try(:first).try(:members)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@ class Seed < ActiveRecord::Base
|
||||
extend FriendlyId
|
||||
friendly_id :seed_slug, use: :slugged
|
||||
|
||||
attr_accessible :owner_id, :crop_id, :description, :quantity, :plant_before,
|
||||
attr_accessible :owner_id, :crop_id, :description, :quantity, :plant_before,
|
||||
:tradable_to, :slug
|
||||
|
||||
belongs_to :crop
|
||||
@@ -10,6 +10,7 @@ class Seed < ActiveRecord::Base
|
||||
|
||||
default_scope order("created_at desc")
|
||||
|
||||
validates :crop, :presence => {:message => "must be present and exist in our database"}
|
||||
validates :quantity,
|
||||
:numericality => { :only_integer => true },
|
||||
:allow_nil => true
|
||||
@@ -55,6 +56,6 @@ class Seed < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def seed_slug
|
||||
"#{owner.login_name}-#{crop.name}".downcase.gsub(' ', '-')
|
||||
"#{owner.login_name}-#{crop}".downcase.gsub(' ', '-')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
%dd= link_to 'media@growstuff.org', 'mailto:media@growstuff.org'
|
||||
%dl
|
||||
%dt Twitter
|
||||
%dd= link_to '@Growstuff', 'http:/twitter.com/Growstuff'
|
||||
%dd= link_to '@growstufforg', 'http://twitter.com/growstufforg'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%h1 Editing account_type
|
||||
-content_for :title, "Editing account type"
|
||||
|
||||
= render 'form'
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%h1 Listing account_types
|
||||
- content_for :title, "Listing account types"
|
||||
|
||||
%table
|
||||
%tr
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%h1 New account_type
|
||||
- content_for :title, "New account type"
|
||||
|
||||
= render 'form'
|
||||
|
||||
|
||||
@@ -12,4 +12,6 @@
|
||||
|
||||
= link_to 'Edit', edit_account_type_path(@account_type)
|
||||
\|
|
||||
= link_to 'Delete', @account_type, :method => :delete, :data => { :confirm => 'Are you sure?' }
|
||||
\|
|
||||
= link_to 'Back', account_types_path
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%h1 Editing account
|
||||
- content_for :title, "Editing account"
|
||||
|
||||
= render 'form'
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%h1 Listing accounts
|
||||
- content_for :title, "Listing accounts"
|
||||
|
||||
%table
|
||||
%tr
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%h1 New account
|
||||
- content_for :title, "New account"
|
||||
|
||||
= render 'form'
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
%h2 Manage
|
||||
|
||||
%ul
|
||||
%ul#admin_links
|
||||
%li= link_to "Account types", account_types_path
|
||||
%li= link_to "Products", products_path
|
||||
%li= link_to "Roles", roles_path
|
||||
|
||||
@@ -36,4 +36,4 @@
|
||||
@
|
||||
= price_with_currency(o.price)
|
||||
%br/
|
||||
%td= link_to 'Details', order, :class => 'btn btn-mini'
|
||||
%td= link_to 'Details', order, :class => 'btn btn-default btn-xs'
|
||||
|
||||
25
app/views/alternate_names/_form.html.haml
Normal file
25
app/views/alternate_names/_form.html.haml
Normal file
@@ -0,0 +1,25 @@
|
||||
= form_for @alternate_name, :html => {:class => 'form-horizontal', :role => "form"} do |f|
|
||||
- if @alternate_name.errors.any?
|
||||
#error_explanation
|
||||
%h2= "#{pluralize(@alternate_name.errors.count, "error")} prohibited this alternate_name from being saved:"
|
||||
%ul
|
||||
- @alternate_name.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
||||
%p
|
||||
%span.help-block
|
||||
For detailed crop wrangling guidelines, please consult the
|
||||
=link_to "crop wrangling guide", "http://wiki.growstuff.org/index.php/Crop_wrangling"
|
||||
on the Growstuff wiki.
|
||||
|
||||
.form-group
|
||||
= f.label :crop_id, :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= collection_select(:alternate_name, :crop_id, Crop.all, :id, :name, { :selected => @alternate_name.crop_id || @crop.id }, :class => 'form-control')
|
||||
.form-group
|
||||
= f.label :name, :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_field :name, :class => 'form-control'
|
||||
.form-group
|
||||
.form-actions.col-md-offset-2.col-md-8
|
||||
= f.submit 'Save', :class => 'btn btn-primary'
|
||||
9
app/views/alternate_names/edit.html.haml
Normal file
9
app/views/alternate_names/edit.html.haml
Normal file
@@ -0,0 +1,9 @@
|
||||
- content_for :title, "Edit alternate name"
|
||||
|
||||
%p
|
||||
Added by
|
||||
= @alternate_name.creator
|
||||
= distance_of_time_in_words(@alternate_name.created_at, Time.zone.now)
|
||||
ago.
|
||||
|
||||
= render 'form'
|
||||
23
app/views/alternate_names/index.html.haml
Normal file
23
app/views/alternate_names/index.html.haml
Normal file
@@ -0,0 +1,23 @@
|
||||
- content_for :title, "Listing alternate names"
|
||||
|
||||
- if can? :create, AlternateName
|
||||
%p= link_to 'New alternate name', new_alternate_name_path, :class => 'btn btn-primary'
|
||||
|
||||
%table
|
||||
%tr
|
||||
%th Alternate name
|
||||
%th Crop
|
||||
%th
|
||||
%th
|
||||
|
||||
- @alternate_names.each do |alternate_name|
|
||||
%tr
|
||||
%td= link_to alternate_name.name, alternate_name
|
||||
%td= alternate_name.crop
|
||||
%td= link_to 'Show', alternate_name
|
||||
%td
|
||||
- if can? :edit, alternate_name
|
||||
= link_to 'Edit', edit_alternate_name_path(alternate_name), :class => 'btn btn-default btn-xs'
|
||||
%td
|
||||
- if can? :destroy, alternate_name
|
||||
= link_to 'Delete', alternate_name, method: :delete, data: { confirm: 'Are you sure?' }, :class => 'btn btn-default btn-xs'
|
||||
3
app/views/alternate_names/new.html.haml
Normal file
3
app/views/alternate_names/new.html.haml
Normal file
@@ -0,0 +1,3 @@
|
||||
- content_for :title, "New alternate name"
|
||||
|
||||
= render 'form'
|
||||
13
app/views/alternate_names/show.html.haml
Normal file
13
app/views/alternate_names/show.html.haml
Normal file
@@ -0,0 +1,13 @@
|
||||
%p#notice= notice
|
||||
|
||||
%p
|
||||
%b Alternate name:
|
||||
= @alternate_name.name
|
||||
%p
|
||||
%b Crop:
|
||||
= link_to @alternate_name.crop, @alternate_name.crop
|
||||
|
||||
- if can? :edit, @alternate_name
|
||||
= link_to 'Edit', edit_alternate_name_path(@alternate_name), :class => 'btn btn-default btn-xs'
|
||||
\|
|
||||
= link_to 'Back', alternate_names_path
|
||||
@@ -1,4 +1,4 @@
|
||||
%h1 Linked accounts on other sites
|
||||
- content_for :title, "Linked accounts on other sites"
|
||||
|
||||
- if @authentications
|
||||
- unless @authentications.empty?
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
= form_for @comment do |f|
|
||||
= form_for(@comment, :html => {:class => "form-horizontal", :role => "form"}) do |f|
|
||||
- if @comment.errors.any?
|
||||
#error_explanation
|
||||
%h2= "#{pluralize(@comment.errors.count, "error")} prohibited this comment from being saved:"
|
||||
@@ -6,8 +6,9 @@
|
||||
- @comment.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
||||
.field
|
||||
= f.text_area :body, :rows => 6, :class => 'input-block-level'
|
||||
.form-group
|
||||
= f.label :body, "Your comment:"
|
||||
= f.text_area :body, :rows => 6, :class => 'form-control'
|
||||
%span.help-block
|
||||
= render :partial => "shared/markdown_help"
|
||||
.actions
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
.well
|
||||
.comment
|
||||
.row-fluid
|
||||
.span1
|
||||
.row
|
||||
.col-md-1
|
||||
= render :partial => "members/avatar", :locals => { :member => comment.author }
|
||||
.span11
|
||||
.col-md-11
|
||||
.comment-meta
|
||||
Posted by
|
||||
= link_to comment.author.login_name, member_path(comment.author)
|
||||
@@ -17,8 +17,8 @@
|
||||
- if can? :edit, comment or can? :destroy, comment
|
||||
.comment-actions
|
||||
- if can? :edit, comment
|
||||
= link_to 'Edit', edit_comment_path(comment), :class => 'btn btn-mini'
|
||||
= link_to 'Edit', edit_comment_path(comment), :class => 'btn btn-default btn-xs'
|
||||
- if can? :destroy, comment
|
||||
= link_to 'Delete', comment, method: :delete, |
|
||||
data: { confirm: 'Are you sure?' }, :class => 'btn btn-mini'
|
||||
data: { confirm: 'Are you sure?' }, :class => 'btn btn-default btn-xs'
|
||||
|
||||
|
||||
@@ -4,6 +4,4 @@
|
||||
|
||||
= render :partial => "posts/comments", :locals => {:post => @post || @comment.post}
|
||||
|
||||
%p Your comment:
|
||||
|
||||
= render 'form'
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
%li
|
||||
= link_to "#{seed.owner} will trade #{seed.tradable_to}.", seed_path(seed)
|
||||
= 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 your seeds to trade.", new_seed_path()
|
||||
= 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' }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
= form_for @crop, :html => {:class => 'form-horizontal'} do |f|
|
||||
= form_for @crop, :html => {:class => 'form-horizontal', :role => "form"} do |f|
|
||||
- if @crop.errors.any?
|
||||
#error_explanation
|
||||
%h3= "#{pluralize(@crop.errors.count, "error")} prohibited this crop from being saved:"
|
||||
@@ -12,32 +12,34 @@
|
||||
=link_to "crop wrangling guide", "http://wiki.growstuff.org/index.php/Crop_wrangling"
|
||||
on the Growstuff wiki.
|
||||
|
||||
.control-group
|
||||
= f.label :name, :class => 'control-label'
|
||||
.controls
|
||||
= f.text_field :name
|
||||
%span.help-inline Name in US English; singular; capitalize proper nouns only.
|
||||
.control-group
|
||||
= f.label :en_wikipedia_url, 'Wikipedia URL', :class => 'control-label'
|
||||
.controls
|
||||
= f.text_field :en_wikipedia_url
|
||||
%span.help-inline Link to this crop's page on the English language Wikipedia.
|
||||
.control-group
|
||||
= f.label :parent_id, 'Parent crop', :class => 'control-label'
|
||||
.controls
|
||||
= collection_select(:crop, :parent_id, Crop.all, :id, :name, {:include_blank => true})
|
||||
%span.help-inline Optional. For setting up crop hierarchies for varieties etc.
|
||||
.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.
|
||||
.form-group
|
||||
= f.label :en_wikipedia_url, 'Wikipedia URL', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_field :en_wikipedia_url, :class => 'form-control'
|
||||
%span.help-block Link to this crop's page on the English language Wikipedia.
|
||||
.form-group
|
||||
= f.label :parent_id, 'Parent crop', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= collection_select(:crop, :parent_id, Crop.all, :id, :name, {:include_blank => true}, :class => 'form-control')
|
||||
%span.help-block Optional. For setting up crop hierarchies for varieties etc.
|
||||
%p
|
||||
%span.help-block
|
||||
You may enter up to 3 scientific names for a crop. Most crops will have only one.
|
||||
= f.fields_for :scientific_names do |sn|
|
||||
.control-group
|
||||
= sn.label :scientific_name, "Scientific name", :class => 'control-label'
|
||||
.controls
|
||||
= sn.text_field :scientific_name
|
||||
.form-group
|
||||
= sn.label :scientific_name, "Scientific name", :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= sn.text_field :scientific_name, :class => 'form-control'
|
||||
.col-md-2
|
||||
- if sn.object && sn.object.persisted?
|
||||
%label.checkbox
|
||||
= sn.check_box :_destroy
|
||||
= sn.label :_destroy, "Delete"
|
||||
.form-actions
|
||||
= f.submit 'Save', :class => 'btn btn-primary'
|
||||
.form-group
|
||||
.form-actions.col-md-offset-2.col-md-8
|
||||
= f.submit 'Save', :class => 'btn btn-primary'
|
||||
|
||||
@@ -4,16 +4,18 @@
|
||||
Nobody has harvested this crop yet.
|
||||
- else
|
||||
%ul
|
||||
- crop.harvests.each do |harvest|
|
||||
- crop.harvests.take(3).each do |harvest|
|
||||
%li
|
||||
= link_to "#{harvest.owner} harvested #{display_quantity(harvest)}.", harvest_path(harvest)
|
||||
= render :partial => 'members/location', :locals => { :member => harvest.owner }
|
||||
%small
|
||||
= distance_of_time_in_words(harvest.created_at, Time.zone.now)
|
||||
ago.
|
||||
|
||||
%p
|
||||
= link_to "View all #{crop.name} harvests", harvests_by_crop_path(crop)
|
||||
- if current_member
|
||||
= link_to "Track your #{crop.name} harvests.", new_harvest_path()
|
||||
%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" }
|
||||
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
= link_to |
|
||||
image_tag( |
|
||||
crop.default_photo ? crop.default_photo.thumbnail_url : 'placeholder_150.png', |
|
||||
:alt => crop.name |
|
||||
:alt => crop.name, |
|
||||
:class => 'image-responsive crop-image' |
|
||||
), |
|
||||
crop, |
|
||||
:rel => "popover", |
|
||||
'data-trigger' => 'hover', |
|
||||
'data-title' => crop.name, |
|
||||
'data-content' => "#{ render :partial => 'crops/popover', :locals => { :crop => crop } }", |
|
||||
'data-html' => true
|
||||
'data-html' => true |
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
.well
|
||||
.row-fluid
|
||||
.span4
|
||||
= link_to image_tag((crop.default_photo ? crop.default_photo.thumbnail_url : 'placeholder_150.png'), :alt => '', :class => 'img-rounded'), crop
|
||||
.span8
|
||||
.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
|
||||
.col-md-8
|
||||
%h3{:style => 'padding-top: 0px; margin-top: 0px'}
|
||||
= link_to crop, crop
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
.row-fluid
|
||||
- if !crop.photos.empty?
|
||||
%ul.thumbnails
|
||||
.row
|
||||
- if !crop.photos.empty?
|
||||
- crop.photos.first(3).each do |p|
|
||||
%li.span4
|
||||
.col-md-4
|
||||
= render :partial => "photos/thumbnail", :locals => { :photo => p }
|
||||
|
||||
21
app/views/crops/_plantings.html.haml
Normal file
21
app/views/crops/_plantings.html.haml
Normal file
@@ -0,0 +1,21 @@
|
||||
%h4 Plantings
|
||||
- if crop.plantings.empty?
|
||||
%p
|
||||
Nobody has planted this crop yet.
|
||||
- else
|
||||
%ul
|
||||
- crop.plantings.take(3).each do |planting|
|
||||
%li
|
||||
= link_to "#{planting.owner} planted #{planting.quantity} #{planting.planted_from}.", planting_path(planting)
|
||||
= render :partial => 'members/location', :locals => { :member => planting.owner }
|
||||
%small
|
||||
= distance_of_time_in_words(planting.created_at, Time.zone.now)
|
||||
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" }
|
||||
|
||||
@@ -5,18 +5,19 @@
|
||||
View any crop page to see which of our members have planted it and find
|
||||
information on how to grow it yourself.
|
||||
|
||||
= form_tag(crops_path, :method => :get, :class => 'form-inline') do
|
||||
= label_tag :sort, "Sort by:", :class => 'control-label'
|
||||
= select_tag "sort", options_for_select({"Popularity" => 'popular', "Alphabetical" => 'alpha'}, @sort || 'popular')
|
||||
= form_tag(crops_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 by popularity" => 'popular', "Sort alphabetically" => 'alpha'}, @sort || 'popular'), :class => 'form-control'
|
||||
= submit_tag "Show", :class => 'btn btn-primary'
|
||||
|
||||
%div.pagination
|
||||
= page_entries_info @crops, :model => "crops"
|
||||
= will_paginate @crops
|
||||
|
||||
%ul.thumbnails
|
||||
.row
|
||||
- @crops.each do |crop|
|
||||
%li.span2.six-across
|
||||
.col-md-2.six-across
|
||||
= render :partial => "thumbnail", :locals => { :crop => crop }
|
||||
|
||||
- if can? :create, Crop
|
||||
@@ -28,7 +29,7 @@
|
||||
= will_paginate @crops
|
||||
|
||||
|
||||
%ul.inline
|
||||
%ul.list-inline
|
||||
%li The data on this page is available in the following formats:
|
||||
%li= link_to "CSV", crops_path(:format => 'csv')
|
||||
%li= link_to "JSON", crops_path(:format => 'json')
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
%div#exact_match
|
||||
%h2 Exact match
|
||||
|
||||
%ul.thumbnails
|
||||
%li.span2.six-across
|
||||
.row
|
||||
.col-md-2.six-across
|
||||
= render :partial => "thumbnail", :locals => { :crop => @exact_match }
|
||||
|
||||
- if ! @partial_matches.empty?
|
||||
%div#partial_matches
|
||||
%h2 Partial matches
|
||||
%ul.thumbnails
|
||||
.row
|
||||
- @partial_matches.each do |c|
|
||||
%li.span2.six-across
|
||||
.col-md-2.six-across
|
||||
= render :partial => "thumbnail", :locals => { :crop => c }
|
||||
|
||||
@@ -1,18 +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'
|
||||
|
||||
.row-fluid
|
||||
.span9
|
||||
%p
|
||||
- if can? :create, Planting
|
||||
= link_to "Plant this", new_planting_path(:crop_id => @crop.id), :class => 'btn btn-primary'
|
||||
- else
|
||||
= render :partial => 'shared/signin_signup', :locals => { :to => 'plant this crop' }
|
||||
- if can? :create, Harvest
|
||||
= link_to "Harvest this", new_harvest_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-primary'
|
||||
- if can? :create, Seed
|
||||
= link_to 'Add seeds to stash', new_seed_path(:params => { :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-primary'
|
||||
|
||||
.row
|
||||
.col-md-9
|
||||
|
||||
= render :partial => 'photos', :locals => { :crop => @crop }
|
||||
|
||||
@@ -33,14 +33,20 @@
|
||||
|
||||
%div#cropmap
|
||||
|
||||
- if @crop.plantings.size > 0
|
||||
%a{:name => 'posts'}
|
||||
%div.pagination
|
||||
= page_entries_info @posts, :model => "posts"
|
||||
= will_paginate @posts, :params => {:anchor => "posts"}
|
||||
|
||||
%h2 All plantings
|
||||
- unless @posts.empty?
|
||||
- @posts.each do |post|
|
||||
= render :partial => "posts/single", :locals => { :post => post, :subject => true }
|
||||
|
||||
- @crop.plantings.each do |p|
|
||||
= render :partial => "plantings/thumbnail", :locals => { :planting => p, :title => 'owner' }
|
||||
%div.pagination
|
||||
= page_entries_info @posts, :model => "posts"
|
||||
= will_paginate @posts, :params => {:anchor => "posts"}
|
||||
|
||||
.span3
|
||||
.col-md-3
|
||||
- if can? :edit, @crop or can? :destroy, @crop
|
||||
%h4 Crop wrangling
|
||||
%p
|
||||
@@ -49,22 +55,37 @@
|
||||
%strong CROP WRANGLER
|
||||
%p
|
||||
- if can? :edit, @crop
|
||||
= link_to 'Edit crop', edit_crop_path(@crop), { :class => 'btn btn-mini' }
|
||||
= link_to 'Edit crop', edit_crop_path(@crop), { :class => 'btn btn-default btn-xs' }
|
||||
- if can? :destroy, @crop
|
||||
= link_to 'Delete crop', @crop, method: :delete, data: { confirm: 'Are you sure?' }, :class => 'btn btn-mini'
|
||||
= link_to 'Delete crop', @crop, method: :delete, data: { confirm: 'Are you sure?' }, :class => 'btn btn-default btn-xs'
|
||||
|
||||
%h4 Scientific names
|
||||
%ul
|
||||
- @crop.scientific_names.each do |sn|
|
||||
%li
|
||||
= sn.scientific_name
|
||||
- if can? :edit, sn
|
||||
= link_to 'Edit', edit_scientific_name_path(sn), { :class => 'btn btn-mini' }
|
||||
- if can? :destroy, sn
|
||||
= link_to 'Delete', sn, method: :delete, data: { confirm: 'Are you sure?' }, :class => 'btn btn-mini'
|
||||
%p
|
||||
- if can? :edit, @crop
|
||||
= link_to 'Add', new_scientific_name_path( :crop_id => @crop.id ), { :class => 'btn btn-mini' }
|
||||
.scientific_names
|
||||
%h4 Scientific names
|
||||
%ul
|
||||
- @crop.scientific_names.each do |sn|
|
||||
%li
|
||||
= sn.scientific_name
|
||||
- if can? :edit, sn
|
||||
= link_to 'Edit', edit_scientific_name_path(sn), { :class => 'btn btn-default btn-xs' }
|
||||
- if can? :destroy, sn
|
||||
= link_to 'Delete', sn, method: :delete, data: { confirm: 'Are you sure?' }, :class => 'btn btn-default btn-xs'
|
||||
%p
|
||||
- if can? :edit, @crop
|
||||
= link_to 'Add', new_scientific_name_path( :crop_id => @crop.id ), { :class => 'btn btn-default btn-xs' }
|
||||
|
||||
.alternate_names
|
||||
%h4 Alternate names
|
||||
%ul
|
||||
- @crop.alternate_names.each do |an|
|
||||
%li
|
||||
= an.name
|
||||
- if can? :edit, an
|
||||
= link_to 'Edit', edit_alternate_name_path(an), { :class => 'btn btn-default btn-xs' }
|
||||
- if can? :destroy, an
|
||||
= link_to 'Delete', an, method: :delete, data: { confirm: 'Are you sure?' }, :class => 'btn btn-default btn-xs'
|
||||
%p
|
||||
- if can? :edit, @crop
|
||||
= link_to 'Add', new_alternate_name_path( :crop_id => @crop.id ), { :class => 'btn btn-default btn-xs' }
|
||||
|
||||
= render :partial => 'varieties', :locals => { :crop => @crop }
|
||||
|
||||
@@ -76,5 +97,6 @@
|
||||
%ul
|
||||
%li= link_to 'Wikipedia (English)', @crop.en_wikipedia_url
|
||||
|
||||
= render :partial => 'plantings', :locals => { :crop => @crop }
|
||||
= render :partial => 'harvests', :locals => { :crop => @crop }
|
||||
= render :partial => 'find_seeds', :locals => { :crop => @crop }
|
||||
|
||||
@@ -8,6 +8,13 @@
|
||||
%li= link_to "Full crop hierarchy", crops_hierarchy_path
|
||||
%li= link_to "Add Crop", new_crop_path
|
||||
|
||||
%div.crop_wranglers
|
||||
%h2 Crop Wranglers:
|
||||
%ul
|
||||
- @crop_wranglers.each do |crop_wrangler|
|
||||
%li.crop_wrangler
|
||||
= link_to crop_wrangler.login_name, crop_wrangler
|
||||
|
||||
%h2 Recently added crops
|
||||
|
||||
%div.pagination
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
- content_for :title, "Resend confirmation instructions"
|
||||
|
||||
= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f|
|
||||
= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post, :class => 'form-horizontal', :role => 'form' }) do |f|
|
||||
= devise_error_messages!
|
||||
|
||||
%p Enter either your login name or your email address to resend the confirmation email.
|
||||
|
||||
= f.label :login
|
||||
= f.text_field :login
|
||||
= f.submit "Resend confirmation instructions"
|
||||
.form-group
|
||||
= f.label :login, :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_field :login, :class => 'form-control'
|
||||
.form-group
|
||||
.form-actions.col-md-offset-2.col-md-8
|
||||
= f.submit "Resend confirmation instructions", :class => 'btn btn-primary'
|
||||
|
||||
= render "devise/shared/links"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
Your account on #{site_name} has been created. You just need to confirm
|
||||
your email address through the link below:
|
||||
|
||||
%p= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token)
|
||||
%p= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @token)
|
||||
|
||||
%p
|
||||
Once you're confirmed, you can sign in with your login name
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
Someone has requested a link to reset your password on #{site_name}.
|
||||
We presume this was you, in which case you can do so through this link:
|
||||
|
||||
%p= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token)
|
||||
%p= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @token)
|
||||
|
||||
%p
|
||||
If it wasn't you, then someone's made a typo or has been messing
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user