mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-05-25 09:19:15 -04:00
Compare commits
629 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
90e9017a19 | ||
|
|
9f3cb7ee8b | ||
|
|
df952a1779 | ||
|
|
3748f954c5 | ||
|
|
dcbacddb58 | ||
|
|
4e7e82c8a8 | ||
|
|
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.1.8
|
||||
|
||||
42
.travis.yml
42
.travis.yml
@@ -1,12 +1,38 @@
|
||||
---
|
||||
sudo: false
|
||||
language: ruby
|
||||
|
||||
env: GROWSTUFF_SITE_NAME="Growstuff (travis)" RAILS_SECRET_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
|
||||
bundler_args: --without development production staging
|
||||
cache: bundler
|
||||
env:
|
||||
matrix:
|
||||
- GROWSTUFF_SITE_NAME="Growstuff (travis)" RAILS_SECRET_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' GROWSTUFF_ELASTICSEARCH='true'
|
||||
- GROWSTUFF_SITE_NAME="Growstuff (travis)" RAILS_SECRET_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' GROWSTUFF_ELASTICSEARCH='false'
|
||||
global:
|
||||
secure: "Z5TpM2jEX4UCvNePnk/LwltQX48U2u9BRc+Iypr1x9QW2o228QJhPIOH39a8RMUrepGnkQIq9q3ZRUn98RfrJz1yThtlNFL3NmzdQ57gKgjGwfpa0e4Dwj/ZJqV2D84tDGjvdVYLP7zzaYZxQcwk/cgNpzKf/jq97HLNP7CYuf4="
|
||||
bundler_args: "--without development production staging"
|
||||
rvm:
|
||||
- 2.1.5
|
||||
- 2.1.8
|
||||
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)
|
||||
|
||||
35
Gemfile
35
Gemfile
@@ -1,8 +1,8 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
ruby '2.1.5'
|
||||
ruby '2.1.8'
|
||||
|
||||
gem 'rails', '4.1.9'
|
||||
gem 'rails', '~> 4.1.11'
|
||||
|
||||
gem 'bundler', '>=1.1.5'
|
||||
|
||||
@@ -16,7 +16,7 @@ gem 'less-rails', '~> 2.5.0'
|
||||
# CSS framework
|
||||
gem 'less-rails-bootstrap', '~> 3.2.0'
|
||||
|
||||
gem 'uglifier', '~> 2.5.3' # JavaScript compressor
|
||||
gem 'uglifier', '~> 2.7.2' # JavaScript compressor
|
||||
|
||||
gem 'jquery-rails'
|
||||
gem 'jquery-ui-rails', '~> 5.0.2'
|
||||
@@ -31,6 +31,12 @@ gem 'figaro' # for handling config via ENV variables
|
||||
gem 'cancancan', '~> 1.9' # for checking member privileges
|
||||
gem 'gibbon' # for Mailchimp newsletter subscriptions
|
||||
gem 'csv_shaper' # CSV export
|
||||
gem 'ruby-units' # for unit conversion
|
||||
|
||||
gem 'comfortable_mexican_sofa', '~> 1.12.0' # content management system
|
||||
|
||||
gem 'kaminari' # pagination
|
||||
gem 'bootstrap-kaminari-views' # bootstrap views for kaminari
|
||||
|
||||
# vendored activemerchant for testing- needed for bogus paypal
|
||||
# gateway monkeypatch
|
||||
@@ -55,7 +61,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 +82,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 +95,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 +106,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
|
||||
|
||||
282
Gemfile.lock
282
Gemfile.lock
@@ -20,27 +20,29 @@ 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.2)
|
||||
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)
|
||||
@@ -48,7 +50,10 @@ GEM
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.3.6)
|
||||
arel (5.0.1.20140414130214)
|
||||
bcrypt (3.1.9)
|
||||
autoprefixer-rails (5.1.1)
|
||||
execjs
|
||||
json
|
||||
bcrypt (3.1.11)
|
||||
better_errors (2.0.0)
|
||||
coderay (>= 1.0.0)
|
||||
erubis (>= 2.6.6)
|
||||
@@ -56,8 +61,16 @@ GEM
|
||||
binding_of_caller (0.7.2)
|
||||
debug_inspector (>= 0.0.1)
|
||||
bluecloth (2.2.0)
|
||||
bonsai-elasticsearch-rails (0.0.4)
|
||||
bootstrap-datepicker-rails (1.3.0.2)
|
||||
railties (>= 3.0)
|
||||
bootstrap-kaminari-views (0.0.5)
|
||||
kaminari (>= 0.13)
|
||||
rails (>= 3.1)
|
||||
bootstrap-sass (3.3.3)
|
||||
autoprefixer-rails (>= 5.0.0.1)
|
||||
sass (>= 3.2.19)
|
||||
bootstrap_form (2.2.0)
|
||||
builder (3.2.2)
|
||||
byebug (3.5.1)
|
||||
columnize (~> 0.8)
|
||||
@@ -73,7 +86,15 @@ GEM
|
||||
capybara-email (2.4.0)
|
||||
capybara (~> 2.4)
|
||||
mail
|
||||
childprocess (0.5.6)
|
||||
ffi (~> 1.0, >= 1.0.11)
|
||||
climate_control (0.0.3)
|
||||
activesupport (>= 3.0)
|
||||
cliver (0.3.2)
|
||||
cocaine (0.5.7)
|
||||
climate_control (>= 0.0.3, < 1.0)
|
||||
codemirror-rails (4.8)
|
||||
railties (>= 3.0, < 5)
|
||||
coderay (1.1.0)
|
||||
coffee-rails (4.1.0)
|
||||
coffee-script (>= 2.2.0)
|
||||
@@ -83,6 +104,21 @@ GEM
|
||||
execjs
|
||||
coffee-script-source (1.8.0)
|
||||
columnize (0.9.0)
|
||||
comfortable_mexican_sofa (1.12.7)
|
||||
active_link_to (>= 1.0.0)
|
||||
bootstrap-sass (>= 3.2.0)
|
||||
bootstrap_form (>= 2.2.0)
|
||||
codemirror-rails (>= 3.0.0)
|
||||
coffee-rails (>= 3.1.0)
|
||||
haml-rails (>= 0.3.0)
|
||||
jquery-rails (>= 3.0.0)
|
||||
jquery-ui-rails (>= 5.0.0)
|
||||
kramdown (>= 1.0.0)
|
||||
paperclip (>= 4.0.0)
|
||||
plupload-rails (>= 1.2.1)
|
||||
rails (>= 4.0.0, < 5)
|
||||
rails-i18n (>= 4.0.0)
|
||||
sass-rails (>= 4.0.3)
|
||||
commonjs (0.2.7)
|
||||
coveralls (0.7.1)
|
||||
multi_json (~> 1.3)
|
||||
@@ -93,10 +129,10 @@ GEM
|
||||
csv_shaper (1.1.1)
|
||||
activesupport (>= 3.0.0)
|
||||
dalli (2.7.2)
|
||||
database_cleaner (1.3.0)
|
||||
database_cleaner (1.5.0)
|
||||
debug_inspector (0.0.2)
|
||||
debugger-linecache (1.2.0)
|
||||
devise (3.4.1)
|
||||
devise (3.5.6)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 3.2.6, < 5)
|
||||
@@ -105,20 +141,40 @@ GEM
|
||||
warden (~> 1.2.3)
|
||||
diff-lcs (1.2.5)
|
||||
docile (1.1.5)
|
||||
domain_name (0.5.24)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
easy_translate (0.5.0)
|
||||
json
|
||||
thread
|
||||
thread_safe
|
||||
elasticsearch (1.0.6)
|
||||
elasticsearch-api (= 1.0.6)
|
||||
elasticsearch-transport (= 1.0.6)
|
||||
elasticsearch-api (1.0.6)
|
||||
multi_json
|
||||
elasticsearch-model (0.1.6)
|
||||
activesupport (> 3)
|
||||
elasticsearch (> 0.4)
|
||||
hashie
|
||||
elasticsearch-rails (0.1.6)
|
||||
elasticsearch-transport (1.0.6)
|
||||
faraday
|
||||
multi_json
|
||||
erubis (2.7.0)
|
||||
execjs (2.2.2)
|
||||
excon (0.43.0)
|
||||
execjs (2.6.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)
|
||||
faraday (0.9.1)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ffi (1.9.10)
|
||||
figaro (1.0.0)
|
||||
thor (~> 0.14)
|
||||
flickraw (0.9.8)
|
||||
formatador (0.2.5)
|
||||
friendly_id (5.0.4)
|
||||
activerecord (>= 4.0.0)
|
||||
gibbon (1.1.4)
|
||||
@@ -127,6 +183,20 @@ GEM
|
||||
gravatar-ultimate (2.0.0)
|
||||
activesupport (>= 2.3.14)
|
||||
rack
|
||||
guard (2.12.8)
|
||||
formatador (>= 0.2.4)
|
||||
listen (>= 2.7, <= 4.0)
|
||||
lumberjack (~> 1.0)
|
||||
nenv (~> 0.1)
|
||||
notiffany (~> 0.0)
|
||||
pry (>= 0.9.12)
|
||||
shellany (~> 0.0)
|
||||
thor (>= 0.18.1)
|
||||
guard-compat (1.2.1)
|
||||
guard-rspec (4.6.2)
|
||||
guard (~> 2.1)
|
||||
guard-compat (~> 1.1)
|
||||
rspec (>= 2.99.0, < 4.0)
|
||||
haml (4.1.0.beta.1)
|
||||
tilt
|
||||
haml-rails (0.6.0)
|
||||
@@ -136,6 +206,9 @@ GEM
|
||||
html2haml (>= 1.0.1)
|
||||
railties (>= 4.0.1)
|
||||
hashie (3.3.2)
|
||||
heroku-api (0.3.22)
|
||||
excon (~> 0.38)
|
||||
multi_json (~> 1.8)
|
||||
highline (1.6.21)
|
||||
hike (1.2.3)
|
||||
hpricot (0.8.6)
|
||||
@@ -144,6 +217,8 @@ GEM
|
||||
haml (>= 4.0.0.rc.1)
|
||||
hpricot (~> 0.8.6)
|
||||
ruby_parser (~> 3.1.1)
|
||||
http-cookie (1.0.2)
|
||||
domain_name (~> 0.5)
|
||||
httparty (0.13.3)
|
||||
json (~> 1.8)
|
||||
multi_xml (>= 0.5.2)
|
||||
@@ -157,7 +232,7 @@ GEM
|
||||
slop (>= 3.5.0)
|
||||
term-ansicolor
|
||||
terminal-table
|
||||
jquery-rails (3.1.2)
|
||||
jquery-rails (3.1.3)
|
||||
railties (>= 3.0, < 5.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
jquery-ui-rails (5.0.3)
|
||||
@@ -165,8 +240,12 @@ GEM
|
||||
js-routes (0.9.9)
|
||||
railties (>= 3.2)
|
||||
sprockets-rails
|
||||
json (1.8.2)
|
||||
json (1.8.3)
|
||||
kaminari (0.16.3)
|
||||
actionpack (>= 3.0.0)
|
||||
activesupport (>= 3.0.0)
|
||||
kgio (2.9.2)
|
||||
kramdown (1.5.0)
|
||||
launchy (2.4.3)
|
||||
addressable (~> 2.3)
|
||||
leaflet-markercluster-rails (0.7.0)
|
||||
@@ -182,19 +261,29 @@ GEM
|
||||
letter_opener (1.3.0)
|
||||
launchy (~> 2.2)
|
||||
libv8 (3.16.14.7)
|
||||
mail (2.6.3)
|
||||
mime-types (>= 1.16, < 3)
|
||||
listen (3.0.2)
|
||||
rb-fsevent (>= 0.9.3)
|
||||
rb-inotify (>= 0.9)
|
||||
lumberjack (1.0.9)
|
||||
mail (2.6.4)
|
||||
mime-types (>= 1.16, < 4)
|
||||
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 (2.99.1)
|
||||
mimemagic (0.3.0)
|
||||
mini_portile2 (2.0.0)
|
||||
minitest (5.8.4)
|
||||
multi_json (1.11.2)
|
||||
multi_xml (0.5.5)
|
||||
netrc (0.10.0)
|
||||
multipart-post (2.0.0)
|
||||
nenv (0.2.0)
|
||||
netrc (0.10.3)
|
||||
newrelic_rpm (3.9.8.273)
|
||||
nokogiri (1.6.5)
|
||||
mini_portile (~> 0.6.0)
|
||||
nokogiri (1.6.7.2)
|
||||
mini_portile2 (~> 2.0.0.rc2)
|
||||
notiffany (0.0.6)
|
||||
nenv (~> 0.1)
|
||||
shellany (~> 0.0)
|
||||
oauth (0.4.7)
|
||||
omniauth (1.2.2)
|
||||
hashie (>= 1.2, < 4)
|
||||
@@ -208,8 +297,16 @@ GEM
|
||||
multi_json (~> 1.3)
|
||||
omniauth-oauth (~> 1.0)
|
||||
orm_adapter (0.5.0)
|
||||
paperclip (4.3.0)
|
||||
activemodel (>= 3.2.0)
|
||||
activesupport (>= 3.2.0)
|
||||
cocaine (~> 0.5.5)
|
||||
mime-types
|
||||
mimemagic (= 0.3.0)
|
||||
pg (0.17.1)
|
||||
poltergeist (1.5.1)
|
||||
plupload-rails (1.2.1)
|
||||
rails (>= 3.1)
|
||||
poltergeist (1.6.0)
|
||||
capybara (~> 2.1)
|
||||
cliver (~> 0.3.1)
|
||||
multi_json (~> 1.0)
|
||||
@@ -218,78 +315,100 @@ GEM
|
||||
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.3)
|
||||
i18n (~> 0.6)
|
||||
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)
|
||||
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)
|
||||
rake (11.1.2)
|
||||
rb-fsevent (0.9.5)
|
||||
rb-inotify (0.9.5)
|
||||
ffi (>= 0.5.0)
|
||||
ref (1.0.5)
|
||||
responders (1.1.2)
|
||||
railties (>= 3.2, < 4.2)
|
||||
rest-client (1.7.2)
|
||||
rest-client (1.8.0)
|
||||
http-cookie (>= 1.0.2, < 2.0)
|
||||
mime-types (>= 1.16, < 3.0)
|
||||
netrc (~> 0.7)
|
||||
rspec (3.4.0)
|
||||
rspec-core (~> 3.4.0)
|
||||
rspec-expectations (~> 3.4.0)
|
||||
rspec-mocks (~> 3.4.0)
|
||||
rspec-activemodel-mocks (1.0.1)
|
||||
activemodel (>= 3.0)
|
||||
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)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-mocks (3.4.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-rails (3.4.2)
|
||||
actionpack (>= 3.0, < 4.3)
|
||||
activesupport (>= 3.0, < 4.3)
|
||||
railties (>= 3.0, < 4.3)
|
||||
rspec-core (~> 3.4.0)
|
||||
rspec-expectations (~> 3.4.0)
|
||||
rspec-mocks (~> 3.4.0)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-support (3.4.1)
|
||||
ruby-units (1.4.5)
|
||||
ruby_parser (3.1.3)
|
||||
sexp_processor (~> 4.1)
|
||||
rubyzip (1.1.7)
|
||||
sass (3.2.19)
|
||||
sass-rails (4.0.5)
|
||||
railties (>= 4.0.0, < 5.0)
|
||||
sass (~> 3.2.2)
|
||||
sprockets (~> 2.8, < 3.0)
|
||||
sprockets-rails (~> 2.0)
|
||||
selenium-webdriver (2.47.1)
|
||||
childprocess (~> 0.5)
|
||||
multi_json (~> 1.0)
|
||||
rubyzip (~> 1.0)
|
||||
websocket (~> 1.0)
|
||||
sexp_processor (4.4.4)
|
||||
shellany (0.0.1)
|
||||
simplecov (0.9.1)
|
||||
docile (~> 1.1.0)
|
||||
multi_json (~> 1.0)
|
||||
simplecov-html (~> 0.8.0)
|
||||
simplecov-html (0.8.0)
|
||||
slop (3.6.0)
|
||||
sprockets (2.12.3)
|
||||
sprockets (2.12.4)
|
||||
hike (~> 1.2)
|
||||
multi_json (~> 1.0)
|
||||
rack (~> 1.0)
|
||||
tilt (~> 1.1, != 1.3.0)
|
||||
sprockets-rails (2.2.2)
|
||||
sprockets-rails (2.3.3)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
@@ -301,27 +420,31 @@ GEM
|
||||
ref
|
||||
thor (0.19.1)
|
||||
thread (0.1.4)
|
||||
thread_safe (0.3.4)
|
||||
thread_safe (0.3.5)
|
||||
tilt (1.4.1)
|
||||
tins (1.3.3)
|
||||
tzinfo (1.2.2)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (2.5.3)
|
||||
uglifier (2.7.2)
|
||||
execjs (>= 0.3.0)
|
||||
json (>= 1.8.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.1)
|
||||
unicorn (4.8.3)
|
||||
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.2)
|
||||
websocket-driver (0.5.4)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.0)
|
||||
websocket-extensions (0.1.2)
|
||||
will_paginate (3.0.7)
|
||||
xpath (2.0.0)
|
||||
nokogiri (~> 1.3)
|
||||
@@ -335,18 +458,23 @@ DEPENDENCIES
|
||||
better_errors
|
||||
binding_of_caller
|
||||
bluecloth
|
||||
bonsai-elasticsearch-rails
|
||||
bootstrap-datepicker-rails
|
||||
bootstrap-kaminari-views
|
||||
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
|
||||
@@ -354,12 +482,16 @@ DEPENDENCIES
|
||||
geocoder!
|
||||
gibbon
|
||||
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)
|
||||
@@ -373,16 +505,22 @@ DEPENDENCIES
|
||||
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)
|
||||
rspec-rails (~> 3.4.0)
|
||||
ruby-units
|
||||
sass-rails (~> 4.0.4)
|
||||
selenium-webdriver
|
||||
therubyracer (~> 0.12)
|
||||
uglifier (~> 2.5.3)
|
||||
uglifier (~> 2.7.2)
|
||||
unicorn
|
||||
webrat
|
||||
will_paginate (~> 3.0)
|
||||
|
||||
BUNDLED WITH
|
||||
1.11.2
|
||||
|
||||
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
|
||||
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 |
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
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,3 +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,5 +1,3 @@
|
||||
|
||||
|
||||
@import "twitter/bootstrap/bootstrap";
|
||||
@import "custom_bootstrap/variables";
|
||||
// this padding needs to be done before the responsive stuff is imported
|
||||
@@ -99,12 +97,25 @@ p.stats {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.homepage-members {
|
||||
height: 100px;
|
||||
.member-cards {
|
||||
display: flex;
|
||||
flex: none;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.member-thumbnail {
|
||||
padding: .25em;
|
||||
|
||||
.homepage-members:nth-child(odd) {
|
||||
margin-left: 0px;
|
||||
div {
|
||||
width: 5em;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
div ~ div {
|
||||
width: 15em;
|
||||
padding-left: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
#placesmap, #cropmap {
|
||||
@@ -168,16 +179,31 @@ p.stats {
|
||||
|
||||
.crop-thumbnail {
|
||||
height: 220px;
|
||||
.scientific-name small, .crop-name a {
|
||||
.cropinfo {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1em;
|
||||
padding-bottom: 2px;
|
||||
|
||||
.cropname {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.scientificname {
|
||||
font-size: small;
|
||||
font-style: italic;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.plantingcount {
|
||||
font-size: small;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.crop-name a {
|
||||
padding-top: 2px;
|
||||
}
|
||||
@@ -201,8 +227,10 @@ li.crop-hierarchy {
|
||||
margin: 40px 0px 0px 0px !important;
|
||||
}
|
||||
|
||||
// footer
|
||||
|
||||
footer {
|
||||
#contact, #about-growstuff, #policies {
|
||||
#footer1, #footer2, #footer3 {
|
||||
text-align: left;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 2em;
|
||||
@@ -229,6 +257,17 @@ footer {
|
||||
}
|
||||
}
|
||||
|
||||
// ensure footer is pushed to bottom of browser window
|
||||
|
||||
#maincontainer {
|
||||
min-height: 80%;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
.crop-image, .member-image {
|
||||
width: 100%;
|
||||
@@ -257,6 +296,12 @@ footer {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.alert {
|
||||
a {
|
||||
font-weight: 800;
|
||||
}
|
||||
}
|
||||
|
||||
// Overrides applying only to mobile view. This must be at the end of the overrides file.
|
||||
|
||||
@media only screen and (max-width: 767px) {
|
||||
@@ -274,3 +319,42 @@ footer {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
/* override "info" alert boxes to be green, not blue, on Growstuff */
|
||||
@state-info-text: darken(@green, 10%);
|
||||
@state-info-bg: lighten(@green, 50%);
|
||||
|
||||
/* and set "success" to be the same, as it was just very slightly
|
||||
* different because the default bootstrap green is slightly different
|
||||
* from ours */
|
||||
@state-success-text: darken(@green, 10%);
|
||||
@state-success-bg: lighten(@green, 50%);
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -33,7 +37,7 @@ class ApplicationController < ActionController::Base
|
||||
rescue_from CanCan::AccessDenied do |exception|
|
||||
redirect_to request.referer || root_url, :alert => exception.message
|
||||
end
|
||||
|
||||
|
||||
def set_locale
|
||||
I18n.locale = params[:locale] || extract_locale_from_subdomain || I18n.default_locale
|
||||
end
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require 'will_paginate/array'
|
||||
|
||||
class CropsController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :hierarchy, :search, :show]
|
||||
load_and_authorize_resource
|
||||
@@ -9,10 +11,12 @@ class CropsController < ApplicationController
|
||||
@sort = params[:sort]
|
||||
if @sort == 'alpha'
|
||||
# alphabetical order
|
||||
@crops = Crop.includes(:scientific_names, {:plantings => :photos}).paginate(:page => params[:page])
|
||||
@crops = Crop.includes(:scientific_names, {:plantings => :photos})
|
||||
@paginated_crops = @crops.approved.paginate(:page => params[:page])
|
||||
else
|
||||
# default to sorting by popularity
|
||||
@crops = Crop.popular.includes(:scientific_names, {:plantings => :photos}).paginate(:page => params[:page])
|
||||
@crops = Crop.popular.includes(:scientific_names, {:plantings => :photos})
|
||||
@paginated_crops = @crops.approved.paginate(:page => params[:page])
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
@@ -32,7 +36,18 @@ class CropsController < ApplicationController
|
||||
|
||||
# GET /crops/wrangle
|
||||
def wrangle
|
||||
@crops = Crop.recent.paginate(:page => params[:page])
|
||||
@approval_status = params[:approval_status]
|
||||
case @approval_status
|
||||
when "pending"
|
||||
@crops = Crop.pending_approval
|
||||
when "rejected"
|
||||
@crops = Crop.rejected
|
||||
else
|
||||
@crops = Crop.recent
|
||||
end
|
||||
|
||||
@crops = @crops.paginate(:page => params[:page])
|
||||
|
||||
@crop_wranglers = Role.crop_wranglers
|
||||
respond_to do |format|
|
||||
format.html
|
||||
@@ -49,18 +64,13 @@ class CropsController < ApplicationController
|
||||
|
||||
# GET /crops/search
|
||||
def search
|
||||
@search = params[:search]
|
||||
@exact_match = Crop.find_by_name(params[:search])
|
||||
|
||||
@partial_matches = Crop.search(params[:search])
|
||||
# exclude exact match from partial match list
|
||||
@partial_matches = @partial_matches.reject{ |r| @exact_match && r.eql?(@exact_match) }
|
||||
|
||||
@fuzzy = Crop.search(params[:term])
|
||||
@term = params[:term]
|
||||
@matches = Crop.search(@term)
|
||||
@paginated_matches = @matches.paginate(:page => params[:page])
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json { render :json => @fuzzy }
|
||||
format.json { render :json => @matches }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -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,8 +1,11 @@
|
||||
class FollowsController < ApplicationController
|
||||
before_filter :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
skip_load_resource :only => :create
|
||||
|
||||
# POST /follows
|
||||
def create
|
||||
|
||||
|
||||
@follow = current_member.follows.build(:followed_id => follow_params[:followed_id])
|
||||
|
||||
if @follow.save
|
||||
|
||||
@@ -20,7 +20,7 @@ class HarvestsController < ApplicationController
|
||||
format.html { @harvests = @harvests.paginate(:page => params[:page]) }
|
||||
format.json { render json: @harvests }
|
||||
format.csv do
|
||||
specifics = (@owner ? "#{@owner.name}-" : @crop ? "#{@crop.name}-" : nil)
|
||||
specifics = (@owner ? "#{@owner.login_name}-" : @crop ? "#{@crop.name}-" : nil)
|
||||
@filename = "Growstuff-#{specifics}Harvests-#{Time.zone.now.to_s(:number)}.csv"
|
||||
render :csv => @harvests
|
||||
end
|
||||
@@ -107,6 +107,6 @@ class HarvestsController < ApplicationController
|
||||
|
||||
def harvest_params
|
||||
params.require(:harvest).permit(:crop_id, :harvested_at, :description, :owner_id,
|
||||
:quantity, :unit, :weight_quantity, :weight_unit, :plant_part_id, :slug)
|
||||
:quantity, :unit, :weight_quantity, :weight_unit, :plant_part_id, :slug, :si_weight)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
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
|
||||
|
||||
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
|
||||
@@ -44,6 +49,27 @@ class MembersController < ApplicationController
|
||||
@followers = @member.followers.paginate(:page => params[:page])
|
||||
end
|
||||
|
||||
EMAIL_TYPE_STRING = {
|
||||
send_notification_email: "direct message notifications",
|
||||
send_planting_reminder: "planting reminders"
|
||||
}
|
||||
|
||||
def unsubscribe
|
||||
begin
|
||||
verifier = ActiveSupport::MessageVerifier.new(ENV['RAILS_SECRET_TOKEN'])
|
||||
decrypted_message = verifier.verify(params[:message])
|
||||
|
||||
@member = Member.find(decrypted_message[:member_id])
|
||||
@type = decrypted_message[:type]
|
||||
@member.update(@type => false)
|
||||
|
||||
flash.now[:notice] = "You have been unsubscribed from #{EMAIL_TYPE_STRING[@type]} emails."
|
||||
|
||||
rescue ActiveSupport::MessageVerifier::InvalidSignature
|
||||
flash.now[:alert] = "We're sorry, there was an error updating your settings."
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def expire_cache_fragments
|
||||
|
||||
@@ -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,21 @@ class NotificationsController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
# GET /notifications/1/reply
|
||||
def reply
|
||||
@notification = Notification.new
|
||||
@sender_notification = Notification.find(params[:id])
|
||||
@recipient = @sender_notification.sender
|
||||
@subject = @sender_notification.subject =~ /^Re: / ?
|
||||
@sender_notification.subject :
|
||||
"Re: " + @sender_notification.subject
|
||||
|
||||
|
||||
respond_to do |format|
|
||||
format.html # reply.html.haml
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /notifications/1
|
||||
def destroy
|
||||
@notification = Notification.find(params[:id])
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -21,9 +21,17 @@ class PlacesController < ApplicationController
|
||||
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
|
||||
|
||||
@@ -20,7 +20,7 @@ class PlantingsController < ApplicationController
|
||||
format.json { render json: @plantings }
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -37,4 +37,12 @@ module HarvestsHelper
|
||||
end
|
||||
end
|
||||
|
||||
def display_harvest_description(harvest)
|
||||
if harvest.description.empty?
|
||||
"No description provided."
|
||||
else
|
||||
truncate(harvest.description, length: 130, separator: ' ', omission: '... ') { link_to "Read more", harvest_path(harvest) }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -5,12 +5,7 @@ module NotificationsHelper
|
||||
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,10 +2,21 @@ class Notifier < ActionMailer::Base
|
||||
include NotificationsHelper
|
||||
default from: "Growstuff <noreply@growstuff.org>"
|
||||
|
||||
def verifier()
|
||||
if ENV['RAILS_SECRET_TOKEN']
|
||||
return ActiveSupport::MessageVerifier.new(ENV['RAILS_SECRET_TOKEN'])
|
||||
else
|
||||
raise "RAILS_SECRET_TOKEN environment variable not set - have you created config/application.yml?"
|
||||
end
|
||||
end
|
||||
|
||||
def notify(notification)
|
||||
@notification = notification
|
||||
@reply_link = reply_link(@notification)
|
||||
|
||||
# Encrypting
|
||||
@signed_message = verifier.generate ({ member_id: @notification.recipient.id, type: :send_notification_email })
|
||||
|
||||
mail(:to => @notification.recipient.email,
|
||||
:subject => @notification.subject)
|
||||
end
|
||||
@@ -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?")
|
||||
end
|
||||
end
|
||||
|
||||
def new_crop_request(member, request)
|
||||
@member, @request = member, request
|
||||
mail(:to => @member.email, :subject => "#{@request.requester.login_name} has requested #{@request.name} as a new crop")
|
||||
end
|
||||
|
||||
def crop_request_approved(member, crop)
|
||||
@member, @crop = member, crop
|
||||
mail(:to => @member.email, :subject => "#{crop.name.capitalize} has been approved")
|
||||
end
|
||||
|
||||
def crop_request_rejected(member, crop)
|
||||
@member, @crop = member, crop
|
||||
mail(:to => @member.email, :subject => "#{crop.name.capitalize} has been rejected")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -21,7 +21,24 @@ class Ability
|
||||
cannot :read, Account
|
||||
cannot :read, AccountType
|
||||
|
||||
# nobody should be able to view unapproved crops unless they
|
||||
# are wranglers or admins
|
||||
cannot :read, Crop
|
||||
can :read, Crop, :approval_status => "approved"
|
||||
# scientific names should only be viewable if associated crop is approved
|
||||
cannot :read, ScientificName
|
||||
can :read, ScientificName do |sn|
|
||||
sn.crop.approved?
|
||||
end
|
||||
# ... same for alternate names
|
||||
cannot :read, AlternateName
|
||||
can :read, AlternateName do |an|
|
||||
an.crop.approved?
|
||||
end
|
||||
|
||||
if member
|
||||
# members can see even rejected or pending crops if they requested it
|
||||
can :read, Crop, :requester_id => member.id
|
||||
|
||||
# managing your own user settings
|
||||
can :update, Member, :id => member.id
|
||||
@@ -29,6 +46,7 @@ class Ability
|
||||
# can read/delete notifications that were sent to them
|
||||
can :read, Notification, :recipient_id => member.id
|
||||
can :destroy, Notification, :recipient_id => member.id
|
||||
can :reply, Notification, :recipient_id => member.id
|
||||
# can send a private message to anyone but themselves
|
||||
# note: sadly, we can't test for this from the view, but it works
|
||||
# for the model/controller
|
||||
@@ -45,6 +63,9 @@ class Ability
|
||||
can :manage, AlternateName
|
||||
end
|
||||
|
||||
# any member can create a crop provisionally
|
||||
can :create, Crop
|
||||
|
||||
# can create & destroy their own authentications against other sites.
|
||||
can :create, Authentication
|
||||
can :destroy, Authentication, :member_id => member.id
|
||||
@@ -94,9 +115,11 @@ class Ability
|
||||
cannot :destroy, OrderItem, :order => { :member_id => member.id, :completed_at => nil }
|
||||
|
||||
# following/unfollowing permissions
|
||||
can :create, Follow do |f|
|
||||
!member.already_following?(f.followed) && f.followed_id != member.id
|
||||
end
|
||||
can :create, Follow
|
||||
cannot :create, Follow, :followed_id => member.id # can't follow yourself
|
||||
|
||||
can :destroy, Follow
|
||||
cannot :destroy, Follow, :followed_id => member.id # can't unfollow yourself
|
||||
|
||||
if member.has_role? :admin
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class AlternateName < ActiveRecord::Base
|
||||
after_commit { |an| an.crop.__elasticsearch__.index_document if an.crop && ENV['GROWSTUFF_ELASTICSEARCH'] == "true" }
|
||||
belongs_to :crop
|
||||
belongs_to :creator, :class_name => 'Member'
|
||||
end
|
||||
|
||||
@@ -2,18 +2,19 @@ class Crop < ActiveRecord::Base
|
||||
extend FriendlyId
|
||||
friendly_id :name, use: [:slugged, :finders]
|
||||
|
||||
has_many :scientific_names
|
||||
has_many :scientific_names, after_add: :update_index, after_remove: :update_index
|
||||
accepts_nested_attributes_for :scientific_names,
|
||||
:allow_destroy => true,
|
||||
:reject_if => :all_blank
|
||||
|
||||
has_many :alternate_names
|
||||
has_many :alternate_names, after_add: :update_index, after_remove: :update_index, dependent: :destroy
|
||||
has_many :plantings
|
||||
has_many :photos, :through => :plantings
|
||||
has_many :seeds
|
||||
has_many :harvests
|
||||
has_many :plant_parts, -> { uniq }, :through => :harvests
|
||||
belongs_to :creator, :class_name => 'Member'
|
||||
belongs_to :requester, :class_name => 'Member'
|
||||
|
||||
belongs_to :parent, :class_name => 'Crop'
|
||||
has_many :varieties, :class_name => 'Crop', :foreign_key => 'parent_id'
|
||||
@@ -21,23 +22,101 @@ class Crop < ActiveRecord::Base
|
||||
before_destroy {|crop| crop.posts.clear}
|
||||
|
||||
default_scope { order("lower(name) asc") }
|
||||
scope :recent, -> { reorder("created_at desc") }
|
||||
scope :toplevel, -> { where(:parent_id => nil) }
|
||||
scope :popular, -> { reorder("plantings_count desc, lower(name) asc") }
|
||||
scope :randomized, -> { reorder('random()') } # ok on sqlite and psql, but not on mysql
|
||||
scope :recent, -> { where(:approval_status => "approved").reorder("created_at desc") }
|
||||
scope :toplevel, -> { where(:approval_status => "approved", :parent_id => nil) }
|
||||
scope :popular, -> { where(:approval_status => "approved").reorder("plantings_count desc, lower(name) asc") }
|
||||
scope :randomized, -> { where(:approval_status => "approved").reorder('random()') } # ok on sqlite and psql, but not on mysql
|
||||
scope :pending_approval, -> { where(:approval_status => "pending") }
|
||||
scope :approved, -> { where(:approval_status => "approved") }
|
||||
scope :rejected, -> { where(:approval_status => "rejected") }
|
||||
|
||||
## Wikipedia urls are only necessary when approving a crop
|
||||
validates :en_wikipedia_url,
|
||||
:format => {
|
||||
:with => /\Ahttps?:\/\/en\.wikipedia\.org\/wiki/,
|
||||
:message => 'is not a valid English Wikipedia URL'
|
||||
}
|
||||
},
|
||||
:if => :approved?
|
||||
|
||||
## Reasons are only necessary when rejecting
|
||||
validates :reason_for_rejection, :presence => true, :if => :rejected?
|
||||
|
||||
## This validation addresses a race condition
|
||||
validate :approval_status_cannot_be_changed_again
|
||||
|
||||
validate :must_be_rejected_if_rejected_reasons_present
|
||||
|
||||
validate :must_have_meaningful_reason_for_rejection
|
||||
|
||||
####################################
|
||||
# Elastic search configuration
|
||||
include Elasticsearch::Model
|
||||
include Elasticsearch::Model::Callbacks
|
||||
# In order to avoid clashing between different environments,
|
||||
# use Rails.env as a part of index name (eg. development_growstuff)
|
||||
index_name [Rails.env, "growstuff"].join('_')
|
||||
settings index: { number_of_shards: 1 },
|
||||
analysis: {
|
||||
tokenizer: {
|
||||
gs_edgeNGram_tokenizer: {
|
||||
type: "edgeNGram", # edgeNGram: NGram match from the start of a token
|
||||
min_gram: 3,
|
||||
max_gram: 10,
|
||||
# token_chars: Elasticsearch will split on characters
|
||||
# that don’t belong to any of these classes
|
||||
token_chars: [ "letter", "digit" ]
|
||||
}
|
||||
},
|
||||
analyzer: {
|
||||
gs_edgeNGram_analyzer: {
|
||||
tokenizer: "gs_edgeNGram_tokenizer",
|
||||
filter: ["lowercase"]
|
||||
}
|
||||
},
|
||||
} do
|
||||
mappings dynamic: 'false' do
|
||||
indexes :id, type: 'long'
|
||||
indexes :name, type: 'string', analyzer: 'gs_edgeNGram_analyzer'
|
||||
indexes :approval_status, type: 'string'
|
||||
indexes :scientific_names do
|
||||
indexes :scientific_name,
|
||||
type: 'string',
|
||||
analyzer: 'gs_edgeNGram_analyzer',
|
||||
# Disabling field-length norm (norm). If the norm option is turned on(by default),
|
||||
# higher weigh would be given for shorter fields, which in our case is irrelevant.
|
||||
norms: { enabled: false }
|
||||
end
|
||||
indexes :alternate_names do
|
||||
indexes :name, type: 'string', analyzer: 'gs_edgeNGram_analyzer'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def as_indexed_json(options={})
|
||||
self.as_json(
|
||||
only: [:id, :name, :approval_status],
|
||||
include: {
|
||||
scientific_names: { only: :scientific_name },
|
||||
alternate_names: { only: :name }
|
||||
})
|
||||
end
|
||||
|
||||
# update the Elasticsearch index (only if we're using it in this
|
||||
# environment)
|
||||
def update_index(name_obj)
|
||||
if ENV["GROWSTUFF_ELASTICSEARCH"] == "true"
|
||||
__elasticsearch__.index_document
|
||||
end
|
||||
end
|
||||
|
||||
# End Elasticsearch section
|
||||
|
||||
def to_s
|
||||
return name
|
||||
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
|
||||
@@ -200,11 +299,76 @@ class Crop < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def rejection_explanation
|
||||
if reason_for_rejection == "other"
|
||||
return rejection_notes
|
||||
else
|
||||
return reason_for_rejection
|
||||
end
|
||||
end
|
||||
|
||||
# Crop.search(string)
|
||||
# searches for crops whose names match the string given
|
||||
# just uses SQL LIKE for now, but can be made fancier later
|
||||
def self.search(query)
|
||||
where("name ILIKE ?", "%#{query}%")
|
||||
if ENV['GROWSTUFF_ELASTICSEARCH'] == "true"
|
||||
search_str = query.nil? ? "" : query.downcase
|
||||
response = __elasticsearch__.search( {
|
||||
# Finds documents which match any field, but uses the _score from
|
||||
# the best field insead of adding up _score from each field.
|
||||
query: {
|
||||
multi_match: {
|
||||
query: "#{search_str}",
|
||||
analyzer: "standard",
|
||||
fields: ["name", "scientific_names.scientific_name", "alternate_names.name"]
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
term: {approval_status: "approved"}
|
||||
},
|
||||
size: 50
|
||||
}
|
||||
)
|
||||
return response.records.to_a
|
||||
else
|
||||
# if we don't have elasticsearch, just do a basic SQL query.
|
||||
# also, make sure it's an actual array not an activerecord
|
||||
# collection, so it matches what we get from elasticsearch and we can
|
||||
# manipulate it in the same ways (eg. deleting elements without deleting
|
||||
# the whole record from the db)
|
||||
matches = Crop.approved.where("name ILIKE ?", "%#{query}%").to_a
|
||||
|
||||
# we want to make sure that exact matches come first, even if not
|
||||
# using elasticsearch (eg. in development)
|
||||
exact_match = Crop.approved.find_by_name(query)
|
||||
if exact_match
|
||||
matches.delete(exact_match)
|
||||
matches.unshift(exact_match)
|
||||
end
|
||||
|
||||
return matches
|
||||
end
|
||||
end
|
||||
|
||||
# Custom validations
|
||||
|
||||
def approval_status_cannot_be_changed_again
|
||||
previous = previous_changes.include?(:approval_status) ? previous_changes.approval_status : {}
|
||||
if previous.include?(:rejected) || previous.include?(:approved)
|
||||
errors.add(:approval_status, "has already been set to #{approval_status}")
|
||||
end
|
||||
end
|
||||
|
||||
def must_be_rejected_if_rejected_reasons_present
|
||||
unless rejected?
|
||||
if reason_for_rejection.present? || rejection_notes.present?
|
||||
errors.add(:approval_status, "must be rejected if a reason for rejection is present")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def must_have_meaningful_reason_for_rejection
|
||||
if reason_for_rejection == "other" && rejection_notes.blank?
|
||||
errors.add(:rejection_notes, "must be added if the reason for rejection is \"other\"")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -7,6 +7,17 @@ class Garden < ActiveRecord::Base
|
||||
has_many :plantings, -> { order(created_at: :desc) }, :dependent => :destroy
|
||||
has_many :crops, :through => :plantings
|
||||
|
||||
has_and_belongs_to_many :photos
|
||||
|
||||
before_destroy do |garden|
|
||||
photolist = garden.photos.to_a # save a temp copy of the photo list
|
||||
garden.photos.clear # clear relationship b/w garden and photo
|
||||
|
||||
photolist.each do |photo|
|
||||
photo.destroy_if_unused
|
||||
end
|
||||
end
|
||||
|
||||
# set up geocoding
|
||||
geocoded_by :location
|
||||
after_validation :geocode
|
||||
@@ -17,13 +28,19 @@ class Garden < ActiveRecord::Base
|
||||
scope :active, -> { where(:active => true) }
|
||||
scope :inactive, -> { where(:active => false) }
|
||||
|
||||
validates :location,
|
||||
:length => { :maximum => 255 }
|
||||
|
||||
validates :name,
|
||||
:format => {
|
||||
:with => /\S/
|
||||
}
|
||||
},
|
||||
:length => { :maximum => 255 }
|
||||
|
||||
validates :area,
|
||||
:numericality => { :only_integer => false, :greater_than_or_equal_to => 0 },
|
||||
:numericality => {
|
||||
:only_integer => false,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
|
||||
AREA_UNITS_VALUES = {
|
||||
@@ -83,4 +100,8 @@ class Garden < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def default_photo
|
||||
return photos.first
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -20,10 +20,16 @@ class Harvest < ActiveRecord::Base
|
||||
|
||||
default_scope { order('created_at DESC') }
|
||||
|
||||
validates :crop, :approved => true
|
||||
|
||||
validates :crop, :presence => {:message => "must be present and exist in our database"}
|
||||
|
||||
validates :plant_part, :presence => {:message => "must be present and exist in our database"}
|
||||
|
||||
validates :quantity,
|
||||
:numericality => { :only_integer => false },
|
||||
:numericality => {
|
||||
:only_integer => false,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
|
||||
UNITS_VALUES = {
|
||||
@@ -59,6 +65,17 @@ class Harvest < ActiveRecord::Base
|
||||
|
||||
after_validation :cleanup_quantities
|
||||
|
||||
before_save :set_si_weight
|
||||
|
||||
# we're storing the harvest weight in kilograms in the db too
|
||||
# to make data manipulation easier
|
||||
def set_si_weight
|
||||
if self.weight_unit != nil
|
||||
weight_string = "#{self.weight_quantity} #{self.weight_unit}"
|
||||
self.si_weight = Unit(weight_string).convert_to("kg").to_s("%0.3f").delete(" kg").to_f
|
||||
end
|
||||
end
|
||||
|
||||
def cleanup_quantities
|
||||
if quantity == 0
|
||||
self.quantity = nil
|
||||
|
||||
@@ -32,6 +32,7 @@ class Member < ActiveRecord::Base
|
||||
scope :confirmed, -> { where('confirmed_at IS NOT NULL') }
|
||||
scope :located, -> { where("location <> '' and latitude IS NOT NULL and longitude IS NOT NULL") }
|
||||
scope :recently_signed_in, -> { reorder('updated_at DESC') }
|
||||
scope :recently_joined, -> { reorder("confirmed_at desc") }
|
||||
scope :wants_newsletter, -> { where(:newsletter => true) }
|
||||
|
||||
has_many :follows, :class_name => "Follow", :foreign_key => "follower_id"
|
||||
@@ -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
|
||||
|
||||
@@ -3,6 +3,8 @@ class Notification < ActiveRecord::Base
|
||||
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) }
|
||||
|
||||
@@ -10,7 +12,7 @@ class Notification < ActiveRecord::Base
|
||||
after_create :send_email
|
||||
|
||||
def self.unread_count
|
||||
self.unread.count
|
||||
self.unread.size
|
||||
end
|
||||
|
||||
def replace_blank_subject
|
||||
|
||||
@@ -3,16 +3,18 @@ class Photo < ActiveRecord::Base
|
||||
|
||||
has_and_belongs_to_many :plantings
|
||||
has_and_belongs_to_many :harvests
|
||||
has_and_belongs_to_many :gardens
|
||||
before_destroy do |photo|
|
||||
photo.plantings.clear
|
||||
photo.harvests.clear
|
||||
photo.gardens.clear
|
||||
end
|
||||
|
||||
default_scope { order("created_at desc") }
|
||||
|
||||
# remove photos that aren't used by anything
|
||||
def destroy_if_unused
|
||||
unless plantings.size > 0 or harvests.size > 0
|
||||
unless plantings.size > 0 or harvests.size > 0 or gardens.size > 0
|
||||
self.destroy
|
||||
end
|
||||
end
|
||||
|
||||
@@ -30,10 +30,14 @@ class Planting < ActiveRecord::Base
|
||||
|
||||
default_scope { order("created_at desc") }
|
||||
|
||||
validates :crop_id, :presence => {:message => "must be present and exist in our database"}
|
||||
validates :crop, :approved => true
|
||||
|
||||
validates :crop, :presence => {:message => "must be present and exist in our database"}
|
||||
|
||||
validates :quantity,
|
||||
:numericality => { :only_integer => true },
|
||||
:numericality => {
|
||||
:only_integer => true,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
|
||||
SUNNINESS_VALUES = %w(sun semi-shade shade)
|
||||
@@ -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.today)
|
||||
planted_at.present? && current_date.to_date >= planted_at
|
||||
end
|
||||
|
||||
def percentage_grown(current_date = Date.today)
|
||||
return nil unless days_before_maturity && planted?(current_date)
|
||||
|
||||
days = (current_date.to_date - planted_at.to_date).to_i
|
||||
|
||||
return 0 if current_date < planted_at
|
||||
return 100 if days > days_before_maturity
|
||||
percent = (days/days_before_maturity*100).to_i
|
||||
|
||||
if percent >= 100
|
||||
percent = 100
|
||||
end
|
||||
|
||||
percent
|
||||
end
|
||||
|
||||
# return a list of interesting plantings, for the homepage etc.
|
||||
# we can't do this via a scope (as far as we know) so sadly we have to
|
||||
# do it this way.
|
||||
@@ -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
|
||||
|
||||
@@ -10,12 +10,40 @@ class Post < ActiveRecord::Base
|
||||
# also has_many notifications, but kinda meaningless to get at them
|
||||
# from this direction, so we won't set up an association for now.
|
||||
|
||||
after_create do
|
||||
recipients = Array.new
|
||||
sender = self.author.id
|
||||
self.body.scan(Haml::Filters::GrowstuffMarkdown::MEMBER_REGEX) do |m|
|
||||
# find member case-insensitively and add to list of recipients
|
||||
member = Member.where('lower(login_name) = ?', $1.downcase).first
|
||||
recipients << member if member and not recipients.include?(member)
|
||||
end
|
||||
self.body.scan(Haml::Filters::GrowstuffMarkdown::MEMBER_AT_REGEX) do |m|
|
||||
# find member case-insensitively and add to list of recipients
|
||||
member = Member.where('lower(login_name) = ?', $1[1..-1].downcase).first
|
||||
recipients << member if member and not recipients.include?(member)
|
||||
end
|
||||
# don't send notifications to yourself
|
||||
recipients.map{ |r| r.id }.each do |recipient|
|
||||
if recipient != sender
|
||||
Notification.create(
|
||||
:recipient_id => recipient,
|
||||
:sender_id => sender,
|
||||
:subject => "#{self.author} mentioned you in their post #{self.subject}",
|
||||
:body => self.body,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
default_scope { order("created_at desc") }
|
||||
|
||||
validates :subject,
|
||||
: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'
|
||||
end
|
||||
|
||||
@@ -7,9 +7,23 @@ class Seed < ActiveRecord::Base
|
||||
|
||||
default_scope { order("created_at desc") }
|
||||
|
||||
validates :crop, :approved => true
|
||||
|
||||
validates :crop, :presence => {:message => "must be present and exist in our database"}
|
||||
validates :quantity,
|
||||
:numericality => { :only_integer => true },
|
||||
:numericality => {
|
||||
:only_integer => true,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
validates :days_until_maturity_min,
|
||||
:numericality => {
|
||||
:only_integer => true,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
validates :days_until_maturity_max,
|
||||
:numericality => {
|
||||
:only_integer => true,
|
||||
:greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
|
||||
scope :tradable, -> { where("tradable_to != 'nowhere'") }
|
||||
@@ -20,6 +34,32 @@ class Seed < ActiveRecord::Base
|
||||
:allow_nil => false,
|
||||
:allow_blank => false
|
||||
|
||||
ORGANIC_VALUES = [
|
||||
'certified organic',
|
||||
'non-certified organic',
|
||||
'conventional/non-organic',
|
||||
'unknown']
|
||||
validates :organic, :inclusion => { :in => ORGANIC_VALUES,
|
||||
:message => "You must say whether the seeds are organic or not, or that you don't know" },
|
||||
:allow_nil => false,
|
||||
:allow_blank => false
|
||||
|
||||
GMO_VALUES = [
|
||||
'certified GMO-free',
|
||||
'non-certified GMO-free',
|
||||
'GMO',
|
||||
'unknown']
|
||||
validates :gmo, :inclusion => { :in => GMO_VALUES,
|
||||
:message => "You must say whether the seeds are genetically modified or not, or that you don't know" },
|
||||
:allow_nil => false,
|
||||
:allow_blank => false
|
||||
|
||||
HEIRLOOM_VALUES = %w(heirloom hybrid unknown)
|
||||
validates :heirloom, :inclusion => { :in => HEIRLOOM_VALUES,
|
||||
:message => "You must say whether the seeds are heirloom, hybrid, or unknown" },
|
||||
:allow_nil => false,
|
||||
:allow_blank => false
|
||||
|
||||
def tradable?
|
||||
if self.tradable_to == 'nowhere'
|
||||
return false
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
- content_for :title, "Crops"
|
||||
- content_for :title, t('.title')
|
||||
- content_for :subtitle, t('.subtitle', crops_size: @crops.size)
|
||||
|
||||
- if can? :wrangle, Crop
|
||||
= link_to 'Wrangle Crops', wrangle_crops_path, :class => 'btn btn-primary'
|
||||
%p
|
||||
#{ENV['GROWSTUFF_SITE_NAME']} tracks who's growing what, where.
|
||||
View any crop page to see which of our members have planted it and find
|
||||
@@ -12,11 +15,10 @@
|
||||
= submit_tag "Show", :class => 'btn btn-primary'
|
||||
|
||||
%div.pagination
|
||||
= page_entries_info @crops, :model => "crops"
|
||||
= will_paginate @crops
|
||||
= will_paginate @paginated_crops
|
||||
|
||||
.row
|
||||
- @crops.each do |crop|
|
||||
- @paginated_crops.each do |crop|
|
||||
.col-md-2.six-across
|
||||
= render :partial => "thumbnail", :locals => { :crop => crop }
|
||||
|
||||
@@ -25,8 +27,7 @@
|
||||
= link_to 'New Crop', new_crop_path, {:class => 'btn btn-primary'}
|
||||
|
||||
%div.pagination
|
||||
= page_entries_info @crops, :model => "crops"
|
||||
= will_paginate @crops
|
||||
= will_paginate @paginated_crops
|
||||
|
||||
|
||||
%ul.list-inline
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
- content_for :title, "New crop"
|
||||
- content_for :title, (can?(:wrangle, @crop) ? "New crop" : "Suggest a crop")
|
||||
|
||||
- unless can? :wrangler, @crop
|
||||
|
||||
%p Thanks for taking the time to suggest a crop! Our crop database is managed by volunteers, and we appreciate your help. Here are some things to consider when suggesting a new crop:
|
||||
|
||||
%ul
|
||||
%li First, you might want to #{link_to 'search our crops', crops_search_path} to make sure we don't have it already, perhaps under an alternate name.
|
||||
|
||||
%li The Growstuff database only contains edible crops. In future we hope to support other crops, but for now, if your suggestion is not edible we won't be able to add it.
|
||||
|
||||
%li At this time, we are only adding crops which have a Wikipedia page. If you want to add a specific variety of crop that doesn't have its own Wikipedia entry, please use the more general form of the crop instead and put the name of your variety in the notes/description.
|
||||
|
||||
= render 'form'
|
||||
|
||||
@@ -1,26 +1,35 @@
|
||||
- content_for :title, "Crops matching #{@search}"
|
||||
- if @term
|
||||
- content_for :title, "Crops matching \"#{@term}\""
|
||||
- if @matches
|
||||
- content_for :subtitle, "#{@matches.size} total"
|
||||
- else
|
||||
- content_for :title, "Crop search"
|
||||
|
||||
- unless (@exact_match || @partial_matches.length > 0)
|
||||
%div
|
||||
= form_tag crops_search_path, :method => :get, :id => 'crop-search', :class => 'form-inline' do
|
||||
.form-group
|
||||
= label_tag :term, "Search crops:", :class => 'sr-only'
|
||||
= text_field_tag 'term', nil, :class => 'search-query input-medium form-control', :placeholder => 'Search crops', :value => @term
|
||||
= submit_tag "Search", :class => 'btn btn-primary'
|
||||
|
||||
- if @matches.empty?
|
||||
%h2 No results found
|
||||
%p
|
||||
Sorry, we couldn't find any crops that matched your search for "#{@search}".
|
||||
Sorry, we couldn't find any crops that matched your search for "#{@term}".
|
||||
%p
|
||||
Try
|
||||
= link_to "browsing our crop database", crops_path
|
||||
instead.
|
||||
|
||||
- if @exact_match
|
||||
%div#exact_match
|
||||
%h2 Exact match
|
||||
- else
|
||||
%div.pagination
|
||||
= will_paginate @paginated_matches
|
||||
|
||||
%div#paginated_matches
|
||||
.row
|
||||
.col-md-2.six-across
|
||||
= render :partial => "thumbnail", :locals => { :crop => @exact_match }
|
||||
|
||||
- if ! @partial_matches.empty?
|
||||
%div#partial_matches
|
||||
%h2 Partial matches
|
||||
.row
|
||||
- @partial_matches.each do |c|
|
||||
- @paginated_matches.each do |c|
|
||||
.col-md-2.six-across
|
||||
= render :partial => "thumbnail", :locals => { :crop => c }
|
||||
|
||||
%div.pagination
|
||||
= will_paginate @paginated_matches
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
- content_for :title, @crop.name
|
||||
- content_for :subtitle, @crop.default_scientific_name
|
||||
- content_for :buttonbar do
|
||||
- if can? :create, Planting
|
||||
= link_to "Plant this", new_planting_path(:crop_id => @crop.id), :class => 'btn btn-default'
|
||||
|
||||
- if can? :create, Harvest
|
||||
= link_to "Harvest this", new_harvest_path(:crop_id => @crop.id), :class => 'btn btn-default'
|
||||
= render :partial => 'approval_status_message', :locals => { :crop => @crop }
|
||||
|
||||
- if can? :create, Seed
|
||||
= link_to 'Add seeds to stash', new_seed_path(:params => { :crop_id => @crop.id }), :class => 'btn btn-default'
|
||||
- if @crop.approved?
|
||||
- content_for :buttonbar do
|
||||
- if can? :create, Planting
|
||||
= link_to "Plant this", new_planting_path(:crop_id => @crop.id), :class => 'btn btn-default'
|
||||
|
||||
- if can? :create, Harvest
|
||||
= link_to "Harvest this", new_harvest_path(:crop_id => @crop.id), :class => 'btn btn-default'
|
||||
|
||||
- if can? :create, Seed
|
||||
= link_to 'Add seeds to stash', new_seed_path(:params => { :crop_id => @crop.id }), :class => 'btn btn-default'
|
||||
- if member_signed_in?
|
||||
= display_seed_availability(@current_member, @crop)
|
||||
= link_to "View your seeds", seeds_by_owner_path(:owner => current_member.slug)
|
||||
|
||||
.row
|
||||
.col-md-9
|
||||
|
||||
@@ -15,28 +15,50 @@
|
||||
%li.crop_wrangler
|
||||
= link_to crop_wrangler.login_name, crop_wrangler
|
||||
|
||||
%h2 Recently added crops
|
||||
.tabbable
|
||||
%ul.nav.nav-tabs
|
||||
%li{:class => @approval_status.blank? ? 'active' : ''}
|
||||
= link_to "Recently added", wrangle_crops_path
|
||||
%li{:class => @approval_status == "pending" ? 'active' : ''}
|
||||
= link_to "Pending approval", wrangle_crops_path(:approval_status => "pending")
|
||||
%li{:class => @approval_status == "rejected" ? 'active' : ''}
|
||||
= link_to "Rejected", wrangle_crops_path(:approval_status => "rejected")
|
||||
|
||||
%h2
|
||||
- if @approval_status == "pending"
|
||||
Requested Crops
|
||||
- elsif @approval_status == "rejected"
|
||||
Rejected Crops
|
||||
- else
|
||||
Recently added crops
|
||||
|
||||
|
||||
%div.pagination
|
||||
= page_entries_info @crops, :model => "crops"
|
||||
= will_paginate @crops
|
||||
|
||||
%table.table.table-striped
|
||||
%table{:class => "table table-striped", :id => @approval_status.blank? ? 'recently-added-crops' : "#{@approval_status}-crops"}
|
||||
%tr
|
||||
%th System name
|
||||
%th English Wikipedia URL
|
||||
%th Scientific names
|
||||
%th Added by
|
||||
%th Requested by
|
||||
- if @approval_status == "rejected"
|
||||
%th Rejected by
|
||||
- if @approval_status != "rejected" && @approval_status != "pending"
|
||||
%th Added by
|
||||
%th When
|
||||
- @crops.each do |c|
|
||||
%tr
|
||||
%td= link_to c.name, c
|
||||
%td= link_to c.name, edit_crop_path(c)
|
||||
%td= link_to c.en_wikipedia_url, c.en_wikipedia_url
|
||||
%td
|
||||
- c.scientific_names.each do |s|
|
||||
= link_to s.scientific_name, s
|
||||
%br/
|
||||
%td= link_to c.creator, c.creator
|
||||
%td= c.requester.present? ? (link_to c.requester, c.requester) : "N/A"
|
||||
- unless @approval_status == "pending"
|
||||
%td= c.creator.present? ? (link_to c.creator, c.creator) : "N/A"
|
||||
%td
|
||||
= distance_of_time_in_words(c.created_at, Time.zone.now)
|
||||
ago.
|
||||
@@ -44,3 +66,5 @@
|
||||
%div.pagination
|
||||
= page_entries_info @crops, :model => "crops"
|
||||
= will_paginate @crops
|
||||
|
||||
|
||||
|
||||
@@ -9,24 +9,28 @@
|
||||
%span.help-block If you change your email address you will have to reconfirm.
|
||||
|
||||
.form-group
|
||||
.col-md-offset-2.col-md-8
|
||||
= f.check_box :show_email
|
||||
Show email publicly on your profile page.
|
||||
.col-md-offset-2.col-md-8.checkbox
|
||||
%label
|
||||
= f.check_box :show_email
|
||||
Show email publicly on your profile page.
|
||||
|
||||
.form-group
|
||||
.col-md-offset-2.col-md-8
|
||||
= f.check_box :send_notification_email
|
||||
Receive emailed copies of Inbox notifications (eg. private messages).
|
||||
.col-md-offset-2.col-md-8.checkbox
|
||||
%label
|
||||
= f.check_box :send_notification_email
|
||||
Receive emailed copies of Inbox notifications (eg. private messages).
|
||||
|
||||
.form-group
|
||||
.col-md-offset-2.col-md-8
|
||||
= f.check_box :send_planting_reminder
|
||||
Receive regular reminders to track your planting and harvesting.
|
||||
.col-md-offset-2.col-md-8.checkbox
|
||||
%label
|
||||
= f.check_box :send_planting_reminder
|
||||
Receive regular reminders to track your planting and harvesting.
|
||||
|
||||
.form-group
|
||||
.col-md-offset-2.col-md-8
|
||||
= f.check_box :newsletter
|
||||
Subscribe to the #{ENV['GROWSTUFF_SITE_NAME']} newsletter
|
||||
.col-md-offset-2.col-md-8.checkbox
|
||||
%label
|
||||
= f.check_box :newsletter
|
||||
Subscribe to the #{ENV['GROWSTUFF_SITE_NAME']} newsletter
|
||||
.help-block
|
||||
= render :partial => 'newsletter_blurb'
|
||||
|
||||
|
||||
@@ -26,15 +26,17 @@
|
||||
.col-md-8= f.password_field :password_confirmation, :class => 'form-control'
|
||||
|
||||
.form-group
|
||||
.col-md-offset-2.col-md-8
|
||||
= f.check_box :tos_agreement
|
||||
I agree to the
|
||||
= succeed "." do
|
||||
= link_to 'Terms of Service', url_for(:action => 'tos', :controller => '/policy')
|
||||
.col-md-offset-2.col-md-8.checkbox
|
||||
%label
|
||||
= f.check_box :tos_agreement
|
||||
I agree to the
|
||||
= succeed "." do
|
||||
= link_to 'Terms of Service', url_for(:action => 'tos', :controller => '/policy')
|
||||
.form-group
|
||||
.col-md-offset-2.col-md-8
|
||||
= f.check_box :newsletter, :checked => true
|
||||
Subscribe to the #{ENV['GROWSTUFF_SITE_NAME']} newsletter
|
||||
.col-md-offset-2.col-md-8.checkbox
|
||||
%label
|
||||
= f.check_box :newsletter, :checked => true
|
||||
Subscribe to the #{ENV['GROWSTUFF_SITE_NAME']} newsletter
|
||||
.help-inline
|
||||
= render :partial => 'newsletter_blurb'
|
||||
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
|
||||
- if devise_mapping.rememberable?
|
||||
.form-group
|
||||
.col-md-8.col-md-offset-2
|
||||
= f.check_box :remember_me
|
||||
Remember me
|
||||
.col-md-8.col-md-offset-2.checkbox
|
||||
%label
|
||||
= f.check_box :remember_me
|
||||
Remember me
|
||||
|
||||
.form-group
|
||||
.form-actions.col-md-8.col-md-offset-2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
= form_for @forum, :html => { :class => 'form-horizontal', :role => "form" } do |f|
|
||||
- if @forum.errors.any?
|
||||
#error_explanation
|
||||
%h2= "#{pluralize(@forum.errors.count, "error")} prohibited this forum from being saved:"
|
||||
%h2= "#{pluralize(@forum.errors.size, "error")} prohibited this forum from being saved:"
|
||||
%ul
|
||||
- @forum.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
- content_for :title, "Forums"
|
||||
- content_for :title, t('.title')
|
||||
|
||||
- if can? :create, Forum
|
||||
%p
|
||||
@@ -7,7 +7,7 @@
|
||||
- @forums.each do |forum|
|
||||
%h2= forum
|
||||
%p
|
||||
= pluralize(forum.posts.count, "post")
|
||||
= pluralize(forum.posts.size, "post")
|
||||
|
|
||||
=link_to "Visit forum", forum
|
||||
|
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
Posts
|
||||
=link_to "Post something", new_post_path(:forum_id => @forum.id), :class => 'btn btn-primary'
|
||||
|
||||
- if @forum.posts.count > 0
|
||||
- if @forum.posts.size > 0
|
||||
=render :partial => "posts/summary", :locals => { :posts => @forum.posts }
|
||||
- else
|
||||
No posts yet.
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
= required_field_help_text
|
||||
|
||||
= form_for(@garden, :html => {:class => "form-horizontal", :role => "form"}) do |f|
|
||||
- if @garden.errors.any?
|
||||
#error_explanation
|
||||
%h2= "#{pluralize(@garden.errors.count, "error")} prohibited this garden from being saved:"
|
||||
%h2= "#{pluralize(@garden.errors.size, "error")} prohibited this garden from being saved:"
|
||||
%ul
|
||||
- @garden.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
||||
.form-group
|
||||
.form-group.required
|
||||
= f.label :name, :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_field :name, :class => 'form-control'
|
||||
= f.text_field :name, :class => 'form-control', :maxlength => 255, :required => "required"
|
||||
|
||||
.form-group
|
||||
= f.label :description, :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_area :description, :rows => 6, :class => 'form-control'
|
||||
= f.text_area :description, :rows => 6, :class => 'form-control', :placeholder => 'optional'
|
||||
|
||||
.form-group
|
||||
= f.label :location, :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_field :location, :value => @garden.location || current_member.location, :class => 'form-control'
|
||||
= f.text_field :location, :value => @garden.location || current_member.location, :class => 'form-control', :placeholder => 'optional', :maxlength => 255
|
||||
%span.help-block
|
||||
If you have a location set in your profile, it will be used when
|
||||
you create a new garden.
|
||||
@@ -31,7 +33,7 @@
|
||||
.form-group
|
||||
= f.label :area, :class => 'control-label col-md-2'
|
||||
.col-md-2
|
||||
= f.number_field :area, :class => 'input-small form-control'
|
||||
= f.number_field :area, :class => 'input-small form-control', :placeholder => 'optional'
|
||||
.col-md-2
|
||||
= f.select(:area_unit, Garden::AREA_UNITS_VALUES, {:include_blank => false}, :class => 'form-control')
|
||||
|
||||
|
||||
36
app/views/gardens/_thumbnail.html.haml
Normal file
36
app/views/gardens/_thumbnail.html.haml
Normal file
@@ -0,0 +1,36 @@
|
||||
.panel.panel-success
|
||||
.panel-heading
|
||||
%h3.panel-title
|
||||
= link_to "#{garden.owner.login_name}'s garden", garden.owner
|
||||
- if can? :edit, garden
|
||||
%a.pull-right{:href => edit_garden_path(garden), :role => "button", :id => "edit_garden_glyphicon"}
|
||||
%span.glyphicon.glyphicon-pencil{:title => "Edit"}
|
||||
.panel-body{:id => "gardens_panel_body"}
|
||||
.row
|
||||
.col-md-4
|
||||
= link_to image_tag((garden.default_photo ? garden.default_photo.thumbnail_url : 'placeholder_150.png'), :alt => garden.name, :class => 'img'), garden
|
||||
.col-md-8
|
||||
%dl.dl-horizontal
|
||||
%dt Name :
|
||||
%dd= link_to garden.name, garden
|
||||
%dt Location :
|
||||
%dd
|
||||
- if garden.location.blank?
|
||||
not specified
|
||||
- else
|
||||
= link_to garden.location, place_path(garden.location)
|
||||
%dt Area :
|
||||
%dd= garden.area.nil? ? "not specified" : pluralize(garden.area, garden.area_unit)
|
||||
%dt Active? :
|
||||
%dd= garden.active ? "Yes" : "No"
|
||||
.col-md-12
|
||||
%b
|
||||
= "#{pluralize(garden.plantings.size, "Planting")} : "
|
||||
= display_garden_plantings(garden.plantings.current)
|
||||
- if garden.plantings.size > 2
|
||||
%br
|
||||
= link_to "See more plantings >>", garden_path(garden)
|
||||
.panel-footer
|
||||
%dt Description
|
||||
%dd
|
||||
= display_garden_description(garden)
|
||||
@@ -14,56 +14,19 @@
|
||||
- else
|
||||
= render :partial => 'shared/signin_signup', :locals => { :to => 'add a new garden' }
|
||||
|
||||
- if @gardens.length > 0
|
||||
|
||||
%div.pagination
|
||||
= page_entries_info @gardens, :model => "gardens"
|
||||
= will_paginate @gardens
|
||||
|
||||
%table.table.table-striped
|
||||
%tr
|
||||
- unless @owner
|
||||
%th Owner
|
||||
%th Garden name
|
||||
%th Description
|
||||
%th Location
|
||||
%th Area
|
||||
%th Active?
|
||||
%th Plantings
|
||||
%th
|
||||
%div.pagination
|
||||
= page_entries_info @gardens, :model => "gardens"
|
||||
= will_paginate @gardens
|
||||
|
||||
.row
|
||||
- if @gardens.size > 0
|
||||
- @gardens.each do |garden|
|
||||
%tr
|
||||
- unless @owner
|
||||
%td= link_to garden.owner.login_name, garden.owner
|
||||
%td= link_to garden.name, garden
|
||||
%td= garden.description
|
||||
%td
|
||||
- if ! garden.location.blank?
|
||||
= link_to garden.location, place_path(garden.location)
|
||||
%td
|
||||
- if garden.area
|
||||
= pluralize(garden.area, garden.area_unit)
|
||||
%td= garden.active ? "Yes" : "No"
|
||||
%td
|
||||
- if garden.plantings.empty?
|
||||
None
|
||||
- else
|
||||
%ul
|
||||
- garden.plantings.each do |p|
|
||||
%li
|
||||
= p.quantity
|
||||
= link_to p.crop.name, p
|
||||
- if p.planted_at
|
||||
planted on
|
||||
= p.planted_at
|
||||
|
||||
%td= link_to 'Details', garden, :class => 'btn btn-default btn-xs'
|
||||
.col-md-6
|
||||
=render :partial => 'gardens/thumbnail', :locals => {:garden => garden}
|
||||
- else
|
||||
%p There are no gardens to display.
|
||||
|
||||
%div.pagination
|
||||
= page_entries_info @gardens, :model => "gardens"
|
||||
= will_paginate @gardens
|
||||
|
||||
- else
|
||||
%p There are no gardens to display.
|
||||
%div.pagination
|
||||
= page_entries_info @gardens, :model => "gardens"
|
||||
= will_paginate @gardens
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
.row
|
||||
.col-md-9
|
||||
|
||||
- if can? :edit, @garden or can? :delete, @garden
|
||||
%p.btn-group
|
||||
- if can? :edit, @garden
|
||||
@@ -28,22 +27,46 @@
|
||||
%div
|
||||
:growstuff_markdown
|
||||
#{strip_tags @garden.description}
|
||||
- unless @garden.description
|
||||
.row-fluid
|
||||
%p No description available yet.
|
||||
|
||||
%h3
|
||||
What's planted here?
|
||||
- if can? :edit, @garden
|
||||
%p
|
||||
Why not
|
||||
= link_to 'tell us more.', edit_garden_path(@garden)
|
||||
|
||||
- if @garden.plantings.current.count > 0
|
||||
- @garden.plantings.current.each do |p|
|
||||
= render :partial => "plantings/thumbnail", :locals => { :planting => p }
|
||||
- else
|
||||
%p
|
||||
Nothing is currently planted here.
|
||||
- if @garden.photos.size > 0 or (can? :edit, @garden and can? :create, Photo)
|
||||
.row-fluid
|
||||
%h3 Photos
|
||||
%p= pluralize(@garden.photos.length, "photo")
|
||||
.row-fluid
|
||||
%ul.thumbnails
|
||||
- @garden.photos.each do |p|
|
||||
.col-md-2.six-across
|
||||
= render :partial => 'photos/thumbnail', :locals => { :photo => p }
|
||||
.row-fluid
|
||||
- if can? :create, Photo and can? :edit, @garden
|
||||
%p
|
||||
= link_to "Add photo", new_photo_path(:type => "garden", :id => @garden.id), :class => 'btn btn-primary'
|
||||
|
||||
- if @garden.plantings.finished.count > 0
|
||||
.row-fluid
|
||||
%h3 What's planted here?
|
||||
- if @garden.plantings.current.size > 0
|
||||
- @garden.plantings.current.each.with_index do |planting_current, index_current|
|
||||
= render partial: "plantings/thumbnail", locals: {:planting => planting_current}
|
||||
- else
|
||||
%p
|
||||
Nothing is currently planted here.
|
||||
|
||||
.row-fluid
|
||||
%h3 Previously planted in this garden
|
||||
- @garden.plantings.finished.each do |p|
|
||||
= render :partial => "plantings/thumbnail", :locals => { :planting => p }
|
||||
|
||||
- if @garden.plantings.finished.size > 0
|
||||
- @garden.plantings.finished.each.with_index do |planting_finished|
|
||||
= render partial: "plantings/thumbnail", locals: {:planting => planting_finished}
|
||||
- else
|
||||
%p
|
||||
Nothing has been planted here.
|
||||
.col-md-3
|
||||
%h4 About this garden
|
||||
%p
|
||||
@@ -67,7 +90,7 @@
|
||||
- else
|
||||
= link_to "#{othergarden}", garden_path(othergarden)
|
||||
|
||||
- if @garden.owner.gardens.inactive.count > 0
|
||||
- if @garden.owner.gardens.inactive.size > 0
|
||||
%h4= "Inactive gardens"
|
||||
%ul
|
||||
- @garden.owner.gardens.inactive.each do |othergarden|
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
= required_field_help_text
|
||||
|
||||
= form_for(@harvest, :html => {:class => "form-horizontal", :role => :form}) do |f|
|
||||
- if @harvest.errors.any?
|
||||
#error_explanation
|
||||
%h2= "#{pluralize(@harvest.errors.count, "error")} prohibited this harvest from being saved:"
|
||||
%h2= "#{pluralize(@harvest.errors.size, "error")} prohibited this harvest from being saved:"
|
||||
%ul
|
||||
- @harvest.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
||||
.form-group
|
||||
.form-group.required
|
||||
= f.label :crop, 'What did you harvest?', :class => 'control-label col-md-2'
|
||||
.col-md-4
|
||||
= auto_suggest @harvest, :crop, :class => 'form-control col-md-2', :default => @crop
|
||||
.col-md-4
|
||||
= collection_select(:harvest, :plant_part_id, PlantPart.all, :id, :name, { :selected => @harvest.plant_part_id }, { :class => 'form-control' })
|
||||
= collection_select(:harvest, :plant_part_id, PlantPart.all, :id, :name, { :selected => @harvest.plant_part_id }, { :class => 'form-control', :prompt => 'e.g. fruit', :required => "required" })
|
||||
%span.help-block.col-md-8
|
||||
Can't find what you're looking for?
|
||||
= link_to "Request new crops.", Growstuff::Application.config.new_crops_request_link
|
||||
= link_to "Request new crops.", new_crop_path
|
||||
|
||||
.form-group
|
||||
= f.label :harvested_at, 'When?', :class => 'control-label col-md-2'
|
||||
= f.label :harvested_at, 'When?', :class => 'control-label col-md-2', :placeholder => 'optional'
|
||||
.col-md-2
|
||||
= f.text_field :harvested_at, :value => @harvest.harvested_at ? @harvest.harvested_at.to_s(:ymd) : '', :class => 'add-datepicker form-control'
|
||||
|
||||
@@ -27,20 +29,20 @@
|
||||
-# Some browsers (eg Firefox for Android) assume "number" means
|
||||
-# "integer" unless you specify step="any":
|
||||
-# http://blog.isotoma.com/2012/03/html5-input-typenumber-and-decimalsfloats-in-chrome/
|
||||
= f.number_field :quantity, :class => 'input-small', :step => 'any', :class => 'form-control'
|
||||
= f.number_field :quantity, :class => 'input-small', :step => 'any', :class => 'form-control', :placeholder => 'optional'
|
||||
.col-md-2
|
||||
= f.select(:unit, Harvest::UNITS_VALUES, {:include_blank => false}, :class => 'input-medium form-control')
|
||||
|
||||
.form-group
|
||||
= f.label :weight_quantity, 'Weighing (in total):', :class => 'control-label col-md-2'
|
||||
.col-md-2
|
||||
= f.number_field :weight_quantity, :class => 'input-small', :step => 'any', :class => 'form-control'
|
||||
= f.number_field :weight_quantity, :class => 'input-small', :step => 'any', :class => 'form-control', :placeholder => 'optional'
|
||||
.col-md-2
|
||||
= f.select(:weight_unit, Harvest::WEIGHT_UNITS_VALUES, {:include_blank => false}, :class => 'form-control')
|
||||
.form-group
|
||||
= f.label :description, 'Notes', :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_area :description, :rows => 6, :class => 'form-control'
|
||||
= f.text_area :description, :rows => 6, :class => 'form-control', :placeholder => 'optional'
|
||||
|
||||
.form-group
|
||||
.form-actions.col-md-offset-2.col-md-8
|
||||
|
||||
25
app/views/harvests/_thumbnail.html.haml
Normal file
25
app/views/harvests/_thumbnail.html.haml
Normal file
@@ -0,0 +1,25 @@
|
||||
.panel.panel-success
|
||||
.panel-heading
|
||||
%h3.panel-title
|
||||
= link_to "#{harvest.owner.login_name}'s harvest", harvest.owner
|
||||
- if can? :edit, harvest
|
||||
%a.pull-right{:href => edit_harvest_path(harvest), :role => "button", :id => "edit_harvest_glyphicon"}
|
||||
%span.glyphicon.glyphicon-pencil{:title => "Edit"}
|
||||
.panel-body
|
||||
.row
|
||||
.col-md-4
|
||||
= link_to image_tag((harvest.crop.default_photo ? harvest.crop.default_photo.thumbnail_url : 'placeholder_150.png'), :alt => harvest.crop.name, :class => 'img'), harvest.crop
|
||||
.col-md-8
|
||||
%dl.dl-horizontal
|
||||
%dt Crop :
|
||||
%dd= link_to harvest.crop.name, harvest.crop
|
||||
%dt Plant part :
|
||||
%dd= link_to harvest.plant_part, harvest.plant_part
|
||||
%dt Quantity :
|
||||
%dd= display_quantity(harvest)
|
||||
%dt Harvest date :
|
||||
%dd= harvest.harvested_at
|
||||
.panel-footer
|
||||
%dt Description
|
||||
%dd
|
||||
= display_harvest_description(harvest)
|
||||
@@ -10,6 +10,7 @@ csv.headers :id,
|
||||
:unit,
|
||||
:weight_quantity,
|
||||
:weight_unit,
|
||||
:si_weight,
|
||||
:date_harvested,
|
||||
:description,
|
||||
:date_added,
|
||||
@@ -31,7 +32,7 @@ csv.headers :id,
|
||||
csv.cell :plant_part_id, h.plant_part ? h.plant_part.id : ''
|
||||
csv.cell :plant_part_name, h.plant_part ? h.plant_part.to_s : ''
|
||||
|
||||
csv.cells :quantity, :unit, :weight_quantity, :weight_unit
|
||||
csv.cells :quantity, :unit, :weight_quantity, :weight_unit, :si_weight
|
||||
|
||||
csv.cell :date_harvested, h.created_at.to_s(:db)
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
- content_for :title, @owner ? "#{@owner}'s harvests" : @crop ? "Everyone's #{@crop.name} harvests" : "Everyone's harvests"
|
||||
- content_for :title, @owner ? t('.title.owner_harvests', owner: @owner) : @crop ? t('.title.crop_harvests', crop: @crop.name) : t('.title.default')
|
||||
- if @owner
|
||||
= link_to "View #{@owner}'s profile >>", member_path(@owner)
|
||||
|
||||
%p
|
||||
#{ENV['GROWSTUFF_SITE_NAME']} helps you track what you're
|
||||
@@ -21,36 +23,15 @@
|
||||
%div.pagination
|
||||
= page_entries_info @harvests, :model => "harvests"
|
||||
= will_paginate @harvests
|
||||
|
||||
- if @harvests.length > 0
|
||||
|
||||
%table.table.table-striped
|
||||
%tr
|
||||
- unless @owner
|
||||
%th Owner
|
||||
%th Crop
|
||||
%th Plant part
|
||||
%th Date
|
||||
%th Quantity
|
||||
%th Description
|
||||
%th
|
||||
|
||||
.row
|
||||
- if @harvests.size > 0
|
||||
- @harvests.each do |harvest|
|
||||
%tr
|
||||
- unless @owner
|
||||
%td= link_to harvest.owner.login_name, harvest.owner
|
||||
%td= link_to harvest.crop.name, harvest.crop
|
||||
%td
|
||||
- if harvest.plant_part
|
||||
= link_to harvest.plant_part.name, harvest.plant_part
|
||||
%td= harvest.harvested_at
|
||||
%td= display_quantity(harvest)
|
||||
%td= harvest.description
|
||||
%td= link_to 'Details', harvest, :class => 'btn btn-default btn-xs'
|
||||
.col-md-6
|
||||
=render :partial => 'harvests/thumbnail', :locals => {:harvest => harvest}
|
||||
|
||||
%div.pagination
|
||||
= page_entries_info @harvests, :model => "harvests"
|
||||
= will_paginate @harvests
|
||||
%div.pagination
|
||||
= page_entries_info @harvests, :model => "harvests"
|
||||
= will_paginate @harvests
|
||||
|
||||
%ul.list-inline
|
||||
%li The data on this page is available in the following formats:
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
:growstuff_markdown
|
||||
#{ @harvest.description != "" ? @harvest.description : "No description given." }
|
||||
|
||||
- if @harvest.photos.count > 0 or (can? :edit, @harvest and can? :create, Photo)
|
||||
- if @harvest.photos.size > 0 or (can? :edit, @harvest and can? :create, Photo)
|
||||
%h2 Pictures
|
||||
|
||||
%ul.thumbnails
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
.hidden-xs
|
||||
- members = Member.interesting.first(6)
|
||||
- if members.present?
|
||||
%section
|
||||
%h2= t('.title')
|
||||
|
||||
.row
|
||||
.member-cards
|
||||
- members.each do |m|
|
||||
.col-md-4.homepage-members
|
||||
= render :partial => "members/thumbnail", :locals => { :member => m }
|
||||
= render :partial => "members/thumbnail", :locals => { :member => m }
|
||||
|
||||
%p.text-right
|
||||
= link_to "#{t('.view_all')} »", members_path
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
%h2= t('.title')
|
||||
|
||||
- cache cache_key_for(Seed) do
|
||||
- if seeds.length > 0
|
||||
- if seeds.size > 0
|
||||
|
||||
%table.table.table-striped
|
||||
%tr
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
- cache("homepage_stats") do
|
||||
%p.stats
|
||||
= t('.message_html', { member: link_to(t('.member_linktext', count: Member.confirmed.count.to_i), members_path),
|
||||
number_crops: link_to(t('.number_crops_linktext', count: Crop.count.to_i), crops_path),
|
||||
= t('.message_html', { member: link_to(t('.member_linktext', count: Member.confirmed.size.to_i), members_path),
|
||||
number_crops: link_to(t('.number_crops_linktext', count: Crop.count.to_i), crops_path),
|
||||
number_plantings: link_to(t('.number_plantings_linktext', count: Planting.count.to_i), plantings_path),
|
||||
number_gardens: link_to(t('.number_gardens_linktext', count: Garden.count.to_i), gardens_path) })
|
||||
|
||||
|
||||
@@ -1,29 +1,12 @@
|
||||
.navbar.navbar-default.navbar-bottom
|
||||
.container
|
||||
.row
|
||||
.col-md-4#about-growstuff
|
||||
%ul
|
||||
%li= link_to t('about'), "http://wiki.growstuff.org/index.php/About%20Growstuff"
|
||||
%li= link_to t('our_values'), "http://wiki.growstuff.org/index.php/Values"
|
||||
%li= link_to t('open_source'), "https://github.com/Growstuff/growstuff"
|
||||
%li= link_to t('growstuff_team'), "http://wiki.growstuff.org/index.php/Team"
|
||||
%li= link_to t('get_involved'), "http://wiki.growstuff.org/index.php/Get_involved"
|
||||
.col-md-4#policies
|
||||
%ul
|
||||
%li= link_to t('terms_of_service'), url_for(:controller => '/policy', :action => 'tos')
|
||||
%li= link_to t('privacy_policy'), url_for(:controller => '/policy', :action => 'privacy')
|
||||
%li= link_to t('data_use_policy'), url_for(:controller => '/policy', :action => 'api')
|
||||
%li= link_to t('community_guidelines'), url_for(:controller => '/policy', :action => 'community')
|
||||
.col-md-4#contact
|
||||
%ul
|
||||
%li= link_to t('support_'), url_for(:controller => '/support')
|
||||
%li= link_to t('contact'), url_for(:controller => '/about', :action => 'contact')
|
||||
%p
|
||||
= link_to('http://twitter.com/growstufforg', :target => "_blank") do
|
||||
= image_tag("twitter_32.png", :alt => 'Twitter: @growstufforg')
|
||||
|
||||
= link_to('https://www.facebook.com/Growstufforg', :target => "_blank") do
|
||||
= image_tag("facebook_32.png", :alt => 'Facebook')
|
||||
|
||||
= link_to('http://blog.growstuff.org/', :target => "_blank") do
|
||||
= image_tag("blog_32.png", :alt => 'Growstuff Blog')
|
||||
.col-md-4#footer1
|
||||
!= cms_snippet_content(:footer1)
|
||||
.col-md-4#footer2
|
||||
!= cms_snippet_content(:footer2)
|
||||
.col-md-4#footer3
|
||||
!= cms_snippet_content(:footer3)
|
||||
%div(style="float: right;")
|
||||
%a(href="http://opendefinition.org/ossd/")
|
||||
%img(src="http://assets.okfn.org/images/ok_buttons/os_80x15_blue.png" alt="")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.sr-only
|
||||
=link_to "Skip navigation menu", "#skipnav"
|
||||
=link_to t(".skip"), "#skipnav"
|
||||
.navbar.navbar-default.navbar-fixed-top(role="navigation")
|
||||
.container
|
||||
.navbar-header
|
||||
@@ -10,67 +10,69 @@
|
||||
%span.icon-bar
|
||||
%a.navbar-brand(href=root_path)
|
||||
= image_tag("growstuff-brand.png", :size => "200x50", :alt => ENV['GROWSTUFF_SITE_NAME'])
|
||||
= form_tag crops_search_path, :method => :get, :id => 'navbar-search', :class => 'navbar-form pull-right' do
|
||||
.input
|
||||
= label_tag :term, "Search crop database:", :class => 'sr-only'
|
||||
= text_field_tag 'term', nil, :class => 'search-query input-medium form-control', :placeholder => 'Search crops'
|
||||
= submit_tag "Search", :class => 'btn sr-only'
|
||||
|
||||
.navbar-collapse.collapse#navbar-collapse
|
||||
%ul.nav.navbar-nav
|
||||
%ul.nav.navbar-nav.pull-right
|
||||
%li.dropdown<
|
||||
%a.dropdown-toggle{'data-toggle' => 'dropdown', :href => crops_path}
|
||||
Crops
|
||||
%b.caret
|
||||
%ul.dropdown-menu
|
||||
%li= link_to "Browse Crops", crops_path
|
||||
%li= link_to "Seeds", seeds_path
|
||||
%li= link_to "Plantings", plantings_path
|
||||
%li= link_to "Harvests", harvests_path
|
||||
%li= link_to t('.browse_crops'), crops_path
|
||||
%li= link_to t('.seeds'), seeds_path
|
||||
%li= link_to t('.plantings'), plantings_path
|
||||
%li= link_to t('.harvests'), harvests_path
|
||||
%li.dropdown<
|
||||
%a.dropdown-toggle{'data-toggle' => 'dropdown', :href => members_path}
|
||||
Community
|
||||
%b.caret
|
||||
%ul.dropdown-menu
|
||||
%li= link_to "Community Map", places_path
|
||||
%li= link_to "Browse Members", members_path
|
||||
%li= link_to "Posts", posts_path
|
||||
%li= link_to "Forums", forums_path
|
||||
|
||||
%li= link_to t('.community_map'), places_path
|
||||
%li= link_to t('.browse_members'), members_path
|
||||
%li= link_to t('.posts'), posts_path
|
||||
%li= link_to t('.forums'), forums_path
|
||||
%li= link_to t('.support_growstuff'), shop_path
|
||||
|
||||
- if member_signed_in?
|
||||
%li.dropdown<
|
||||
%a.dropdown-toggle{'data-toggle' => 'dropdown', :href => root_path}
|
||||
- if current_member.notifications.unread_count > 0
|
||||
Your Stuff (#{current_member.notifications.unread_count})
|
||||
= t('.your_stuff', unread_count: current_member.notifications.unread_count)
|
||||
- else
|
||||
Your Stuff
|
||||
#{current_member.login_name}
|
||||
%b.caret
|
||||
%ul.dropdown-menu
|
||||
%li= link_to "Profile", member_path(current_member)
|
||||
%li= link_to "Gardens", gardens_by_owner_path(:owner => current_member.slug)
|
||||
%li= link_to "Plantings", plantings_by_owner_path(:owner => current_member.slug)
|
||||
%li= link_to "Harvests", harvests_by_owner_path(:owner => current_member.slug)
|
||||
%li= link_to "Seeds", seeds_by_owner_path(:owner => current_member.slug)
|
||||
%li= link_to "Posts", posts_by_author_path(:author => current_member.slug)
|
||||
%li= link_to "Account", orders_path
|
||||
%li= link_to t('.profile'), member_path(current_member)
|
||||
%li= link_to t('.gardens'), gardens_by_owner_path(:owner => current_member.slug)
|
||||
%li= link_to t('.plantings'), plantings_by_owner_path(:owner => current_member.slug)
|
||||
%li= link_to t('.harvest'), harvests_by_owner_path(:owner => current_member.slug)
|
||||
%li= link_to t('.seeds'), seeds_by_owner_path(:owner => current_member.slug)
|
||||
%li= link_to t('.posts'), posts_by_author_path(:author => current_member.slug)
|
||||
%li= link_to t('.account'), orders_path
|
||||
%li
|
||||
- if current_member.notifications.unread_count > 0
|
||||
= link_to("Inbox (#{current_member.notifications.unread_count})", notifications_path)
|
||||
= link_to(t('.inbox_unread', unread_count: current_member.notifications.unread_count), notifications_path)
|
||||
- else
|
||||
= link_to("Inbox", notifications_path)
|
||||
= link_to(t('.inbox'), notifications_path)
|
||||
- if current_member.has_role?(:crop_wrangler) || current_member.has_role?(:admin)
|
||||
%li{:class => 'divider', :role => 'presentation'}
|
||||
- if current_member.has_role?(:crop_wrangler)
|
||||
%li= link_to "Crop Wrangling", wrangle_crops_path
|
||||
%li= link_to t('.crop_wrangling'), wrangle_crops_path
|
||||
- if current_member.has_role?(:admin)
|
||||
%li= link_to "Admin", admin_path
|
||||
%li= link_to "Support Growstuff", shop_path
|
||||
%li= link_to t('.admin'), admin_path
|
||||
|
||||
|
||||
%li= link_to "Sign out", destroy_member_session_path, :method => :delete
|
||||
|
||||
- else
|
||||
%li= link_to 'Sign in', new_member_session_path
|
||||
%li= link_to 'Sign up', new_member_registration_path
|
||||
%li= link_to 'Sign in', new_member_session_path, :id => 'navbar-signin'
|
||||
%li= link_to 'Sign up', new_member_registration_path, :id => 'navbar-signup'
|
||||
|
||||
= form_tag crops_search_path, :method => :get, :class => 'navbar-form pull-right' do
|
||||
.input
|
||||
= text_field_tag 'search', nil, :class => 'search-query input-medium form-control', :placeholder => 'Search crops'
|
||||
|
||||
- # anchor tag for accessibility link to skip the navigation menu
|
||||
%a{:name => 'skipnav'}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
%head
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta property="og:image" content="#{image_url 'growstuff-apple-touch-icon-precomposed.png'}"/>
|
||||
<meta property="og:title" content="#{content_for?(:title) ? yield(:title) + " - #{ ENV['GROWSTUFF_SITE_NAME']} " : ENV['GROWSTUFF_SITE_NAME']}" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="#{root_url}" />
|
||||
<meta property="og:site_name" content="#{ENV['GROWSTUFF_SITE_NAME']}" />
|
||||
|
||||
- if (content_for?(:member_rss_login_name) && content_for(:member_rss_slug))
|
||||
= auto_discovery_link_tag(:rss, { :controller => "/members", :action => 'show', :format => "rss", :id => yield(:member_rss_slug) }, { :title => "#{ ENV['GROWSTUFF_SITE_NAME'] }- #{yield(:member_rss_login_name)}'s posts" })
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
!!! 5
|
||||
%html(lang="en")
|
||||
%html(lang="en" prefix="og: http://ogp.me/ns#")
|
||||
= render :partial => "layouts/meta"
|
||||
%body
|
||||
= render :partial => "layouts/header"
|
||||
= render :partial => "layouts/crowdfunding"
|
||||
|
||||
.container
|
||||
#maincontainer.container
|
||||
.row
|
||||
.col-md-12
|
||||
- if content_for?(:title)
|
||||
|
||||
@@ -9,3 +9,16 @@
|
||||
= member.account_type
|
||||
account
|
||||
|
||||
%p
|
||||
%strong Last Login:
|
||||
= member.last_sign_in_at
|
||||
|
||||
%p
|
||||
%strong Member Roles:
|
||||
%br
|
||||
- if member.has_role? :admin
|
||||
Administrator
|
||||
- if member.has_role? :crop_wrangler
|
||||
Crop Wrangler
|
||||
- else
|
||||
Member
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user