mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-05-25 09:19:15 -04:00
Compare commits
646 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19f4ee51ed | ||
|
|
7be346d92f | ||
|
|
51a0a33b2a | ||
|
|
0df52b3cd8 | ||
|
|
18cd8f5966 | ||
|
|
5a31619b46 | ||
|
|
4b4e0cf69a | ||
|
|
8659ebca2d | ||
|
|
238e0be682 | ||
|
|
4324ceb8ca | ||
|
|
19d6c8f293 | ||
|
|
bcb74c7ed8 | ||
|
|
4a341bb7ab | ||
|
|
02615dc522 | ||
|
|
436527b902 | ||
|
|
260b16da11 | ||
|
|
cf0a646699 | ||
|
|
12ad16a05a | ||
|
|
d525afb07c | ||
|
|
3ff3ffa457 | ||
|
|
e3c689ba6b | ||
|
|
82553d6e0a | ||
|
|
3644a8124f | ||
|
|
5cfa051d75 | ||
|
|
69fb98146b | ||
|
|
01b9e76814 | ||
|
|
594e3470b4 | ||
|
|
7bb7a18b66 | ||
|
|
de4b89fdf7 | ||
|
|
e8d7ed0c2d | ||
|
|
a3ad9189b1 | ||
|
|
da588b7fdb | ||
|
|
a7f9e113d6 | ||
|
|
c3a883de16 | ||
|
|
aa2a761a58 | ||
|
|
a800630b01 | ||
|
|
b69bb219a3 | ||
|
|
a9330f2d77 | ||
|
|
de8bcc38d3 | ||
|
|
99a3be08eb | ||
|
|
d9f04d1fa9 | ||
|
|
1791ed5b01 | ||
|
|
683ec9dd9d | ||
|
|
668f6d3a2b | ||
|
|
90e9017a19 | ||
|
|
9f3cb7ee8b | ||
|
|
df952a1779 | ||
|
|
3748f954c5 | ||
|
|
dcbacddb58 | ||
|
|
b0adec20e7 | ||
|
|
4e7e82c8a8 | ||
|
|
6905cd410d | ||
|
|
66bb130a1a | ||
|
|
03ae327e30 | ||
|
|
c1fde41f1f | ||
|
|
a10f6e4783 | ||
|
|
a76d2a3eb0 | ||
|
|
049886459a | ||
|
|
b57cb581dd | ||
|
|
f23cb78dcb | ||
|
|
bfffaab77f | ||
|
|
32af1b28a8 | ||
|
|
9fa72fa5f7 | ||
|
|
e35b15c868 | ||
|
|
22e0e8fba0 | ||
|
|
8caea57c47 | ||
|
|
24dd02a439 | ||
|
|
45c8092a94 | ||
|
|
48829dba3c | ||
|
|
46ee2168e1 | ||
|
|
5dd52ba17f | ||
|
|
78a65f26c6 | ||
|
|
83929cc8ee | ||
|
|
a49f359f9e | ||
|
|
be87d2861a | ||
|
|
481ca79cc1 | ||
|
|
346979c640 | ||
|
|
ec891fc0b2 | ||
|
|
ef6dd88b1e | ||
|
|
5884718fd0 | ||
|
|
0c315981df | ||
|
|
342323e566 | ||
|
|
d7582625c5 | ||
|
|
1cb4181ffc | ||
|
|
9e7a80cb86 | ||
|
|
a8924f95a7 | ||
|
|
7afd38a1ee | ||
|
|
1908f670d9 | ||
|
|
7ef1eb5852 | ||
|
|
f36e9d726e | ||
|
|
0adb24fa4d | ||
|
|
aacf7b1f09 | ||
|
|
b74d89c482 | ||
|
|
3e8f017ad0 | ||
|
|
2e45857e1f | ||
|
|
b8b511e747 | ||
|
|
85f7ca4058 | ||
|
|
ff779b6679 | ||
|
|
f357916779 | ||
|
|
1c4d740217 | ||
|
|
fbb442dfee | ||
|
|
631747e919 | ||
|
|
ff00b2c985 | ||
|
|
a0cdf3a8b2 | ||
|
|
c87a5f2d6b | ||
|
|
53ed4f5b24 | ||
|
|
3300c303be | ||
|
|
e84aaeb56d | ||
|
|
cad361ed7a | ||
|
|
1187719e7b | ||
|
|
859cf7f215 | ||
|
|
6d97a060c3 | ||
|
|
fc04dde1e8 | ||
|
|
e3c52b1a56 | ||
|
|
3258a6754c | ||
|
|
db876ff107 | ||
|
|
b87194336f | ||
|
|
0e98f84da7 | ||
|
|
b9ce3d4fe6 | ||
|
|
d65ab59d35 | ||
|
|
65e0752376 | ||
|
|
3251dd1c54 | ||
|
|
c404d8220d | ||
|
|
48409698ab | ||
|
|
4d7c4f38ae | ||
|
|
c2e4686a23 | ||
|
|
24df32ba7f | ||
|
|
744caef4f2 | ||
|
|
5cac8743f8 | ||
|
|
9c4d83dad3 | ||
|
|
fd3e69c9ab | ||
|
|
b6dfeb980c | ||
|
|
e784ec9b33 | ||
|
|
1df0c36e72 | ||
|
|
a5e7a8d315 | ||
|
|
cafd49c143 | ||
|
|
7c7c66348c | ||
|
|
00ae4ed49f | ||
|
|
12a1484a26 | ||
|
|
5bacdb71cc | ||
|
|
6565e79057 | ||
|
|
f4e53a58de | ||
|
|
97cf1347d5 | ||
|
|
367e298d48 | ||
|
|
e765387e22 | ||
|
|
7b30c4237b | ||
|
|
b788cb44ef | ||
|
|
f61e2438e8 | ||
|
|
e503b1079d | ||
|
|
ccca343959 | ||
|
|
63de10efd4 | ||
|
|
17c5fd61a3 | ||
|
|
44b8500fa8 | ||
|
|
5a12b47c7c | ||
|
|
3db13785a1 | ||
|
|
69d94f7deb | ||
|
|
1ec188c793 | ||
|
|
438b2444df | ||
|
|
de981689fc | ||
|
|
0681fac406 | ||
|
|
9682300b85 | ||
|
|
6f95f1fecf | ||
|
|
43fe29f113 | ||
|
|
fa50ff47bb | ||
|
|
5b19d236d0 | ||
|
|
cad2c90a4f | ||
|
|
919c25ca67 | ||
|
|
29f3cc3238 | ||
|
|
bc9a025788 | ||
|
|
a593aa2a4b | ||
|
|
de63fdc952 | ||
|
|
e7d2ae2c40 | ||
|
|
1019834c41 | ||
|
|
362f7a78b1 | ||
|
|
a2eb568eac | ||
|
|
9d62c012f1 | ||
|
|
cbb50df8d0 | ||
|
|
91a128ae7e | ||
|
|
d9dd797c33 | ||
|
|
f970fc4db2 | ||
|
|
8873986562 | ||
|
|
40b5a47aae | ||
|
|
f29c0ad085 | ||
|
|
96b0198d41 | ||
|
|
48649d1986 | ||
|
|
9e2e93b544 | ||
|
|
1f0f55dc81 | ||
|
|
e3738ca0c6 | ||
|
|
b0d4f9c731 | ||
|
|
83b54365ba | ||
|
|
5a33b2b754 | ||
|
|
69cb87fd0f | ||
|
|
bc058b9152 | ||
|
|
efd6328436 | ||
|
|
19adabc55f | ||
|
|
ccde5b230b | ||
|
|
33f28d1727 | ||
|
|
aa3cf729c8 | ||
|
|
611adc0728 | ||
|
|
a97acfb1ca | ||
|
|
7e03ef1687 | ||
|
|
c12791e428 | ||
|
|
bbe7d967b4 | ||
|
|
f5336bd8f8 | ||
|
|
5a35a3da01 | ||
|
|
fad9eddbc4 | ||
|
|
044f62eae2 | ||
|
|
4786e3e087 | ||
|
|
715a004b13 | ||
|
|
96a007ef3b | ||
|
|
a03d044049 | ||
|
|
9a6c32fe6d | ||
|
|
fc33269f47 | ||
|
|
a80001ffe7 | ||
|
|
30032f5527 | ||
|
|
fa8b10af58 | ||
|
|
9c469fb217 | ||
|
|
6b944e145e | ||
|
|
7748c40ccf | ||
|
|
745281545a | ||
|
|
7b3aefacd3 | ||
|
|
134465d023 | ||
|
|
a581b759a3 | ||
|
|
734b57e395 | ||
|
|
12d151b68c | ||
|
|
472acd0e81 | ||
|
|
7f88b167b4 | ||
|
|
897eac4fae | ||
|
|
41ab646b20 | ||
|
|
8c017b24e0 | ||
|
|
12e3351c77 | ||
|
|
85e4708b71 | ||
|
|
0816b6b114 | ||
|
|
91b5c3e798 | ||
|
|
fc1dc0e4c3 | ||
|
|
9e7957709d | ||
|
|
492bdd915f | ||
|
|
07c976b1fc | ||
|
|
2faada7f14 | ||
|
|
133a67a8f2 | ||
|
|
16dd1e5183 | ||
|
|
4193e38034 | ||
|
|
9eaaa8856f | ||
|
|
3ea9f2b5f2 | ||
|
|
723329ac49 | ||
|
|
89424b931b | ||
|
|
9f3d3b2b8f | ||
|
|
80c1e1bf23 | ||
|
|
01e676678c | ||
|
|
43d7c36fc5 | ||
|
|
6a6f83c6a4 | ||
|
|
f73bb81eb6 | ||
|
|
0407df880b | ||
|
|
20219e23dc | ||
|
|
49bbbeb431 | ||
|
|
82bc07ccd0 | ||
|
|
212651a279 | ||
|
|
1568f4b7d8 | ||
|
|
b7706d0064 | ||
|
|
3a232e1d08 | ||
|
|
a27d273978 | ||
|
|
4e7b0cf698 | ||
|
|
28ac7ff886 | ||
|
|
fa0cb55789 | ||
|
|
42ffc49d74 | ||
|
|
d4d210447a | ||
|
|
7232f4614c | ||
|
|
b9a29115a2 | ||
|
|
23dbfea05a | ||
|
|
3259291eea | ||
|
|
0d0042dba8 | ||
|
|
edcbc939e3 | ||
|
|
c568498941 | ||
|
|
c6c8492528 | ||
|
|
3420f18fb9 | ||
|
|
377a54e692 | ||
|
|
8c1d88b663 | ||
|
|
c746c6d6d7 | ||
|
|
7607daa83a | ||
|
|
f11c1b3d54 | ||
|
|
fc44e87fb9 | ||
|
|
ccc63381a6 | ||
|
|
9b64f5fec1 | ||
|
|
e138b3e8ab | ||
|
|
92db75b3d8 | ||
|
|
755a60447f | ||
|
|
b599818512 | ||
|
|
0a2d0d499c | ||
|
|
e638acd2de | ||
|
|
8d5367be9a | ||
|
|
516274b2b7 | ||
|
|
f13a66391c | ||
|
|
b01385a1e4 | ||
|
|
4ca4d6b030 | ||
|
|
3181c97a2a | ||
|
|
372a7f080d | ||
|
|
29a8628c42 | ||
|
|
6d81ef198c | ||
|
|
e94cbcef02 | ||
|
|
9304e44c08 | ||
|
|
3a9077050e | ||
|
|
64cf71ab4f | ||
|
|
3fb9283ca7 | ||
|
|
a73d492062 | ||
|
|
2528d8af8a | ||
|
|
10cb6c18aa | ||
|
|
34e61082b9 | ||
|
|
b868364e96 | ||
|
|
45c0adb044 | ||
|
|
6c72345d26 | ||
|
|
4648464fb5 | ||
|
|
429e54733d | ||
|
|
daa8717807 | ||
|
|
44b260b1e8 | ||
|
|
9bd7448ae6 | ||
|
|
bb88041fef | ||
|
|
87f1c6c26d | ||
|
|
0e2a5fd205 | ||
|
|
5bb04dc1a4 | ||
|
|
7cedd98b71 | ||
|
|
2209d65aa6 | ||
|
|
ba906868d8 | ||
|
|
4cb3444dcd | ||
|
|
60df62f3fa | ||
|
|
e3a620a109 | ||
|
|
3476d58642 | ||
|
|
a7feac3740 | ||
|
|
ad5730a81c | ||
|
|
e35ebab380 | ||
|
|
3b42806b77 | ||
|
|
c5fbda0223 | ||
|
|
38dbdc8307 | ||
|
|
f830ed1cf2 | ||
|
|
66402e5471 | ||
|
|
8410b6db1e | ||
|
|
aef4800ad3 | ||
|
|
a3b9c50fea | ||
|
|
add275e772 | ||
|
|
9a2818baa9 | ||
|
|
c47a1bc361 | ||
|
|
e04f78d42d | ||
|
|
d6b83454fe | ||
|
|
8ce7c25374 | ||
|
|
c1ab161b89 | ||
|
|
0d55b54371 | ||
|
|
46ac06698a | ||
|
|
81ce6ed8a3 | ||
|
|
0f8d1e7db1 | ||
|
|
507e5a0ebc | ||
|
|
76608a981c | ||
|
|
81d2f9829e | ||
|
|
b7d7f6896e | ||
|
|
88e8e3a59e | ||
|
|
d95bd0e063 | ||
|
|
0c80a00f31 | ||
|
|
1fe9a7d5e0 | ||
|
|
bfb4053bb4 | ||
|
|
2aad8a0ed0 | ||
|
|
7c1cce40b2 | ||
|
|
d7ef598654 | ||
|
|
bd0da36d63 | ||
|
|
6dedf1b030 | ||
|
|
c253c86787 | ||
|
|
59d823ff8b | ||
|
|
c8903ba25f | ||
|
|
3a5cf37dc0 | ||
|
|
8631b1fc99 | ||
|
|
0c6b6e0e0e | ||
|
|
9f33f497bc | ||
|
|
920ca2948b | ||
|
|
ad6c1ba2dc | ||
|
|
674d78721d | ||
|
|
3a1bd78252 | ||
|
|
b9b2f4a57e | ||
|
|
e4e9b63e4d | ||
|
|
40adc379ae | ||
|
|
9e53105f43 | ||
|
|
6505254e6c | ||
|
|
2d73dd2869 | ||
|
|
2abe6d7d12 | ||
|
|
ead9a250af | ||
|
|
3a9ec8cf6d | ||
|
|
8990e803a0 | ||
|
|
c76e9ccbf6 | ||
|
|
375f647e2d | ||
|
|
1b62c5cb97 | ||
|
|
0f52ea5aad | ||
|
|
2740b5e47b | ||
|
|
3894127f5c | ||
|
|
17ccea4b28 | ||
|
|
7bca5c8845 | ||
|
|
017df0484c | ||
|
|
d338bab82b | ||
|
|
35f8c126b6 | ||
|
|
eafe90e295 | ||
|
|
7b911034b8 | ||
|
|
798b89db11 | ||
|
|
506e3e550c | ||
|
|
8ab3167a28 | ||
|
|
436e7e3c59 | ||
|
|
9f1091a1c8 | ||
|
|
3b1e855da3 | ||
|
|
da95581099 | ||
|
|
d7f8bff17a | ||
|
|
a7caa2fbaa | ||
|
|
b215ef03c7 | ||
|
|
3cbbbc79fb | ||
|
|
eb5e9e926b | ||
|
|
ef2d1eb683 | ||
|
|
965f87393d | ||
|
|
023333b15f | ||
|
|
2241a760c2 | ||
|
|
e4dabd1725 | ||
|
|
1dfac3fb2f | ||
|
|
d0f856d389 | ||
|
|
ac6aa730c1 | ||
|
|
cd12412b46 | ||
|
|
9e146cde18 | ||
|
|
9b195d1d2e | ||
|
|
eb76db93e4 | ||
|
|
6f80102f79 | ||
|
|
90ca5ec13b | ||
|
|
28288c51fe | ||
|
|
70cdab96c4 | ||
|
|
e906d293a2 | ||
|
|
abdc43ef40 | ||
|
|
3ed7edbfbf | ||
|
|
95974ab21b | ||
|
|
7e9f18e89e | ||
|
|
e09f050088 | ||
|
|
5bfab90b5b | ||
|
|
7a064c0667 | ||
|
|
3289e7b950 | ||
|
|
102763f2ab | ||
|
|
09a78d4661 | ||
|
|
e0d8126514 | ||
|
|
1c9081e788 | ||
|
|
5cceb2c4ff | ||
|
|
66fcad69fa | ||
|
|
9bcd2d0aa6 | ||
|
|
2ab73d3df6 | ||
|
|
623dbdd418 | ||
|
|
78c4fc36b1 | ||
|
|
9ccef5471f | ||
|
|
0fc4c3cb4d | ||
|
|
c61f7b8e72 | ||
|
|
b81e034254 | ||
|
|
07f7572b13 | ||
|
|
2697fea249 | ||
|
|
cbb4f9d7ac | ||
|
|
0dbc2e1964 | ||
|
|
319fc12ebb | ||
|
|
2db7bf638b | ||
|
|
36a9514add | ||
|
|
497121a221 | ||
|
|
e17a3a2ab1 | ||
|
|
49a5a26f17 | ||
|
|
b66b8263c1 | ||
|
|
c3429cc0f3 | ||
|
|
f7a2ec2054 | ||
|
|
95ecdad024 | ||
|
|
e6cbbb3e3d | ||
|
|
b78ccaa097 | ||
|
|
593d6e7ec1 | ||
|
|
4c6b96e590 | ||
|
|
8631c78f9a | ||
|
|
a69ad34359 | ||
|
|
1099e4c9fe | ||
|
|
3a53f4f8a8 | ||
|
|
076a6cc1df | ||
|
|
b842bff9cb | ||
|
|
80a28085f1 | ||
|
|
0dd8cbccf0 | ||
|
|
abece6473b | ||
|
|
ff1b941690 | ||
|
|
85a6eb6195 | ||
|
|
87bfceb035 | ||
|
|
b95975d632 | ||
|
|
075cdb8272 | ||
|
|
7db4dee61a | ||
|
|
0993917dc6 | ||
|
|
d6cb20e2c4 | ||
|
|
3e65656c7b | ||
|
|
47dc94f820 | ||
|
|
c1870d46a2 | ||
|
|
1988c22626 | ||
|
|
a795d452c6 | ||
|
|
656b0e44d8 | ||
|
|
af39df5e0c | ||
|
|
1f23e1a646 | ||
|
|
ca47127197 | ||
|
|
7ca89908cd | ||
|
|
2c94f61843 | ||
|
|
7326acba81 | ||
|
|
77b4b76f14 | ||
|
|
eca27d18ea | ||
|
|
2f67ffd2f8 | ||
|
|
40d13fadb3 | ||
|
|
3791f4aa6f | ||
|
|
79c60dc7c7 | ||
|
|
5035b65883 | ||
|
|
4211ebec76 | ||
|
|
d02edc3dd1 | ||
|
|
e86200b942 | ||
|
|
776b5450f9 | ||
|
|
c80b42d9bc | ||
|
|
6aa37e6e26 | ||
|
|
089a3d5c24 | ||
|
|
1741567e19 | ||
|
|
de5b16e384 | ||
|
|
a8c203aea0 | ||
|
|
2aa30475e9 | ||
|
|
9a5e15b292 | ||
|
|
bb9695b272 | ||
|
|
d436fd86f8 | ||
|
|
3f393b0937 | ||
|
|
fb271633d9 | ||
|
|
e40fad76fd | ||
|
|
41db12d8d7 | ||
|
|
28d29291a7 | ||
|
|
22e0379769 | ||
|
|
b60bbbcb4e | ||
|
|
0da121a48d | ||
|
|
6b6ffd6e58 | ||
|
|
21dbf87ab9 | ||
|
|
a5fcd9f860 | ||
|
|
2853bf5c70 | ||
|
|
2d55d88db8 | ||
|
|
2567e7cd74 | ||
|
|
e2bac619b7 | ||
|
|
0bdab0d9fc | ||
|
|
e55fe55dc7 | ||
|
|
5a474d523c | ||
|
|
d126392ee2 | ||
|
|
c38dc4661d | ||
|
|
675ac5a03f | ||
|
|
233f9dfd88 | ||
|
|
b414598b07 | ||
|
|
2c4e768a3a | ||
|
|
04d61bd040 | ||
|
|
5a96b7efd6 | ||
|
|
329e147add | ||
|
|
334b5bf63f | ||
|
|
58940a6765 | ||
|
|
2442539e1b | ||
|
|
b56237115e | ||
|
|
038192095d | ||
|
|
9a18b4b62b | ||
|
|
c6f5abc036 | ||
|
|
ad76a04e8f | ||
|
|
0b70be4939 | ||
|
|
da2590791e | ||
|
|
79a7958519 | ||
|
|
f4d452f3bc | ||
|
|
d08e2f09db | ||
|
|
d5cc3f300a | ||
|
|
1982cecc31 | ||
|
|
2f05f1dbdb | ||
|
|
a67a55c599 | ||
|
|
253b5a3f85 | ||
|
|
45aaf31722 | ||
|
|
d0b9917e84 | ||
|
|
32dcacf9ba | ||
|
|
0e0c309c15 | ||
|
|
6d1385f00c | ||
|
|
dabfeeea3c | ||
|
|
20b9996f14 | ||
|
|
1ea5257da4 | ||
|
|
3c360ab1b8 | ||
|
|
b1c60572ef | ||
|
|
14ad572f18 | ||
|
|
1481d01cf8 | ||
|
|
492781ea3d | ||
|
|
542d978d9c | ||
|
|
c893dcd271 | ||
|
|
57552455e3 | ||
|
|
fe69e2f11d | ||
|
|
684a3d2229 | ||
|
|
3d738e1b7c | ||
|
|
a5daa156ab | ||
|
|
018b2b4711 | ||
|
|
d12ec968c4 | ||
|
|
531a0bd9ea | ||
|
|
7950c577e9 | ||
|
|
3bc77f9b09 | ||
|
|
05260c05c2 | ||
|
|
d0ea54237e | ||
|
|
f404d54d02 | ||
|
|
5a2d9eabf4 | ||
|
|
1b936100e7 | ||
|
|
34ea6eb37f | ||
|
|
2c006ec430 | ||
|
|
f541261e43 | ||
|
|
f910fdfa73 | ||
|
|
72f86c4ad0 | ||
|
|
0cb192ce36 | ||
|
|
3aadc5d68f | ||
|
|
1387f381d2 | ||
|
|
7257f2e557 | ||
|
|
025bfdb4b0 | ||
|
|
1a50566328 | ||
|
|
ae914daa0c | ||
|
|
b984475335 | ||
|
|
59f5101858 | ||
|
|
e7d3e4d6dd | ||
|
|
79e1835216 | ||
|
|
e4072fb395 | ||
|
|
666d6dac48 | ||
|
|
ea5b340933 | ||
|
|
534c299383 | ||
|
|
99eb33ccbb | ||
|
|
760e5ca74e | ||
|
|
cd57c9cd34 | ||
|
|
2a184bcb2e | ||
|
|
df63819602 | ||
|
|
628d5b24c1 | ||
|
|
8221d5b441 | ||
|
|
ad7cfdabd0 | ||
|
|
4237dfb269 | ||
|
|
c512b079fa | ||
|
|
b4cd151a03 | ||
|
|
eab958eac4 | ||
|
|
17e94e01d0 | ||
|
|
6451077d1d | ||
|
|
ce265e281a | ||
|
|
f4511c79e6 | ||
|
|
06c7703628 | ||
|
|
619e8590c8 | ||
|
|
88efcf4da6 | ||
|
|
556ba33172 | ||
|
|
9d7b939d42 | ||
|
|
0bea278c5c | ||
|
|
65814f1ef6 | ||
|
|
db0eb3e4da | ||
|
|
323f635b7b | ||
|
|
729dba8e0b | ||
|
|
85c3e74cdd | ||
|
|
f9cec41360 | ||
|
|
19dcf6f3d5 | ||
|
|
1d28c30680 | ||
|
|
9a3c4e69e8 | ||
|
|
56025d3d33 | ||
|
|
59cae7a8ce | ||
|
|
9a9f859b70 | ||
|
|
ba3a1f6298 | ||
|
|
10e6e7c3cb |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -13,3 +13,5 @@ Pathogen:
|
||||
custom_plan.rb
|
||||
zeus.json
|
||||
.bundle
|
||||
config/application.yml
|
||||
.idea/**
|
||||
@@ -1 +1 @@
|
||||
2.1.5
|
||||
2.2.4
|
||||
|
||||
41
.travis.yml
41
.travis.yml
@@ -1,12 +1,37 @@
|
||||
---
|
||||
sudo: false
|
||||
language: ruby
|
||||
|
||||
env: GROWSTUFF_SITE_NAME="Growstuff (travis)" RAILS_SECRET_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
|
||||
bundler_args: --without development production staging
|
||||
cache: bundler
|
||||
env:
|
||||
matrix:
|
||||
- GROWSTUFF_SITE_NAME="Growstuff (travis)" RAILS_SECRET_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' GROWSTUFF_ELASTICSEARCH='true'
|
||||
- GROWSTUFF_SITE_NAME="Growstuff (travis)" RAILS_SECRET_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' GROWSTUFF_ELASTICSEARCH='false'
|
||||
global:
|
||||
secure: "Z5TpM2jEX4UCvNePnk/LwltQX48U2u9BRc+Iypr1x9QW2o228QJhPIOH39a8RMUrepGnkQIq9q3ZRUn98RfrJz1yThtlNFL3NmzdQ57gKgjGwfpa0e4Dwj/ZJqV2D84tDGjvdVYLP7zzaYZxQcwk/cgNpzKf/jq97HLNP7CYuf4="
|
||||
rvm:
|
||||
- 2.1.5
|
||||
- 2.2.4
|
||||
before_script:
|
||||
- psql -c 'create database growstuff_test;' -U postgres
|
||||
- psql -c 'create database growstuff_test;' -U postgres
|
||||
script:
|
||||
- bundle exec rake db:migrate --trace
|
||||
- bundle exec rspec spec/
|
||||
- bundle exec rake db:migrate --trace
|
||||
- bundle exec rspec spec/
|
||||
services:
|
||||
- elasticsearch
|
||||
before_deploy:
|
||||
- bundle exec script/heroku_maintenance.rb on
|
||||
deploy:
|
||||
provider: heroku
|
||||
api_key:
|
||||
secure: WrQxf0fEKkCdXrjcejurobOnNNz3he4dDwjBbToXbQTQNDObPp7NetJrLsfM8FiUFEeOuvhIHHiDQtMvY720zGGAGxDptvgFS+0QHCUqoTRZA/yFfUmHlG2jROXTzk5uVK0AE4k6Ion5kX8+mM0EnMT/7u+MTFiukrJctSiEXfg=
|
||||
on:
|
||||
repo: Growstuff/growstuff
|
||||
app:
|
||||
dev: growstuff-staging
|
||||
travis_deploy: tranquil-basin-3130
|
||||
travis_containers: tranquil-basin-3130
|
||||
run:
|
||||
- "rake db:migrate"
|
||||
- "script/deploy-tasks.sh"
|
||||
- restart
|
||||
after_deploy:
|
||||
- bundle exec script/heroku_maintenance.rb off
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ submit the change with your pull request.
|
||||
- Cesy / [cesy](https://github.com/cesy)
|
||||
- Miles Gould / [pozorvlak](https://github.com/pozorvlak)
|
||||
- Taylor Griffin / [tygriffin](https://github.com/tygriffin)
|
||||
- Mackenzie Morgan / [maco](https://github.com/maco)
|
||||
|
||||
## Contributors
|
||||
|
||||
@@ -23,7 +24,6 @@ submit the change with your pull request.
|
||||
- Maia Sauren / [sauramaia](https://github.com/sauramaia)
|
||||
- Norman Ancajas / [nbancajas](https://github.com/nbancajas)
|
||||
- Jonathan "Duke" Leto / [leto](https://github.com/leto)
|
||||
- Mackenzie Morgan / [maco](https://github.com/maco)
|
||||
- Amy Hendrix / [sabreuse](https://github.com/sabreuse)
|
||||
- CephLPod / [cephLpod](https://github.com/cephLpod/)
|
||||
- Gemma Mason / [gemmaellen](https://github.com/gemmaellen)
|
||||
@@ -52,4 +52,17 @@ submit the change with your pull request.
|
||||
- Kevin Yang / [kevieyang](https://github.com/kevieyang)
|
||||
- Justin Hamman / [juzham](https://github.com/juzham)
|
||||
- Rocky Jaiswal / [rocky-jaiswal](https://github.com/rocky-jaiswal)
|
||||
|
||||
- Robert Landreaux / [robertlandreaux](https://github.com/robertlandreaux)
|
||||
- Savant Krishna / [sksavant](https://github.com/sksavant)
|
||||
- Jake Yesbeck / [yez](https://github.com/yez)
|
||||
- Mauricio Gonzalez / [mauricio-gonzalez](https://github.com/mauricio-gonzalez)
|
||||
- Andrey Bazhutkin / [andrba](https://github.com/andrba)
|
||||
- Gabriel Sandoval / [gabrielsandoval](https://github.com/gabrielsandoval)
|
||||
- Cjay Billones / [CjayBillones](https://github.com/CjayBillones)
|
||||
- Katy Ereira / [maccath](https://github.com/maccath)
|
||||
- Gabrielle DeWitt / [gabrielle27](https://github.com/gabrielle27)
|
||||
- Manmeet Singh / [manmeetsingh](https://github.com/manmeetsingh)
|
||||
- Jym Paul Carandang / [jacarandang](https://github.com/jacarandang)
|
||||
- Anthony Atkinson / [sha1sum](https://github.com/sha1sum)
|
||||
- Terence Conquest / [twconquest](https://github.com/twconquest)
|
||||
- Daniel O'Connor / [CloCkWeRX](https://github.com/CloCkWeRX)
|
||||
|
||||
53
Gemfile
53
Gemfile
@@ -1,22 +1,20 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
ruby '2.1.5'
|
||||
ruby '2.2.4'
|
||||
|
||||
gem 'rails', '4.1.9'
|
||||
gem 'rails', '~> 4.1.11'
|
||||
|
||||
gem 'bundler', '>=1.1.5'
|
||||
|
||||
gem 'sass-rails', '~> 4.0.4'
|
||||
gem 'sass-rails', '~> 5.0.4'
|
||||
gem 'coffee-rails', '~> 4.1.0'
|
||||
gem 'haml'
|
||||
|
||||
# Another CSS preprocessor, used for Bootstrap overrides
|
||||
gem 'less', '~>2.5.0'
|
||||
gem 'less-rails', '~> 2.5.0'
|
||||
# CSS framework
|
||||
gem 'less-rails-bootstrap', '~> 3.2.0'
|
||||
gem 'bootstrap-sass', '~> 3.3.6'
|
||||
gem 'font-awesome-sass'
|
||||
|
||||
gem 'uglifier', '~> 2.5.3' # JavaScript compressor
|
||||
gem 'uglifier', '~> 2.7.2' # JavaScript compressor
|
||||
|
||||
gem 'jquery-rails'
|
||||
gem 'jquery-ui-rails', '~> 5.0.2'
|
||||
@@ -29,8 +27,14 @@ gem 'unicorn' # http server
|
||||
gem 'pg'
|
||||
gem 'figaro' # for handling config via ENV variables
|
||||
gem 'cancancan', '~> 1.9' # for checking member privileges
|
||||
gem 'gibbon' # for Mailchimp newsletter subscriptions
|
||||
gem 'gibbon', '~>1.2.0' # for Mailchimp newsletter subscriptions
|
||||
gem 'csv_shaper' # CSV export
|
||||
gem 'ruby-units' # for unit conversion
|
||||
|
||||
gem 'comfortable_mexican_sofa', '~> 1.12.0' # content management system
|
||||
|
||||
gem 'kaminari' # pagination
|
||||
gem 'bootstrap-kaminari-views' # bootstrap views for kaminari
|
||||
|
||||
# vendored activemerchant for testing- needed for bogus paypal
|
||||
# gateway monkeypatch
|
||||
@@ -40,14 +44,6 @@ gem 'activemerchant', '1.33.0',
|
||||
gem 'active_utils', '1.0.5',
|
||||
:path => 'vendor/gems/active_utils-1.0.5'
|
||||
|
||||
# less-rails depends on a JavaScript engine; we use therubyracer.
|
||||
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
||||
# long term, we'll probably want node.js for performance, but this will do
|
||||
# for now as it's easier for new people to install
|
||||
gem 'therubyracer', '~> 0.12', :platforms => :ruby
|
||||
# libv8 is needed by therubyracer and is a bit finicky
|
||||
gem 'libv8', '3.16.14.7'
|
||||
|
||||
# Markdown formatting for updates etc
|
||||
gem 'bluecloth'
|
||||
|
||||
@@ -55,7 +51,7 @@ gem 'bluecloth'
|
||||
gem 'will_paginate', '~> 3.0'
|
||||
|
||||
# user signup/login/etc
|
||||
gem 'devise', '~> 3.4.1'
|
||||
gem 'devise', '~> 3.5.0'
|
||||
|
||||
# nicely formatted URLs
|
||||
gem 'friendly_id', '~> 5.0.4'
|
||||
@@ -76,6 +72,12 @@ gem 'omniauth'
|
||||
gem 'omniauth-twitter'
|
||||
gem 'omniauth-flickr', '>= 0.0.15'
|
||||
|
||||
# client for Elasticsearch. Elasticsearch is a flexible
|
||||
# and powerful, distributed, real-time search and analytics engine.
|
||||
# An example of the use in the project is fuzzy crop search.
|
||||
gem "elasticsearch-model"
|
||||
gem "elasticsearch-rails"
|
||||
|
||||
gem 'rake', '>= 10.0.0'
|
||||
|
||||
group :production, :staging do
|
||||
@@ -83,6 +85,7 @@ group :production, :staging do
|
||||
gem 'dalli'
|
||||
gem 'memcachier'
|
||||
gem 'rails_12factor' # supresses heroku plugin injection
|
||||
gem 'bonsai-elasticsearch-rails' # Integration with Bonsa-Elasticsearch on heroku
|
||||
end
|
||||
|
||||
group :development do
|
||||
@@ -93,19 +96,27 @@ group :development do
|
||||
gem 'better_errors'
|
||||
gem 'binding_of_caller'
|
||||
gem 'letter_opener'
|
||||
gem 'quiet_assets'
|
||||
gem 'guard'
|
||||
gem 'guard-rspec'
|
||||
end
|
||||
|
||||
group :development, :test do
|
||||
gem 'haml-rails' # HTML templating language
|
||||
gem 'rspec-rails', '~> 3.1.0' # unit testing framework
|
||||
gem 'rspec-rails', '~> 3.4.0' # unit testing framework
|
||||
gem 'rspec-activemodel-mocks'
|
||||
gem 'byebug' # debugging
|
||||
gem 'database_cleaner', '~> 1.3.0'
|
||||
gem 'database_cleaner', '~> 1.5.0'
|
||||
gem 'webrat' # provides HTML matchers for view tests
|
||||
gem 'factory_girl_rails', '~> 4.5.0' # for creating test data
|
||||
gem 'coveralls', require: false # coverage analysis
|
||||
gem 'capybara' # integration tests
|
||||
gem 'capybara-email' # integration tests for email
|
||||
gem 'poltergeist', '~> 1.5.1' # for headless JS testing
|
||||
gem 'poltergeist', '~> 1.6' # for headless JS testing
|
||||
gem 'i18n-tasks' # adds tests for finding missing and unused translations
|
||||
gem 'selenium-webdriver'
|
||||
end
|
||||
|
||||
group :travis do
|
||||
gem 'heroku-api'
|
||||
end
|
||||
|
||||
481
Gemfile.lock
481
Gemfile.lock
@@ -20,83 +20,115 @@ PATH
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actionmailer (4.1.9)
|
||||
actionpack (= 4.1.9)
|
||||
actionview (= 4.1.9)
|
||||
actionmailer (4.1.15)
|
||||
actionpack (= 4.1.15)
|
||||
actionview (= 4.1.15)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
actionpack (4.1.9)
|
||||
actionview (= 4.1.9)
|
||||
activesupport (= 4.1.9)
|
||||
actionpack (4.1.15)
|
||||
actionview (= 4.1.15)
|
||||
activesupport (= 4.1.15)
|
||||
rack (~> 1.5.2)
|
||||
rack-test (~> 0.6.2)
|
||||
actionview (4.1.9)
|
||||
activesupport (= 4.1.9)
|
||||
actionview (4.1.15)
|
||||
activesupport (= 4.1.15)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
activemodel (4.1.9)
|
||||
activesupport (= 4.1.9)
|
||||
active_link_to (1.0.3)
|
||||
actionpack
|
||||
activemodel (4.1.15)
|
||||
activesupport (= 4.1.15)
|
||||
builder (~> 3.1)
|
||||
activerecord (4.1.9)
|
||||
activemodel (= 4.1.9)
|
||||
activesupport (= 4.1.9)
|
||||
activerecord (4.1.15)
|
||||
activemodel (= 4.1.15)
|
||||
activesupport (= 4.1.15)
|
||||
arel (~> 5.0.0)
|
||||
activesupport (4.1.9)
|
||||
activesupport (4.1.15)
|
||||
i18n (~> 0.6, >= 0.6.9)
|
||||
json (~> 1.7, >= 1.7.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.1)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.3.6)
|
||||
addressable (2.4.0)
|
||||
arel (5.0.1.20140414130214)
|
||||
bcrypt (3.1.9)
|
||||
better_errors (2.0.0)
|
||||
ast (2.2.0)
|
||||
autoprefixer-rails (6.3.6.1)
|
||||
execjs
|
||||
bcrypt (3.1.11)
|
||||
better_errors (2.1.1)
|
||||
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)
|
||||
bonsai-elasticsearch-rails (0.0.4)
|
||||
bootstrap-datepicker-rails (1.6.1.1)
|
||||
railties (>= 3.0)
|
||||
bootstrap-kaminari-views (0.0.5)
|
||||
kaminari (>= 0.13)
|
||||
rails (>= 3.1)
|
||||
bootstrap-sass (3.3.6)
|
||||
autoprefixer-rails (>= 5.2.1)
|
||||
sass (>= 3.3.4)
|
||||
bootstrap_form (2.3.0)
|
||||
builder (3.2.2)
|
||||
byebug (3.5.1)
|
||||
columnize (~> 0.8)
|
||||
debugger-linecache (~> 1.2)
|
||||
slop (~> 3.6)
|
||||
cancancan (1.9.2)
|
||||
capybara (2.4.4)
|
||||
byebug (9.0.4)
|
||||
cancancan (1.14.0)
|
||||
capybara (2.7.1)
|
||||
addressable
|
||||
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-email (2.5.0)
|
||||
capybara (~> 2.4)
|
||||
mail
|
||||
childprocess (0.5.9)
|
||||
ffi (~> 1.0, >= 1.0.11)
|
||||
climate_control (0.0.3)
|
||||
activesupport (>= 3.0)
|
||||
cliver (0.3.2)
|
||||
coderay (1.1.0)
|
||||
coffee-rails (4.1.0)
|
||||
cocaine (0.5.8)
|
||||
climate_control (>= 0.0.3, < 1.0)
|
||||
codemirror-rails (5.11)
|
||||
railties (>= 3.0, < 5)
|
||||
coderay (1.1.1)
|
||||
coffee-rails (4.1.1)
|
||||
coffee-script (>= 2.2.0)
|
||||
railties (>= 4.0.0, < 5.0)
|
||||
coffee-script (2.3.0)
|
||||
railties (>= 4.0.0, < 5.1.x)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.8.0)
|
||||
columnize (0.9.0)
|
||||
commonjs (0.2.7)
|
||||
coveralls (0.7.1)
|
||||
multi_json (~> 1.3)
|
||||
rest-client
|
||||
simplecov (>= 0.7)
|
||||
term-ansicolor
|
||||
thor
|
||||
csv_shaper (1.1.1)
|
||||
coffee-script-source (1.10.0)
|
||||
comfortable_mexican_sofa (1.12.9)
|
||||
active_link_to (>= 1.0.0)
|
||||
bootstrap-sass (>= 3.2.0)
|
||||
bootstrap_form (>= 2.2.0)
|
||||
codemirror-rails (>= 3.0.0)
|
||||
coffee-rails (>= 3.1.0)
|
||||
haml-rails (>= 0.3.0)
|
||||
jquery-rails (>= 3.0.0)
|
||||
jquery-ui-rails (>= 5.0.0)
|
||||
kramdown (>= 1.0.0)
|
||||
paperclip (>= 4.0.0)
|
||||
plupload-rails (>= 1.2.1)
|
||||
rails (>= 4.0.0, < 5)
|
||||
rails-i18n (>= 4.0.0)
|
||||
sass-rails (>= 4.0.3)
|
||||
concurrent-ruby (1.0.2)
|
||||
coveralls (0.8.13)
|
||||
json (~> 1.8)
|
||||
simplecov (~> 0.11.0)
|
||||
term-ansicolor (~> 1.3)
|
||||
thor (~> 0.19.1)
|
||||
tins (~> 1.6.0)
|
||||
csv_shaper (1.2.0)
|
||||
activesupport (>= 3.0.0)
|
||||
dalli (2.7.2)
|
||||
database_cleaner (1.3.0)
|
||||
dalli (2.7.6)
|
||||
database_cleaner (1.5.3)
|
||||
debug_inspector (0.0.2)
|
||||
debugger-linecache (1.2.0)
|
||||
devise (3.4.1)
|
||||
devise (3.5.10)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 3.2.6, < 5)
|
||||
@@ -109,220 +141,288 @@ GEM
|
||||
json
|
||||
thread
|
||||
thread_safe
|
||||
elasticsearch (1.0.17)
|
||||
elasticsearch-api (= 1.0.17)
|
||||
elasticsearch-transport (= 1.0.17)
|
||||
elasticsearch-api (1.0.17)
|
||||
multi_json
|
||||
elasticsearch-model (0.1.9)
|
||||
activesupport (> 3)
|
||||
elasticsearch (> 0.4)
|
||||
hashie
|
||||
elasticsearch-rails (0.1.9)
|
||||
elasticsearch-transport (1.0.17)
|
||||
faraday
|
||||
multi_json
|
||||
erubis (2.7.0)
|
||||
execjs (2.2.2)
|
||||
excon (0.49.0)
|
||||
execjs (2.7.0)
|
||||
factory_girl (4.5.0)
|
||||
activesupport (>= 3.0.0)
|
||||
factory_girl_rails (4.5.0)
|
||||
factory_girl (~> 4.5.0)
|
||||
railties (>= 3.0.0)
|
||||
figaro (1.0.0)
|
||||
faraday (0.9.2)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ffi (1.9.10)
|
||||
figaro (1.1.1)
|
||||
thor (~> 0.14)
|
||||
flickraw (0.9.8)
|
||||
friendly_id (5.0.4)
|
||||
font-awesome-sass (4.6.2)
|
||||
sass (>= 3.2)
|
||||
formatador (0.2.5)
|
||||
friendly_id (5.0.5)
|
||||
activerecord (>= 4.0.0)
|
||||
gibbon (1.1.4)
|
||||
gibbon (1.2.0)
|
||||
httparty
|
||||
multi_json (>= 1.3.4)
|
||||
multi_json (>= 1.9.0)
|
||||
gravatar-ultimate (2.0.0)
|
||||
activesupport (>= 2.3.14)
|
||||
rack
|
||||
haml (4.1.0.beta.1)
|
||||
guard (2.14.0)
|
||||
formatador (>= 0.2.4)
|
||||
listen (>= 2.7, < 4.0)
|
||||
lumberjack (~> 1.0)
|
||||
nenv (~> 0.1)
|
||||
notiffany (~> 0.0)
|
||||
pry (>= 0.9.12)
|
||||
shellany (~> 0.0)
|
||||
thor (>= 0.18.1)
|
||||
guard-compat (1.2.1)
|
||||
guard-rspec (4.6.5)
|
||||
guard (~> 2.1)
|
||||
guard-compat (~> 1.1)
|
||||
rspec (>= 2.99.0, < 4.0)
|
||||
haml (4.0.7)
|
||||
tilt
|
||||
haml-rails (0.6.0)
|
||||
haml-rails (0.9.0)
|
||||
actionpack (>= 4.0.1)
|
||||
activesupport (>= 4.0.1)
|
||||
haml (>= 3.1, < 5.0)
|
||||
haml (>= 4.0.6, < 5.0)
|
||||
html2haml (>= 1.0.1)
|
||||
railties (>= 4.0.1)
|
||||
hashie (3.3.2)
|
||||
highline (1.6.21)
|
||||
hike (1.2.3)
|
||||
hpricot (0.8.6)
|
||||
html2haml (1.0.1)
|
||||
hashie (3.4.4)
|
||||
heroku-api (0.4.2)
|
||||
excon (~> 0.45)
|
||||
multi_json (~> 1.8)
|
||||
highline (1.7.8)
|
||||
html2haml (2.0.0)
|
||||
erubis (~> 2.7.0)
|
||||
haml (>= 4.0.0.rc.1)
|
||||
hpricot (~> 0.8.6)
|
||||
ruby_parser (~> 3.1.1)
|
||||
haml (~> 4.0.0)
|
||||
nokogiri (~> 1.6.0)
|
||||
ruby_parser (~> 3.5)
|
||||
httparty (0.13.3)
|
||||
json (~> 1.8)
|
||||
multi_xml (>= 0.5.2)
|
||||
i18n (0.7.0)
|
||||
i18n-tasks (0.7.8)
|
||||
activesupport
|
||||
i18n-tasks (0.9.5)
|
||||
activesupport (>= 4.0.2)
|
||||
ast (>= 2.1.0)
|
||||
easy_translate (>= 0.5.0)
|
||||
erubis
|
||||
highline
|
||||
highline (>= 1.7.3)
|
||||
i18n
|
||||
slop (>= 3.5.0)
|
||||
term-ansicolor
|
||||
terminal-table
|
||||
jquery-rails (3.1.2)
|
||||
parser (>= 2.2.3.0)
|
||||
term-ansicolor (>= 1.3.2)
|
||||
terminal-table (>= 1.5.1)
|
||||
jquery-rails (3.1.4)
|
||||
railties (>= 3.0, < 5.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
jquery-ui-rails (5.0.3)
|
||||
jquery-ui-rails (5.0.5)
|
||||
railties (>= 3.2.16)
|
||||
js-routes (0.9.9)
|
||||
js-routes (1.2.5)
|
||||
railties (>= 3.2)
|
||||
sprockets-rails
|
||||
json (1.8.2)
|
||||
kgio (2.9.2)
|
||||
json (1.8.3)
|
||||
kaminari (0.16.3)
|
||||
actionpack (>= 3.0.0)
|
||||
activesupport (>= 3.0.0)
|
||||
kgio (2.10.0)
|
||||
kramdown (1.11.1)
|
||||
launchy (2.4.3)
|
||||
addressable (~> 2.3)
|
||||
leaflet-markercluster-rails (0.7.0)
|
||||
railties (>= 3.1)
|
||||
leaflet-rails (0.7.4)
|
||||
less (2.5.1)
|
||||
commonjs (~> 0.2.7)
|
||||
less-rails (2.5.0)
|
||||
actionpack (>= 3.1)
|
||||
less (~> 2.5.0)
|
||||
less-rails-bootstrap (3.2.0)
|
||||
less-rails (~> 2.5.0)
|
||||
letter_opener (1.3.0)
|
||||
leaflet-rails (0.7.7)
|
||||
letter_opener (1.4.1)
|
||||
launchy (~> 2.2)
|
||||
libv8 (3.16.14.7)
|
||||
mail (2.6.3)
|
||||
mime-types (>= 1.16, < 3)
|
||||
listen (3.1.5)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
ruby_dep (~> 1.2)
|
||||
lumberjack (1.0.10)
|
||||
mail (2.6.4)
|
||||
mime-types (>= 1.16, < 4)
|
||||
memcachier (0.0.2)
|
||||
method_source (0.8.2)
|
||||
mime-types (2.4.3)
|
||||
mini_portile (0.6.1)
|
||||
minitest (5.5.1)
|
||||
multi_json (1.10.1)
|
||||
mime-types (3.0)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2016.0221)
|
||||
mimemagic (0.3.0)
|
||||
mini_portile2 (2.0.0)
|
||||
minitest (5.9.0)
|
||||
multi_json (1.11.3)
|
||||
multi_xml (0.5.5)
|
||||
netrc (0.10.0)
|
||||
newrelic_rpm (3.9.8.273)
|
||||
nokogiri (1.6.5)
|
||||
mini_portile (~> 0.6.0)
|
||||
oauth (0.4.7)
|
||||
omniauth (1.2.2)
|
||||
multipart-post (2.0.0)
|
||||
nenv (0.3.0)
|
||||
newrelic_rpm (3.15.2.317)
|
||||
nokogiri (1.6.7.2)
|
||||
mini_portile2 (~> 2.0.0.rc2)
|
||||
notiffany (0.1.0)
|
||||
nenv (~> 0.1)
|
||||
shellany (~> 0.0)
|
||||
oauth (0.5.1)
|
||||
omniauth (1.3.1)
|
||||
hashie (>= 1.2, < 4)
|
||||
rack (~> 1.0)
|
||||
omniauth-flickr (0.0.15)
|
||||
rack (>= 1.0, < 3)
|
||||
omniauth-flickr (0.0.19)
|
||||
multi_json (~> 1.11.0)
|
||||
omniauth-oauth (~> 1.0)
|
||||
omniauth-oauth (1.0.1)
|
||||
omniauth-oauth (1.1.0)
|
||||
oauth
|
||||
omniauth (~> 1.0)
|
||||
omniauth-twitter (1.1.0)
|
||||
multi_json (~> 1.3)
|
||||
omniauth-oauth (~> 1.0)
|
||||
omniauth-twitter (1.2.1)
|
||||
json (~> 1.3)
|
||||
omniauth-oauth (~> 1.1)
|
||||
orm_adapter (0.5.0)
|
||||
pg (0.17.1)
|
||||
poltergeist (1.5.1)
|
||||
paperclip (4.3.6)
|
||||
activemodel (>= 3.2.0)
|
||||
activesupport (>= 3.2.0)
|
||||
cocaine (~> 0.5.5)
|
||||
mime-types
|
||||
mimemagic (= 0.3.0)
|
||||
parser (2.3.1.0)
|
||||
ast (~> 2.2)
|
||||
pg (0.18.4)
|
||||
plupload-rails (1.2.1)
|
||||
rails (>= 3.1)
|
||||
poltergeist (1.9.0)
|
||||
capybara (~> 2.1)
|
||||
cliver (~> 0.3.1)
|
||||
multi_json (~> 1.0)
|
||||
websocket-driver (>= 0.2.0)
|
||||
pry (0.10.1)
|
||||
pry (0.10.3)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.8.1)
|
||||
slop (~> 3.4)
|
||||
rack (1.5.2)
|
||||
quiet_assets (1.1.0)
|
||||
railties (>= 3.1, < 5.0)
|
||||
rack (1.5.5)
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rails (4.1.9)
|
||||
actionmailer (= 4.1.9)
|
||||
actionpack (= 4.1.9)
|
||||
actionview (= 4.1.9)
|
||||
activemodel (= 4.1.9)
|
||||
activerecord (= 4.1.9)
|
||||
activesupport (= 4.1.9)
|
||||
rails (4.1.15)
|
||||
actionmailer (= 4.1.15)
|
||||
actionpack (= 4.1.15)
|
||||
actionview (= 4.1.15)
|
||||
activemodel (= 4.1.15)
|
||||
activerecord (= 4.1.15)
|
||||
activesupport (= 4.1.15)
|
||||
bundler (>= 1.3.0, < 2.0)
|
||||
railties (= 4.1.9)
|
||||
railties (= 4.1.15)
|
||||
sprockets-rails (~> 2.0)
|
||||
rails-i18n (4.0.8)
|
||||
i18n (~> 0.7)
|
||||
railties (~> 4.0)
|
||||
rails_12factor (0.0.3)
|
||||
rails_serve_static_assets
|
||||
rails_stdout_logging
|
||||
rails_serve_static_assets (0.0.2)
|
||||
rails_stdout_logging (0.0.3)
|
||||
railties (4.1.9)
|
||||
actionpack (= 4.1.9)
|
||||
activesupport (= 4.1.9)
|
||||
rails_serve_static_assets (0.0.5)
|
||||
rails_stdout_logging (0.0.5)
|
||||
railties (4.1.15)
|
||||
actionpack (= 4.1.15)
|
||||
activesupport (= 4.1.15)
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
raindrops (0.13.0)
|
||||
rake (10.4.2)
|
||||
ref (1.0.5)
|
||||
raindrops (0.16.0)
|
||||
rake (11.1.2)
|
||||
rb-fsevent (0.9.7)
|
||||
rb-inotify (0.9.7)
|
||||
ffi (>= 0.5.0)
|
||||
responders (1.1.2)
|
||||
railties (>= 3.2, < 4.2)
|
||||
rest-client (1.7.2)
|
||||
mime-types (>= 1.16, < 3.0)
|
||||
netrc (~> 0.7)
|
||||
rspec-activemodel-mocks (1.0.1)
|
||||
rspec (3.4.0)
|
||||
rspec-core (~> 3.4.0)
|
||||
rspec-expectations (~> 3.4.0)
|
||||
rspec-mocks (~> 3.4.0)
|
||||
rspec-activemodel-mocks (1.0.3)
|
||||
activemodel (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
rspec-mocks (>= 2.99, < 4.0)
|
||||
rspec-core (3.1.7)
|
||||
rspec-support (~> 3.1.0)
|
||||
rspec-expectations (3.1.2)
|
||||
rspec-core (3.4.4)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-expectations (3.4.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.1.0)
|
||||
rspec-mocks (3.1.3)
|
||||
rspec-support (~> 3.1.0)
|
||||
rspec-rails (3.1.0)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
rspec-core (~> 3.1.0)
|
||||
rspec-expectations (~> 3.1.0)
|
||||
rspec-mocks (~> 3.1.0)
|
||||
rspec-support (~> 3.1.0)
|
||||
rspec-support (3.1.2)
|
||||
ruby_parser (3.1.3)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-mocks (3.4.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-rails (3.4.2)
|
||||
actionpack (>= 3.0, < 4.3)
|
||||
activesupport (>= 3.0, < 4.3)
|
||||
railties (>= 3.0, < 4.3)
|
||||
rspec-core (~> 3.4.0)
|
||||
rspec-expectations (~> 3.4.0)
|
||||
rspec-mocks (~> 3.4.0)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-support (3.4.1)
|
||||
ruby-units (2.0.0)
|
||||
ruby_dep (1.3.1)
|
||||
ruby_parser (3.8.2)
|
||||
sexp_processor (~> 4.1)
|
||||
sass (3.2.19)
|
||||
sass-rails (4.0.5)
|
||||
rubyzip (1.2.0)
|
||||
sass (3.4.22)
|
||||
sass-rails (5.0.4)
|
||||
railties (>= 4.0.0, < 5.0)
|
||||
sass (~> 3.2.2)
|
||||
sprockets (~> 2.8, < 3.0)
|
||||
sprockets-rails (~> 2.0)
|
||||
sexp_processor (4.4.4)
|
||||
simplecov (0.9.1)
|
||||
sass (~> 3.1)
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
sprockets-rails (>= 2.0, < 4.0)
|
||||
tilt (>= 1.1, < 3)
|
||||
selenium-webdriver (2.53.0)
|
||||
childprocess (~> 0.5)
|
||||
rubyzip (~> 1.0)
|
||||
websocket (~> 1.0)
|
||||
sexp_processor (4.7.0)
|
||||
shellany (0.0.1)
|
||||
simplecov (0.11.2)
|
||||
docile (~> 1.1.0)
|
||||
multi_json (~> 1.0)
|
||||
simplecov-html (~> 0.8.0)
|
||||
simplecov-html (0.8.0)
|
||||
json (~> 1.8)
|
||||
simplecov-html (~> 0.10.0)
|
||||
simplecov-html (0.10.0)
|
||||
slop (3.6.0)
|
||||
sprockets (2.12.3)
|
||||
hike (~> 1.2)
|
||||
multi_json (~> 1.0)
|
||||
rack (~> 1.0)
|
||||
tilt (~> 1.1, != 1.3.0)
|
||||
sprockets-rails (2.2.2)
|
||||
sprockets (3.6.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
sprockets-rails (2.3.3)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
term-ansicolor (1.3.0)
|
||||
term-ansicolor (1.3.2)
|
||||
tins (~> 1.0)
|
||||
terminal-table (1.4.5)
|
||||
therubyracer (0.12.1)
|
||||
libv8 (~> 3.16.14.0)
|
||||
ref
|
||||
terminal-table (1.5.2)
|
||||
thor (0.19.1)
|
||||
thread (0.1.4)
|
||||
thread_safe (0.3.4)
|
||||
tilt (1.4.1)
|
||||
tins (1.3.3)
|
||||
thread (0.2.2)
|
||||
thread_safe (0.3.5)
|
||||
tilt (2.0.4)
|
||||
tins (1.6.0)
|
||||
tzinfo (1.2.2)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (2.5.3)
|
||||
uglifier (2.7.2)
|
||||
execjs (>= 0.3.0)
|
||||
json (>= 1.8.0)
|
||||
unicorn (4.8.3)
|
||||
unicorn (5.1.0)
|
||||
kgio (~> 2.6)
|
||||
rack
|
||||
raindrops (~> 0.7)
|
||||
warden (1.2.3)
|
||||
warden (1.2.6)
|
||||
rack (>= 1.0)
|
||||
webrat (0.7.3)
|
||||
nokogiri (>= 1.2.0)
|
||||
rack (>= 1.0)
|
||||
rack-test (>= 0.5.3)
|
||||
websocket-driver (0.5.0)
|
||||
websocket (1.2.3)
|
||||
websocket-driver (0.6.4)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.0)
|
||||
will_paginate (3.0.7)
|
||||
websocket-extensions (0.1.2)
|
||||
will_paginate (3.1.0)
|
||||
xpath (2.0.0)
|
||||
nokogiri (~> 1.3)
|
||||
|
||||
@@ -335,54 +435,69 @@ DEPENDENCIES
|
||||
better_errors
|
||||
binding_of_caller
|
||||
bluecloth
|
||||
bonsai-elasticsearch-rails
|
||||
bootstrap-datepicker-rails
|
||||
bootstrap-kaminari-views
|
||||
bootstrap-sass (~> 3.3.6)
|
||||
bundler (>= 1.1.5)
|
||||
byebug
|
||||
cancancan (~> 1.9)
|
||||
capybara
|
||||
capybara-email
|
||||
coffee-rails (~> 4.1.0)
|
||||
comfortable_mexican_sofa (~> 1.12.0)
|
||||
coveralls
|
||||
csv_shaper
|
||||
dalli
|
||||
database_cleaner (~> 1.3.0)
|
||||
devise (~> 3.4.1)
|
||||
database_cleaner (~> 1.5.0)
|
||||
devise (~> 3.5.0)
|
||||
elasticsearch-model
|
||||
elasticsearch-rails
|
||||
factory_girl_rails (~> 4.5.0)
|
||||
figaro
|
||||
flickraw
|
||||
font-awesome-sass
|
||||
friendly_id (~> 5.0.4)
|
||||
geocoder!
|
||||
gibbon
|
||||
gibbon (~> 1.2.0)
|
||||
gravatar-ultimate
|
||||
guard
|
||||
guard-rspec
|
||||
haml
|
||||
haml-rails
|
||||
heroku-api
|
||||
i18n-tasks
|
||||
jquery-rails
|
||||
jquery-ui-rails (~> 5.0.2)
|
||||
js-routes
|
||||
kaminari
|
||||
leaflet-markercluster-rails
|
||||
leaflet-rails
|
||||
less (~> 2.5.0)
|
||||
less-rails (~> 2.5.0)
|
||||
less-rails-bootstrap (~> 3.2.0)
|
||||
letter_opener
|
||||
libv8 (= 3.16.14.7)
|
||||
memcachier
|
||||
newrelic_rpm
|
||||
omniauth
|
||||
omniauth-flickr (>= 0.0.15)
|
||||
omniauth-twitter
|
||||
pg
|
||||
poltergeist (~> 1.5.1)
|
||||
poltergeist (~> 1.6)
|
||||
pry
|
||||
rails (= 4.1.9)
|
||||
quiet_assets
|
||||
rails (~> 4.1.11)
|
||||
rails_12factor
|
||||
rake (>= 10.0.0)
|
||||
rspec-activemodel-mocks
|
||||
rspec-rails (~> 3.1.0)
|
||||
sass-rails (~> 4.0.4)
|
||||
therubyracer (~> 0.12)
|
||||
uglifier (~> 2.5.3)
|
||||
rspec-rails (~> 3.4.0)
|
||||
ruby-units
|
||||
sass-rails (~> 5.0.4)
|
||||
selenium-webdriver
|
||||
uglifier (~> 2.7.2)
|
||||
unicorn
|
||||
webrat
|
||||
will_paginate (~> 3.0)
|
||||
|
||||
RUBY VERSION
|
||||
ruby 2.1.8p440
|
||||
|
||||
BUNDLED WITH
|
||||
1.12.4
|
||||
|
||||
13
Guardfile
Normal file
13
Guardfile
Normal file
@@ -0,0 +1,13 @@
|
||||
guard :rspec,
|
||||
cmd: 'bundle exec rspec --format documentation',
|
||||
failed_mode: :keep do
|
||||
watch(%r{^spec/.+_spec\.rb$})
|
||||
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/libs/#{m[1]}_spec.rb" }
|
||||
watch('spec/spec_helper.rb') { "spec" }
|
||||
|
||||
# Rails example
|
||||
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
||||
watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
||||
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
||||
watch('config/routes.rb') { "spec/routing" }
|
||||
end
|
||||
@@ -32,6 +32,14 @@ frontend features. We welcome contributions -- see
|
||||
* Drop in to one of our [discussion forums](http://wiki.growstuff.org/index.php/Discussion_forums) to chat to other developers, get help, etc.
|
||||
* You may also be interested in our [API](http://wiki.growstuff.org/index.php/API).
|
||||
|
||||
The wiki is down right now, so here's what you need to do on Mac OS X to get set up.
|
||||
|
||||
```
|
||||
gem install bundle
|
||||
gem install pg -v '0.18.4' -- --with-pg-config=/Applications/Postgres.app/Contents/Versions/latest/bin/pg_config
|
||||
bundle install
|
||||
```
|
||||
|
||||
## For designers, writers, researchers, data wranglers, and other contributors
|
||||
|
||||
There are heaps of ways to get involved and contribute no matter what
|
||||
|
||||
BIN
app/assets/images/sunniness_not specified.png
Normal file
BIN
app/assets/images/sunniness_not specified.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
app/assets/images/sunniness_semi-shade.png
Normal file
BIN
app/assets/images/sunniness_semi-shade.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
app/assets/images/sunniness_shade.png
Normal file
BIN
app/assets/images/sunniness_shade.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.3 KiB |
BIN
app/assets/images/sunniness_sun.png
Normal file
BIN
app/assets/images/sunniness_sun.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
@@ -16,7 +16,6 @@
|
||||
//= require jquery
|
||||
//= require jquery_ujs
|
||||
//= require jquery-ui/autocomplete
|
||||
//= require twitter/bootstrap
|
||||
//= require_tree .
|
||||
//= require bootstrap-sprockets
|
||||
//= require bootstrap-datepicker
|
||||
|
||||
//= require_tree .
|
||||
|
||||
1
app/assets/javascripts/comfy/admin/cms/custom.js.coffee
Normal file
1
app/assets/javascripts/comfy/admin/cms/custom.js.coffee
Normal file
@@ -0,0 +1 @@
|
||||
# Custom JS for the admin area
|
||||
@@ -47,3 +47,7 @@ function showCropMap(cropmap) {
|
||||
|
||||
cropmap.addLayer(markers);
|
||||
}
|
||||
|
||||
$('.btn.toggle.crop-hierarchy').click(function () {
|
||||
$('.toggle.crop-hierarchy').toggleClass('hide');
|
||||
});
|
||||
|
||||
@@ -9,11 +9,11 @@ jQuery ->
|
||||
finished = $('#planting_finished_at')
|
||||
if @checked
|
||||
if previousValue.length
|
||||
date = previousValue
|
||||
date = previousValue
|
||||
finished.val(date)
|
||||
else
|
||||
finished.trigger('focus')
|
||||
else
|
||||
previousValue = finished.val()
|
||||
finished.val('')
|
||||
)
|
||||
)
|
||||
|
||||
@@ -4,3 +4,42 @@
|
||||
|
||||
jQuery ->
|
||||
$('.add-datepicker').datepicker('format' : 'yyyy-mm-dd')
|
||||
$('#add-sci_name-row').css("display", "inline-block")
|
||||
$('#remove-sci_name-row').css("display", "inline-block")
|
||||
$("#add-alt_name-row").css("display", "inline-block")
|
||||
$("#remove-alt_name-row").css("display", "inline-block")
|
||||
|
||||
$ ->
|
||||
sci_template = "<div id='sci_template[INDEX]' class='template col-md-12'><div class='col-md-2'><label>Scientific name INDEX:</label></div><div class='col-md-8'><input name='sci_name[INDEX]' class='form-control', id='sci_name[INDEX]')'></input><span class='help-block'>Scientific name of crop.</span></div><div class='col-md-2'></div></div>"
|
||||
|
||||
sci_index = $('#scientific_names .template').length + 1
|
||||
|
||||
$('#add-sci_name-row').click ->
|
||||
compiled_input = $(sci_template.split("INDEX").join(sci_index))
|
||||
$('#scientific_names').append(compiled_input)
|
||||
sci_index = sci_index + 1
|
||||
|
||||
$('#remove-sci_name-row').click ->
|
||||
if (sci_index > 2)
|
||||
sci_index = sci_index - 1
|
||||
tmp = 'sci_template[' + sci_index + ']'
|
||||
element = document.getElementById(tmp)
|
||||
element.remove()
|
||||
|
||||
alt_template = "<div id='alt_template[INDEX]' class='template col-md-12'><div class='col-md-2'><label>Alternate name INDEX:</label></div><div class='col-md-8'><input name='alt_name[INDEX]' class='form-control', id='alt_name[INDEX]')'></input><span class='help-block'>Alternate name of crop.</span></div><div class='col-md-2'></div></div>"
|
||||
|
||||
alt_index = $('#alternate_names .template').length + 1
|
||||
|
||||
$('#add-alt_name-row').click ->
|
||||
compiled_input = $(alt_template.split("INDEX").join(alt_index))
|
||||
$('#alternate_names').append(compiled_input)
|
||||
alt_index = alt_index + 1
|
||||
|
||||
$('#remove-alt_name-row').click ->
|
||||
if (alt_index > 2)
|
||||
alt_index = alt_index - 1
|
||||
tmp = 'alt_template[' + alt_index + ']'
|
||||
element = document.getElementById(tmp)
|
||||
console.log("%s",tmp)
|
||||
element.remove()
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
/*
|
||||
* This is a manifest file that'll automatically include all the stylesheets available in this directory
|
||||
* 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 .
|
||||
*/
|
||||
7
app/assets/stylesheets/application.sass
Normal file
7
app/assets/stylesheets/application.sass
Normal file
@@ -0,0 +1,7 @@
|
||||
@import 'jquery-ui/autocomplete'
|
||||
@import 'bootstrap-datepicker'
|
||||
@import 'leaflet'
|
||||
@import 'leaflet.markercluster'
|
||||
@import 'leaflet.markercluster.default'
|
||||
@import 'custom_bootstrap/custom_bootstrap'
|
||||
@import 'overrides'
|
||||
1
app/assets/stylesheets/comfy/admin/cms/custom.sass
Normal file
1
app/assets/stylesheets/comfy/admin/cms/custom.sass
Normal file
@@ -0,0 +1 @@
|
||||
// custom CSS for admin area
|
||||
@@ -1,56 +0,0 @@
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
// !!! 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";
|
||||
@@ -0,0 +1,59 @@
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
// !!! AUTOMATICALLY GENERATED FILE. DO NOT MODIFY !!!
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
// Core variables and mixins
|
||||
@import "bootstrap-sprockets"
|
||||
@import "bootstrap/variables"
|
||||
// Modify this for custom colors, font-sizes, etc
|
||||
@import "custom_bootstrap/variables"
|
||||
@import "bootstrap/mixins"
|
||||
// Modify this for custom mixins
|
||||
@import "custom_bootstrap/mixins"
|
||||
|
||||
// Reset and dependencies
|
||||
@import "bootstrap/normalize"
|
||||
@import "bootstrap/print"
|
||||
@import "bootstrap/glyphicons"
|
||||
|
||||
// Core CSS
|
||||
@import "bootstrap/scaffolding"
|
||||
@import "bootstrap/type"
|
||||
@import "bootstrap/code"
|
||||
@import "bootstrap/grid"
|
||||
@import "bootstrap/tables"
|
||||
@import "bootstrap/forms"
|
||||
@import "bootstrap/buttons"
|
||||
|
||||
// Components
|
||||
@import "bootstrap/component-animations"
|
||||
@import "bootstrap/dropdowns"
|
||||
@import "bootstrap/button-groups"
|
||||
@import "bootstrap/input-groups"
|
||||
@import "bootstrap/navs"
|
||||
@import "bootstrap/navbar"
|
||||
@import "bootstrap/breadcrumbs"
|
||||
@import "bootstrap/pagination"
|
||||
@import "bootstrap/pager"
|
||||
@import "bootstrap/labels"
|
||||
@import "bootstrap/badges"
|
||||
@import "bootstrap/jumbotron"
|
||||
@import "bootstrap/thumbnails"
|
||||
@import "bootstrap/alerts"
|
||||
@import "bootstrap/progress-bars"
|
||||
@import "bootstrap/media"
|
||||
@import "bootstrap/list-group"
|
||||
@import "bootstrap/panels"
|
||||
@import "bootstrap/responsive-embed"
|
||||
@import "bootstrap/wells"
|
||||
@import "bootstrap/close"
|
||||
|
||||
// Components w/ JavaScript
|
||||
@import "bootstrap/modals"
|
||||
@import "bootstrap/tooltip"
|
||||
@import "bootstrap/popovers"
|
||||
@import "bootstrap/carousel"
|
||||
|
||||
// Utility classes
|
||||
@import "bootstrap/utilities"
|
||||
@import "bootstrap/responsive-utilities"
|
||||
@@ -1,54 +0,0 @@
|
||||
// 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%);
|
||||
54
app/assets/stylesheets/custom_bootstrap/variables.sass
Normal file
54
app/assets/stylesheets/custom_bootstrap/variables.sass
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 'bootstrap/variables.scss'
|
||||
|
||||
// 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,3 +0,0 @@
|
||||
.leaflet-popup-content-wrapper, .leaflet-popup-tip {
|
||||
border: none;
|
||||
}
|
||||
10
app/assets/stylesheets/leaflet_overrides.sass
Normal file
10
app/assets/stylesheets/leaflet_overrides.sass
Normal file
@@ -0,0 +1,10 @@
|
||||
.leaflet-popup-content-wrapper,
|
||||
.leaflet-popup-tip
|
||||
border: none
|
||||
|
||||
.thumbnail
|
||||
background: #fff !important
|
||||
border: solid 1px whitesmoke
|
||||
|
||||
.thumbnail .crop-thumbnail .cropinfo
|
||||
padding-top: 14px
|
||||
@@ -1,276 +0,0 @@
|
||||
|
||||
|
||||
@import "twitter/bootstrap/bootstrap";
|
||||
@import "custom_bootstrap/variables";
|
||||
// this padding needs to be done before the responsive stuff is imported
|
||||
body {
|
||||
// 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";
|
||||
|
||||
// Set the correct sprite paths
|
||||
@iconSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings");
|
||||
@iconWhiteSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings-white");
|
||||
|
||||
// Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines)
|
||||
@fontAwesomeEotPath: asset-url("fontawesome-webfont.eot");
|
||||
@fontAwesomeEotPath_iefix: asset-url("fontawesome-webfont.eot#iefix");
|
||||
@fontAwesomeWoffPath: asset-url("fontawesome-webfont.woff");
|
||||
@fontAwesomeTtfPath: asset-url("fontawesome-webfont.ttf");
|
||||
@fontAwesomeSvgPath: asset-url("fontawesome-webfont.svg#fontawesomeregular");
|
||||
|
||||
// Font Awesome
|
||||
//@import "fontawesome/font-awesome";
|
||||
|
||||
// Glyphicons
|
||||
//@import "twitter/bootstrap/sprites.less";
|
||||
|
||||
|
||||
.list-inline > li.first {
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
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%;
|
||||
}
|
||||
|
||||
.main {
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
margin-left: -1px;
|
||||
border-left: 1px solid darken(@beige, 10%);
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
// this is used for eg. crops and members index pages
|
||||
.six-across:nth-child(6n+1) {
|
||||
margin-left: 0px
|
||||
}
|
||||
|
||||
.three-across:nth-child(3n+1) {
|
||||
margin-left: 0px;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
// let's condense the hero unit a little
|
||||
.jumbotron {
|
||||
padding-top: 30px;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
// info under the main heading on homepage
|
||||
.jumbotron .info {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
// signup widget on homepage
|
||||
.jumbotron .signup {
|
||||
background-color: lighten(@green, 40%);
|
||||
border: 1px solid lighten(@green, 20%);
|
||||
border-radius: 6px;
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
line-height: 200%;
|
||||
}
|
||||
|
||||
// stats shown on homepage. eg. "999 members..."
|
||||
p.stats {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.homepage-members {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.homepage-members:nth-child(odd) {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
#placesmap, #cropmap {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
#membermap {
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
.member-location {
|
||||
font-size: small;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.member-location a {
|
||||
color: @brown;
|
||||
}
|
||||
|
||||
.photo-thumbnail {
|
||||
position:relative;
|
||||
padding:0;
|
||||
img {
|
||||
width:100%;
|
||||
}
|
||||
.text {
|
||||
display:none;
|
||||
color:#000;
|
||||
position:absolute;
|
||||
bottom:0;
|
||||
background:rgba(0, 0, 0, 0.8);
|
||||
width:100%;
|
||||
margin:0;
|
||||
}
|
||||
p {
|
||||
padding:5px;
|
||||
margin:0;
|
||||
color:#fff;
|
||||
}
|
||||
&:hover {
|
||||
.text {
|
||||
display:block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.thumbnail {
|
||||
border: none;
|
||||
text-align: center;
|
||||
margin-bottom: 1.5em;
|
||||
|
||||
.member-thumbnail {
|
||||
text-align: left;
|
||||
img {
|
||||
height: 85px;
|
||||
width: 85px;
|
||||
max-width: 85px
|
||||
}
|
||||
}
|
||||
|
||||
.crop-thumbnail {
|
||||
height: 220px;
|
||||
.scientific-name small, .crop-name a {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1em;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.crop-name a {
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.scientific-name small {
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
li.crop-hierarchy {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.navbar-bottom {
|
||||
margin: 40px 0px 0px 0px !important;
|
||||
}
|
||||
|
||||
footer {
|
||||
#contact, #about-growstuff, #policies {
|
||||
text-align: left;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 2em;
|
||||
ul {
|
||||
list-style-type: none;
|
||||
list-style-position: outside;
|
||||
padding-left: 0px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
a {
|
||||
color: @navbar-default-link-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
color: @navbar-default-link-hover-color;
|
||||
}
|
||||
a:active {
|
||||
color: @navbar-default-link-active-color;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-bottom.navbar {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.crop-image, .member-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
@media only screen and (max-width: 767px) {
|
||||
.sidebar {
|
||||
margin-left: 0;
|
||||
border-left: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
#map {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.navbar .nav > li {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
290
app/assets/stylesheets/overrides.sass
Normal file
290
app/assets/stylesheets/overrides.sass
Normal file
@@ -0,0 +1,290 @@
|
||||
@import "bootstrap-sprockets"
|
||||
@import "bootstrap"
|
||||
@import "custom_bootstrap/variables"
|
||||
// this padding needs to be done before the responsive stuff is imported
|
||||
body
|
||||
// modifying this for our promotional banner. can be replaced after if
|
||||
// needed.
|
||||
// padding-top: $navbar-height + 15px
|
||||
padding-top: $navbar-height
|
||||
|
||||
|
||||
//@import "bootstrap/responsive"
|
||||
|
||||
// Font Awesome
|
||||
@import "font-awesome-sprockets"
|
||||
@import "font-awesome"
|
||||
|
||||
// Glyphicons
|
||||
//@import "bootstrap/glyphicons"
|
||||
|
||||
|
||||
.list-inline > li.first
|
||||
padding-left: 0px
|
||||
|
||||
h2
|
||||
font-size: 150%
|
||||
|
||||
|
||||
//#subtitle
|
||||
// color: lighten($brown, 30%)
|
||||
// font-style: italic
|
||||
// font-weight: normal
|
||||
// margin-top: 0px
|
||||
// padding-left: 1em
|
||||
// padding-top: 0px
|
||||
|
||||
h3
|
||||
font-size: 120%
|
||||
|
||||
.main
|
||||
padding-right: 1em
|
||||
|
||||
.sidebar
|
||||
border-left: 1px solid darken($beige, 10%)
|
||||
margin-left: -1px
|
||||
padding-left: 1em
|
||||
|
||||
// this is used for eg. crops and members index pages
|
||||
.six-across:nth-child(6n+1)
|
||||
margin-left: 0px
|
||||
|
||||
.three-across:nth-child(3n+1)
|
||||
margin-left: 0px
|
||||
clear: both
|
||||
|
||||
// let's condense the hero unit a little
|
||||
.jumbotron
|
||||
padding-top: 30px
|
||||
padding-bottom: 30px
|
||||
|
||||
// info under the main heading on homepage
|
||||
.jumbotron .info
|
||||
padding-top: 15px
|
||||
|
||||
// signup widget on homepage
|
||||
.jumbotron .signup
|
||||
background-color: lighten($green, 40%)
|
||||
border: 1px solid lighten($green, 20%)
|
||||
border-radius: 6px
|
||||
line-height: 200%
|
||||
padding: 15px
|
||||
text-align: center
|
||||
|
||||
// stats shown on homepage. eg. "999 members..."
|
||||
p.stats
|
||||
font-weight: bold
|
||||
|
||||
.member-cards
|
||||
display: flex
|
||||
flex: none
|
||||
flex-wrap: wrap
|
||||
justify-content: space-between
|
||||
|
||||
.member-thumbnail
|
||||
padding: .25em
|
||||
|
||||
div
|
||||
width: 5em
|
||||
display: inline-block
|
||||
vertical-align: top
|
||||
|
||||
.member-thumbnail div~div
|
||||
padding-left: 1em
|
||||
width: 15em
|
||||
|
||||
#placesmap, #cropmap
|
||||
height: 500px
|
||||
|
||||
#membermap
|
||||
height: 250px
|
||||
|
||||
.member-location
|
||||
font-size: small
|
||||
font-style: italic
|
||||
|
||||
|
||||
.member-location a
|
||||
color: $brown
|
||||
|
||||
|
||||
.photo-thumbnail
|
||||
padding: 0
|
||||
position: relative
|
||||
|
||||
img
|
||||
width: 100%
|
||||
|
||||
.text
|
||||
display: none
|
||||
color: #000
|
||||
position: absolute
|
||||
bottom: 0
|
||||
background: rgba(0, 0, 0, 0.8)
|
||||
width: 100%
|
||||
margin: 0
|
||||
|
||||
p
|
||||
padding: 5px
|
||||
margin: 0
|
||||
color: #fff
|
||||
|
||||
&:hover
|
||||
.text
|
||||
display: block
|
||||
|
||||
.thumbnail
|
||||
border: none
|
||||
text-align: center
|
||||
margin-bottom: 1.5em
|
||||
|
||||
.member-thumbnail
|
||||
text-align: left
|
||||
img
|
||||
height: 85px
|
||||
width: 85px
|
||||
max-width: 85px
|
||||
|
||||
.crop-thumbnail
|
||||
height: 220px
|
||||
.cropinfo
|
||||
display: inline-block
|
||||
max-width: 100%
|
||||
white-space: nowrap
|
||||
line-height: 1em
|
||||
padding-bottom: 2px
|
||||
|
||||
.cropname
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
|
||||
.scientificname
|
||||
font-size: small
|
||||
font-style: italic
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
|
||||
.plantingcount
|
||||
font-size: small
|
||||
|
||||
.crop-name a
|
||||
padding-top: 2px
|
||||
|
||||
.scientific-name small
|
||||
margin-bottom: -2px
|
||||
|
||||
li.crop-hierarchy
|
||||
list-style-type: disc
|
||||
|
||||
.navbar-brand
|
||||
margin: 0px
|
||||
padding: 0px
|
||||
|
||||
.navbar-bottom
|
||||
margin: 40px 0px 0px 0px !important
|
||||
|
||||
// footer
|
||||
footer
|
||||
#footer1, #footer2, #footer3
|
||||
text-align: left
|
||||
padding-top: 1em
|
||||
padding-bottom: 2em
|
||||
ul
|
||||
list-style-type: none
|
||||
list-style-position: outside
|
||||
padding-left: 0px
|
||||
margin-left: 0px
|
||||
|
||||
a
|
||||
color: $navbar-default-link-color
|
||||
text-decoration: none
|
||||
|
||||
a:hover
|
||||
color: $navbar-default-link-hover-color
|
||||
|
||||
a:active
|
||||
color: $navbar-default-link-active-color
|
||||
|
||||
.navbar-bottom.navbar
|
||||
border-radius: 0
|
||||
|
||||
// ensure footer is pushed to bottom of browser window
|
||||
|
||||
#maincontainer
|
||||
min-height: 80%
|
||||
|
||||
html, body
|
||||
height: 100%
|
||||
|
||||
.crop-image, .member-image
|
||||
width: 100%
|
||||
height: 100%
|
||||
|
||||
// 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
|
||||
|
||||
.alert
|
||||
a
|
||||
font-weight: 800
|
||||
|
||||
// Overrides applying only to mobile view. This must be at the end of the overrides file.
|
||||
|
||||
@media only screen and (max-width: 767px)
|
||||
.sidebar
|
||||
margin-left: 0
|
||||
border-left: none
|
||||
padding-left: 0
|
||||
|
||||
#map
|
||||
height: 300px
|
||||
|
||||
.navbar .nav > li
|
||||
display: block
|
||||
|
||||
/* override "info" alert boxes to be green, not blue, on Growstuff */
|
||||
$state-info-text: darken($green, 10%)
|
||||
$state-info-bg: lighten($green, 50%)
|
||||
|
||||
/* and set "success" to be the same, as it was just very slightly
|
||||
* different because the default bootstrap green is slightly different
|
||||
* from ours */
|
||||
$state-success-text: darken($green, 10%)
|
||||
$state-success-bg: lighten($green, 50%)
|
||||
|
||||
.hide
|
||||
display: none
|
||||
|
||||
#add-sci_name-row, #remove-sci_name-row, #add-alt_name-row, #remove-alt_name-row
|
||||
display: none
|
||||
|
||||
.panel-footer
|
||||
height: 6em
|
||||
|
||||
#gardens_panel_body
|
||||
height: 20em
|
||||
|
||||
.form-group.required .control-label:before
|
||||
content: "* "
|
||||
color: red
|
||||
|
||||
.margin-bottom
|
||||
margin-bottom: 1em
|
||||
|
||||
.red
|
||||
color: red
|
||||
@@ -8,7 +8,7 @@ class Admin::OrdersController < ApplicationController
|
||||
|
||||
def search
|
||||
authorize! :manage, :all
|
||||
@orders = Order.search({:by => params[:search_by], :for => params[:search_text]})
|
||||
@orders = Order.search({by: params[:search_by], for: params[:search_text]})
|
||||
|
||||
if @orders.empty?
|
||||
flash[:alert] = "Couldn't find order with #{params[:search_by]} = #{params[:search_text]}"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class AlternateNamesController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
before_filter :authenticate_member!, except: [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /alternate_names
|
||||
|
||||
@@ -22,6 +22,10 @@ class ApplicationController < ActionController::Base
|
||||
stored_location_for(:member) || root_path
|
||||
end
|
||||
|
||||
def after_sign_out_path_for(resource_or_scope)
|
||||
request.referrer
|
||||
end
|
||||
|
||||
# tweak CanCan defaults because we don't have a "current_user" method
|
||||
# this means that we use current_user in specs but current_member everywhere
|
||||
# else in the code.
|
||||
@@ -31,9 +35,9 @@ class ApplicationController < ActionController::Base
|
||||
|
||||
# CanCan error handling
|
||||
rescue_from CanCan::AccessDenied do |exception|
|
||||
redirect_to request.referer || root_url, :alert => exception.message
|
||||
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
|
||||
@@ -48,7 +52,7 @@ class ApplicationController < ActionController::Base
|
||||
protected
|
||||
|
||||
def configure_permitted_parameters
|
||||
devise_parameter_sanitizer.for(:sign_up) do |member|
|
||||
devise_parameter_sanitizer.for(:sign_up) do |member|
|
||||
member.permit(:login_name, :email, :password, :password_confirmation,
|
||||
:remember_me, :login,
|
||||
# terms of service
|
||||
|
||||
@@ -20,14 +20,14 @@ class AuthenticationsController < ApplicationController
|
||||
|
||||
@authentication = current_member.authentications
|
||||
.create_with(
|
||||
:name => name,
|
||||
:token => auth['credentials']['token'],
|
||||
:secret => auth['credentials']['secret']
|
||||
name: name,
|
||||
token: auth['credentials']['token'],
|
||||
secret: auth['credentials']['secret']
|
||||
)
|
||||
.find_or_create_by(
|
||||
:provider => auth['provider'],
|
||||
:uid => auth['uid'],
|
||||
:name => name)
|
||||
provider: auth['provider'],
|
||||
uid: auth['uid'],
|
||||
name: name)
|
||||
|
||||
flash[:notice] = "Authentication successful."
|
||||
else
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
class CommentsController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
before_filter :authenticate_member!, except: [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /comments
|
||||
# GET /comments.json
|
||||
def index
|
||||
@comments = Comment.paginate(:page => params[:page])
|
||||
@comments = Comment.paginate(page: params[:page])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
format.json { render json: @comments }
|
||||
format.rss { render :layout => false }
|
||||
format.rss { render layout: false }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -39,7 +39,7 @@ class CommentsController < ApplicationController
|
||||
end
|
||||
else
|
||||
redirect_to request.referer || root_url,
|
||||
:alert => "Can't post a comment on a non-existent post"
|
||||
alert: "Can't post a comment on a non-existent post"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
require 'will_paginate/array'
|
||||
|
||||
class CropsController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :hierarchy, :search, :show]
|
||||
before_filter :authenticate_member!, except: [:index, :hierarchy, :search, :show]
|
||||
load_and_authorize_resource
|
||||
skip_authorize_resource :only => [:hierarchy, :search]
|
||||
skip_authorize_resource only: [:hierarchy, :search]
|
||||
|
||||
# GET /crops
|
||||
# GET /crops.json
|
||||
@@ -9,30 +11,43 @@ class CropsController < ApplicationController
|
||||
@sort = params[:sort]
|
||||
if @sort == 'alpha'
|
||||
# alphabetical order
|
||||
@crops = Crop.includes(:scientific_names, {:plantings => :photos}).paginate(:page => params[:page])
|
||||
@crops = Crop.includes(:scientific_names, {plantings: :photos})
|
||||
@paginated_crops = @crops.approved.paginate(page: params[:page])
|
||||
else
|
||||
# default to sorting by popularity
|
||||
@crops = Crop.popular.includes(:scientific_names, {:plantings => :photos}).paginate(:page => params[:page])
|
||||
@crops = Crop.popular.includes(:scientific_names, {plantings: :photos})
|
||||
@paginated_crops = @crops.approved.paginate(page: params[:page])
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json { render :json => @crops }
|
||||
format.json { render json: @crops }
|
||||
format.rss do
|
||||
@crops = Crop.recent.includes(:scientific_names, :creator)
|
||||
render :rss => @crops
|
||||
render rss: @crops
|
||||
end
|
||||
format.csv do
|
||||
@filename = "Growstuff-Crops-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@crops = Crop.includes(:scientific_names, :plantings, :seeds, :creator)
|
||||
render :csv => @crops
|
||||
render csv: @crops
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# GET /crops/wrangle
|
||||
def wrangle
|
||||
@crops = Crop.recent.paginate(:page => params[:page])
|
||||
@approval_status = params[:approval_status]
|
||||
case @approval_status
|
||||
when "pending"
|
||||
@crops = Crop.pending_approval
|
||||
when "rejected"
|
||||
@crops = Crop.rejected
|
||||
else
|
||||
@crops = Crop.recent
|
||||
end
|
||||
|
||||
@crops = @crops.paginate(page: params[:page])
|
||||
|
||||
@crop_wranglers = Role.crop_wranglers
|
||||
respond_to do |format|
|
||||
format.html
|
||||
@@ -49,32 +64,27 @@ class CropsController < ApplicationController
|
||||
|
||||
# GET /crops/search
|
||||
def search
|
||||
@search = params[:search]
|
||||
@exact_match = Crop.find_by_name(params[:search])
|
||||
|
||||
@partial_matches = Crop.search(params[:search])
|
||||
# exclude exact match from partial match list
|
||||
@partial_matches = @partial_matches.reject{ |r| @exact_match && r.eql?(@exact_match) }
|
||||
|
||||
@fuzzy = Crop.search(params[:term])
|
||||
@term = params[:term]
|
||||
@matches = Crop.search(@term)
|
||||
@paginated_matches = @matches.paginate(page: params[:page])
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json { render :json => @fuzzy }
|
||||
format.json { render json: @matches }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /crops/1
|
||||
# GET /crops/1.json
|
||||
def show
|
||||
@crop = Crop.includes(:scientific_names, {:plantings => :photos}).find(params[:id])
|
||||
@posts = @crop.posts.paginate(:page => params[:page])
|
||||
@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
|
||||
format.json do
|
||||
render :json => @crop.to_json(:include => {
|
||||
:plantings => { :include => { :owner => { :only => [:id, :login_name, :location, :latitude, :longitude] }}}
|
||||
render json: @crop.to_json(include: {
|
||||
plantings: { include: { owner: { only: [:id, :login_name, :location, :latitude, :longitude] }}}
|
||||
})
|
||||
end
|
||||
end
|
||||
@@ -84,9 +94,9 @@ class CropsController < ApplicationController
|
||||
# GET /crops/new.json
|
||||
def new
|
||||
@crop = Crop.new
|
||||
3.times do
|
||||
@crop.scientific_names.build
|
||||
end
|
||||
@crop.alternate_names.build
|
||||
@crop.scientific_names.build
|
||||
|
||||
respond_to do |format|
|
||||
format.html # new.html.haml
|
||||
format.json { render json: @crop }
|
||||
@@ -96,17 +106,41 @@ class CropsController < ApplicationController
|
||||
# GET /crops/1/edit
|
||||
def edit
|
||||
@crop = Crop.find(params[:id])
|
||||
@crop.alternate_names.build if @crop.alternate_names.blank?
|
||||
@crop.scientific_names.build if @crop.scientific_names.blank?
|
||||
|
||||
end
|
||||
|
||||
# POST /crops
|
||||
# POST /crops.json
|
||||
def create
|
||||
params[:crop][:creator_id] = current_member.id
|
||||
|
||||
@crop = Crop.new(crop_params)
|
||||
|
||||
if current_member.has_role? :crop_wrangler
|
||||
@crop.creator = current_member
|
||||
success_msg = "Crop was successfully created."
|
||||
else
|
||||
@crop.requester = current_member
|
||||
@crop.approval_status = "pending"
|
||||
success_msg = "Crop was successfully requested."
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
if @crop.save
|
||||
format.html { redirect_to @crop, notice: 'Crop was successfully created.' }
|
||||
params[:alt_name].each do |index, value|
|
||||
@crop.alternate_names.create(name: value, creator_id: current_member.id)
|
||||
end
|
||||
params[:sci_name].each do |index, value|
|
||||
@crop.scientific_names.create(scientific_name: value, creator_id: current_member.id)
|
||||
end
|
||||
unless current_member.has_role? :crop_wrangler
|
||||
Role.crop_wranglers.each do |w|
|
||||
Notifier.new_crop_request(w, @crop).deliver!
|
||||
end
|
||||
end
|
||||
|
||||
format.html { redirect_to @crop, notice: success_msg }
|
||||
format.json { render json: @crop, status: :created, location: @crop }
|
||||
else
|
||||
format.html { render action: "new" }
|
||||
@@ -120,8 +154,35 @@ class CropsController < ApplicationController
|
||||
def update
|
||||
@crop = Crop.find(params[:id])
|
||||
|
||||
previous_status = @crop.approval_status
|
||||
|
||||
@crop.creator = current_member if previous_status == "pending"
|
||||
|
||||
respond_to do |format|
|
||||
if @crop.update(crop_params)
|
||||
if !params[:alt_name].nil?
|
||||
@crop.alternate_names.each do |alt_name|
|
||||
alt_name.destroy
|
||||
end
|
||||
|
||||
params[:alt_name].each do |index, value|
|
||||
alt_name = @crop.alternate_names.create(name: value, creator_id: current_member.id)
|
||||
end
|
||||
|
||||
@crop.scientific_names.each do |sci_name|
|
||||
sci_name.destroy
|
||||
end
|
||||
params[:sci_name].each do |index, value|
|
||||
sci_name = @crop.scientific_names.create(scientific_name: value, creator_id: current_member.id)
|
||||
end
|
||||
end
|
||||
|
||||
if previous_status == "pending"
|
||||
requester = @crop.requester
|
||||
new_status = @crop.approval_status
|
||||
Notifier.crop_request_approved(requester, @crop).deliver! if new_status == "approved"
|
||||
Notifier.crop_request_rejected(requester, @crop).deliver! if new_status == "rejected"
|
||||
end
|
||||
format.html { redirect_to @crop, notice: 'Crop was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
@@ -146,6 +207,6 @@ class CropsController < ApplicationController
|
||||
private
|
||||
|
||||
def crop_params
|
||||
params.require(:crop).permit(:en_wikipedia_url, :name, :parent_id, :creator_id, :scientific_names_attributes)
|
||||
params.require(:crop).permit(:en_wikipedia_url, :name, :parent_id, :creator_id, :approval_status, :request_notes, :reason_for_rejection, :rejection_notes, scientific_names_attributes: [:scientific_name, :_destroy, :id])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
class FollowsController < ApplicationController
|
||||
before_filter :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
skip_load_resource only: :create
|
||||
|
||||
# POST /follows
|
||||
def create
|
||||
|
||||
@follow = current_member.follows.build(:followed_id => follow_params[:followed_id])
|
||||
|
||||
@follow = current_member.follows.build(followed_id: follow_params[:followed_id])
|
||||
|
||||
if @follow.save
|
||||
flash[:notice] = "Followed #{ @follow.followed.login_name }"
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
class GardensController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
before_filter :authenticate_member!, except: [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
|
||||
# GET /gardens
|
||||
# GET /gardens.json
|
||||
def index
|
||||
@gardens = Garden.paginate(:page => params[:page])
|
||||
@gardens = Garden.paginate(page: params[:page])
|
||||
@owner = Member.find_by_slug(params[:owner])
|
||||
if @owner
|
||||
@gardens = @owner.gardens.paginate(:page => params[:page])
|
||||
@gardens = @owner.gardens.paginate(page: params[:page])
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
@@ -87,7 +87,7 @@ class GardensController < ApplicationController
|
||||
expire_fragment("homepage_stats")
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to gardens_by_owner_path(:owner => @garden.owner), notice: 'Garden was successfully deleted.' }
|
||||
format.html { redirect_to gardens_by_owner_path(owner: @garden.owner), notice: 'Garden was successfully deleted.' }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class HarvestsController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
before_filter :authenticate_member!, except: [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
|
||||
@@ -17,12 +17,12 @@ class HarvestsController < ApplicationController
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html { @harvests = @harvests.paginate(:page => params[:page]) }
|
||||
format.html { @harvests = @harvests.paginate(page: params[:page]) }
|
||||
format.json { render json: @harvests }
|
||||
format.csv do
|
||||
specifics = (@owner ? "#{@owner.name}-" : @crop ? "#{@crop.name}-" : nil)
|
||||
specifics = (@owner ? "#{@owner.login_name}-" : @crop ? "#{@crop.name}-" : nil)
|
||||
@filename = "Growstuff-#{specifics}Harvests-#{Time.zone.now.to_s(:number)}.csv"
|
||||
render :csv => @harvests
|
||||
render csv: @harvests
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -107,6 +107,6 @@ class HarvestsController < ApplicationController
|
||||
|
||||
def harvest_params
|
||||
params.require(:harvest).permit(:crop_id, :harvested_at, :description, :owner_id,
|
||||
:quantity, :unit, :weight_quantity, :weight_unit, :plant_part_id, :slug)
|
||||
:quantity, :unit, :weight_quantity, :weight_unit, :plant_part_id, :slug, :si_weight)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
class MembersController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
|
||||
skip_authorize_resource :only => :nearby
|
||||
skip_authorize_resource only: [:nearby, :unsubscribe]
|
||||
|
||||
after_action :expire_cache_fragments, :only => :create
|
||||
after_action :expire_cache_fragments, only: :create
|
||||
|
||||
def index
|
||||
@members = Member.confirmed.paginate(:page => params[:page])
|
||||
@sort = params[:sort]
|
||||
if @sort == 'recently_joined'
|
||||
@members = Member.confirmed.recently_joined.paginate(page: params[:page])
|
||||
else
|
||||
@members = Member.confirmed.paginate(page: params[:page])
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.haml
|
||||
format.json { render :json => @members.to_json(:only => [:id, :login_name, :slug, :bio, :created_at, :location, :latitude, :longitude]) }
|
||||
format.json { render json: @members.to_json(only: [:id, :login_name, :slug, :bio, :created_at, :location, :latitude, :longitude]) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -26,22 +31,43 @@ 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.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 }
|
||||
layout: false,
|
||||
locals: { member: @member }
|
||||
)}
|
||||
end
|
||||
end
|
||||
|
||||
def view_follows
|
||||
@member = Member.confirmed.find(params[:login_name])
|
||||
@follows = @member.followed.paginate(:page => params[:page])
|
||||
@follows = @member.followed.paginate(page: params[:page])
|
||||
end
|
||||
|
||||
def view_followers
|
||||
@member = Member.confirmed.find(params[:login_name])
|
||||
@followers = @member.followers.paginate(:page => params[:page])
|
||||
@followers = @member.followers.paginate(page: params[:page])
|
||||
end
|
||||
|
||||
EMAIL_TYPE_STRING = {
|
||||
send_notification_email: "direct message notifications",
|
||||
send_planting_reminder: "planting reminders"
|
||||
}
|
||||
|
||||
def unsubscribe
|
||||
begin
|
||||
verifier = ActiveSupport::MessageVerifier.new(ENV['RAILS_SECRET_TOKEN'])
|
||||
decrypted_message = verifier.verify(params[:message])
|
||||
|
||||
@member = Member.find(decrypted_message[:member_id])
|
||||
@type = decrypted_message[:type]
|
||||
@member.update(@type => false)
|
||||
|
||||
flash.now[:notice] = "You have been unsubscribed from #{EMAIL_TYPE_STRING[@type]} emails."
|
||||
|
||||
rescue ActiveSupport::MessageVerifier::InvalidSignature
|
||||
flash.now[:alert] = "We're sorry, there was an error updating your settings."
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -5,7 +5,7 @@ class NotificationsController < ApplicationController
|
||||
|
||||
# GET /notifications
|
||||
def index
|
||||
@notifications = Notification.where(recipient_id: current_member)
|
||||
@notifications = Notification.where(recipient_id: current_member).page(params[:page])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
@@ -36,6 +36,23 @@ class NotificationsController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
# GET /notifications/1/reply
|
||||
def reply
|
||||
@notification = Notification.new
|
||||
@sender_notification = Notification.find(params[:id])
|
||||
@sender_notification.read = true
|
||||
@sender_notification.save
|
||||
@recipient = @sender_notification.sender
|
||||
@subject = @sender_notification.subject =~ /^Re: / ?
|
||||
@sender_notification.subject :
|
||||
"Re: " + @sender_notification.subject
|
||||
|
||||
|
||||
respond_to do |format|
|
||||
format.html # reply.html.haml
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /notifications/1
|
||||
def destroy
|
||||
@notification = Notification.find(params[:id])
|
||||
|
||||
@@ -8,7 +8,7 @@ class OrderItemsController < ApplicationController
|
||||
params[:order_item][:price] = params[:order_item][:price].to_f * 100 # convert to cents
|
||||
end
|
||||
@order_item = OrderItem.new(order_item_params)
|
||||
@order_item.order = current_member.current_order || Order.create(:member_id => current_member.id)
|
||||
@order_item.order = current_member.current_order || Order.create(member_id: current_member.id)
|
||||
|
||||
respond_to do |format|
|
||||
if @order_item.save
|
||||
|
||||
@@ -34,15 +34,15 @@ class OrdersController < ApplicationController
|
||||
@order = Order.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @order.update_attributes(:referral_code => params[:referral_code])
|
||||
if @order.update_attributes(referral_code: params[:referral_code])
|
||||
response = EXPRESS_GATEWAY.setup_purchase(
|
||||
@order.total,
|
||||
:items => @order.activemerchant_items,
|
||||
:currency => Growstuff::Application.config.currency,
|
||||
:no_shipping => true,
|
||||
:ip => request.remote_ip,
|
||||
:return_url => complete_order_url,
|
||||
:cancel_return_url => shop_url
|
||||
items: @order.activemerchant_items,
|
||||
currency: Growstuff::Application.config.currency,
|
||||
no_shipping: true,
|
||||
ip: request.remote_ip,
|
||||
return_url: complete_order_url,
|
||||
cancel_return_url: shop_url
|
||||
)
|
||||
format.html { redirect_to EXPRESS_GATEWAY.redirect_url_for(response.token) }
|
||||
else
|
||||
@@ -58,10 +58,10 @@ class OrdersController < ApplicationController
|
||||
if (params[:token] && params['PayerID'])
|
||||
purchase = EXPRESS_GATEWAY.purchase(
|
||||
@order.total,
|
||||
:currency => Growstuff::Application.config.currency,
|
||||
:ip => request.remote_ip,
|
||||
:payer_id => params['PayerID'],
|
||||
:token => params[:token]
|
||||
currency: Growstuff::Application.config.currency,
|
||||
ip: request.remote_ip,
|
||||
payer_id: params['PayerID'],
|
||||
token: params[:token]
|
||||
)
|
||||
if purchase.success?
|
||||
@order.completed_at = Time.zone.now
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
class PhotosController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
before_filter :authenticate_member!, except: [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /photos
|
||||
# GET /photos.json
|
||||
def index
|
||||
@photos = Photo.paginate(:page => params[:page])
|
||||
@photos = Photo.paginate(page: params[:page])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
@@ -65,7 +65,7 @@ class PhotosController < ApplicationController
|
||||
|
||||
# several models can have photos. we need to know what model and the id
|
||||
# for the entry to attach the photo to
|
||||
valid_models = ["planting", "harvest"]
|
||||
valid_models = ["planting", "harvest", "garden"]
|
||||
if params[:type]
|
||||
if valid_models.include?(params[:type])
|
||||
if params[:id]
|
||||
@@ -124,7 +124,8 @@ class PhotosController < ApplicationController
|
||||
def destroy
|
||||
@photo = Photo.find(params[:id])
|
||||
@photo.destroy
|
||||
|
||||
flash[:alert] = "Photo successfully deleted."
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to photos_url }
|
||||
format.json { head :no_content }
|
||||
|
||||
@@ -5,7 +5,7 @@ class PlacesController < ApplicationController
|
||||
respond_to do |format|
|
||||
format.html
|
||||
# json response is whatever we want to map here
|
||||
format.json { render :json => Member.located.to_json(:only => [:id, :login_name, :slug, :location, :latitude, :longitude]) }
|
||||
format.json { render json: Member.located.to_json(only: [:id, :login_name, :slug, :location, :latitude, :longitude]) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -16,14 +16,22 @@ class PlacesController < ApplicationController
|
||||
@nearby_members = Member.nearest_to(params[:place])
|
||||
respond_to do |format|
|
||||
format.html # show.html.haml
|
||||
format.json { render :json => @nearby_members.to_json(:only => [:id, :login_name, :slug, :location, :latitude, :longitude]) }
|
||||
format.json { render json: @nearby_members.to_json(only: [:id, :login_name, :slug, :location, :latitude, :longitude]) }
|
||||
end
|
||||
end
|
||||
|
||||
def search
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
redirect_to place_path(params[:new_place])
|
||||
if params[:new_place].empty?
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
redirect_to places_path, alert: 'Please enter a valid location'
|
||||
end
|
||||
end
|
||||
else
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
redirect_to place_path(params[:new_place])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class PlantingsController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
before_filter :authenticate_member!, except: [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /plantings
|
||||
@@ -8,21 +8,21 @@ class PlantingsController < ApplicationController
|
||||
@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])
|
||||
@plantings = @owner.plantings.includes(:owner, :crop, :garden).paginate(page: params[:page])
|
||||
elsif @crop
|
||||
@plantings = @crop.plantings.includes(:owner, :crop, :garden).paginate(:page => params[:page])
|
||||
@plantings = @crop.plantings.includes(:owner, :crop, :garden).paginate(page: params[:page])
|
||||
else
|
||||
@plantings = Planting.includes(:owner, :crop, :garden).paginate(:page => params[:page])
|
||||
@plantings = Planting.includes(:owner, :crop, :garden).paginate(page: params[:page])
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html { @plantings = @plantings.paginate(:page => params[:page]) }
|
||||
format.html { @plantings = @plantings.paginate(page: params[:page]) }
|
||||
format.json { render json: @plantings }
|
||||
format.rss { render :layout => false } #index.rss.builder
|
||||
format.rss { render layout: false } #index.rss.builder
|
||||
format.csv do
|
||||
specifics = (@owner ? "#{@owner.name}-" : @crop ? "#{@crop.name}-" : nil)
|
||||
specifics = (@owner ? "#{@owner.login_name}-" : @crop ? "#{@crop.name}-" : nil)
|
||||
@filename = "Growstuff-#{specifics}Plantings-#{Time.zone.now.to_s(:number)}.csv"
|
||||
render :csv => @plantings
|
||||
render csv: @plantings
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -71,6 +71,7 @@ class PlantingsController < ApplicationController
|
||||
|
||||
respond_to do |format|
|
||||
if @planting.save
|
||||
@planting.update_attribute(:days_before_maturity, update_days_before_maturity(@planting, planting_params[:crop_id]))
|
||||
format.html { redirect_to @planting, notice: 'Planting was successfully created.' }
|
||||
format.json { render json: @planting, status: :created, location: @planting }
|
||||
expire_fragment("homepage_stats")
|
||||
@@ -89,6 +90,7 @@ class PlantingsController < ApplicationController
|
||||
|
||||
respond_to do |format|
|
||||
if @planting.update(planting_params)
|
||||
@planting.update_attribute(:days_before_maturity, update_days_before_maturity(@planting, planting_params[:crop_id]))
|
||||
format.html { redirect_to @planting, notice: 'Planting was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
@@ -119,4 +121,12 @@ class PlantingsController < ApplicationController
|
||||
:quantity, :sunniness, :planted_from, :owner_id, :finished,
|
||||
:finished_at)
|
||||
end
|
||||
|
||||
def update_days_before_maturity(planting, crop_id)
|
||||
if planting.finished_at.nil?
|
||||
planting.calculate_days_before_maturity(planting, crop_id)
|
||||
else
|
||||
(planting.finished_at - planting.planted_at).to_i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class PostsController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
before_filter :authenticate_member!, except: [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /posts
|
||||
@@ -8,29 +8,29 @@ class PostsController < ApplicationController
|
||||
def index
|
||||
@author = Member.find_by_slug(params[:author])
|
||||
if @author
|
||||
@posts = @author.posts.includes(:author, { :comments => :author }).paginate(:page => params[:page])
|
||||
@posts = @author.posts.includes(:author, { comments: :author }).paginate(page: params[:page])
|
||||
else
|
||||
@posts = Post.includes(:author, { :comments => :author }).paginate(:page => params[:page])
|
||||
@posts = Post.includes(:author, { comments: :author }).paginate(page: params[:page])
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.haml
|
||||
format.json { render json: @posts }
|
||||
format.rss { render :layout => false } #index.rss.builder
|
||||
format.rss { render layout: false } #index.rss.builder
|
||||
end
|
||||
end
|
||||
|
||||
# GET /posts/1
|
||||
# GET /posts/1.json
|
||||
def show
|
||||
@post = Post.includes(:author, { :comments => :author }).find(params[:id])
|
||||
@post = Post.includes(:author, { comments: :author }).find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.haml
|
||||
format.json { render json: @post }
|
||||
format.rss { render(
|
||||
:layout => false,
|
||||
:locals => { :post => @post }
|
||||
layout: false,
|
||||
locals: { post: @post }
|
||||
)}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -28,7 +28,7 @@ class RegistrationsController < Devise::RegistrationsController
|
||||
if successfully_updated
|
||||
set_flash_message :notice, :updated
|
||||
# Sign in the member bypassing validation in case their password changed
|
||||
sign_in @member, :bypass => true
|
||||
sign_in @member, bypass: true
|
||||
redirect_to edit_member_registration_path
|
||||
else
|
||||
render "edit"
|
||||
|
||||
20
app/controllers/robots_controller.rb
Normal file
20
app/controllers/robots_controller.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
class RobotsController < ApplicationController
|
||||
|
||||
DEFAULT_FILENAME = 'config/robots.txt'.freeze
|
||||
|
||||
def robots
|
||||
filename = if subdomain && subdomain != 'www'
|
||||
"config/robots.#{ subdomain }.txt"
|
||||
end
|
||||
|
||||
file_to_render = File.exists?(filename.to_s) ? filename : DEFAULT_FILENAME
|
||||
|
||||
render file: file_to_render, layout: false, content_type: 'text/plain'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def subdomain
|
||||
request.subdomain.present? ? request.subdomain : nil
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,5 @@
|
||||
class ScientificNamesController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
before_filter :authenticate_member!, except: [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /scientific_names
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class SeedsController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
before_filter :authenticate_member!, except: [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /seeds
|
||||
@@ -8,17 +8,17 @@ class SeedsController < ApplicationController
|
||||
@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])
|
||||
@seeds = @owner.seeds.includes(:owner, :crop).paginate(page: params[:page])
|
||||
elsif @crop
|
||||
@seeds = @crop.seeds.includes(:owner, :crop).paginate(:page => params[:page])
|
||||
@seeds = @crop.seeds.includes(:owner, :crop).paginate(page: params[:page])
|
||||
else
|
||||
@seeds = Seed.includes(:owner, :crop).paginate(:page => params[:page])
|
||||
@seeds = Seed.includes(:owner, :crop).paginate(page: params[:page])
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
format.json { render json: @seeds }
|
||||
format.rss { render :layout => false } #index.rss.builder
|
||||
format.rss { render layout: false } #index.rss.builder
|
||||
format.csv do
|
||||
if @owner
|
||||
@filename = "Growstuff-#{@owner}-Seeds-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@@ -27,7 +27,7 @@ class SeedsController < ApplicationController
|
||||
@filename = "Growstuff-Seeds-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@seeds = Seed.includes(:owner, :crop)
|
||||
end
|
||||
render :csv => @seeds
|
||||
render csv: @seeds
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -110,7 +110,9 @@ class SeedsController < ApplicationController
|
||||
private
|
||||
|
||||
def seed_params
|
||||
params.require(:seed).permit(:owner_id, :crop_id, :description, :quantity, :plant_before,
|
||||
:tradable_to, :slug)
|
||||
params.require(:seed).permit(
|
||||
:owner_id, :crop_id, :description, :quantity, :plant_before,
|
||||
:days_until_maturity_min, :days_until_maturity_max, :organic, :gmo,
|
||||
:heirloom, :tradable_to, :slug)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -21,7 +21,7 @@ module ApplicationHelper
|
||||
link = "http://www.wolframalpha.com/input/?i=#{pid}+#{currency}"
|
||||
return link_to "(convert)",
|
||||
link,
|
||||
:target => "_blank"
|
||||
target: "_blank"
|
||||
end
|
||||
|
||||
# Produces a cache key for uniquely identifying cached fragments.
|
||||
@@ -31,5 +31,24 @@ module ApplicationHelper
|
||||
"#{klass.name.downcase.pluralize}/#{identifier}-#{count}-#{max_updated_at}"
|
||||
end
|
||||
|
||||
def required_field_help_text
|
||||
asterisk = content_tag :span, '*', class: ['red']
|
||||
text = content_tag :em, 'denotes a required field'
|
||||
content_tag :div, asterisk + ' '.html_safe + text, class: ['margin-bottom']
|
||||
end
|
||||
|
||||
#
|
||||
# Returns an image uri for a given member.
|
||||
#
|
||||
# Falls back to Gravatar
|
||||
#
|
||||
def avatar_uri(member, size = 150)
|
||||
return member.preferred_avatar_uri if member.preferred_avatar_uri.present?
|
||||
|
||||
Gravatar.new(member.email).image_url({
|
||||
size: size,
|
||||
default: :identicon
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -19,4 +19,4 @@ module AutoSuggestHelper
|
||||
}.html_safe
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
17
app/helpers/crops_helper.rb
Normal file
17
app/helpers/crops_helper.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
module CropsHelper
|
||||
def display_seed_availability(member, crop)
|
||||
total_quantity = 0
|
||||
member.seeds.each do |seed|
|
||||
if seed.crop.name == crop.name
|
||||
total_quantity = total_quantity + seed.quantity
|
||||
end
|
||||
end
|
||||
|
||||
if (total_quantity != 0)
|
||||
"You have #{pluralize(total_quantity, "seed")} of this crop."
|
||||
else
|
||||
"You don't have any seeds of this crop."
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
25
app/helpers/gardens_helper.rb
Normal file
25
app/helpers/gardens_helper.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
module GardensHelper
|
||||
|
||||
def display_garden_description(garden)
|
||||
if garden.description.nil?
|
||||
"no description provided."
|
||||
else
|
||||
truncate(garden.description, length: 130, separator: ' ', omission: '... ') { link_to "Read more", garden_path(garden) }
|
||||
end
|
||||
end
|
||||
|
||||
def display_garden_plantings(plantings)
|
||||
if plantings.blank?
|
||||
"None"
|
||||
else
|
||||
output = ""
|
||||
plantings.first(2).each do |planting|
|
||||
output += "<li>"
|
||||
output += planting.quantity.nil? ? "0 " : "#{planting.quantity} "
|
||||
output += link_to planting.crop.name, planting.crop
|
||||
output += ", planted on #{planting.planted_at}</li>"
|
||||
end
|
||||
output.html_safe
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -18,11 +18,11 @@ module HarvestsHelper
|
||||
def display_human_quantity(harvest)
|
||||
if ! harvest.quantity.blank? && harvest.quantity > 0
|
||||
if harvest.unit == 'individual' # just the number
|
||||
number_to_human(harvest.quantity, :strip_insignificant_zeros => true)
|
||||
number_to_human(harvest.quantity, strip_insignificant_zeros: true)
|
||||
elsif ! harvest.unit.blank? # pluralize anything else
|
||||
return pluralize(number_to_human(harvest.quantity, :strip_insignificant_zeros => true), harvest.unit)
|
||||
return pluralize(number_to_human(harvest.quantity, strip_insignificant_zeros: true), harvest.unit)
|
||||
else
|
||||
return "#{number_to_human(harvest.quantity, :strip_insignificant_zeros => true)} #{harvest.unit}"
|
||||
return "#{number_to_human(harvest.quantity, strip_insignificant_zeros: true)} #{harvest.unit}"
|
||||
end
|
||||
else
|
||||
return nil
|
||||
@@ -31,10 +31,18 @@ module HarvestsHelper
|
||||
|
||||
def display_weight(harvest)
|
||||
if ! harvest.weight_quantity.blank? && harvest.weight_quantity > 0
|
||||
return "#{number_to_human(harvest.weight_quantity, :strip_insignificant_zeros => true)} #{harvest.weight_unit}"
|
||||
return "#{number_to_human(harvest.weight_quantity, strip_insignificant_zeros: true)} #{harvest.weight_unit}"
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
def display_harvest_description(harvest)
|
||||
if harvest.description.empty?
|
||||
"No description provided."
|
||||
else
|
||||
truncate(harvest.description, length: 130, separator: ' ', omission: '... ') { link_to "Read more", harvest_path(harvest) }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -2,15 +2,10 @@ module NotificationsHelper
|
||||
def reply_link(notification)
|
||||
if notification.post
|
||||
# comment on the post in question
|
||||
new_comment_url(:post_id => notification.post.id)
|
||||
new_comment_url(post_id: notification.post.id)
|
||||
else
|
||||
# by default, reply link sends a PM in return
|
||||
new_notification_url(
|
||||
:recipient_id => notification.sender.id,
|
||||
:subject => notification.subject =~ /^Re: / ?
|
||||
notification.subject :
|
||||
"Re: " + notification.subject
|
||||
)
|
||||
reply_notification_url(notification)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
45
app/helpers/plantings_helper.rb
Normal file
45
app/helpers/plantings_helper.rb
Normal file
@@ -0,0 +1,45 @@
|
||||
module PlantingsHelper
|
||||
|
||||
def display_days_before_maturity(planting)
|
||||
if planting.finished?
|
||||
0
|
||||
elsif !planting.finished_at.nil?
|
||||
((p = planting.finished_at - DateTime.now).to_i) <= 0 ? 0 : p.to_i
|
||||
elsif planting.days_before_maturity.nil?
|
||||
"unknown"
|
||||
else
|
||||
((p = (planting.planted_at + planting.days_before_maturity) - DateTime.now).to_i <= 0) ? 0 : p.to_i
|
||||
end
|
||||
end
|
||||
|
||||
def display_finished(planting)
|
||||
if !planting.finished_at.nil?
|
||||
planting.finished_at
|
||||
elsif planting.finished
|
||||
"Yes (no date specified)"
|
||||
else
|
||||
"(no date specified)"
|
||||
end
|
||||
end
|
||||
|
||||
def display_planted_from(planting)
|
||||
!planting.planted_from.blank? ? planting.planted_from : "not specified"
|
||||
end
|
||||
|
||||
def display_planting_quantity(planting)
|
||||
!planting.quantity.blank? ? planting.quantity : "not specified"
|
||||
end
|
||||
|
||||
def display_planting(planting)
|
||||
if planting.quantity.to_i > 0 && planting.planted_from.present?
|
||||
return "#{planting.owner} planted #{pluralize(planting.quantity, planting.planted_from)}."
|
||||
elsif planting.quantity.to_i > 0
|
||||
return "#{planting.owner} planted #{pluralize(planting.quantity, 'unit')}."
|
||||
elsif planting.planted_from.present?
|
||||
return "#{planting.owner} planted #{planting.planted_from.pluralize}."
|
||||
else
|
||||
return "#{planting.owner}."
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
11
app/helpers/seeds_helper.rb
Normal file
11
app/helpers/seeds_helper.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
module SeedsHelper
|
||||
|
||||
def display_seed_description(seed)
|
||||
if seed.description.nil?
|
||||
"no description provided."
|
||||
else
|
||||
truncate(seed.description, length: 130, separator: ' ', omission: '... ') { link_to "Read more", seed_path(seed) }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -2,12 +2,23 @@ class Notifier < ActionMailer::Base
|
||||
include NotificationsHelper
|
||||
default from: "Growstuff <noreply@growstuff.org>"
|
||||
|
||||
def verifier()
|
||||
if ENV['RAILS_SECRET_TOKEN']
|
||||
return ActiveSupport::MessageVerifier.new(ENV['RAILS_SECRET_TOKEN'])
|
||||
else
|
||||
raise "RAILS_SECRET_TOKEN environment variable not set - have you created config/application.yml?"
|
||||
end
|
||||
end
|
||||
|
||||
def notify(notification)
|
||||
@notification = notification
|
||||
@reply_link = reply_link(@notification)
|
||||
|
||||
mail(:to => @notification.recipient.email,
|
||||
:subject => @notification.subject)
|
||||
# Encrypting
|
||||
@signed_message = verifier.generate ({ member_id: @notification.recipient.id, type: :send_notification_email })
|
||||
|
||||
mail(to: @notification.recipient.email,
|
||||
subject: @notification.subject)
|
||||
end
|
||||
|
||||
def planting_reminder(member)
|
||||
@@ -16,10 +27,28 @@ class Notifier < ActionMailer::Base
|
||||
@plantings = @member.plantings.first(5)
|
||||
@harvests = @member.harvests.first(5)
|
||||
|
||||
# Encrypting
|
||||
@signed_message = verifier.generate ({ member_id: @member.id, type: :send_planting_reminder })
|
||||
|
||||
if @member.send_planting_reminder
|
||||
mail(:to => @member.email,
|
||||
:subject => "What have you planted lately?")
|
||||
mail(to: @member.email,
|
||||
subject: "What have you planted lately?")
|
||||
end
|
||||
end
|
||||
|
||||
def new_crop_request(member, request)
|
||||
@member, @request = member, request
|
||||
mail(to: @member.email, subject: "#{@request.requester.login_name} has requested #{@request.name} as a new crop")
|
||||
end
|
||||
|
||||
def crop_request_approved(member, crop)
|
||||
@member, @crop = member, crop
|
||||
mail(to: @member.email, subject: "#{crop.name.capitalize} has been approved")
|
||||
end
|
||||
|
||||
def crop_request_rejected(member, crop)
|
||||
@member, @crop = member, crop
|
||||
mail(to: @member.email, subject: "#{crop.name.capitalize} has been rejected")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -21,14 +21,32 @@ class Ability
|
||||
cannot :read, Account
|
||||
cannot :read, AccountType
|
||||
|
||||
# nobody should be able to view unapproved crops unless they
|
||||
# are wranglers or admins
|
||||
cannot :read, Crop
|
||||
can :read, Crop, approval_status: "approved"
|
||||
# scientific names should only be viewable if associated crop is approved
|
||||
cannot :read, ScientificName
|
||||
can :read, ScientificName do |sn|
|
||||
sn.crop.approved?
|
||||
end
|
||||
# ... same for alternate names
|
||||
cannot :read, AlternateName
|
||||
can :read, AlternateName do |an|
|
||||
an.crop.approved?
|
||||
end
|
||||
|
||||
if member
|
||||
# members can see even rejected or pending crops if they requested it
|
||||
can :read, Crop, requester_id: member.id
|
||||
|
||||
# managing your own user settings
|
||||
can :update, Member, :id => member.id
|
||||
can :update, Member, id: member.id
|
||||
|
||||
# can read/delete notifications that were sent to them
|
||||
can :read, Notification, :recipient_id => member.id
|
||||
can :destroy, Notification, :recipient_id => member.id
|
||||
can :read, Notification, recipient_id: member.id
|
||||
can :destroy, Notification, recipient_id: member.id
|
||||
can :reply, Notification, recipient_id: member.id
|
||||
# can send a private message to anyone but themselves
|
||||
# note: sadly, we can't test for this from the view, but it works
|
||||
# for the model/controller
|
||||
@@ -45,58 +63,63 @@ class Ability
|
||||
can :manage, AlternateName
|
||||
end
|
||||
|
||||
# any member can create a crop provisionally
|
||||
can :create, Crop
|
||||
|
||||
# can create & destroy their own authentications against other sites.
|
||||
can :create, Authentication
|
||||
can :destroy, Authentication, :member_id => member.id
|
||||
can :destroy, Authentication, member_id: member.id
|
||||
|
||||
# anyone can create a post, or comment on a post,
|
||||
# but only the author can edit/destroy it.
|
||||
can :create, Post
|
||||
can :update, Post, :author_id => member.id
|
||||
can :destroy, Post, :author_id => member.id
|
||||
can :update, Post, author_id: member.id
|
||||
can :destroy, Post, author_id: member.id
|
||||
can :create, Comment
|
||||
can :update, Comment, :author_id => member.id
|
||||
can :destroy, Comment, :author_id => member.id
|
||||
can :update, Comment, author_id: member.id
|
||||
can :destroy, Comment, author_id: member.id
|
||||
|
||||
# same deal for gardens and plantings
|
||||
can :create, Garden
|
||||
can :update, Garden, :owner_id => member.id
|
||||
can :destroy, Garden, :owner_id => member.id
|
||||
can :update, Garden, owner_id: member.id
|
||||
can :destroy, Garden, owner_id: member.id
|
||||
|
||||
can :create, Planting
|
||||
can :update, Planting, :garden => { :owner_id => member.id }
|
||||
can :destroy, Planting, :garden => { :owner_id => member.id }
|
||||
can :update, Planting, garden: { owner_id: member.id }
|
||||
can :destroy, Planting, garden: { owner_id: member.id }
|
||||
|
||||
can :create, Harvest
|
||||
can :update, Harvest, :owner_id => member.id
|
||||
can :destroy, Harvest, :owner_id => member.id
|
||||
can :update, Harvest, owner_id: member.id
|
||||
can :destroy, Harvest, owner_id: member.id
|
||||
|
||||
can :create, Photo
|
||||
can :update, Photo, :owner_id => member.id
|
||||
can :destroy, Photo, :owner_id => member.id
|
||||
can :update, Photo, owner_id: member.id
|
||||
can :destroy, Photo, owner_id: member.id
|
||||
|
||||
can :create, Seed
|
||||
can :update, Seed, :owner_id => member.id
|
||||
can :destroy, Seed, :owner_id => member.id
|
||||
can :update, Seed, owner_id: member.id
|
||||
can :destroy, Seed, owner_id: member.id
|
||||
|
||||
# orders/shop/etc
|
||||
can :create, Order
|
||||
can :read, Order, :member_id => member.id
|
||||
can :complete, Order, :member_id => member.id, :completed_at => nil
|
||||
can :checkout, Order, :member_id => member.id, :completed_at => nil
|
||||
can :cancel, Order, :member_id => member.id, :completed_at => nil
|
||||
can :destroy, Order, :member_id => member.id, :completed_at => nil
|
||||
can :read, Order, member_id: member.id
|
||||
can :complete, Order, member_id: member.id, completed_at: nil
|
||||
can :checkout, Order, member_id: member.id, completed_at: nil
|
||||
can :cancel, Order, member_id: member.id, completed_at: nil
|
||||
can :destroy, Order, member_id: member.id, completed_at: nil
|
||||
|
||||
can :create, OrderItem
|
||||
# for now, let's not let people mess with individual order items
|
||||
cannot :read, OrderItem, :order => { :member_id => member.id }
|
||||
cannot :update, OrderItem, :order => { :member_id => member.id, :completed_at => nil }
|
||||
cannot :destroy, OrderItem, :order => { :member_id => member.id, :completed_at => nil }
|
||||
cannot :read, OrderItem, order: { member_id: member.id }
|
||||
cannot :update, OrderItem, order: { member_id: member.id, completed_at: nil }
|
||||
cannot :destroy, OrderItem, order: { member_id: member.id, completed_at: nil }
|
||||
|
||||
# following/unfollowing permissions
|
||||
can :create, Follow do |f|
|
||||
!member.already_following?(f.followed) && f.followed_id != member.id
|
||||
end
|
||||
can :create, Follow
|
||||
cannot :create, Follow, followed_id: member.id # can't follow yourself
|
||||
|
||||
can :destroy, Follow
|
||||
cannot :destroy, Follow, followed_id: member.id # can't unfollow yourself
|
||||
|
||||
if member.has_role? :admin
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ class Account < ActiveRecord::Base
|
||||
belongs_to :member
|
||||
belongs_to :account_type
|
||||
|
||||
validates :member_id, :uniqueness => {
|
||||
:message => 'already has account details associated with it'
|
||||
validates :member_id, uniqueness: {
|
||||
message: 'already has account details associated with it'
|
||||
}
|
||||
|
||||
before_create do |account|
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class AlternateName < ActiveRecord::Base
|
||||
after_commit { |an| an.crop.__elasticsearch__.index_document if an.crop && ENV['GROWSTUFF_ELASTICSEARCH'] == "true" }
|
||||
belongs_to :crop
|
||||
belongs_to :creator, :class_name => 'Member'
|
||||
belongs_to :creator, class_name: 'Member'
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class Comment < ActiveRecord::Base
|
||||
belongs_to :author, :class_name => 'Member'
|
||||
belongs_to :author, class_name: 'Member'
|
||||
belongs_to :post
|
||||
|
||||
default_scope { order("created_at DESC") }
|
||||
@@ -11,11 +11,11 @@ class Comment < ActiveRecord::Base
|
||||
# don't send notifications to yourself
|
||||
if recipient != sender
|
||||
Notification.create(
|
||||
:recipient_id => recipient,
|
||||
:sender_id => sender,
|
||||
:subject => "#{self.author} commented on #{self.post.subject}",
|
||||
:body => self.body,
|
||||
:post_id => self.post.id
|
||||
recipient_id: recipient,
|
||||
sender_id: sender,
|
||||
subject: "#{self.author} commented on #{self.post.subject}",
|
||||
body: self.body,
|
||||
post_id: self.post.id
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,42 +2,121 @@ class Crop < ActiveRecord::Base
|
||||
extend FriendlyId
|
||||
friendly_id :name, use: [:slugged, :finders]
|
||||
|
||||
has_many :scientific_names
|
||||
has_many :scientific_names, after_add: :update_index, after_remove: :update_index
|
||||
accepts_nested_attributes_for :scientific_names,
|
||||
:allow_destroy => true,
|
||||
:reject_if => :all_blank
|
||||
allow_destroy: true,
|
||||
reject_if: :all_blank
|
||||
|
||||
has_many :alternate_names
|
||||
has_many :alternate_names, after_add: :update_index, after_remove: :update_index, dependent: :destroy
|
||||
has_many :plantings
|
||||
has_many :photos, :through => :plantings
|
||||
has_many :photos, through: :plantings
|
||||
has_many :seeds
|
||||
has_many :harvests
|
||||
has_many :plant_parts, -> { uniq }, :through => :harvests
|
||||
belongs_to :creator, :class_name => 'Member'
|
||||
has_many :plant_parts, -> { uniq }, through: :harvests
|
||||
belongs_to :creator, class_name: 'Member'
|
||||
belongs_to :requester, class_name: 'Member'
|
||||
|
||||
belongs_to :parent, :class_name => 'Crop'
|
||||
has_many :varieties, :class_name => 'Crop', :foreign_key => 'parent_id'
|
||||
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") }
|
||||
scope :toplevel, -> { where(:parent_id => nil) }
|
||||
scope :popular, -> { reorder("plantings_count desc, lower(name) asc") }
|
||||
scope :randomized, -> { reorder('random()') } # ok on sqlite and psql, but not on mysql
|
||||
scope :recent, -> { where(approval_status: "approved").reorder("created_at desc") }
|
||||
scope :toplevel, -> { where(approval_status: "approved", parent_id: nil) }
|
||||
scope :popular, -> { where(approval_status: "approved").reorder("plantings_count desc, lower(name) asc") }
|
||||
scope :randomized, -> { where(approval_status: "approved").reorder('random()') } # ok on sqlite and psql, but not on mysql
|
||||
scope :pending_approval, -> { where(approval_status: "pending") }
|
||||
scope :approved, -> { where(approval_status: "approved") }
|
||||
scope :rejected, -> { where(approval_status: "rejected") }
|
||||
|
||||
## Wikipedia urls are only necessary when approving a crop
|
||||
validates :en_wikipedia_url,
|
||||
:format => {
|
||||
:with => /\Ahttps?:\/\/en\.wikipedia\.org\/wiki/,
|
||||
:message => 'is not a valid English Wikipedia URL'
|
||||
}
|
||||
format: {
|
||||
with: /\Ahttps?:\/\/en\.wikipedia\.org\/wiki/,
|
||||
message: 'is not a valid English Wikipedia URL'
|
||||
},
|
||||
if: :approved?
|
||||
|
||||
## Reasons are only necessary when rejecting
|
||||
validates :reason_for_rejection, presence: true, if: :rejected?
|
||||
|
||||
## This validation addresses a race condition
|
||||
validate :approval_status_cannot_be_changed_again
|
||||
|
||||
validate :must_be_rejected_if_rejected_reasons_present
|
||||
|
||||
validate :must_have_meaningful_reason_for_rejection
|
||||
|
||||
####################################
|
||||
# Elastic search configuration
|
||||
include Elasticsearch::Model
|
||||
include Elasticsearch::Model::Callbacks
|
||||
# In order to avoid clashing between different environments,
|
||||
# use Rails.env as a part of index name (eg. development_growstuff)
|
||||
index_name [Rails.env, "growstuff"].join('_')
|
||||
settings index: { number_of_shards: 1 },
|
||||
analysis: {
|
||||
tokenizer: {
|
||||
gs_edgeNGram_tokenizer: {
|
||||
type: "edgeNGram", # edgeNGram: NGram match from the start of a token
|
||||
min_gram: 3,
|
||||
max_gram: 10,
|
||||
# token_chars: Elasticsearch will split on characters
|
||||
# that don’t belong to any of these classes
|
||||
token_chars: [ "letter", "digit" ]
|
||||
}
|
||||
},
|
||||
analyzer: {
|
||||
gs_edgeNGram_analyzer: {
|
||||
tokenizer: "gs_edgeNGram_tokenizer",
|
||||
filter: ["lowercase"]
|
||||
}
|
||||
},
|
||||
} do
|
||||
mappings dynamic: 'false' do
|
||||
indexes :id, type: 'long'
|
||||
indexes :name, type: 'string', analyzer: 'gs_edgeNGram_analyzer'
|
||||
indexes :approval_status, type: 'string'
|
||||
indexes :scientific_names do
|
||||
indexes :scientific_name,
|
||||
type: 'string',
|
||||
analyzer: 'gs_edgeNGram_analyzer',
|
||||
# Disabling field-length norm (norm). If the norm option is turned on(by default),
|
||||
# higher weigh would be given for shorter fields, which in our case is irrelevant.
|
||||
norms: { enabled: false }
|
||||
end
|
||||
indexes :alternate_names do
|
||||
indexes :name, type: 'string', analyzer: 'gs_edgeNGram_analyzer'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def as_indexed_json(options={})
|
||||
self.as_json(
|
||||
only: [:id, :name, :approval_status],
|
||||
include: {
|
||||
scientific_names: { only: :scientific_name },
|
||||
alternate_names: { only: :name }
|
||||
})
|
||||
end
|
||||
|
||||
# update the Elasticsearch index (only if we're using it in this
|
||||
# environment)
|
||||
def update_index(name_obj)
|
||||
if ENV["GROWSTUFF_ELASTICSEARCH"] == "true"
|
||||
__elasticsearch__.index_document
|
||||
end
|
||||
end
|
||||
|
||||
# End Elasticsearch section
|
||||
|
||||
def to_s
|
||||
return name
|
||||
end
|
||||
|
||||
def default_scientific_name
|
||||
if scientific_names.count > 0
|
||||
if scientific_names.size > 0
|
||||
return scientific_names.first.scientific_name
|
||||
else
|
||||
return nil
|
||||
@@ -98,18 +177,38 @@ class Crop < ActiveRecord::Base
|
||||
def interesting?
|
||||
min_plantings = 3 # needs this many plantings to be interesting
|
||||
min_photos = 3 # needs this many photos to be interesting
|
||||
return false unless photos.count >= min_photos
|
||||
return false unless photos.size >= min_photos
|
||||
return false unless plantings_count >= min_plantings
|
||||
return true
|
||||
end
|
||||
|
||||
def pending?
|
||||
approval_status == "pending"
|
||||
end
|
||||
|
||||
def approved?
|
||||
approval_status == "approved"
|
||||
end
|
||||
|
||||
def rejected?
|
||||
approval_status == "rejected"
|
||||
end
|
||||
|
||||
def approval_statuses
|
||||
[ 'rejected', 'pending', 'approved' ]
|
||||
end
|
||||
|
||||
def reasons_for_rejection
|
||||
[ "already in database", "not edible", "not enough information", "other" ]
|
||||
end
|
||||
|
||||
# Crop.interesting
|
||||
# returns a list of interesting crops, for use on the homepage etc
|
||||
def Crop.interesting
|
||||
howmany = 12 # max number to find
|
||||
interesting_crops = Array.new
|
||||
Crop.randomized.each do |c|
|
||||
break if interesting_crops.length == howmany
|
||||
Crop.includes(:photos).randomized.each do |c|
|
||||
break if interesting_crops.size == howmany
|
||||
next unless c.interesting?
|
||||
interesting_crops.push(c)
|
||||
end
|
||||
@@ -132,14 +231,14 @@ class Crop < ActiveRecord::Base
|
||||
|
||||
crop = Crop.find_or_create_by(name: name)
|
||||
crop.update_attributes(
|
||||
:en_wikipedia_url => en_wikipedia_url,
|
||||
:creator_id => cropbot.id
|
||||
en_wikipedia_url: en_wikipedia_url,
|
||||
creator_id: cropbot.id
|
||||
)
|
||||
|
||||
if parent
|
||||
parent = Crop.find_by_name(parent)
|
||||
if parent
|
||||
crop.update_attributes(:parent_id => parent.id)
|
||||
crop.update_attributes(parent_id: parent.id)
|
||||
else
|
||||
logger.warn("Warning: parent crop #{parent} not found")
|
||||
end
|
||||
@@ -165,13 +264,13 @@ class Crop < ActiveRecord::Base
|
||||
raise "cropbot account not found: run rake db:seed" unless cropbot
|
||||
|
||||
names_to_add.each do |n|
|
||||
if self.scientific_names.exists?(:scientific_name => n)
|
||||
if self.scientific_names.exists?(scientific_name: n)
|
||||
logger.warn("Warning: skipping duplicate scientific name #{n} for #{self}")
|
||||
else
|
||||
|
||||
self.scientific_names.create(
|
||||
:scientific_name => n,
|
||||
:creator_id => cropbot.id
|
||||
scientific_name: n,
|
||||
creator_id: cropbot.id
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -187,12 +286,12 @@ class Crop < ActiveRecord::Base
|
||||
names_to_add = alternate_names.split(%r{,\s*})
|
||||
|
||||
names_to_add.each do |n|
|
||||
if self.alternate_names.exists?(:name => n)
|
||||
if self.alternate_names.exists?(name: n)
|
||||
logger.warn("Warning: skipping duplicate alternate name #{n} for #{self}")
|
||||
else
|
||||
self.alternate_names.create(
|
||||
:name => n,
|
||||
:creator_id => cropbot.id
|
||||
name: n,
|
||||
creator_id: cropbot.id
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -200,11 +299,76 @@ class Crop < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def rejection_explanation
|
||||
if reason_for_rejection == "other"
|
||||
return rejection_notes
|
||||
else
|
||||
return reason_for_rejection
|
||||
end
|
||||
end
|
||||
|
||||
# Crop.search(string)
|
||||
# searches for crops whose names match the string given
|
||||
# just uses SQL LIKE for now, but can be made fancier later
|
||||
def self.search(query)
|
||||
where("name ILIKE ?", "%#{query}%")
|
||||
if ENV['GROWSTUFF_ELASTICSEARCH'] == "true"
|
||||
search_str = query.nil? ? "" : query.downcase
|
||||
response = __elasticsearch__.search( {
|
||||
# Finds documents which match any field, but uses the _score from
|
||||
# the best field insead of adding up _score from each field.
|
||||
query: {
|
||||
multi_match: {
|
||||
query: "#{search_str}",
|
||||
analyzer: "standard",
|
||||
fields: ["name", "scientific_names.scientific_name", "alternate_names.name"]
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
term: {approval_status: "approved"}
|
||||
},
|
||||
size: 50
|
||||
}
|
||||
)
|
||||
return response.records.to_a
|
||||
else
|
||||
# if we don't have elasticsearch, just do a basic SQL query.
|
||||
# also, make sure it's an actual array not an activerecord
|
||||
# collection, so it matches what we get from elasticsearch and we can
|
||||
# manipulate it in the same ways (eg. deleting elements without deleting
|
||||
# the whole record from the db)
|
||||
matches = Crop.approved.where("name ILIKE ?", "%#{query}%").to_a
|
||||
|
||||
# we want to make sure that exact matches come first, even if not
|
||||
# using elasticsearch (eg. in development)
|
||||
exact_match = Crop.approved.find_by_name(query)
|
||||
if exact_match
|
||||
matches.delete(exact_match)
|
||||
matches.unshift(exact_match)
|
||||
end
|
||||
|
||||
return matches
|
||||
end
|
||||
end
|
||||
|
||||
# Custom validations
|
||||
|
||||
def approval_status_cannot_be_changed_again
|
||||
previous = previous_changes.include?(:approval_status) ? previous_changes.approval_status : {}
|
||||
if previous.include?(:rejected) || previous.include?(:approved)
|
||||
errors.add(:approval_status, "has already been set to #{approval_status}")
|
||||
end
|
||||
end
|
||||
|
||||
def must_be_rejected_if_rejected_reasons_present
|
||||
unless rejected?
|
||||
if reason_for_rejection.present? || rejection_notes.present?
|
||||
errors.add(:approval_status, "must be rejected if a reason for rejection is present")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def must_have_meaningful_reason_for_rejection
|
||||
if reason_for_rejection == "other" && rejection_notes.blank?
|
||||
errors.add(:rejection_notes, "must be added if the reason for rejection is \"other\"")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
class Follow < ActiveRecord::Base
|
||||
belongs_to :follower, class_name: "Member"
|
||||
belongs_to :followed, class_name: "Member"
|
||||
validates :follower_id, uniqueness: { :scope => :followed_id }
|
||||
validates :follower_id, uniqueness: { scope: :followed_id }
|
||||
|
||||
after_create do
|
||||
Notification.create(
|
||||
:recipient_id => self.followed_id,
|
||||
:sender_id => self.follower_id,
|
||||
:subject => "#{self.follower.login_name} is now following you",
|
||||
:body => "#{self.follower.login_name} just followed you on #{ENV["GROWSTUFF_SITE_NAME"]}. "
|
||||
recipient_id: self.followed_id,
|
||||
sender_id: self.follower_id,
|
||||
subject: "#{self.follower.login_name} is now following you",
|
||||
body: "#{self.follower.login_name} just followed you on #{ENV["GROWSTUFF_SITE_NAME"]}. "
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ class Forum < ActiveRecord::Base
|
||||
friendly_id :name, use: [:slugged, :finders]
|
||||
|
||||
has_many :posts
|
||||
belongs_to :owner, :class_name => "Member"
|
||||
belongs_to :owner, class_name: "Member"
|
||||
|
||||
def to_s
|
||||
return name
|
||||
|
||||
@@ -3,9 +3,20 @@ class Garden < ActiveRecord::Base
|
||||
extend FriendlyId
|
||||
friendly_id :garden_slug, use: [:slugged, :finders]
|
||||
|
||||
belongs_to :owner, :class_name => 'Member', :foreign_key => 'owner_id'
|
||||
has_many :plantings, -> { order(created_at: :desc) }, :dependent => :destroy
|
||||
has_many :crops, :through => :plantings
|
||||
belongs_to :owner, class_name: 'Member', foreign_key: 'owner_id'
|
||||
has_many :plantings, -> { order(created_at: :desc) }, dependent: :destroy
|
||||
has_many :crops, through: :plantings
|
||||
|
||||
has_and_belongs_to_many :photos
|
||||
|
||||
before_destroy do |garden|
|
||||
photolist = garden.photos.to_a # save a temp copy of the photo list
|
||||
garden.photos.clear # clear relationship b/w garden and photo
|
||||
|
||||
photolist.each do |photo|
|
||||
photo.destroy_if_unused
|
||||
end
|
||||
end
|
||||
|
||||
# set up geocoding
|
||||
geocoded_by :location
|
||||
@@ -14,17 +25,23 @@ class Garden < ActiveRecord::Base
|
||||
after_save :mark_inactive_garden_plantings_as_finished
|
||||
|
||||
default_scope { order("lower(name) asc") }
|
||||
scope :active, -> { where(:active => true) }
|
||||
scope :inactive, -> { where(:active => false) }
|
||||
scope :active, -> { where(active: true) }
|
||||
scope :inactive, -> { where(active: false) }
|
||||
|
||||
validates :location,
|
||||
length: { maximum: 255 }
|
||||
|
||||
validates :name,
|
||||
:format => {
|
||||
:with => /\S/
|
||||
}
|
||||
format: {
|
||||
with: /\S/
|
||||
},
|
||||
length: { maximum: 255 }
|
||||
|
||||
validates :area,
|
||||
:numericality => { :only_integer => false, :greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
numericality: {
|
||||
only_integer: false,
|
||||
greater_than_or_equal_to: 0 },
|
||||
allow_nil: true
|
||||
|
||||
AREA_UNITS_VALUES = {
|
||||
"square metres" => "square metre",
|
||||
@@ -32,10 +49,10 @@ class Garden < ActiveRecord::Base
|
||||
"hectares" => "hectare",
|
||||
"acres" => "acre"
|
||||
}
|
||||
validates :area_unit, :inclusion => { :in => AREA_UNITS_VALUES.values,
|
||||
:message => "%{value} is not a valid area unit" },
|
||||
:allow_nil => true,
|
||||
:allow_blank => true
|
||||
validates :area_unit, inclusion: { in: AREA_UNITS_VALUES.values,
|
||||
message: "%{value} is not a valid area unit" },
|
||||
allow_nil: true,
|
||||
allow_blank: true
|
||||
|
||||
after_validation :cleanup_area
|
||||
|
||||
@@ -83,4 +100,8 @@ class Garden < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def default_photo
|
||||
return photos.first
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@ class Harvest < ActiveRecord::Base
|
||||
friendly_id :harvest_slug, use: [:slugged, :finders]
|
||||
|
||||
belongs_to :crop
|
||||
belongs_to :owner, :class_name => 'Member'
|
||||
belongs_to :owner, class_name: 'Member'
|
||||
belongs_to :plant_part
|
||||
|
||||
has_and_belongs_to_many :photos
|
||||
@@ -20,11 +20,17 @@ class Harvest < ActiveRecord::Base
|
||||
|
||||
default_scope { order('created_at DESC') }
|
||||
|
||||
validates :crop, :presence => {:message => "must be present and exist in our database"}
|
||||
validates :crop, approved: true
|
||||
|
||||
validates :crop, presence: {message: "must be present and exist in our database"}
|
||||
|
||||
validates :plant_part, presence: {message: "must be present and exist in our database"}
|
||||
|
||||
validates :quantity,
|
||||
:numericality => { :only_integer => false },
|
||||
:allow_nil => true
|
||||
numericality: {
|
||||
only_integer: false,
|
||||
greater_than_or_equal_to: 0 },
|
||||
allow_nil: true
|
||||
|
||||
UNITS_VALUES = {
|
||||
"individual" => "individual",
|
||||
@@ -38,27 +44,38 @@ class Harvest < ActiveRecord::Base
|
||||
"baskets" => "basket",
|
||||
"bushels" => "bushel"
|
||||
}
|
||||
validates :unit, :inclusion => { :in => UNITS_VALUES.values,
|
||||
:message => "%{value} is not a valid unit" },
|
||||
:allow_nil => true,
|
||||
:allow_blank => true
|
||||
validates :unit, inclusion: { in: UNITS_VALUES.values,
|
||||
message: "%{value} is not a valid unit" },
|
||||
allow_nil: true,
|
||||
allow_blank: true
|
||||
|
||||
validates :weight_quantity,
|
||||
:numericality => { :only_integer => false },
|
||||
:allow_nil => true
|
||||
numericality: { only_integer: false },
|
||||
allow_nil: true
|
||||
|
||||
WEIGHT_UNITS_VALUES = {
|
||||
"kg" => "kg",
|
||||
"lb" => "lb",
|
||||
"oz" => "oz"
|
||||
}
|
||||
validates :weight_unit, :inclusion => { :in => WEIGHT_UNITS_VALUES.values,
|
||||
:message => "%{value} is not a valid unit" },
|
||||
:allow_nil => true,
|
||||
:allow_blank => true
|
||||
validates :weight_unit, inclusion: { in: WEIGHT_UNITS_VALUES.values,
|
||||
message: "%{value} is not a valid unit" },
|
||||
allow_nil: true,
|
||||
allow_blank: true
|
||||
|
||||
after_validation :cleanup_quantities
|
||||
|
||||
before_save :set_si_weight
|
||||
|
||||
# we're storing the harvest weight in kilograms in the db too
|
||||
# to make data manipulation easier
|
||||
def set_si_weight
|
||||
if self.weight_unit != nil
|
||||
weight_string = "#{self.weight_quantity} #{self.weight_unit}"
|
||||
self.si_weight = Unit.new(weight_string).convert_to("kg").to_s("%0.3f").delete(" kg").to_f
|
||||
end
|
||||
end
|
||||
|
||||
def cleanup_quantities
|
||||
if quantity == 0
|
||||
self.quantity = nil
|
||||
@@ -87,7 +104,7 @@ class Harvest < ActiveRecord::Base
|
||||
# 2 buckets of apricots, weighing 10kg
|
||||
string = ''
|
||||
if self.quantity
|
||||
string += "#{number_to_human(self.quantity.to_s, :strip_insignificant_zeros => true)} "
|
||||
string += "#{number_to_human(self.quantity.to_s, strip_insignificant_zeros: true)} "
|
||||
if self.unit == 'individual'
|
||||
string += 'individual '
|
||||
else
|
||||
@@ -108,7 +125,7 @@ class Harvest < ActiveRecord::Base
|
||||
end
|
||||
|
||||
if self.weight_quantity
|
||||
string += " weighing #{number_to_human(self.weight_quantity, :strip_insignificant_zeros => true)} #{self.weight_unit}"
|
||||
string += " weighing #{number_to_human(self.weight_quantity, strip_insignificant_zeros: true)} #{self.weight_unit}"
|
||||
end
|
||||
|
||||
return string
|
||||
|
||||
@@ -4,26 +4,26 @@ class Member < ActiveRecord::Base
|
||||
|
||||
friendly_id :login_name, use: [:slugged, :finders]
|
||||
|
||||
has_many :posts, :foreign_key => 'author_id'
|
||||
has_many :comments, :foreign_key => 'author_id'
|
||||
has_many :forums, :foreign_key => 'owner_id'
|
||||
has_many :posts, foreign_key: 'author_id'
|
||||
has_many :comments, foreign_key: 'author_id'
|
||||
has_many :forums, foreign_key: 'owner_id'
|
||||
|
||||
has_many :gardens, :foreign_key => 'owner_id'
|
||||
has_many :plantings, :foreign_key => 'owner_id'
|
||||
has_many :gardens, foreign_key: 'owner_id'
|
||||
has_many :plantings, foreign_key: 'owner_id'
|
||||
|
||||
has_many :seeds, :foreign_key => 'owner_id'
|
||||
has_many :harvests, :foreign_key => 'owner_id'
|
||||
has_many :seeds, foreign_key: 'owner_id'
|
||||
has_many :harvests, foreign_key: 'owner_id'
|
||||
|
||||
has_and_belongs_to_many :roles
|
||||
|
||||
has_many :notifications, :foreign_key => 'recipient_id'
|
||||
has_many :sent_notifications, :foreign_key => 'sender_id'
|
||||
has_many :notifications, foreign_key: 'recipient_id'
|
||||
has_many :sent_notifications, foreign_key: 'sender_id'
|
||||
|
||||
has_many :authentications
|
||||
|
||||
has_many :orders
|
||||
has_one :account
|
||||
has_one :account_type, :through => :account
|
||||
has_one :account_type, through: :account
|
||||
|
||||
has_many :photos
|
||||
|
||||
@@ -32,13 +32,14 @@ class Member < ActiveRecord::Base
|
||||
scope :confirmed, -> { where('confirmed_at IS NOT NULL') }
|
||||
scope :located, -> { where("location <> '' and latitude IS NOT NULL and longitude IS NOT NULL") }
|
||||
scope :recently_signed_in, -> { reorder('updated_at DESC') }
|
||||
scope :wants_newsletter, -> { where(:newsletter => true) }
|
||||
scope :recently_joined, -> { reorder("confirmed_at desc") }
|
||||
scope :wants_newsletter, -> { where(newsletter: true) }
|
||||
|
||||
has_many :follows, :class_name => "Follow", :foreign_key => "follower_id"
|
||||
has_many :followed, :through => :follows
|
||||
has_many :follows, class_name: "Follow", foreign_key: "follower_id"
|
||||
has_many :followed, through: :follows
|
||||
|
||||
has_many :inverse_follows, :class_name => "Follow", :foreign_key => "followed_id"
|
||||
has_many :followers, :through => :inverse_follows, :source => :follower
|
||||
has_many :inverse_follows, class_name: "Follow", foreign_key: "followed_id"
|
||||
has_many :followers, through: :inverse_follows, source: :follower
|
||||
|
||||
# Include default devise modules. Others available are:
|
||||
# :token_authenticatable, :confirmable,
|
||||
@@ -57,34 +58,34 @@ class Member < ActiveRecord::Base
|
||||
attr_accessor :login
|
||||
|
||||
# Requires acceptance of the Terms of Service
|
||||
validates_acceptance_of :tos_agreement, :allow_nil => false,
|
||||
:accept => true
|
||||
validates_acceptance_of :tos_agreement, allow_nil: false,
|
||||
accept: true
|
||||
|
||||
validates :login_name,
|
||||
:length => {
|
||||
:minimum => 2,
|
||||
:maximum => 25,
|
||||
:message => "should be between 2 and 25 characters long"
|
||||
length: {
|
||||
minimum: 2,
|
||||
maximum: 25,
|
||||
message: "should be between 2 and 25 characters long"
|
||||
},
|
||||
:exclusion => {
|
||||
:in => %w(growstuff admin moderator staff nearby),
|
||||
:message => "name is reserved"
|
||||
exclusion: {
|
||||
in: %w(growstuff admin moderator staff nearby),
|
||||
message: "name is reserved"
|
||||
},
|
||||
:format => {
|
||||
:with => /\A\w+\z/,
|
||||
:message => "may only include letters, numbers, or underscores"
|
||||
format: {
|
||||
with: /\A\w+\z/,
|
||||
message: "may only include letters, numbers, or underscores"
|
||||
},
|
||||
:uniqueness => {
|
||||
:case_sensitive => false
|
||||
uniqueness: {
|
||||
case_sensitive: false
|
||||
}
|
||||
|
||||
# Give each new member a default garden
|
||||
after_create {|member| Garden.create(:name => "Garden", :owner_id => member.id) }
|
||||
after_create {|member| Garden.create(name: "Garden", owner_id: member.id) }
|
||||
|
||||
# and an account record (for paid accounts etc)
|
||||
# we use find_or_create to avoid accidentally creating a second one,
|
||||
# which can happen sometimes especially with FactoryGirl associations
|
||||
after_create {|member| Account.find_or_create_by(:member_id => member.id) }
|
||||
after_create {|member| Account.find_or_create_by(member_id: member.id) }
|
||||
|
||||
after_save :update_newsletter_subscription
|
||||
|
||||
@@ -92,7 +93,7 @@ class Member < ActiveRecord::Base
|
||||
def self.find_first_by_auth_conditions(warden_conditions)
|
||||
conditions = warden_conditions.dup
|
||||
if login = conditions.delete(:login)
|
||||
where(conditions).where(["lower(login_name) = :value OR lower(email) = :value", { :value => login.downcase }]).first
|
||||
where(conditions).where(["lower(login_name) = :value OR lower(email) = :value", { value: login.downcase }]).first
|
||||
else
|
||||
where(conditions).first
|
||||
end
|
||||
@@ -107,7 +108,7 @@ class Member < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def current_order
|
||||
orders.where(:completed_at => nil).first
|
||||
orders.where(completed_at: nil).first
|
||||
end
|
||||
|
||||
# when purchasing a product that gives you a paid account, this method
|
||||
@@ -162,15 +163,15 @@ class Member < ActiveRecord::Base
|
||||
result = false
|
||||
if set
|
||||
result = flickr.photosets.getPhotos(
|
||||
:photoset_id => set,
|
||||
:page => page_num,
|
||||
:per_page => 30
|
||||
photoset_id: set,
|
||||
page: page_num,
|
||||
per_page: 30
|
||||
)
|
||||
else
|
||||
result = flickr.people.getPhotos(
|
||||
:user_id => 'me',
|
||||
:page => page_num,
|
||||
:per_page => 30
|
||||
user_id: 'me',
|
||||
page: page_num,
|
||||
per_page: 30
|
||||
)
|
||||
end
|
||||
if result
|
||||
@@ -201,7 +202,7 @@ class Member < ActiveRecord::Base
|
||||
howmany = 12 # max number to find
|
||||
interesting_members = Array.new
|
||||
Member.confirmed.located.recently_signed_in.each do |m|
|
||||
break if interesting_members.length == howmany
|
||||
break if interesting_members.size == howmany
|
||||
if m.interesting?
|
||||
interesting_members.push(m)
|
||||
end
|
||||
@@ -238,10 +239,10 @@ class Member < ActiveRecord::Base
|
||||
return true if (Rails.env.test? && !testing)
|
||||
gb = Gibbon::API.new
|
||||
res = gb.lists.subscribe({
|
||||
:id => Gibbon::API.api_key,
|
||||
:email => { :email => email },
|
||||
:merge_vars => { :login_name => login_name },
|
||||
:double_optin => false # they already confirmed their email with us
|
||||
id: Gibbon::API.api_key,
|
||||
email: { email: email },
|
||||
merge_vars: { login_name: login_name },
|
||||
double_optin: false # they already confirmed their email with us
|
||||
})
|
||||
end
|
||||
|
||||
@@ -249,17 +250,17 @@ class Member < ActiveRecord::Base
|
||||
return true if (Rails.env.test? && !testing)
|
||||
gb = Gibbon::API.new
|
||||
res = gb.lists.unsubscribe({
|
||||
:id => ENV['GROWSTUFF_MAILCHIMP_NEWSLETTER_ID'],
|
||||
:email => { :email => email }
|
||||
id: ENV['GROWSTUFF_MAILCHIMP_NEWSLETTER_ID'],
|
||||
email: { email: email }
|
||||
})
|
||||
end
|
||||
|
||||
def already_following?(member)
|
||||
self.follows.exists?(:followed_id => member.id)
|
||||
self.follows.exists?(followed_id: member.id)
|
||||
end
|
||||
|
||||
def get_follow(member)
|
||||
self.follows.where(:followed_id => member.id).first if already_following?(member)
|
||||
self.follows.where(followed_id: member.id).first if already_following?(member)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
class Notification < ActiveRecord::Base
|
||||
belongs_to :sender, :class_name => 'Member'
|
||||
belongs_to :recipient, :class_name => 'Member'
|
||||
belongs_to :sender, class_name: 'Member'
|
||||
belongs_to :recipient, class_name: 'Member'
|
||||
belongs_to :post
|
||||
|
||||
validates :subject, length: { maximum: 255 }
|
||||
|
||||
default_scope { order('created_at DESC') }
|
||||
scope :unread, -> { where(:read => false) }
|
||||
scope :unread, -> { where(read: false) }
|
||||
|
||||
before_create :replace_blank_subject
|
||||
after_create :send_email
|
||||
|
||||
def self.unread_count
|
||||
self.unread.count
|
||||
self.unread.size
|
||||
end
|
||||
|
||||
def replace_blank_subject
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
class Order < ActiveRecord::Base
|
||||
belongs_to :member
|
||||
|
||||
has_many :order_items, :dependent => :destroy
|
||||
has_many :order_items, dependent: :destroy
|
||||
|
||||
default_scope { order('created_at DESC') }
|
||||
|
||||
validates :referral_code, :format => {
|
||||
:with => /\A[a-zA-Z0-9 ]*\z/,
|
||||
:message => "may only include letters and numbers"
|
||||
validates :referral_code, format: {
|
||||
with: /\A[a-zA-Z0-9 ]*\z/,
|
||||
message: "may only include letters and numbers"
|
||||
}
|
||||
|
||||
before_save :standardize_referral_code
|
||||
@@ -27,9 +27,9 @@ class Order < ActiveRecord::Base
|
||||
items = []
|
||||
order_items.each do |i|
|
||||
items.push({
|
||||
:name => i.product.name,
|
||||
:quantity => i.quantity,
|
||||
:amount => i.price
|
||||
name: i.product.name,
|
||||
quantity: i.quantity,
|
||||
amount: i.price
|
||||
})
|
||||
end
|
||||
return items
|
||||
@@ -87,7 +87,7 @@ class Order < ActiveRecord::Base
|
||||
end
|
||||
when "referral_code"
|
||||
# coerce to uppercase
|
||||
return Order.where(:referral_code => args[:for].upcase)
|
||||
return Order.where(referral_code: args[:for].upcase)
|
||||
end
|
||||
end
|
||||
return []
|
||||
|
||||
@@ -4,7 +4,7 @@ class OrderItem < ActiveRecord::Base
|
||||
|
||||
validate :price_must_be_greater_than_minimum
|
||||
|
||||
validates_uniqueness_of :order_id, :message => "may only have one item."
|
||||
validates_uniqueness_of :order_id, message: "may only have one item."
|
||||
|
||||
def price_must_be_greater_than_minimum
|
||||
@product = Product.find(product_id)
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
class Photo < ActiveRecord::Base
|
||||
belongs_to :owner, :class_name => 'Member'
|
||||
belongs_to :owner, class_name: 'Member'
|
||||
|
||||
has_and_belongs_to_many :plantings
|
||||
has_and_belongs_to_many :harvests
|
||||
has_and_belongs_to_many :gardens
|
||||
before_destroy do |photo|
|
||||
photo.plantings.clear
|
||||
photo.harvests.clear
|
||||
photo.gardens.clear
|
||||
end
|
||||
|
||||
default_scope { order("created_at desc") }
|
||||
|
||||
# remove photos that aren't used by anything
|
||||
def destroy_if_unused
|
||||
unless plantings.size > 0 or harvests.size > 0
|
||||
unless plantings.size > 0 or harvests.size > 0 or gardens.size > 0
|
||||
self.destroy
|
||||
end
|
||||
end
|
||||
@@ -21,16 +23,16 @@ class Photo < ActiveRecord::Base
|
||||
# for easier stubbing and testing.
|
||||
def flickr_metadata
|
||||
flickr = owner.flickr
|
||||
info = flickr.photos.getInfo(:photo_id => flickr_photo_id)
|
||||
info = flickr.photos.getInfo(photo_id: flickr_photo_id)
|
||||
licenses = flickr.photos.licenses.getInfo()
|
||||
license = licenses.find { |l| l.id == info.license }
|
||||
return {
|
||||
:title => info.title || "Untitled",
|
||||
:license_name => license.name,
|
||||
:license_url => license.url,
|
||||
:thumbnail_url => FlickRaw.url_q(info),
|
||||
:fullsize_url => FlickRaw.url_z(info),
|
||||
:link_url => FlickRaw.url_photopage(info)
|
||||
title: info.title || "Untitled",
|
||||
license_name: license.name,
|
||||
license_url: license.url,
|
||||
thumbnail_url: FlickRaw.url_q(info),
|
||||
fullsize_url: FlickRaw.url_z(info),
|
||||
link_url: FlickRaw.url_photopage(info)
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
class PlantPart < ActiveRecord::Base
|
||||
extend FriendlyId
|
||||
friendly_id :name, :use => [:slugged, :finders]
|
||||
friendly_id :name, use: [:slugged, :finders]
|
||||
|
||||
has_many :harvests
|
||||
has_many :crops, -> { uniq }, :through => :harvests
|
||||
has_many :crops, -> { uniq }, through: :harvests
|
||||
|
||||
def to_s
|
||||
return name
|
||||
|
||||
@@ -3,8 +3,8 @@ class Planting < ActiveRecord::Base
|
||||
friendly_id :planting_slug, use: [:slugged, :finders]
|
||||
|
||||
belongs_to :garden
|
||||
belongs_to :owner, :class_name => 'Member', :counter_cache => true
|
||||
belongs_to :crop, :counter_cache => true
|
||||
belongs_to :owner, class_name: 'Member', counter_cache: true
|
||||
belongs_to :crop, counter_cache: true
|
||||
|
||||
has_and_belongs_to_many :photos
|
||||
|
||||
@@ -18,29 +18,33 @@ class Planting < ActiveRecord::Base
|
||||
end
|
||||
|
||||
default_scope { order("created_at desc") }
|
||||
scope :finished, -> { where(:finished => true) }
|
||||
scope :current, -> { where(:finished => false) }
|
||||
scope :finished, -> { where(finished: true) }
|
||||
scope :current, -> { where(finished: false) }
|
||||
|
||||
delegate :name,
|
||||
:en_wikipedia_url,
|
||||
:default_scientific_name,
|
||||
:plantings_count,
|
||||
:to => :crop,
|
||||
:prefix => true
|
||||
to: :crop,
|
||||
prefix: true
|
||||
|
||||
default_scope { order("created_at desc") }
|
||||
|
||||
validates :crop_id, :presence => {:message => "must be present and exist in our database"}
|
||||
validates :crop, approved: true
|
||||
|
||||
validates :crop, presence: {message: "must be present and exist in our database"}
|
||||
|
||||
validates :quantity,
|
||||
:numericality => { :only_integer => true },
|
||||
:allow_nil => true
|
||||
numericality: {
|
||||
only_integer: true,
|
||||
greater_than_or_equal_to: 0 },
|
||||
allow_nil: true
|
||||
|
||||
SUNNINESS_VALUES = %w(sun semi-shade shade)
|
||||
validates :sunniness, :inclusion => { :in => SUNNINESS_VALUES,
|
||||
:message => "%{value} is not a valid sunniness value" },
|
||||
:allow_nil => true,
|
||||
:allow_blank => true
|
||||
validates :sunniness, inclusion: { in: SUNNINESS_VALUES,
|
||||
message: "%{value} is not a valid sunniness value" },
|
||||
allow_nil: true,
|
||||
allow_blank: true
|
||||
|
||||
PLANTED_FROM_VALUES = [
|
||||
'seed',
|
||||
@@ -55,10 +59,10 @@ class Planting < ActiveRecord::Base
|
||||
'graft',
|
||||
'layering'
|
||||
]
|
||||
validates :planted_from, :inclusion => { :in => PLANTED_FROM_VALUES,
|
||||
:message => "%{value} is not a valid planting method" },
|
||||
:allow_nil => true,
|
||||
:allow_blank => true
|
||||
validates :planted_from, inclusion: { in: PLANTED_FROM_VALUES,
|
||||
message: "%{value} is not a valid planting method" },
|
||||
allow_nil: true,
|
||||
allow_blank: true
|
||||
|
||||
validate :finished_must_be_after_planted
|
||||
|
||||
@@ -90,6 +94,41 @@ class Planting < ActiveRecord::Base
|
||||
return photos.present?
|
||||
end
|
||||
|
||||
def calculate_days_before_maturity(planting, crop)
|
||||
p_crop = Planting.where(crop_id: crop).where.not(id: planting)
|
||||
differences = p_crop.collect do |p|
|
||||
if p.finished and !p.finished_at.nil?
|
||||
(p.finished_at - p.planted_at).to_i
|
||||
end
|
||||
end
|
||||
|
||||
if differences.compact.empty?
|
||||
nil
|
||||
else
|
||||
differences.compact.sum/differences.compact.size
|
||||
end
|
||||
end
|
||||
|
||||
def planted?(current_date = Date.current)
|
||||
planted_at.present? && current_date.to_date >= planted_at
|
||||
end
|
||||
|
||||
def percentage_grown(current_date = Date.current)
|
||||
return nil unless days_before_maturity && planted?(current_date)
|
||||
|
||||
days = (current_date.to_date - planted_at.to_date).to_i
|
||||
|
||||
return 0 if current_date < planted_at
|
||||
return 100 if days > days_before_maturity
|
||||
percent = (days/days_before_maturity*100).to_i
|
||||
|
||||
if percent >= 100
|
||||
percent = 100
|
||||
end
|
||||
|
||||
percent
|
||||
end
|
||||
|
||||
# return a list of interesting plantings, for the homepage etc.
|
||||
# we can't do this via a scope (as far as we know) so sadly we have to
|
||||
# do it this way.
|
||||
@@ -97,8 +136,8 @@ class Planting < ActiveRecord::Base
|
||||
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?
|
||||
Planting.includes(:photos).each do |p|
|
||||
break if interesting_plantings.size == howmany # got enough yet?
|
||||
if require_photo
|
||||
next unless p.photos.present? # skip those without photos, if required
|
||||
end
|
||||
|
||||
@@ -1,21 +1,49 @@
|
||||
class Post < ActiveRecord::Base
|
||||
extend FriendlyId
|
||||
friendly_id :author_date_subject, use: [:slugged, :finders]
|
||||
belongs_to :author, :class_name => 'Member'
|
||||
belongs_to :author, class_name: 'Member'
|
||||
belongs_to :forum
|
||||
has_many :comments, :dependent => :destroy
|
||||
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.
|
||||
|
||||
after_create do
|
||||
recipients = Array.new
|
||||
sender = self.author.id
|
||||
self.body.scan(Haml::Filters::GrowstuffMarkdown::MEMBER_REGEX) do |m|
|
||||
# find member case-insensitively and add to list of recipients
|
||||
member = Member.where('lower(login_name) = ?', $1.downcase).first
|
||||
recipients << member if member and not recipients.include?(member)
|
||||
end
|
||||
self.body.scan(Haml::Filters::GrowstuffMarkdown::MEMBER_AT_REGEX) do |m|
|
||||
# find member case-insensitively and add to list of recipients
|
||||
member = Member.where('lower(login_name) = ?', $1[1..-1].downcase).first
|
||||
recipients << member if member and not recipients.include?(member)
|
||||
end
|
||||
# don't send notifications to yourself
|
||||
recipients.map{ |r| r.id }.each do |recipient|
|
||||
if recipient != sender
|
||||
Notification.create(
|
||||
recipient_id: recipient,
|
||||
sender_id: sender,
|
||||
subject: "#{self.author} mentioned you in their post #{self.subject}",
|
||||
body: self.body,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
default_scope { order("created_at desc") }
|
||||
|
||||
validates :subject,
|
||||
:format => {
|
||||
:with => /\S/
|
||||
}
|
||||
format: {
|
||||
with: /\S/
|
||||
},
|
||||
length: { maximum: 255 }
|
||||
|
||||
|
||||
def author_date_subject
|
||||
# slugs are created before created_at is set
|
||||
@@ -24,7 +52,7 @@ class Post < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def comment_count
|
||||
self.comments.count
|
||||
self.comments.size
|
||||
end
|
||||
|
||||
# return the timestamp of the most recent activity on this post
|
||||
|
||||
@@ -2,8 +2,11 @@ class Product < ActiveRecord::Base
|
||||
has_and_belongs_to_many :orders
|
||||
belongs_to :account_type
|
||||
|
||||
validates :paid_months, :numericality => { :only_integer => true,
|
||||
:allow_nil => true }
|
||||
validates :paid_months,
|
||||
numericality: {
|
||||
only_integer: true,
|
||||
greater_than_or_equal_to: 0 },
|
||||
allow_nil: true
|
||||
|
||||
def to_s
|
||||
name
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class ScientificName < ActiveRecord::Base
|
||||
after_commit { |sn| sn.crop.__elasticsearch__.index_document if sn.crop && ENV['GROWSTUFF_ELASTICSEARCH'] == "true" }
|
||||
belongs_to :crop
|
||||
belongs_to :creator, :class_name => 'Member'
|
||||
belongs_to :creator, class_name: 'Member'
|
||||
end
|
||||
|
||||
@@ -3,22 +3,62 @@ class Seed < ActiveRecord::Base
|
||||
friendly_id :seed_slug, use: [:slugged, :finders]
|
||||
|
||||
belongs_to :crop
|
||||
belongs_to :owner, :class_name => 'Member', :foreign_key => 'owner_id'
|
||||
belongs_to :owner, class_name: 'Member', foreign_key: 'owner_id'
|
||||
|
||||
default_scope { order("created_at desc") }
|
||||
|
||||
validates :crop, :presence => {:message => "must be present and exist in our database"}
|
||||
validates :crop, approved: true
|
||||
|
||||
validates :crop, presence: {message: "must be present and exist in our database"}
|
||||
validates :quantity,
|
||||
:numericality => { :only_integer => true },
|
||||
:allow_nil => true
|
||||
numericality: {
|
||||
only_integer: true,
|
||||
greater_than_or_equal_to: 0 },
|
||||
allow_nil: true
|
||||
validates :days_until_maturity_min,
|
||||
numericality: {
|
||||
only_integer: true,
|
||||
greater_than_or_equal_to: 0 },
|
||||
allow_nil: true
|
||||
validates :days_until_maturity_max,
|
||||
numericality: {
|
||||
only_integer: true,
|
||||
greater_than_or_equal_to: 0 },
|
||||
allow_nil: true
|
||||
|
||||
scope :tradable, -> { where("tradable_to != 'nowhere'") }
|
||||
|
||||
TRADABLE_TO_VALUES = %w(nowhere locally nationally internationally)
|
||||
validates :tradable_to, :inclusion => { :in => TRADABLE_TO_VALUES,
|
||||
:message => "You may only trade seed nowhere, locally, nationally, or internationally" },
|
||||
:allow_nil => false,
|
||||
:allow_blank => false
|
||||
validates :tradable_to, inclusion: { in: TRADABLE_TO_VALUES,
|
||||
message: "You may only trade seed nowhere, locally, nationally, or internationally" },
|
||||
allow_nil: false,
|
||||
allow_blank: false
|
||||
|
||||
ORGANIC_VALUES = [
|
||||
'certified organic',
|
||||
'non-certified organic',
|
||||
'conventional/non-organic',
|
||||
'unknown']
|
||||
validates :organic, inclusion: { in: ORGANIC_VALUES,
|
||||
message: "You must say whether the seeds are organic or not, or that you don't know" },
|
||||
allow_nil: false,
|
||||
allow_blank: false
|
||||
|
||||
GMO_VALUES = [
|
||||
'certified GMO-free',
|
||||
'non-certified GMO-free',
|
||||
'GMO',
|
||||
'unknown']
|
||||
validates :gmo, inclusion: { in: GMO_VALUES,
|
||||
message: "You must say whether the seeds are genetically modified or not, or that you don't know" },
|
||||
allow_nil: false,
|
||||
allow_blank: false
|
||||
|
||||
HEIRLOOM_VALUES = %w(heirloom hybrid unknown)
|
||||
validates :heirloom, inclusion: { in: HEIRLOOM_VALUES,
|
||||
message: "You must say whether the seeds are heirloom, hybrid, or unknown" },
|
||||
allow_nil: false,
|
||||
allow_blank: false
|
||||
|
||||
def tradable?
|
||||
if self.tradable_to == 'nowhere'
|
||||
@@ -42,7 +82,7 @@ class Seed < ActiveRecord::Base
|
||||
interesting_seeds = Array.new
|
||||
|
||||
Seed.tradable.each do |s|
|
||||
break if interesting_seeds.length == howmany
|
||||
break if interesting_seeds.size == howmany
|
||||
if s.interesting?
|
||||
interesting_seeds.push(s)
|
||||
end
|
||||
|
||||
7
app/validators/approved_validator.rb
Normal file
7
app/validators/approved_validator.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
class ApprovedValidator < ActiveModel::EachValidator
|
||||
def validate_each(record, attribute, value)
|
||||
unless record.crop.try(:approved?)
|
||||
record.errors[attribute] << (options[:message] || 'must be approved')
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,7 +1,7 @@
|
||||
= form_for @account_type do |f|
|
||||
- if @account_type.errors.any?
|
||||
#error_explanation
|
||||
%h2= "#{pluralize(@account_type.errors.count, "error")} prohibited this account_type from being saved:"
|
||||
%h2= "#{pluralize(@account_type.errors.size, "error")} prohibited this account_type from being saved:"
|
||||
%ul
|
||||
- @account_type.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
= form_for @account do |f|
|
||||
- if @account.errors.any?
|
||||
#error_explanation
|
||||
%h2= "#{pluralize(@account.errors.count, "error")} prohibited this account from being saved:"
|
||||
%h2= "#{pluralize(@account.errors.size, "error")} prohibited this account from being saved:"
|
||||
%ul
|
||||
- @account.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
%li= link_to "Roles", roles_path
|
||||
%li= link_to "Forums", forums_path
|
||||
%li= link_to "Newsletter subscribers", admin_newsletter_path
|
||||
%li= link_to "CMS", comfy_admin_cms_path
|
||||
|
||||
%h2 Orders
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
- unless @orders.empty?
|
||||
%h2
|
||||
Found
|
||||
= pluralize(@orders.count, "result")
|
||||
= pluralize(@orders.size, "result")
|
||||
|
||||
%table.table.table-striped
|
||||
%tr
|
||||
@@ -28,7 +28,7 @@
|
||||
%td
|
||||
= order.referral_code
|
||||
%td
|
||||
- if order.order_items.count > 0
|
||||
- if order.order_items.size > 0
|
||||
- order.order_items.each do |o|
|
||||
= o.quantity
|
||||
x
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
= form_for @alternate_name, :html => {:class => 'form-horizontal', :role => "form"} do |f|
|
||||
- if @alternate_name.errors.any?
|
||||
#error_explanation
|
||||
%h2= "#{pluralize(@alternate_name.errors.count, "error")} prohibited this alternate_name from being saved:"
|
||||
%h2= "#{pluralize(@alternate_name.errors.size, "error")} prohibited this alternate_name from being saved:"
|
||||
%ul
|
||||
- @alternate_name.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
@@ -16,10 +16,12 @@
|
||||
= f.label :crop_id, :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= collection_select(:alternate_name, :crop_id, Crop.all, :id, :name, { :selected => @alternate_name.crop_id || @crop.id }, :class => 'form-control')
|
||||
|
||||
.form-group
|
||||
= f.label :name, :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_field :name, :class => 'form-control'
|
||||
|
||||
.form-group
|
||||
.form-actions.col-md-offset-2.col-md-8
|
||||
= f.submit 'Save', :class => 'btn btn-primary'
|
||||
= f.submit 'Save', :class => 'btn btn-primary'
|
||||
@@ -1,5 +1,7 @@
|
||||
%p#notice= notice
|
||||
|
||||
= render :partial => 'crops/approval_status_message', :locals => { :crop => @alternate_name.crop }
|
||||
|
||||
%p
|
||||
%b Alternate name:
|
||||
= @alternate_name.name
|
||||
@@ -9,5 +11,5 @@
|
||||
|
||||
- if can? :edit, @alternate_name
|
||||
= link_to 'Edit', edit_alternate_name_path(@alternate_name), :class => 'btn btn-default btn-xs'
|
||||
\|
|
||||
\|
|
||||
= link_to 'Back', alternate_names_path
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
= 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:"
|
||||
%h2= "#{pluralize(@comment.errors.size, "error")} prohibited this comment from being saved:"
|
||||
%ul
|
||||
- @comment.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
||||
.form-group
|
||||
= f.label :body, "Your comment:"
|
||||
= f.text_area :body, :rows => 6, :class => 'form-control'
|
||||
= f.text_area :body, :rows => 6, :class => 'form-control', :autofocus => 'autofocus'
|
||||
%span.help-block
|
||||
= render :partial => "shared/markdown_help"
|
||||
.actions
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
= render :partial => "members/avatar", :locals => { :member => comment.author }
|
||||
.col-md-11
|
||||
.comment-meta
|
||||
Posted by
|
||||
= (comment.created_at == comment.updated_at) ? 'Posted by' : 'Edited by'
|
||||
= link_to comment.author.login_name, member_path(comment.author)
|
||||
at
|
||||
= comment.created_at
|
||||
on
|
||||
= (comment.created_at == comment.updated_at) ? comment.created_at : comment.updated_at
|
||||
|
||||
.comment-body
|
||||
:growstuff_markdown
|
||||
|
||||
11
app/views/crops/_approval_status_message.html.haml
Normal file
11
app/views/crops/_approval_status_message.html.haml
Normal file
@@ -0,0 +1,11 @@
|
||||
- if crop.pending?
|
||||
.alert.alert-danger
|
||||
%b This crop is currently pending approval.
|
||||
%p This crop was requested by #{crop.requester} #{time_ago_in_words(crop.created_at)} ago.
|
||||
- unless crop.request_notes.blank?
|
||||
%p
|
||||
Request notes: #{crop.request_notes}
|
||||
|
||||
- if crop.rejected?
|
||||
.alert.alert-danger
|
||||
%b This crop was rejected for the following reason: #{crop.rejection_explanation}
|
||||
@@ -10,7 +10,8 @@
|
||||
= render :partial => 'members/location', :locals => { :member => seed.owner }
|
||||
%p
|
||||
= link_to "View all #{crop.name} seeds", seeds_by_crop_path(crop)
|
||||
- if current_member
|
||||
= link_to "List achiote seeds to trade", new_seed_path(:crop_id => crop.id)
|
||||
- else
|
||||
= render :partial => 'shared/signin_signup', :locals => { :to => 'list your seeds to trade' }
|
||||
- if crop.approved?
|
||||
- if current_member
|
||||
%p= link_to "List #{crop.name} seeds to trade", new_seed_path(:crop_id => crop.id)
|
||||
- else
|
||||
= render :partial => 'shared/signin_signup', :locals => { :to => 'list your seeds to trade' }
|
||||
|
||||
@@ -1,45 +1,114 @@
|
||||
= 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:"
|
||||
%h3= "#{pluralize(@crop.errors.size, "error")} prohibited this crop from being saved:"
|
||||
%ul
|
||||
- @crop.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
||||
%p
|
||||
%span.help-block
|
||||
For detailed crop wrangling guidelines, please consult the
|
||||
=link_to "crop wrangling guide", "http://wiki.growstuff.org/index.php/Crop_wrangling"
|
||||
on the Growstuff wiki.
|
||||
-# Handy link to crop wrangling policy/style guide, shown to wranglers only
|
||||
- if can? :wrangle, @crop
|
||||
%p
|
||||
%span.help-block
|
||||
For detailed crop wrangling guidelines, please consult the
|
||||
=link_to "crop wrangling guide", "http://wiki.growstuff.org/index.php/Crop_wrangling"
|
||||
on the Growstuff wiki.
|
||||
|
||||
.form-group
|
||||
-# Everyone (wranglers and requesters) sees the basic info section
|
||||
%h2 Basic information
|
||||
|
||||
.form-group#new_crop
|
||||
= f.label :name, :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_field :name, :class => 'form-control'
|
||||
%span.help-block Name in US English; singular; capitalize proper nouns only.
|
||||
%span.help-block
|
||||
The common name for the crop, in English (required).
|
||||
- if can? :wrangle, @crop
|
||||
Wranglers: please ensure this is singular, and capitalize
|
||||
proper nouns only.
|
||||
|
||||
.form-group
|
||||
= f.label :en_wikipedia_url, 'Wikipedia URL', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_field :en_wikipedia_url, :class => 'form-control'
|
||||
%span.help-block Link to this crop's page on the English language Wikipedia.
|
||||
.form-group
|
||||
= f.label :parent_id, 'Parent crop', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= collection_select(:crop, :parent_id, Crop.all, :id, :name, {:include_blank => true}, :class => 'form-control')
|
||||
%span.help-block Optional. For setting up crop hierarchies for varieties etc.
|
||||
%p
|
||||
%span.help-block
|
||||
You may enter up to 3 scientific names for a crop. Most crops will have only one.
|
||||
= f.fields_for :scientific_names do |sn|
|
||||
= f.text_field :en_wikipedia_url, :class => 'form-control', :id => "en_wikipedia_url"
|
||||
%span.help-block
|
||||
Link to the crop's page on the English language Wikipedia (required).
|
||||
|
||||
-# Only crop wranglers see the crop hierarchy (for now)
|
||||
- if can? :wrangle, @crop
|
||||
.form-group
|
||||
= sn.label :scientific_name, "Scientific name", :class => 'control-label col-md-2'
|
||||
= f.label :parent_id, 'Parent crop', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= sn.text_field :scientific_name, :class => 'form-control'
|
||||
.col-md-2
|
||||
- if sn.object && sn.object.persisted?
|
||||
%label.checkbox
|
||||
= sn.check_box :_destroy
|
||||
= sn.label :_destroy, "Delete"
|
||||
= collection_select(:crop, :parent_id, Crop.all, :id, :name, {:include_blank => true}, :class => 'form-control')
|
||||
%span.help-block Optional. For setting up crop hierarchies for varieties etc.
|
||||
|
||||
|
||||
-# Everyone (wranglers and requesters) gets to add scientific names
|
||||
%h2
|
||||
Scientific names
|
||||
= button_tag "+", :id => "add-sci_name-row", :type => "button"
|
||||
= button_tag "-", :id => "remove-sci_name-row", :type => "button"
|
||||
|
||||
.form-group#scientific_names
|
||||
- @crop.scientific_names.each.with_index do |sci, index|
|
||||
.template.col-md-12{ :id => "sci_template[#{index+1}]" }
|
||||
.col-md-2
|
||||
= label_tag :scientific_names, "Scientific name #{index+1}:", :class => 'control-label'
|
||||
.col-md-8
|
||||
= text_field_tag "sci_name[#{index+1}]", sci.scientific_name, :id => "sci_name[#{index+1}]", :class => 'form-control'
|
||||
%span.help-block Scientific name of crop.
|
||||
.col-md-2
|
||||
|
||||
%h2
|
||||
Alternate names
|
||||
= button_tag "+", :id => "add-alt_name-row", :type => "button"
|
||||
= button_tag "-", :id => "remove-alt_name-row", :type => "button"
|
||||
|
||||
.form-group#alternate_names
|
||||
- @crop.alternate_names.each.with_index do |alt, index|
|
||||
.template.col-md-12{ :id => "alt_template[#{index+1}]" }
|
||||
.col-md-2
|
||||
= label_tag :alternate_names, "Alternate name #{index+1}:", :class => 'control-label'
|
||||
.col-md-8
|
||||
= text_field_tag "alt_name[#{index+1}]", alt.name, :id => "alt_name[#{index+1}]", :class => 'form-control'
|
||||
%span.help-block Alternate name of crop.
|
||||
.col-md-2
|
||||
|
||||
-# This is used for comments from crop requesters. We need to show it
|
||||
-# to everyone, but we don't include it on new crops from wranglers.
|
||||
|
||||
- if (can? :wrangle, @crop and @crop.requester) or (cannot? :wrangle, @crop and @crop.new_record?)
|
||||
%h2 Crop request notes
|
||||
.form-group
|
||||
= f.label :request_notes, 'Comments', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_area :request_notes, :rows => 3, :class => 'form-control', :id => 'request_notes'
|
||||
|
||||
-# A final explanation of what's going to happen next, for crop requesters
|
||||
- unless can? :wrangle, @crop
|
||||
%p When you submit this form, your suggestion will be sent to our team of #{link_to 'volunteer crop wranglers', 'http://talk.growstuff.org/c/crop-wrangling'} for review. We'll let you know the outcome as soon as we can.
|
||||
|
||||
-# Now, for crop wranglers, let's have approval/rejection at the bottom of the page
|
||||
- if can? :wrangle, @crop and @crop.requester
|
||||
%h2 Approve or reject pending crops
|
||||
.form-group
|
||||
= f.label :approval_status, 'Approval status', :class=> 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.select(:approval_status, @crop.approval_statuses, {}, {:class => 'form-control'})
|
||||
|
||||
.form-group
|
||||
= f.label :reason_for_rejection, 'Reason for rejection', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.select(:reason_for_rejection, @crop.reasons_for_rejection, {:include_blank => true}, {:class => 'form-control'})
|
||||
|
||||
.form-group
|
||||
= f.label :rejection_notes, 'Rejection notes', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_area :rejection_notes, :rows => 3, :class => 'form-control'
|
||||
%span.help-block
|
||||
Please provide additional notes why this crop request was rejected if the above reasons do not apply.
|
||||
|
||||
|
||||
.form-group
|
||||
.form-actions.col-md-offset-2.col-md-8
|
||||
= f.submit 'Save', :class => 'btn btn-primary'
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
ago.
|
||||
%p
|
||||
= link_to "View all #{crop.name} harvests", harvests_by_crop_path(crop)
|
||||
- if current_member
|
||||
%p
|
||||
= link_to "Harvest #{crop.name}", new_harvest_path(:crop_id => crop.id)
|
||||
- else
|
||||
= render :partial => 'shared/signin_signup', :locals => { :to => "track your #{crop.name} harvests" }
|
||||
- if crop.approved?
|
||||
- if current_member
|
||||
%p= link_to "Harvest #{crop.name}", new_harvest_path(:crop_id => crop.id)
|
||||
- else
|
||||
= render :partial => 'shared/signin_signup', :locals => { :to => "track your #{crop.name} harvests" }
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
%ul
|
||||
- @count ||= 0
|
||||
- unless defined? max
|
||||
- max = 0 # list all without "show all" toggle button
|
||||
- display_crops.each do |c|
|
||||
%li.crop-hierarchy
|
||||
%li.crop-hierarchy{:class => max != 0 && @count >= max ? ['hide', 'toggle'] : []}
|
||||
= link_to c, c
|
||||
- @count += 1
|
||||
- if c.varieties.present?
|
||||
- c.varieties.each do |v|
|
||||
= render :partial => 'hierarchy', :locals => { :display_crops => [ v ] }
|
||||
= render :partial => 'hierarchy', :locals => { :display_crops => [ v ], :max => max }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.well
|
||||
.row
|
||||
.col-md-4
|
||||
= link_to image_tag((crop.default_photo ? crop.default_photo.thumbnail_url : 'placeholder_150.png'), :alt => '', :class => 'img-rounded crop-image'), crop
|
||||
= link_to image_tag((crop.default_photo ? crop.default_photo.thumbnail_url : 'placeholder_150.png'), :alt => '', :class => 'img crop-image'), crop
|
||||
.col-md-8
|
||||
%h3{:style => 'padding-top: 0px; margin-top: 0px'}
|
||||
= link_to crop, crop
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
%ul
|
||||
- crop.plantings.take(3).each do |planting|
|
||||
%li
|
||||
= link_to "#{planting.owner} planted #{planting.quantity} #{planting.planted_from}.", planting_path(planting)
|
||||
= link_to display_planting(planting), 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" }
|
||||
- if crop.approved?
|
||||
- if current_member
|
||||
%p= link_to "Plant #{crop.name}", new_planting_path(:crop_id => crop.id)
|
||||
- else
|
||||
= render :partial => 'shared/signin_signup', :locals => { :to => "track your #{crop.name} plantings" }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%p
|
||||
%small
|
||||
- if crop.scientific_names.count > 0
|
||||
- if crop.scientific_names.size > 0
|
||||
%i
|
||||
= crop.scientific_names.first.scientific_name
|
||||
%br/
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
.thumbnail.crop-thumbnail
|
||||
- if crop
|
||||
= link_to image_tag((crop.default_photo ? crop.default_photo.thumbnail_url : 'placeholder_150.png'), :alt => crop.name, :class => 'img'), crop
|
||||
%p.crop-name
|
||||
= link_to crop.name, crop
|
||||
- if crop.scientific_names.count > 0
|
||||
%br/
|
||||
%i.scientific-name
|
||||
%small
|
||||
= crop.scientific_names.first.scientific_name
|
||||
%br/
|
||||
%small
|
||||
Planted
|
||||
= pluralize(crop.plantings.size, "time")
|
||||
.thumbnail
|
||||
.crop-thumbnail
|
||||
- if crop
|
||||
- cache cache_key_for(Crop, crop.id) do
|
||||
= link_to image_tag((crop.default_photo ? crop.default_photo.thumbnail_url : 'placeholder_150.png'), :alt => crop.name, :class => 'img'), crop
|
||||
.cropinfo
|
||||
.cropname
|
||||
= link_to crop.name, crop
|
||||
- if crop.scientific_names.size > 0
|
||||
.scientificname
|
||||
= crop.scientific_names.first.scientific_name
|
||||
.plantingcount
|
||||
Planted
|
||||
= pluralize(crop.plantings.size, "time")
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
- if crop.parent
|
||||
%p
|
||||
= crop.name
|
||||
is a variety of
|
||||
= succeed "." do
|
||||
= link_to crop.parent, crop.parent
|
||||
.varieties
|
||||
- if crop.parent
|
||||
%p
|
||||
= crop.name
|
||||
is a variety of
|
||||
= succeed "." do
|
||||
= link_to crop.parent, crop.parent
|
||||
|
||||
- unless crop.varieties.empty?
|
||||
%p
|
||||
Varieties of #{crop.name}:
|
||||
- unless crop.varieties.empty?
|
||||
%p
|
||||
Varieties of #{crop.name}:
|
||||
|
||||
= render :partial => 'hierarchy', :locals => { :display_crops => [ crop ] }
|
||||
- max = 5
|
||||
= render :partial => 'hierarchy', :locals => { :display_crops => [ crop ], :max => max }
|
||||
- if max != 0 && @count > max
|
||||
= button_tag "Show all #{@count-1} varieties", :class => 'btn btn-link toggle crop-hierarchy'
|
||||
= button_tag "Show less varieties", :class => 'btn btn-link toggle crop-hierarchy hide'
|
||||
|
||||
- if ! crop.parent and crop.varieties.empty?
|
||||
%p None known.
|
||||
- if ! crop.parent and crop.varieties.empty?
|
||||
%p None known.
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
- content_for :title, "Editing crop"
|
||||
- content_for :title, "Edit crop: #{@crop.name}"
|
||||
|
||||
%p
|
||||
Added by
|
||||
= @crop.creator
|
||||
= distance_of_time_in_words(@crop.created_at, Time.zone.now)
|
||||
ago.
|
||||
- if @crop.approval_status == "approved"
|
||||
- if @crop.requester
|
||||
%p Requested by #{link_to @crop.requester, @crop.requester} #{distance_of_time_in_words(@crop.created_at, Time.zone.now)} ago.
|
||||
%p Approved by #{link_to @crop.creator, @crop.creator}.
|
||||
- else
|
||||
%p Added by #{link_to @crop.creator, @crop.creator} #{distance_of_time_in_words(@crop.created_at, Time.zone.now)} ago.
|
||||
- elsif @crop.approval_status == "pending"
|
||||
.alert.alert-danger
|
||||
%p Requested by #{link_to @crop.requester, @crop.requester} #{distance_of_time_in_words(@crop.created_at, Time.zone.now)} ago.
|
||||
%p Status: #{@crop.approval_status}.
|
||||
- elsif @crop.approval_status == "rejected"
|
||||
.alert.alert-danger
|
||||
%p Requested by #{link_to @crop.requester, @crop.requester} #{distance_of_time_in_words(@crop.created_at, Time.zone.now)} ago.
|
||||
%p Status: #{@crop.approval_status} by #{link_to @crop.creator, @crop.creator}.
|
||||
|
||||
= render 'form'
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ csv.headers *all_headers
|
||||
|
||||
if c.scientific_names.any?
|
||||
csv.cell :default_scientific_name, c.default_scientific_name
|
||||
csv.cell :scientific_name_count, c.scientific_names.count
|
||||
csv.cell :scientific_name_count, c.scientific_names.size
|
||||
end
|
||||
|
||||
if c.parent
|
||||
@@ -58,8 +58,8 @@ csv.headers *all_headers
|
||||
end
|
||||
|
||||
csv.cell :plantings_count, c.plantings_count || 0
|
||||
csv.cell :seeds_count, c.seeds.count
|
||||
csv.cell :harvests_count, c.harvests.count
|
||||
csv.cell :seeds_count, c.seeds.size
|
||||
csv.cell :harvests_count, c.harvests.size
|
||||
|
||||
# Sunniness
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user