mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-05-25 09:19:15 -04:00
Compare commits
665 Commits
2014-01-02
...
api_v1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d066801943 | ||
|
|
016feaf8bf | ||
|
|
1b5d2bb898 | ||
|
|
a3e3abecf3 | ||
|
|
9d49a83918 | ||
|
|
901fed7d35 | ||
|
|
6deb0c659a | ||
|
|
0afd9dfbe2 | ||
|
|
c446ea9232 | ||
|
|
43c241219d | ||
|
|
77e424c412 | ||
|
|
82b70cf893 | ||
|
|
6b0a298fd1 | ||
|
|
78c6220262 | ||
|
|
6f2fa1b72b | ||
|
|
b520f9e78b | ||
|
|
b7d9924fa7 | ||
|
|
ea41021ae8 | ||
|
|
7f88157696 | ||
|
|
a69210ae6b | ||
|
|
de26dd3708 | ||
|
|
2fc6bfb77f | ||
|
|
837121265a | ||
|
|
5e2a3db8c2 | ||
|
|
969c530193 | ||
|
|
e9e238016b | ||
|
|
899d3c3dbe | ||
|
|
7227c1f330 | ||
|
|
609f3a46b2 | ||
|
|
58728f6a61 | ||
|
|
94df504840 | ||
|
|
c1bcbd2e7d | ||
|
|
e5d71df294 | ||
|
|
a47328eed4 | ||
|
|
0c30f2198c | ||
|
|
9f54641e95 | ||
|
|
4b90f6385b | ||
|
|
751a66b49b | ||
|
|
6f3e430840 | ||
|
|
d0b61c259d | ||
|
|
30c91eb23f | ||
|
|
82a5379a25 | ||
|
|
593a20c0c5 | ||
|
|
e7bb285b8e | ||
|
|
b06d48091e | ||
|
|
844791ead9 | ||
|
|
73d6b5065b | ||
|
|
7ddaca6e11 | ||
|
|
2036a0f2e9 | ||
|
|
27ad791f67 | ||
|
|
13c98eac89 | ||
|
|
6d2d6a51d5 | ||
|
|
4e19da56a0 | ||
|
|
969f7e7c23 | ||
|
|
14e8c367e1 | ||
|
|
924efe6af5 | ||
|
|
23f524a236 | ||
|
|
39b47b0d08 | ||
|
|
81d1189203 | ||
|
|
2111cddaef | ||
|
|
eb4fae463f | ||
|
|
8c55a6101d | ||
|
|
2ca42bbae3 | ||
|
|
5de6759917 | ||
|
|
aeb7192f48 | ||
|
|
0ba6562d79 | ||
|
|
996043d8fb | ||
|
|
0eb5ae58aa | ||
|
|
6aea16028e | ||
|
|
5f2ca6bc9d | ||
|
|
9861f8cf51 | ||
|
|
284c28ce9a | ||
|
|
c674f30eb8 | ||
|
|
02994bd5cc | ||
|
|
53d782bc83 | ||
|
|
93e468876d | ||
|
|
52cfa9f676 | ||
|
|
65706f560b | ||
|
|
7d27251be3 | ||
|
|
bd8be01ec7 | ||
|
|
3dbb585467 | ||
|
|
bfce66a8f7 | ||
|
|
f56c184033 | ||
|
|
68dfdbeba1 | ||
|
|
0ffa3a6d0f | ||
|
|
8d74fd52db | ||
|
|
77f40a3dc2 | ||
|
|
43fa356670 | ||
|
|
03fea2a785 | ||
|
|
e687bd43dc | ||
|
|
b5e1b6bc22 | ||
|
|
1749bc52f8 | ||
|
|
b6d2a078b1 | ||
|
|
7023b4969b | ||
|
|
f233d502f7 | ||
|
|
3c3eb1fd78 | ||
|
|
cacc002a01 | ||
|
|
a9fdab5d7c | ||
|
|
caad748d08 | ||
|
|
5b13e8c797 | ||
|
|
267c671259 | ||
|
|
f8df8803ab | ||
|
|
1d071c197a | ||
|
|
50b8acc67c | ||
|
|
3f23dd194d | ||
|
|
98c07ff3f1 | ||
|
|
eb98910bea | ||
|
|
954d5b8427 | ||
|
|
2426c51951 | ||
|
|
af9aa690ba | ||
|
|
24d276da37 | ||
|
|
223a06ef8b | ||
|
|
7b80db9596 | ||
|
|
f3eb5774ef | ||
|
|
800a1f10e3 | ||
|
|
b989168887 | ||
|
|
6921119301 | ||
|
|
0ccd6e843a | ||
|
|
9b10a7c3ce | ||
|
|
36e827b445 | ||
|
|
f62ce257da | ||
|
|
0d893470de | ||
|
|
e9d1d11535 | ||
|
|
c244da8b73 | ||
|
|
48875dacb3 | ||
|
|
a6508b5c9f | ||
|
|
0e11f20910 | ||
|
|
ab449a65f8 | ||
|
|
f795918e17 | ||
|
|
badb0c4fe7 | ||
|
|
7c3059fcae | ||
|
|
06a4fcab71 | ||
|
|
36be5e9533 | ||
|
|
4a8297360c | ||
|
|
0aea1721d8 | ||
|
|
3d10553373 | ||
|
|
af87c9db47 | ||
|
|
e8daba750d | ||
|
|
a79a8dc927 | ||
|
|
a25eb2d631 | ||
|
|
58f1bca683 | ||
|
|
b17aaf262d | ||
|
|
56b7d89d9e | ||
|
|
6de2112c4d | ||
|
|
2aabcce70d | ||
|
|
7595d75d20 | ||
|
|
de6d3589d1 | ||
|
|
9e9aa82099 | ||
|
|
ef8d552301 | ||
|
|
e5cd14cd45 | ||
|
|
1daee9612c | ||
|
|
dc1afb8913 | ||
|
|
496c070efd | ||
|
|
f468a8b77b | ||
|
|
16677a3b86 | ||
|
|
41566d39f3 | ||
|
|
e613907506 | ||
|
|
82a61387a3 | ||
|
|
29d53a8f8b | ||
|
|
d0f7169c59 | ||
|
|
3a797d4434 | ||
|
|
1d3c24ae27 | ||
|
|
006bc54f3f | ||
|
|
792062e0d2 | ||
|
|
52fd2b86d6 | ||
|
|
110ae99d83 | ||
|
|
c447c1cb3a | ||
|
|
0ae68737c3 | ||
|
|
96d2fa1cb6 | ||
|
|
8a97aa1a9b | ||
|
|
dc2cf5275c | ||
|
|
c7c85aaa66 | ||
|
|
edf2f36bcd | ||
|
|
b009603581 | ||
|
|
eacfadae20 | ||
|
|
37eb2a0e34 | ||
|
|
10f6214c6c | ||
|
|
29a5fe07c2 | ||
|
|
77d1d067fa | ||
|
|
cdf6d4b09a | ||
|
|
7e4b51d1cb | ||
|
|
864ce95f11 | ||
|
|
ae27a0a5b5 | ||
|
|
627587ee1b | ||
|
|
9ededef54d | ||
|
|
83fa291060 | ||
|
|
e85cb4598d | ||
|
|
f18feaa131 | ||
|
|
41d63f8f92 | ||
|
|
7c783ecda3 | ||
|
|
26e592db88 | ||
|
|
2515d34277 | ||
|
|
8994bcf28c | ||
|
|
d627d8394e | ||
|
|
a4e7f93421 | ||
|
|
0f75a96944 | ||
|
|
6a7c935d43 | ||
|
|
a3e02a3e61 | ||
|
|
8e6a57c442 | ||
|
|
e4cf636bef | ||
|
|
75e076cef5 | ||
|
|
1d81064ced | ||
|
|
25cb3fd469 | ||
|
|
694bc56f68 | ||
|
|
6bcb9dae4a | ||
|
|
3a46a5bd18 | ||
|
|
c405639f22 | ||
|
|
aaf08469e6 | ||
|
|
d1e0bdb534 | ||
|
|
ff61739716 | ||
|
|
cf07ecfa4a | ||
|
|
6e02f40e0a | ||
|
|
14d039ee01 | ||
|
|
14cf82f1d2 | ||
|
|
a2f2508f0d | ||
|
|
494a600766 | ||
|
|
45d34f7f79 | ||
|
|
9b43bdd29b | ||
|
|
e002cb2d35 | ||
|
|
a5b856b506 | ||
|
|
48ad561b76 | ||
|
|
fcda8742d8 | ||
|
|
57abfa04b3 | ||
|
|
557d07d2f1 | ||
|
|
51d8b2d475 | ||
|
|
a29d11a07c | ||
|
|
0ad217c9a2 | ||
|
|
271d178cc8 | ||
|
|
9a9eeecb46 | ||
|
|
41a4de7e74 | ||
|
|
8497782847 | ||
|
|
97699e5e6a | ||
|
|
98da785d8a | ||
|
|
e2571e9261 | ||
|
|
65c46c334b | ||
|
|
dbfa080609 | ||
|
|
aebd9e1d4e | ||
|
|
87b6780220 | ||
|
|
a7da4b9ce4 | ||
|
|
f9dc17c587 | ||
|
|
d10a62d336 | ||
|
|
a34a54d995 | ||
|
|
baeb5d3525 | ||
|
|
05400a081f | ||
|
|
d8b75eaa66 | ||
|
|
4a62add88e | ||
|
|
529c98f5dc | ||
|
|
e011b54cba | ||
|
|
1b1f1d33e0 | ||
|
|
25abcdc923 | ||
|
|
126cb1d191 | ||
|
|
8ab4f885f1 | ||
|
|
984503480f | ||
|
|
5e48c4392a | ||
|
|
d462254021 | ||
|
|
1acba65a8b | ||
|
|
8f14ffe560 | ||
|
|
93cd067fff | ||
|
|
e3a6742574 | ||
|
|
659a68cf39 | ||
|
|
e3f590ede5 | ||
|
|
6efeb04449 | ||
|
|
52b4e27d8c | ||
|
|
60354264d6 | ||
|
|
87643c3d1a | ||
|
|
aa028db2dc | ||
|
|
983fa8ede0 | ||
|
|
16f4d2f80e | ||
|
|
dd75f2aef9 | ||
|
|
f8ee9d0589 | ||
|
|
87df8661c7 | ||
|
|
f355c393f0 | ||
|
|
eeb2bf9938 | ||
|
|
3cf2a50a34 | ||
|
|
09bef3f9d8 | ||
|
|
2bbccfc44d | ||
|
|
00f58511ac | ||
|
|
374c17c928 | ||
|
|
46eaf37437 | ||
|
|
9bd4160102 | ||
|
|
29d43f4e5b | ||
|
|
aefbce3b1f | ||
|
|
374987488f | ||
|
|
fa9c029493 | ||
|
|
85b5ac12ca | ||
|
|
03235f3952 | ||
|
|
550f3c5326 | ||
|
|
ed537e583b | ||
|
|
2b7b5bad63 | ||
|
|
512ccdbaa2 | ||
|
|
7d54ab347f | ||
|
|
51d9bfe97e | ||
|
|
9d5c15a7da | ||
|
|
e0485ade1d | ||
|
|
fb91c78933 | ||
|
|
138904bd82 | ||
|
|
00b24890f4 | ||
|
|
0c944357c0 | ||
|
|
885ffab012 | ||
|
|
b0ee83bc3e | ||
|
|
4caf7b9b28 | ||
|
|
1c33ab280f | ||
|
|
56eaea9c06 | ||
|
|
3c968ea6be | ||
|
|
a3213a99e8 | ||
|
|
495f4194e7 | ||
|
|
7543221f73 | ||
|
|
7184b04397 | ||
|
|
0e0a13bbd1 | ||
|
|
4f4aad7626 | ||
|
|
baf8b91a4e | ||
|
|
579f79760b | ||
|
|
cdbc4611c9 | ||
|
|
823dfe8000 | ||
|
|
ea9597ba75 | ||
|
|
591b8e03d6 | ||
|
|
35470acb86 | ||
|
|
326248797e | ||
|
|
6704b97542 | ||
|
|
982572d2bd | ||
|
|
1fc100eca4 | ||
|
|
1224bd0ebb | ||
|
|
c95619117b | ||
|
|
919cc45d13 | ||
|
|
0687625049 | ||
|
|
551e4cbe6f | ||
|
|
7518fbec1c | ||
|
|
703de07647 | ||
|
|
c9cd82d7fd | ||
|
|
0af550b6e7 | ||
|
|
c4bd4c178d | ||
|
|
b242e0fb99 | ||
|
|
a8a34d8dc8 | ||
|
|
965a3bf2a1 | ||
|
|
07fcdf9bb7 | ||
|
|
474b56cf6b | ||
|
|
99ffa3775e | ||
|
|
ecbeb986c3 | ||
|
|
e7d9ebe37a | ||
|
|
db7729920b | ||
|
|
2be199abbc | ||
|
|
597e878413 | ||
|
|
29f83b8c18 | ||
|
|
b0378fab69 | ||
|
|
d35639d7cb | ||
|
|
d991765edb | ||
|
|
d55d2c6409 | ||
|
|
082663c885 | ||
|
|
0c89413f92 | ||
|
|
1cd2fd9b07 | ||
|
|
3fbae6a5ee | ||
|
|
fbeafd4299 | ||
|
|
4ebc184188 | ||
|
|
46e20f0562 | ||
|
|
af2d0e22ee | ||
|
|
170d6b474e | ||
|
|
480e67bf2f | ||
|
|
facba15154 | ||
|
|
9f9ef10b6a | ||
|
|
e7dd50c3f1 | ||
|
|
b07d9d8db0 | ||
|
|
40c7124769 | ||
|
|
19a0a914c5 | ||
|
|
8a5d1f2e5e | ||
|
|
30d436cb5d | ||
|
|
e3627f6dcd | ||
|
|
3beae6f500 | ||
|
|
83d3c369c2 | ||
|
|
7fa05d81e9 | ||
|
|
d8c7aba725 | ||
|
|
6bb74039a7 | ||
|
|
2c50c3b703 | ||
|
|
550cb4b265 | ||
|
|
62e1c3bb93 | ||
|
|
cd6fc7e804 | ||
|
|
4e0d62b28d | ||
|
|
028e1194e5 | ||
|
|
55c2fceffb | ||
|
|
698a13ef70 | ||
|
|
848b583d94 | ||
|
|
b260169d2d | ||
|
|
d6e957e2fb | ||
|
|
c68b029d50 | ||
|
|
a3220653cf | ||
|
|
840a5efc4e | ||
|
|
5add85cd7c | ||
|
|
ea31d89bd0 | ||
|
|
e5b15fae1a | ||
|
|
501fda3fc9 | ||
|
|
f6c2873f97 | ||
|
|
1103e2719f | ||
|
|
a5c2c0bb5c | ||
|
|
03ecb7e238 | ||
|
|
2a51f66d88 | ||
|
|
fd651de7ea | ||
|
|
d038ac6d8a | ||
|
|
b91bab117f | ||
|
|
1250206c5e | ||
|
|
9c5f7753ca | ||
|
|
36199054c0 | ||
|
|
13e015b441 | ||
|
|
38b9207122 | ||
|
|
ec25f2ab91 | ||
|
|
793eecfcb7 | ||
|
|
ca41f86d93 | ||
|
|
25d19a25d3 | ||
|
|
8669907319 | ||
|
|
f02f2ae77e | ||
|
|
561b7c45ac | ||
|
|
a684efcf2a | ||
|
|
0969419c84 | ||
|
|
eaaceb5951 | ||
|
|
f3c3d73ef1 | ||
|
|
e0d81f5693 | ||
|
|
7aef1264ce | ||
|
|
957fa3417d | ||
|
|
c282b3b208 | ||
|
|
69162cf228 | ||
|
|
1bed42876c | ||
|
|
8352807d5a | ||
|
|
63673112a7 | ||
|
|
a49880d4da | ||
|
|
b4fdf97056 | ||
|
|
59608c5851 | ||
|
|
e865fc1c82 | ||
|
|
d17ab658c8 | ||
|
|
0a256a2dd0 | ||
|
|
f280ff968b | ||
|
|
1716de2dfc | ||
|
|
094d9c509a | ||
|
|
012f37f366 | ||
|
|
1054f08206 | ||
|
|
a707d9a2ea | ||
|
|
9d6e787883 | ||
|
|
b28ce28703 | ||
|
|
ec060b9cc0 | ||
|
|
948aeb1289 | ||
|
|
1ee79c25f1 | ||
|
|
29752e049e | ||
|
|
8a1dd9d171 | ||
|
|
5df0c9c71a | ||
|
|
b940644ef3 | ||
|
|
bb0f88bc0b | ||
|
|
d03bb5e849 | ||
|
|
250a2b7d02 | ||
|
|
c0ea08ddc1 | ||
|
|
4e9679b22a | ||
|
|
117fd23bea | ||
|
|
0294daf423 | ||
|
|
6e48b7d224 | ||
|
|
d558353e4c | ||
|
|
817663bdaf | ||
|
|
bac836329c | ||
|
|
846a2c89c5 | ||
|
|
f67732e125 | ||
|
|
7c09188bb8 | ||
|
|
f4c2c2481d | ||
|
|
a7d56d4f92 | ||
|
|
a0fc8802b2 | ||
|
|
aeb14021f3 | ||
|
|
8223025d35 | ||
|
|
573e555bcf | ||
|
|
c9857bf1c8 | ||
|
|
6c048dec77 | ||
|
|
7184a0d5d5 | ||
|
|
e63b2fb472 | ||
|
|
e412184737 | ||
|
|
6cbe194615 | ||
|
|
48bc019419 | ||
|
|
aeba4acf0d | ||
|
|
90c43336ca | ||
|
|
82598e75f8 | ||
|
|
7ef6942694 | ||
|
|
863f1dfae7 | ||
|
|
3509b6e403 | ||
|
|
8da3430852 | ||
|
|
98cbac8a70 | ||
|
|
81a996b8cc | ||
|
|
8004632b41 | ||
|
|
48d3488b2d | ||
|
|
e93d4df3c0 | ||
|
|
6503b2ec7c | ||
|
|
d910b5629a | ||
|
|
2fbd596811 | ||
|
|
005a31c880 | ||
|
|
0fd10643ce | ||
|
|
4be08e261a | ||
|
|
10af60f30d | ||
|
|
191363402a | ||
|
|
0714c2a38c | ||
|
|
bf1821f8ce | ||
|
|
c2435cfb8a | ||
|
|
db9ef0bc83 | ||
|
|
b374beae35 | ||
|
|
fdd3951ed7 | ||
|
|
84f6635679 | ||
|
|
27062d2537 | ||
|
|
0263e361c5 | ||
|
|
17c822f6f3 | ||
|
|
4728de5dd8 | ||
|
|
4436562dec | ||
|
|
155d68bab1 | ||
|
|
b7fbe60b83 | ||
|
|
76f2d003c5 | ||
|
|
0d7fbc9b76 | ||
|
|
6ec7b8f897 | ||
|
|
bf217b1618 | ||
|
|
bb458c17e8 | ||
|
|
6cb89d1c75 | ||
|
|
e9b3c33022 | ||
|
|
d7145b327f | ||
|
|
f0cd8df6e0 | ||
|
|
8d0d01991b | ||
|
|
df9169c13d | ||
|
|
088cd36d6f | ||
|
|
e226ce406c | ||
|
|
bc38359219 | ||
|
|
471ecbc458 | ||
|
|
9a09d8a0ae | ||
|
|
ded464ad4c | ||
|
|
8b45a1759c | ||
|
|
21d5156137 | ||
|
|
8ee1499a07 | ||
|
|
0558d854af | ||
|
|
c89e1ec096 | ||
|
|
95130bda67 | ||
|
|
e560258b39 | ||
|
|
60b407986d | ||
|
|
cf41a9b866 | ||
|
|
c2f7cf08ca | ||
|
|
7d624fd9ca | ||
|
|
0718b5818a | ||
|
|
5744499182 | ||
|
|
0174202ef0 | ||
|
|
77d2b0e950 | ||
|
|
8897e5149d | ||
|
|
dd381378f1 | ||
|
|
34c45c8935 | ||
|
|
99c473ab40 | ||
|
|
0dac36df74 | ||
|
|
828e9d318a | ||
|
|
890a21ddb2 | ||
|
|
c8dbdbe298 | ||
|
|
9f1a7aad63 | ||
|
|
f28ddaded1 | ||
|
|
ec9bd2bd7b | ||
|
|
67dae650bc | ||
|
|
a5e4b0816c | ||
|
|
adb275737f | ||
|
|
c489c6ac70 | ||
|
|
fea7da3546 | ||
|
|
f8cb7248b7 | ||
|
|
4972127957 | ||
|
|
3abaca3136 | ||
|
|
8eb644fc66 | ||
|
|
2f8e060b49 | ||
|
|
cdb146c7d7 | ||
|
|
1ac35e7d4e | ||
|
|
0560172565 | ||
|
|
343e7963f2 | ||
|
|
d10c9d96f1 | ||
|
|
174b98b118 | ||
|
|
b2948b4367 | ||
|
|
6e1101576e | ||
|
|
360ca96526 | ||
|
|
ef39d26fbd | ||
|
|
6858ec3f17 | ||
|
|
22d67083f6 | ||
|
|
997186a701 | ||
|
|
050834734f | ||
|
|
ff03405289 | ||
|
|
039eb5472a | ||
|
|
d90268bd16 | ||
|
|
5d587af8b0 | ||
|
|
879e94fe06 | ||
|
|
1d71afdb2f | ||
|
|
73a0ae402c | ||
|
|
932cbbea26 | ||
|
|
34f3c74013 | ||
|
|
dcbfda02b9 | ||
|
|
062add32dc | ||
|
|
05a2c9080f | ||
|
|
7da0d54487 | ||
|
|
b489fda18e | ||
|
|
f7411da5cf | ||
|
|
53a4812571 | ||
|
|
9e5c5239a8 | ||
|
|
ee27b3bd6a | ||
|
|
8384233f4d | ||
|
|
72ce69ea8f | ||
|
|
b61bbc2170 | ||
|
|
bf6bb7f620 | ||
|
|
bc36713449 | ||
|
|
2cd5b1d2ed | ||
|
|
923f943717 | ||
|
|
13fcf9db4b | ||
|
|
bacbaf756e | ||
|
|
3f92f1fb79 | ||
|
|
1c34d428f2 | ||
|
|
b95647e353 | ||
|
|
c21310c4e2 | ||
|
|
b86014b04f | ||
|
|
66a7dca5ab | ||
|
|
64d96f427f | ||
|
|
74e1e07567 | ||
|
|
28c789f85c | ||
|
|
d33d0c507b | ||
|
|
14318ec235 | ||
|
|
813b0da6ef | ||
|
|
79f96f5ef4 | ||
|
|
c50f345f1c | ||
|
|
4b88bf80f1 | ||
|
|
deb706607e | ||
|
|
96675e2eca | ||
|
|
aa07928f1f | ||
|
|
47f182193f | ||
|
|
4ce071a0df | ||
|
|
0a3ecdc5ad | ||
|
|
addd9cbddf | ||
|
|
30c1129cab | ||
|
|
9b278ed05e | ||
|
|
8588d0f901 | ||
|
|
06f1d9cb7e | ||
|
|
f45a8b346f | ||
|
|
338a7512e8 | ||
|
|
e00ad7d5eb | ||
|
|
18ccb0d9fb | ||
|
|
2a260abf05 | ||
|
|
948cb37d26 | ||
|
|
9da92c8f71 | ||
|
|
11e7055497 | ||
|
|
9c899e80a3 | ||
|
|
076db6dd07 | ||
|
|
8620236cb0 | ||
|
|
ce7bf82336 | ||
|
|
3a162e9b31 | ||
|
|
4add2f3dcf | ||
|
|
f3779cd2c9 | ||
|
|
c575638962 | ||
|
|
5afaea90b9 | ||
|
|
60eb4c7296 | ||
|
|
838f3c6e36 | ||
|
|
a1ea1fd9c1 | ||
|
|
435cb4aed9 | ||
|
|
ba27bb6490 | ||
|
|
ac5027ec27 | ||
|
|
084bdc4f40 | ||
|
|
1b85326549 | ||
|
|
f723fea4e2 | ||
|
|
e9a3c0f4ee | ||
|
|
40eac92013 | ||
|
|
5302dd58b1 | ||
|
|
f90da16cb1 | ||
|
|
7a5bfa5e07 | ||
|
|
721692277d | ||
|
|
f89c5e5b86 | ||
|
|
58bd464bca | ||
|
|
78ee229205 | ||
|
|
065005395f | ||
|
|
22cb6d2ad5 | ||
|
|
b02091bc00 | ||
|
|
b8cd4cc7cf | ||
|
|
e8d91c6404 | ||
|
|
83f99dd32e | ||
|
|
ec7ccd8500 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,6 +7,7 @@ coverage
|
||||
.*.sw*
|
||||
*~
|
||||
*.DS_Store
|
||||
config/application.yml
|
||||
credentials*.sh
|
||||
Pathogen:
|
||||
custom_plan.rb
|
||||
|
||||
1
.ruby-gemset
Normal file
1
.ruby-gemset
Normal file
@@ -0,0 +1 @@
|
||||
growstuffdev
|
||||
1
.ruby-version
Normal file
1
.ruby-version
Normal file
@@ -0,0 +1 @@
|
||||
2.1.5
|
||||
52
.rvmrc
52
.rvmrc
@@ -1,52 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This is an RVM Project .rvmrc file, used to automatically load the ruby
|
||||
# development environment upon cd'ing into the directory
|
||||
|
||||
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
|
||||
# Only full ruby name is supported here, for short names use:
|
||||
# echo "rvm use 1.9.3" > .rvmrc
|
||||
environment_id="ruby-1.9.3-p194@growstuffdev"
|
||||
|
||||
# Uncomment the following lines if you want to verify rvm version per project
|
||||
# rvmrc_rvm_version="1.14.10 (stable)" # 1.10.1 seams as a safe start
|
||||
# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
|
||||
# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
|
||||
# return 1
|
||||
# }
|
||||
|
||||
# First we attempt to load the desired environment directly from the environment
|
||||
# file. This is very fast and efficient compared to running through the entire
|
||||
# CLI and selector. If you want feedback on which environment was used then
|
||||
# insert the word 'use' after --create as this triggers verbose mode.
|
||||
if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
|
||||
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
|
||||
then
|
||||
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
|
||||
[[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
|
||||
\. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
|
||||
if [[ $- == *i* ]] # check for interactive shells
|
||||
then echo "Using: $(tput setaf 2)$GEM_HOME$(tput sgr0)" # show the user the ruby and gemset they are using in green
|
||||
else echo "Using: $GEM_HOME" # don't use colors in non-interactive shells
|
||||
fi
|
||||
else
|
||||
# If the environment file has not yet been created, use the RVM CLI to select.
|
||||
rvm --create use "$environment_id" || {
|
||||
echo "Failed to create RVM environment '${environment_id}'."
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
|
||||
# If you use bundler, this might be useful to you:
|
||||
# if [[ -s Gemfile ]] && {
|
||||
# ! builtin command -v bundle >/dev/null ||
|
||||
# builtin command -v bundle | GREP_OPTIONS= \grep $rvm_path/bin/bundle >/dev/null
|
||||
# }
|
||||
# then
|
||||
# printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
|
||||
# gem install bundler
|
||||
# fi
|
||||
# if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null
|
||||
# then
|
||||
# bundle install | GREP_OPTIONS= \grep -vE '^Using|Your bundle is complete'
|
||||
# fi
|
||||
25
.travis.yml
25
.travis.yml
@@ -1,29 +1,12 @@
|
||||
---
|
||||
language: ruby
|
||||
bundler_args: --without development assets
|
||||
env: RAILS_ENV=travis
|
||||
|
||||
env: GROWSTUFF_SITE_NAME="Growstuff (travis)" RAILS_SECRET_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
|
||||
bundler_args: --without development production staging
|
||||
rvm:
|
||||
- 1.9.3
|
||||
- 2.1.5
|
||||
before_script:
|
||||
- psql -c 'create database growstuff_test;' -U postgres
|
||||
script:
|
||||
- bundle exec rake db:migrate --trace
|
||||
- bundle exec rspec spec/
|
||||
|
||||
# after_success:
|
||||
# - if [[ "$TRAVIS_BRANCH" == "dev" ]]; then git remote add heroku git@heroku.com:growstuff-dev.git
|
||||
# - wget -qO- https://toolbelt.heroku.com/install-ubuntu.sh | sh
|
||||
# - echo "Host heroku.com" >> ~/.ssh/config
|
||||
# - echo " StrictHostKeyChecking no" >> ~/.ssh/config
|
||||
# - echo " CheckHostIP no" >> ~/.ssh/config
|
||||
# - echo " UserKnownHostsFile=/dev/null" >> ~/.ssh/config
|
||||
# - heroku keys:clear
|
||||
# - yes | heroku keys:add
|
||||
# - yes | git push heroku dev:master
|
||||
# - heroku run rake db:migrate
|
||||
# - heroku restart
|
||||
# - fi
|
||||
|
||||
# env:
|
||||
# global:
|
||||
# secure: "QFQbCdNGyjeatp/H0j0y0oGiue45fpG2w6eA2QAbq2RmvhabgXbd5WIobN90\ndrae3S7TRxPDpMpus90icykX6EzOTLXCEvaC4rh9pCcRktj3SZqq5b9rVTvs\n1MvlS6HhtsVqsrKjQUb0WmPpnganIzTs0RtGaQspo2joPJO18A4="
|
||||
|
||||
@@ -1,35 +1,13 @@
|
||||
Thanks for contributing to Growstuff! We have different contribution
|
||||
guidelines depending on whether your change is a small one (a one-line
|
||||
bugfix or similar) or a larger one.
|
||||
Thanks for contributing to Growstuff!
|
||||
|
||||
## Small changes
|
||||
When you create a pull request, please include the following:
|
||||
|
||||
Send us a pull request! We will get one of our pairs of coders to
|
||||
review it.
|
||||
|
||||
If you are interested in becoming a more regular contributor or working
|
||||
on larger features, please see the [Growstuff
|
||||
wiki](http://wiki.growstuff.org/) for more information.
|
||||
|
||||
## Larger changes
|
||||
|
||||
Growstuff does pair programming (two coders working together) for all
|
||||
new features and other significant changes. This means that if you
|
||||
submit a pull request and weren't working in a pair, we're unlikely to
|
||||
merge it into the project as-is.
|
||||
|
||||
If you would like to work on any larger change, we would appreciate it
|
||||
if you would get in touch with us, preferably via our [mailing
|
||||
list](http://lists.growstuff.org/mailman/listinfo/discuss), and talk to
|
||||
us about it first. We'll try and hook you up with a partner so you can
|
||||
work as a pair, either in person or remotely depending on where you are.
|
||||
The [Growstuff wiki](http://wiki.growstuff.org/) has lots more
|
||||
information on our dev process, to get you started if you would like to
|
||||
join us.
|
||||
|
||||
If you submit a larger change without working in a pair, we will treat
|
||||
your work as an experimental "spike" and get one of our pairs of
|
||||
programmers to look over it and maybe use what you've done as the basis
|
||||
for re-implementing it using our processes. **We'd much rather work
|
||||
with you, so please talk to us first!**
|
||||
* Mention the issue it solves (eg. #123)
|
||||
* Your code should follow our [Coding style guide](http://wiki.growstuff.org/index.php/Coding_style_guide)
|
||||
* Make sure you have automated tests for your work, where possible.
|
||||
* Add your name (and that of your pair partner, if any) to [CONTRIBUTORS.md](CONTRIBUTORS.md).
|
||||
|
||||
If you would like to discuss your work before submitting a pull request,
|
||||
please join any of our [Discussion
|
||||
forums](http://wiki.growstuff.org/index.php/Discussion_forums), where
|
||||
our dev team will be happy to help you.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
This is a list of contributors to Growstuff's codebase. We maintain
|
||||
this list because we work in pairs, but Github only knows about the
|
||||
this list because we often work in pairs, but Github only knows about the
|
||||
person who actually does the commits. This gives credit to both members
|
||||
of the pair.
|
||||
|
||||
@@ -38,3 +38,17 @@ submit the change with your pull request.
|
||||
- Yaw Boakye / [yawboakye](https://github.com/yawboakye)
|
||||
- Ryan Clark / [IAMRYO](https://github.com/IAMRYO)
|
||||
- Marty Hines / [martyhines](https://github.com/martyhines)
|
||||
- Amelia Greenhall / [ameliagreenhall](https://github.com/ameliagreenhall)
|
||||
- Barb Natali / [barbnatali](https://github.com/barbnatali)
|
||||
- Taylor Griffin / [tygriffin](https://github.com/tygriffin)
|
||||
- Marlena Compton / [Marlena](https://github.com/marlena)
|
||||
- Elizabeth A. Kari / [catfriend](https://github.com/catfriend)
|
||||
- Cheri Allen / [cherimarie](https://github.com/cherimarie)
|
||||
- Maki Sugita / [macckii](https:://github.com/macckii)
|
||||
- Shiho Takagi / [oshiho3](https://github.com/oshiho3)
|
||||
- Emma Winston / [emmawinston](https://github.com/emmawinston)
|
||||
- Kevin Rio / [krio](https://github.com/krio)
|
||||
- Yoong Kang Lim / [yoongkang](https://github.com/yoongkang)
|
||||
- Kevin Yang / [kevieyang](https://github.com/kevieyang)
|
||||
- Justin Hamman / [juzham](https://github.com/juzham)
|
||||
|
||||
|
||||
49
Gemfile
49
Gemfile
@@ -1,6 +1,6 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
ruby "1.9.3"
|
||||
ruby "2.1.5"
|
||||
|
||||
gem 'bundler', '>=1.1.5'
|
||||
|
||||
@@ -12,6 +12,10 @@ gem 'leaflet-rails'
|
||||
gem 'leaflet-markercluster-rails'
|
||||
gem 'unicorn' # http server
|
||||
|
||||
gem 'pg'
|
||||
|
||||
gem 'figaro' # for handling config via ENV variables
|
||||
|
||||
gem 'cancan' # for checking member privileges
|
||||
|
||||
gem 'gibbon' # for Mailchimp newsletter subscriptions
|
||||
@@ -27,7 +31,6 @@ gem 'active_utils', '1.0.5',
|
||||
:path => 'vendor/gems/active_utils-1.0.5'
|
||||
|
||||
group :production, :staging do
|
||||
gem 'pg'
|
||||
gem 'newrelic_rpm'
|
||||
gem 'dalli'
|
||||
gem 'memcachier'
|
||||
@@ -46,13 +49,15 @@ group :assets do
|
||||
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
||||
# long term, we'll probably want node.js for performance, but this will do
|
||||
# for now as it's easier for new people to install
|
||||
gem 'therubyracer', '~> 0.10.2', :platforms => :ruby
|
||||
gem "therubyracer", "~> 0.12", :platforms => :ruby
|
||||
# libv8 is needed by therubyracer and is a bit finicky
|
||||
gem 'libv8', '3.16.14.7'
|
||||
|
||||
# Another CSS preprocessor, used for Bootstrap overrides
|
||||
gem "less-rails"
|
||||
gem "less", '~>2.5.0'
|
||||
gem "less-rails", '~> 2.5.0'
|
||||
# CSS framework
|
||||
gem "twitter-bootstrap-rails",
|
||||
:git => 'https://github.com/seyhunak/twitter-bootstrap-rails.git',
|
||||
:ref => '2c7c52'
|
||||
gem "less-rails-bootstrap", '~> 3.2.0'
|
||||
|
||||
gem 'uglifier', '>= 1.0.3' # JavaScript compressor
|
||||
|
||||
@@ -60,24 +65,27 @@ group :assets do
|
||||
end
|
||||
|
||||
gem 'jquery-rails'
|
||||
gem 'jquery-ui-rails'
|
||||
gem 'js-routes' # provides access to Rails routes in Javascript
|
||||
gem 'flickraw'
|
||||
|
||||
# To use ActiveModel has_secure_password
|
||||
# gem 'bcrypt-ruby', '~> 3.0.0'
|
||||
|
||||
# To use Jbuilder templates for JSON
|
||||
# gem 'jbuilder'
|
||||
gem 'jbuilder'
|
||||
|
||||
# Use unicorn as the app server
|
||||
# gem 'unicorn'
|
||||
|
||||
# To use debugger
|
||||
group :development do
|
||||
# Installation of the debugger gem fails on Travis CI,
|
||||
# so we don't use it in the test environment
|
||||
gem 'debugger'
|
||||
# A debugger and irb alternative. Pry doesn't play nice
|
||||
# with unicorn, so start a Webrick server when debugging
|
||||
# with Pry
|
||||
gem 'pry'
|
||||
gem 'better_errors'
|
||||
gem 'binding_of_caller'
|
||||
gem 'letter_opener'
|
||||
end
|
||||
|
||||
# Markdown formatting for updates etc
|
||||
@@ -87,10 +95,10 @@ gem 'bluecloth'
|
||||
gem 'will_paginate', '~> 3.0'
|
||||
|
||||
# user signup/login/etc
|
||||
gem 'devise'
|
||||
gem 'devise', '~> 3.2.0'
|
||||
|
||||
# nicely formatted URLs
|
||||
gem 'friendly_id'
|
||||
gem 'friendly_id', '~> 4.0.10'
|
||||
|
||||
# gravatars
|
||||
gem 'gravatar-ultimate'
|
||||
@@ -106,18 +114,21 @@ gem 'bootstrap-datepicker-rails'
|
||||
# For connecting to other services (eg Twitter)
|
||||
gem 'omniauth'
|
||||
gem 'omniauth-twitter'
|
||||
gem 'omniauth-flickr'
|
||||
gem 'omniauth-flickr', '>= 0.0.15'
|
||||
|
||||
gem 'rake', '>= 10.0.0'
|
||||
|
||||
group :development, :test do
|
||||
gem 'sqlite3' # database engine
|
||||
end
|
||||
|
||||
group :development, :test, :travis do
|
||||
gem 'byebug' # debugging
|
||||
gem 'haml-rails' # HTML templating language
|
||||
gem 'rspec-rails', '~> 2.12.1' # unit testing framework
|
||||
gem 'database_cleaner', '~> 1.3.0'
|
||||
gem 'webrat' # provides HTML matchers for view tests
|
||||
gem 'factory_girl_rails', '~> 4.0' # for creating test data
|
||||
gem 'coveralls', require: false # coverage analysis
|
||||
gem 'capybara' # integration tests
|
||||
gem 'capybara-email' # integration tests for email
|
||||
gem 'poltergeist', '~> 1.5.1' # for headless JS testing
|
||||
gem 'i18n-tasks' # adds tests for finding missing and unused translations
|
||||
gem 'json_spec' # extra ways to test JSON data
|
||||
end
|
||||
|
||||
274
Gemfile.lock
274
Gemfile.lock
@@ -5,17 +5,6 @@ GIT
|
||||
specs:
|
||||
geocoder (1.1.8)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/seyhunak/twitter-bootstrap-rails.git
|
||||
revision: 2c7c527c354d9068ce49346d4fd8389328d32ce6
|
||||
ref: 2c7c52
|
||||
specs:
|
||||
twitter-bootstrap-rails (2.2.7)
|
||||
actionpack (>= 3.1)
|
||||
execjs
|
||||
rails (>= 3.1)
|
||||
railties (>= 3.1)
|
||||
|
||||
PATH
|
||||
remote: vendor/gems/active_utils-1.0.5
|
||||
specs:
|
||||
@@ -58,131 +47,190 @@ GEM
|
||||
activesupport (3.2.13)
|
||||
i18n (= 0.6.1)
|
||||
multi_json (~> 1.0)
|
||||
arel (3.0.2)
|
||||
bcrypt-ruby (3.1.1)
|
||||
better_errors (1.0.1)
|
||||
addressable (2.3.6)
|
||||
arel (3.0.3)
|
||||
bcrypt (3.1.9)
|
||||
better_errors (2.0.0)
|
||||
coderay (>= 1.0.0)
|
||||
erubis (>= 2.6.6)
|
||||
rack (>= 0.9.0)
|
||||
binding_of_caller (0.7.2)
|
||||
debug_inspector (>= 0.0.1)
|
||||
bluecloth (2.2.0)
|
||||
bootstrap-datepicker-rails (1.1.1.3)
|
||||
bootstrap-datepicker-rails (1.3.0.2)
|
||||
railties (>= 3.0)
|
||||
builder (3.0.4)
|
||||
byebug (3.5.1)
|
||||
columnize (~> 0.8)
|
||||
debugger-linecache (~> 1.2)
|
||||
slop (~> 3.6)
|
||||
cancan (1.6.10)
|
||||
chunky_png (1.2.8)
|
||||
capybara (2.4.4)
|
||||
mime-types (>= 1.16)
|
||||
nokogiri (>= 1.3.3)
|
||||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
xpath (~> 2.0)
|
||||
capybara-email (2.4.0)
|
||||
capybara (~> 2.4)
|
||||
mail
|
||||
chunky_png (1.3.3)
|
||||
cliver (0.3.2)
|
||||
coderay (1.1.0)
|
||||
coffee-rails (3.2.2)
|
||||
coffee-script (>= 2.2.0)
|
||||
railties (~> 3.2.0)
|
||||
coffee-script (2.2.0)
|
||||
coffee-script (2.3.0)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.6.3)
|
||||
colorize (0.5.8)
|
||||
columnize (0.3.6)
|
||||
commonjs (0.2.6)
|
||||
compass (0.12.2)
|
||||
coffee-script-source (1.8.0)
|
||||
columnize (0.8.9)
|
||||
commonjs (0.2.7)
|
||||
compass (0.12.7)
|
||||
chunky_png (~> 1.2)
|
||||
fssm (>= 0.2.7)
|
||||
sass (~> 3.1)
|
||||
sass (~> 3.2.19)
|
||||
compass-rails (1.0.3)
|
||||
compass (>= 0.12.2, < 0.14)
|
||||
coveralls (0.6.7)
|
||||
colorize
|
||||
coveralls (0.7.1)
|
||||
multi_json (~> 1.3)
|
||||
rest-client
|
||||
simplecov (>= 0.7)
|
||||
term-ansicolor
|
||||
thor
|
||||
csv_shaper (1.0.0)
|
||||
csv_shaper (1.1.1)
|
||||
activesupport (>= 3.0.0)
|
||||
dalli (2.6.4)
|
||||
dalli (2.7.2)
|
||||
database_cleaner (1.3.0)
|
||||
debug_inspector (0.0.2)
|
||||
debugger (1.6.1)
|
||||
columnize (>= 0.3.1)
|
||||
debugger-linecache (~> 1.2.0)
|
||||
debugger-ruby_core_source (~> 1.2.3)
|
||||
debugger-linecache (1.2.0)
|
||||
debugger-ruby_core_source (1.2.3)
|
||||
devise (3.0.0)
|
||||
bcrypt-ruby (~> 3.0)
|
||||
devise (3.2.4)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 3.2.6, < 5)
|
||||
thread_safe (~> 0.1)
|
||||
warden (~> 1.2.3)
|
||||
diff-lcs (1.1.3)
|
||||
docile (1.1.5)
|
||||
easy_translate (0.5.0)
|
||||
json
|
||||
thread
|
||||
thread_safe
|
||||
erubis (2.7.0)
|
||||
execjs (1.4.0)
|
||||
multi_json (~> 1.0)
|
||||
factory_girl (4.2.0)
|
||||
execjs (2.2.2)
|
||||
factory_girl (4.5.0)
|
||||
activesupport (>= 3.0.0)
|
||||
factory_girl_rails (4.2.1)
|
||||
factory_girl (~> 4.2.0)
|
||||
factory_girl_rails (4.5.0)
|
||||
factory_girl (~> 4.5.0)
|
||||
railties (>= 3.0.0)
|
||||
flickraw (0.9.6)
|
||||
friendly_id (4.0.9)
|
||||
figaro (1.0.0)
|
||||
thor (~> 0.14)
|
||||
flickraw (0.9.8)
|
||||
friendly_id (4.0.10.1)
|
||||
activerecord (>= 3.0, < 4.0)
|
||||
fssm (0.2.10)
|
||||
gibbon (1.0.0)
|
||||
gibbon (1.1.4)
|
||||
httparty
|
||||
multi_json (>= 1.3.4)
|
||||
gravatar-ultimate (1.0.3)
|
||||
haml (4.0.3)
|
||||
gravatar-ultimate (2.0.0)
|
||||
activesupport (>= 2.3.14)
|
||||
rack
|
||||
haml (4.0.5)
|
||||
tilt
|
||||
haml-rails (0.4)
|
||||
actionpack (>= 3.1, < 4.1)
|
||||
activesupport (>= 3.1, < 4.1)
|
||||
haml (>= 3.1, < 4.1)
|
||||
railties (>= 3.1, < 4.1)
|
||||
hashie (2.0.5)
|
||||
hashie (3.3.2)
|
||||
highline (1.6.21)
|
||||
hike (1.2.3)
|
||||
httparty (0.11.0)
|
||||
multi_json (~> 1.0)
|
||||
multi_xml (>= 0.5.2)
|
||||
i18n (0.6.1)
|
||||
i18n-tasks (0.7.8)
|
||||
activesupport
|
||||
easy_translate (>= 0.5.0)
|
||||
erubis
|
||||
highline
|
||||
i18n
|
||||
slop (>= 3.5.0)
|
||||
term-ansicolor
|
||||
terminal-table
|
||||
jbuilder (2.2.6)
|
||||
activesupport (>= 3.0.0, < 5)
|
||||
multi_json (~> 1.2)
|
||||
journey (1.0.4)
|
||||
jquery-rails (3.0.4)
|
||||
jquery-rails (3.1.2)
|
||||
railties (>= 3.0, < 5.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
jquery-ui-rails (4.1.2)
|
||||
railties (>= 3.1.0)
|
||||
js-routes (0.9.9)
|
||||
railties (>= 3.2)
|
||||
sprockets-rails
|
||||
json (1.7.7)
|
||||
kgio (2.8.0)
|
||||
leaflet-markercluster-rails (0.6.0)
|
||||
json_spec (1.1.4)
|
||||
multi_json (~> 1.0)
|
||||
rspec (>= 2.0, < 4.0)
|
||||
kgio (2.9.2)
|
||||
launchy (2.4.3)
|
||||
addressable (~> 2.3)
|
||||
leaflet-markercluster-rails (0.7.0)
|
||||
railties (>= 3.1)
|
||||
leaflet-rails (0.6.2)
|
||||
less (2.3.2)
|
||||
commonjs (~> 0.2.6)
|
||||
less-rails (2.3.3)
|
||||
leaflet-rails (0.7.4)
|
||||
less (2.5.1)
|
||||
commonjs (~> 0.2.7)
|
||||
less-rails (2.5.0)
|
||||
actionpack (>= 3.1)
|
||||
less (~> 2.3.1)
|
||||
libv8 (3.3.10.4)
|
||||
less (~> 2.5.0)
|
||||
less-rails-bootstrap (3.2.0)
|
||||
less-rails (~> 2.5.0)
|
||||
letter_opener (1.2.0)
|
||||
launchy (~> 2.2)
|
||||
libv8 (3.16.14.7)
|
||||
mail (2.5.4)
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
memcachier (0.0.2)
|
||||
mime-types (1.23)
|
||||
mini_portile (0.5.1)
|
||||
multi_json (1.7.7)
|
||||
method_source (0.8.2)
|
||||
mime-types (1.25.1)
|
||||
mini_portile (0.6.1)
|
||||
multi_json (1.10.1)
|
||||
multi_xml (0.5.5)
|
||||
newrelic_rpm (3.6.5.130)
|
||||
nokogiri (1.6.0)
|
||||
mini_portile (~> 0.5.0)
|
||||
netrc (0.8.0)
|
||||
newrelic_rpm (3.9.7.266)
|
||||
nokogiri (1.6.5)
|
||||
mini_portile (~> 0.6.0)
|
||||
oauth (0.4.7)
|
||||
omniauth (1.1.4)
|
||||
hashie (>= 1.2, < 3)
|
||||
rack
|
||||
omniauth-flickr (0.0.11)
|
||||
omniauth (1.2.2)
|
||||
hashie (>= 1.2, < 4)
|
||||
rack (~> 1.0)
|
||||
omniauth-flickr (0.0.15)
|
||||
omniauth-oauth (~> 1.0)
|
||||
omniauth-oauth (1.0.1)
|
||||
oauth
|
||||
omniauth (~> 1.0)
|
||||
omniauth-twitter (1.0.0)
|
||||
omniauth-twitter (1.1.0)
|
||||
multi_json (~> 1.3)
|
||||
omniauth-oauth (~> 1.0)
|
||||
orm_adapter (0.4.0)
|
||||
pg (0.15.1)
|
||||
polyglot (0.3.3)
|
||||
orm_adapter (0.5.0)
|
||||
pg (0.17.1)
|
||||
poltergeist (1.5.1)
|
||||
capybara (~> 2.1)
|
||||
cliver (~> 0.3.1)
|
||||
multi_json (~> 1.0)
|
||||
websocket-driver (>= 0.2.0)
|
||||
polyglot (0.3.5)
|
||||
pry (0.10.1)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.8.1)
|
||||
slop (~> 3.4)
|
||||
rack (1.4.5)
|
||||
rack-cache (1.2)
|
||||
rack (>= 0.4)
|
||||
rack-ssl (1.3.3)
|
||||
rack-ssl (1.3.4)
|
||||
rack
|
||||
rack-test (0.6.2)
|
||||
rack (>= 1.0)
|
||||
@@ -194,10 +242,10 @@ GEM
|
||||
activesupport (= 3.2.13)
|
||||
bundler (~> 1.0)
|
||||
railties (= 3.2.13)
|
||||
rails_12factor (0.0.2)
|
||||
rails_12factor (0.0.3)
|
||||
rails_serve_static_assets
|
||||
rails_stdout_logging
|
||||
rails_serve_static_assets (0.0.1)
|
||||
rails_serve_static_assets (0.0.2)
|
||||
rails_stdout_logging (0.0.3)
|
||||
railties (3.2.13)
|
||||
actionpack (= 3.2.13)
|
||||
@@ -206,12 +254,18 @@ GEM
|
||||
rake (>= 0.8.7)
|
||||
rdoc (~> 3.4)
|
||||
thor (>= 0.14.6, < 2.0)
|
||||
raindrops (0.11.0)
|
||||
rake (10.1.0)
|
||||
raindrops (0.13.0)
|
||||
rake (10.4.0)
|
||||
rdoc (3.12.2)
|
||||
json (~> 1.4)
|
||||
rest-client (1.6.7)
|
||||
mime-types (>= 1.16)
|
||||
ref (1.0.5)
|
||||
rest-client (1.7.2)
|
||||
mime-types (>= 1.16, < 3.0)
|
||||
netrc (~> 0.7)
|
||||
rspec (2.12.0)
|
||||
rspec-core (~> 2.12.0)
|
||||
rspec-expectations (~> 2.12.0)
|
||||
rspec-mocks (~> 2.12.0)
|
||||
rspec-core (2.12.2)
|
||||
rspec-expectations (2.12.1)
|
||||
diff-lcs (~> 1.1.3)
|
||||
@@ -223,33 +277,43 @@ GEM
|
||||
rspec-core (~> 2.12.0)
|
||||
rspec-expectations (~> 2.12.0)
|
||||
rspec-mocks (~> 2.12.0)
|
||||
sass (3.2.9)
|
||||
sass (3.2.19)
|
||||
sass-rails (3.2.6)
|
||||
railties (~> 3.2.0)
|
||||
sass (>= 3.1.10)
|
||||
tilt (~> 1.3)
|
||||
simplecov (0.7.1)
|
||||
simplecov (0.9.1)
|
||||
docile (~> 1.1.0)
|
||||
multi_json (~> 1.0)
|
||||
simplecov-html (~> 0.7.1)
|
||||
simplecov-html (0.7.1)
|
||||
sprockets (2.2.2)
|
||||
simplecov-html (~> 0.8.0)
|
||||
simplecov-html (0.8.0)
|
||||
slop (3.6.0)
|
||||
sprockets (2.2.3)
|
||||
hike (~> 1.2)
|
||||
multi_json (~> 1.0)
|
||||
rack (~> 1.0)
|
||||
tilt (~> 1.1, != 1.3.0)
|
||||
sqlite3 (1.3.7)
|
||||
therubyracer (0.10.2)
|
||||
libv8 (~> 3.3.10)
|
||||
thor (0.18.1)
|
||||
sprockets-rails (0.0.1)
|
||||
sprockets (>= 1.0.2)
|
||||
term-ansicolor (1.3.0)
|
||||
tins (~> 1.0)
|
||||
terminal-table (1.4.5)
|
||||
therubyracer (0.12.1)
|
||||
libv8 (~> 3.16.14.0)
|
||||
ref
|
||||
thor (0.19.1)
|
||||
thread (0.1.4)
|
||||
thread_safe (0.3.4)
|
||||
tilt (1.4.1)
|
||||
treetop (1.4.14)
|
||||
tins (1.3.3)
|
||||
treetop (1.4.15)
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
tzinfo (0.3.37)
|
||||
uglifier (2.1.2)
|
||||
tzinfo (0.3.42)
|
||||
uglifier (2.2.1)
|
||||
execjs (>= 0.3.0)
|
||||
multi_json (~> 1.0, >= 1.0.2)
|
||||
unicorn (4.6.3)
|
||||
unicorn (4.8.3)
|
||||
kgio (~> 2.6)
|
||||
rack
|
||||
raindrops (~> 0.7)
|
||||
@@ -259,7 +323,10 @@ GEM
|
||||
nokogiri (>= 1.2.0)
|
||||
rack (>= 1.0)
|
||||
rack-test (>= 0.5.3)
|
||||
will_paginate (3.0.4)
|
||||
websocket-driver (0.4.0)
|
||||
will_paginate (3.0.7)
|
||||
xpath (2.0.0)
|
||||
nokogiri (~> 1.3)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
@@ -272,42 +339,55 @@ DEPENDENCIES
|
||||
bluecloth
|
||||
bootstrap-datepicker-rails
|
||||
bundler (>= 1.1.5)
|
||||
byebug
|
||||
cancan
|
||||
capybara
|
||||
capybara-email
|
||||
coffee-rails (~> 3.2.1)
|
||||
compass-rails (~> 1.0.3)
|
||||
coveralls
|
||||
csv_shaper
|
||||
dalli
|
||||
debugger
|
||||
devise
|
||||
database_cleaner (~> 1.3.0)
|
||||
devise (~> 3.2.0)
|
||||
factory_girl_rails (~> 4.0)
|
||||
figaro
|
||||
flickraw
|
||||
friendly_id
|
||||
friendly_id (~> 4.0.10)
|
||||
geocoder!
|
||||
gibbon
|
||||
gravatar-ultimate
|
||||
haml
|
||||
haml-rails
|
||||
i18n-tasks
|
||||
jbuilder
|
||||
jquery-rails
|
||||
jquery-ui-rails
|
||||
js-routes
|
||||
json (~> 1.7.7)
|
||||
json_spec
|
||||
leaflet-markercluster-rails
|
||||
leaflet-rails
|
||||
less-rails
|
||||
less (~> 2.5.0)
|
||||
less-rails (~> 2.5.0)
|
||||
less-rails-bootstrap (~> 3.2.0)
|
||||
letter_opener
|
||||
libv8 (= 3.16.14.7)
|
||||
memcachier
|
||||
newrelic_rpm
|
||||
omniauth
|
||||
omniauth-flickr
|
||||
omniauth-flickr (>= 0.0.15)
|
||||
omniauth-twitter
|
||||
pg
|
||||
poltergeist (~> 1.5.1)
|
||||
pry
|
||||
rack (~> 1.4.5)
|
||||
rails (= 3.2.13)
|
||||
rails_12factor
|
||||
rake (>= 10.0.0)
|
||||
rspec-rails (~> 2.12.1)
|
||||
sass-rails (~> 3.2.3)
|
||||
sqlite3
|
||||
therubyracer (~> 0.10.2)
|
||||
twitter-bootstrap-rails!
|
||||
therubyracer (~> 0.12)
|
||||
uglifier (>= 1.0.3)
|
||||
unicorn
|
||||
webrat
|
||||
|
||||
55
README.md
55
README.md
@@ -5,9 +5,56 @@
|
||||
|
||||
Welcome to the Growstuff project.
|
||||
|
||||
Growstuff is an open source/open data project to create a website for food gardeners.
|
||||
Growstuff is an open source/open data project to create a website for
|
||||
food gardeners. We crowdsource information on what our members are
|
||||
growing and harvesting, aggregate it, and make it available as open data
|
||||
via our API.
|
||||
|
||||
You can find most of our documentation at: http://wiki.growstuff.org/
|
||||
Growstuff was founded in 2012 and has been built by dozens of
|
||||
[contributors](CONTRIBUTORS.md). We are an inclusive, welcoming project, and
|
||||
encourage participation from people of all backgrounds and skill levels.
|
||||
|
||||
Our development uses Extreme Programming practices, which means we pair on all our work. Feel free to fork our code and explore, but if you would like to contribute back to us,
|
||||
please read http://wiki.growstuff.org/index.php/Development_process_overview before sending us a pull request.
|
||||
## Important links
|
||||
|
||||
* [Issues](http://github.com/Growstuff/growstuff/issues) (features we're
|
||||
working on, known bugs, etc)
|
||||
* [Discussion forums](http://wiki.growstuff.org/index.php/Discussion_forums) (mailing lists, IRC, etc)
|
||||
* [Wiki](http://wiki.growstuff.org/) (general documentation)
|
||||
|
||||
## For coders
|
||||
|
||||
Growstuff is built in Ruby on Rails and also uses JavaScript for
|
||||
frontend features. We welcome contributions -- see
|
||||
[CONTRIBUTING](CONTRIBUTING.md) for details.
|
||||
|
||||
* To set up your development environment, see [Getting started](http://wiki.growstuff.org/index.php/Development/Getting_Started).
|
||||
* We encourage [pair programming](http://wiki.growstuff.org/index.php/Pairing), especially for newer developers. [Find a pair programming partner.](http://talk.growstuff.org/t/find-a-pair-programming-partner/13)
|
||||
* Drop in to one of our [discussion forums](http://wiki.growstuff.org/index.php/Discussion_forums) to chat to other developers, get help, etc.
|
||||
* You may also be interested in our [API](http://wiki.growstuff.org/index.php/API).
|
||||
|
||||
## For designers, writers, researchers, data wranglers, and other contributors
|
||||
|
||||
There are heaps of ways to get involved and contribute no matter what
|
||||
your skills and interests.
|
||||
|
||||
You might like to check out:
|
||||
|
||||
* The [Get Involved](http://wiki.growstuff.org/index.php/Get_involved)
|
||||
page on our wiki, which has lots of detail for different areas
|
||||
* [Growstuff Talk](http://talk.growstuff.org/) especially the [Idea category](http://talk.growstuff.org/c/idea)
|
||||
|
||||
Here on Github, you might find these useful:
|
||||
|
||||
* [needs: design](https://github.com/Growstuff/growstuff/labels/needs:%20design) - tasks requiring high-level design
|
||||
* [needs: visual design](https://github.com/Growstuff/growstuff/labels/needs:%20visual design) - tasks requiring visual/graphical design
|
||||
* [needs: documentation](https://github.com/Growstuff/growstuff/labels/needs:%20documentation)
|
||||
* [needs: data](https://github.com/Growstuff/growstuff/labels/needs:%20data) - tasks requiring data entry, data design, data import, or similar
|
||||
|
||||
Feel free to comment on any of the issues you find there, or open up a broader conversation on [Growstuff Talk](http://talk.growstuff.org).
|
||||
|
||||
## Contact
|
||||
|
||||
For more information about this project, contact [info@growstuff.org](mailto:info@growstuff.org).
|
||||
|
||||
You can also contact us on [Twitter](http://twitter.com/growstufforg/) or
|
||||
[Facebook](https://www.facebook.com/pages/Growstuff/1531133417099494).
|
||||
|
||||
BIN
app/assets/images/facebook_32.png
Normal file
BIN
app/assets/images/facebook_32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 572 B |
BIN
app/assets/images/favicon.ico
Normal file
BIN
app/assets/images/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 920 B |
BIN
app/assets/images/growstuff-apple-touch-icon-precomposed.png
Normal file
BIN
app/assets/images/growstuff-apple-touch-icon-precomposed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
BIN
app/assets/images/growstuff-brand.png
Normal file
BIN
app/assets/images/growstuff-brand.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.1 KiB |
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
38
app/assets/javascripts/append_date.js.coffee
Normal file
38
app/assets/javascripts/append_date.js.coffee
Normal file
@@ -0,0 +1,38 @@
|
||||
# Displays datepicker to finished at date
|
||||
# when marking a planting finished using a
|
||||
# button. The button must have class 'append-date'.
|
||||
|
||||
jQuery ->
|
||||
|
||||
el = $('.append-date')
|
||||
|
||||
el.datepicker({'format': 'yyyy-mm-dd'})
|
||||
|
||||
href = el.attr('href')
|
||||
|
||||
originalText = el.text()
|
||||
|
||||
el.click (e) ->
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
|
||||
$(this).text('Confirm without date')
|
||||
|
||||
$(this).bind('click.confirm', (e) ->
|
||||
link = $("<a href='#{href}' data-method='put'></a>")
|
||||
$('body').append(link)
|
||||
$(link).click()
|
||||
)
|
||||
|
||||
$(this).blur (e) ->
|
||||
$(this).text(originalText)
|
||||
$(this).unbind('click.confirm')
|
||||
|
||||
|
||||
el.one 'changeDate', ->
|
||||
date = $(this).datepicker('getDate')
|
||||
url = "#{href}&planting[finished_at]=#{date}"
|
||||
|
||||
link = $("<a href='#{url}' data-method='put'></a>")
|
||||
$('body').append(link)
|
||||
$(link).click()
|
||||
@@ -12,8 +12,11 @@
|
||||
//
|
||||
//= require leaflet
|
||||
//= require leaflet.markercluster
|
||||
//= require js-routes
|
||||
//= require jquery
|
||||
//= require jquery_ujs
|
||||
//= require jquery.ui.autocomplete
|
||||
//= require twitter/bootstrap
|
||||
//= require_tree .
|
||||
//= require bootstrap-datepicker
|
||||
|
||||
|
||||
37
app/assets/javascripts/auto_suggest.js.coffee
Normal file
37
app/assets/javascripts/auto_suggest.js.coffee
Normal file
@@ -0,0 +1,37 @@
|
||||
# Uses JQuery's autocomplete to make a suggestion in lieu of a
|
||||
# preposterously long select dropdown. To implement add code to
|
||||
# the view like this:
|
||||
#
|
||||
# = auto_suggest @resource, :auto_suggest_source
|
||||
#
|
||||
# You must also add a search method to the resource's controller.
|
||||
|
||||
jQuery ->
|
||||
|
||||
if el = $( '.auto-suggest' )
|
||||
|
||||
id = $( '.auto-suggest-id' )
|
||||
|
||||
el.autocomplete
|
||||
minLength: 1,
|
||||
source: el.attr( 'data-source-url' ),
|
||||
focus: ( event, ui ) ->
|
||||
el.val( ui.item.name )
|
||||
id.val( ui.item.id )
|
||||
false
|
||||
select: ( event, ui ) ->
|
||||
el.val( ui.item.name )
|
||||
id.val( ui.item.id )
|
||||
false
|
||||
response: ( event, ui ) ->
|
||||
id.val( "" )
|
||||
for item in ui.content
|
||||
if item.name == el.val()
|
||||
id.val( item.id )
|
||||
|
||||
if el.data( 'uiAutocomplete' )
|
||||
el.data( 'uiAutocomplete' )._renderItem = ( ul, item ) ->
|
||||
$( '<li></li>' )
|
||||
.data( 'item.autocomplete', item )
|
||||
.append( "<a>#{item.name}</a>" )
|
||||
.appendTo( ul )
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
49
app/assets/javascripts/crops.js.erb
Normal file
49
app/assets/javascripts/crops.js.erb
Normal file
@@ -0,0 +1,49 @@
|
||||
if (document.getElementById("cropmap") !== null) {
|
||||
mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>";
|
||||
mapbox_base_url = "https://c.tiles.mapbox.com/v3/" + mapbox_map_id + "/{z}/{x}/{y}.png";
|
||||
|
||||
L.Icon.Default.imagePath = '/assets'
|
||||
|
||||
cropmap = L.map('cropmap').setView([0.0, -0.0], 2);
|
||||
showCropMap(cropmap);
|
||||
}
|
||||
|
||||
function showCropMap(cropmap) {
|
||||
|
||||
L.tileLayer(mapbox_base_url, {
|
||||
attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors under <a href="http://www.openstreetmap.org/copyright">ODbL</a> | Map imagery © <a href="http://mapbox.com">Mapbox</a>',
|
||||
maxZoom: 18
|
||||
}).addTo(cropmap);
|
||||
markers = new L.MarkerClusterGroup({showCoverageOnHover: false, maxClusterRadius: 20 });
|
||||
|
||||
things_to_map = location.pathname + '.json';
|
||||
$.getJSON(things_to_map, function(crop) {
|
||||
$.each(crop.plantings, function(i, planting) {
|
||||
owner = planting.owner;
|
||||
if (owner.latitude && owner.longitude) {
|
||||
marker = new L.Marker(new L.LatLng(owner.latitude, owner.longitude));
|
||||
|
||||
planting_url = "/plantings/" + planting.id;
|
||||
planting_link = "<a href='" + planting_url + "'>" + owner.login_name + "'s " + crop.name + "</a>";
|
||||
|
||||
where = "<p><i>" + owner.location + "</i></p>";
|
||||
|
||||
details = "<p>";
|
||||
if (planting.quantity) {
|
||||
details = details + "Quantity: " + planting.quantity + "<br/>";
|
||||
}
|
||||
if (planting.planted_from) {
|
||||
details = details + "Planted from: " + planting.planted_from + "<br/>";
|
||||
}
|
||||
if (planting.sunniness) {
|
||||
details = details + "Planted in: " + planting.sunniness+ "<br/>";
|
||||
}
|
||||
details = details + "</p>";
|
||||
marker.bindPopup(planting_link + where + details).openPopup();
|
||||
markers.addLayer(marker);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
cropmap.addLayer(markers);
|
||||
}
|
||||
19
app/assets/javascripts/finish_planting.js.coffee
Normal file
19
app/assets/javascripts/finish_planting.js.coffee
Normal file
@@ -0,0 +1,19 @@
|
||||
# Clears the finished at date field when
|
||||
# a planting is marked unfinished, and
|
||||
# repopulates the field with a cached value
|
||||
# marking unfinished is undone.
|
||||
|
||||
jQuery ->
|
||||
previousValue = ''
|
||||
$('#planting_finished').on('click', ->
|
||||
finished = $('#planting_finished_at')
|
||||
if @checked
|
||||
if previousValue.length
|
||||
date = previousValue
|
||||
finished.val(date)
|
||||
else
|
||||
finished.trigger('focus')
|
||||
else
|
||||
previousValue = finished.val()
|
||||
finished.val('')
|
||||
)
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
28
app/assets/javascripts/members.js.erb
Normal file
28
app/assets/javascripts/members.js.erb
Normal file
@@ -0,0 +1,28 @@
|
||||
if (document.getElementById("membermap") !== null) {
|
||||
mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>";
|
||||
mapbox_base_url = "https://c.tiles.mapbox.com/v3/" + mapbox_map_id + "/{z}/{x}/{y}.png";
|
||||
|
||||
L.Icon.Default.imagePath = '/assets'
|
||||
|
||||
|
||||
$.getJSON(location.pathname + '.json', function(member) {
|
||||
if (member.latitude && member.longitude) {
|
||||
membermap = L.map('membermap').setView([member.latitude, member.longitude], 4);
|
||||
|
||||
L.tileLayer(mapbox_base_url, {
|
||||
attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors under <a href="http://www.openstreetmap.org/copyright">ODbL</a> | Map imagery © <a href="http://mapbox.com">Mapbox</a>',
|
||||
maxZoom: 18
|
||||
}).addTo(membermap);
|
||||
marker = new L.Marker(new L.LatLng(member.latitude, member.longitude));
|
||||
|
||||
member_url = "/members/" + member.slug;
|
||||
member_link = "<a href='" + member_url + "'>" + member.login_name + "</a>";
|
||||
|
||||
where = "<p><i>" + member.location + "</i></p>";
|
||||
|
||||
marker.bindPopup(member_link + where).openPopup();
|
||||
marker.addTo(membermap);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,42 +1,44 @@
|
||||
places_base_path = "/places";
|
||||
things_to_map = location.pathname + '.json';
|
||||
cloudmade_key = "<%= Growstuff::Application.config.cloudmade_key %>";
|
||||
cloudmade_url = "http://{s}.tile.cloudmade.com/" + cloudmade_key + "/73038/256/{z}/{x}/{y}.png";
|
||||
nominatim_base_url = 'http://nominatim.openstreetmap.org/search/';
|
||||
nominatim_user_agent_email = "<%= Growstuff::Application.config.user_agent_email %>";
|
||||
if (document.getElementById("placesmap") !== null) {
|
||||
places_base_path = "/places";
|
||||
mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>";
|
||||
mapbox_base_url = "https://c.tiles.mapbox.com/v3/" + mapbox_map_id + "/{z}/{x}/{y}.png";
|
||||
nominatim_base_url = 'http://nominatim.openstreetmap.org/search/';
|
||||
nominatim_user_agent_email = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.user_agent_email %>";
|
||||
|
||||
L.Icon.Default.imagePath = '/assets'
|
||||
L.Icon.Default.imagePath = '/assets'
|
||||
|
||||
if (location.pathname === places_base_path) { //places index page
|
||||
map = L.map('map').setView([0.0, -0.0], 2);
|
||||
showMap(map);
|
||||
} else { // specific place page
|
||||
place = location.pathname.replace(places_base_path + "/", '');
|
||||
nominatim_query_url = nominatim_base_url + place;
|
||||
nominatim_options = {
|
||||
format: "json",
|
||||
callback: "placeholder",
|
||||
limit: 1,
|
||||
email: nominatim_user_agent_email
|
||||
};
|
||||
$.getJSON(nominatim_query_url, nominatim_options, function(data) {
|
||||
map = L.map('map').setView([data[0].lat, data[0].lon], 5);
|
||||
showMap(map);
|
||||
})
|
||||
if (location.pathname === places_base_path) { //places index page
|
||||
placesmap = L.map('placesmap').setView([0.0, -0.0], 2);
|
||||
showMap(placesmap);
|
||||
} else { // specific place page
|
||||
place = location.pathname.replace(places_base_path + "/", '');
|
||||
nominatim_query_url = nominatim_base_url + place;
|
||||
nominatim_options = {
|
||||
format: "json",
|
||||
callback: "placeholder",
|
||||
limit: 1,
|
||||
email: nominatim_user_agent_email
|
||||
};
|
||||
$.getJSON(nominatim_query_url, nominatim_options, function(data) {
|
||||
placesmap = L.map('placesmap').setView([data[0].lat, data[0].lon], 5);
|
||||
showMap(placesmap);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function showMap(map) {
|
||||
L.tileLayer(cloudmade_url, {
|
||||
attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors under <a href="http://www.openstreetmap.org/copyright">ODbL</a> | Map imagery © <a href="http://cloudmade.com">Cloudmade</a>',
|
||||
function showMap(placesmap) {
|
||||
L.tileLayer(mapbox_base_url, {
|
||||
attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors under <a href="http://www.openstreetmap.org/copyright">ODbL</a> | Map imagery © <a href="http://mapbox.com">Mapbox</a>',
|
||||
maxZoom: 18
|
||||
}).addTo(map);
|
||||
markers = new L.MarkerClusterGroup({showCoverageOnHover: false});
|
||||
}).addTo(placesmap);
|
||||
markers = new L.MarkerClusterGroup({showCoverageOnHover: false, maxClusterRadius: 20 });
|
||||
|
||||
things_to_map = location.pathname + '.json';
|
||||
$.getJSON(things_to_map, function(members) {
|
||||
$.each(members, function(i, m) {
|
||||
if (m.latitude && m.longitude) {
|
||||
marker = new L.Marker(new L.LatLng(m.latitude, m.longitude));
|
||||
link = "<p><a href='/members/" + m.login_name + "'>" + m.login_name + "</a></p>";
|
||||
link = "<p><a href='/members/" + m.slug + "'>" + m.login_name + "</a></p>";
|
||||
where = "<p><i>" + m.location + "</i></p>";
|
||||
marker.bindPopup(link + where).openPopup();
|
||||
markers.addLayer(marker);
|
||||
@@ -44,5 +46,5 @@ function showMap(map) {
|
||||
});
|
||||
});
|
||||
|
||||
map.addLayer(markers);
|
||||
placesmap.addLayer(markers);
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -3,10 +3,12 @@
|
||||
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
|
||||
* the top of the compiled file, but it's generally better to create a new file per style scope.
|
||||
*= require_self
|
||||
*= require jquery.ui.autocomplete
|
||||
*= require bootstrap-datepicker
|
||||
*= require leaflet.ie
|
||||
*= require leaflet
|
||||
*= require leaflet.markercluster
|
||||
*= require leaflet.markercluster.default
|
||||
*= require custom_bootstrap/custom_bootstrap
|
||||
*= require overrides.css
|
||||
*= require_tree .
|
||||
*/
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
@import "twitter/bootstrap/bootstrap";
|
||||
|
||||
// this padding needs to be done before the responsive stuff is imported
|
||||
body {
|
||||
padding-top: @navbarHeight + 10px;
|
||||
padding-bottom: @navbarHeight + 10px;
|
||||
}
|
||||
|
||||
@import "twitter/bootstrap/responsive";
|
||||
|
||||
// Set the correct sprite paths
|
||||
@iconSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings");
|
||||
@iconWhiteSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings-white");
|
||||
|
||||
// Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines)
|
||||
@fontAwesomeEotPath: asset-url("fontawesome-webfont.eot");
|
||||
@fontAwesomeEotPath_iefix: asset-url("fontawesome-webfont.eot#iefix");
|
||||
@fontAwesomeWoffPath: asset-url("fontawesome-webfont.woff");
|
||||
@fontAwesomeTtfPath: asset-url("fontawesome-webfont.ttf");
|
||||
@fontAwesomeSvgPath: asset-url("fontawesome-webfont.svg#fontawesomeregular");
|
||||
|
||||
// Font Awesome
|
||||
@import "fontawesome/font-awesome";
|
||||
|
||||
// Glyphicons
|
||||
//@import "twitter/bootstrap/sprites.less";
|
||||
|
||||
// Your custom LESS stylesheets goes here
|
||||
//
|
||||
// Since bootstrap was imported above you have access to its mixins which
|
||||
// you may use and inherit here
|
||||
//
|
||||
// If you'd like to override bootstrap's own variables, you can do so here as well
|
||||
// See http://twitter.github.com/bootstrap/customize.html#variables for their names and documentation
|
||||
//
|
||||
// Example:
|
||||
// @linkColor: #ff0000;
|
||||
|
||||
// Base colours
|
||||
|
||||
@beige: #f3f1ee;
|
||||
@brown: #413f3b;
|
||||
|
||||
@green: #5f8e43;
|
||||
@blue: #2f4365;
|
||||
@red: #8e4d43;
|
||||
@orange: #b2685c;
|
||||
@yellow: #b2935c;
|
||||
|
||||
@bodyBackground: @beige;
|
||||
@textColor: @brown;
|
||||
|
||||
@linkColor: @green;
|
||||
|
||||
// Typography (with help from bootswatch.com's "readable" theme)
|
||||
@import url('//fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic');
|
||||
@sansFontFamily: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
@serifFontFamily: Georgia, "Times New Roman", Times, serif;
|
||||
@monoFontFamily: Monaco, Menlo, Consolas, "Courier New", monospace;
|
||||
|
||||
@baseFontSize: 15px;
|
||||
@baseFontFamily: @serifFontFamily;
|
||||
@baseLineHeight: @baseFontSize * 1.5;
|
||||
@altFontFamily: @sansFontFamily;
|
||||
|
||||
@headingsFontFamily: "Lora", Georgia, "Times New Roman", Times, serif;
|
||||
@headingsFontWeight: bold; // instead of browser default, bold
|
||||
@headingsColor: inherit; // empty to use BS default, @textColor
|
||||
|
||||
// Hero unit
|
||||
@heroUnitBackground: darken(@bodyBackground, 10%);
|
||||
|
||||
// Nav bar
|
||||
@navbarBackground: @brown;
|
||||
@navbarBackgroundHighlight: @brown;
|
||||
@navbarText: @beige;
|
||||
@navbarLinkColor: darken(@beige, 20%);
|
||||
@navbarLinkColorHover: @beige;
|
||||
@navbarLinkColorActive: @beige;
|
||||
@navbarBrandColor: lighten(@green, 20%);
|
||||
|
||||
@dropdownBackground: lighten(@beige, 10%);
|
||||
@dropdownLinkColor: @brown;
|
||||
@dropdownLinkColorHover: @brown;
|
||||
@dropdownLinkBackgroundHover: lighten(@green, 50%);
|
||||
|
||||
|
||||
ul.inline > li.first {
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
.main {
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
margin-left: -1px;
|
||||
border-left: 1px solid darken(@beige, 10%);
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
// this is used for eg. crops and members index pages
|
||||
.six-across:nth-child(6n+1) {
|
||||
margin-left: 0px
|
||||
}
|
||||
|
||||
.three-across:nth-child(3n+1) {
|
||||
margin-left: 0px;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
// let's condense the hero unit a little
|
||||
.hero-unit {
|
||||
padding-top: 30px;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
// info under the main heading on homepage
|
||||
.hero-unit .info {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
// signup widget on homepage
|
||||
.hero-unit .signup {
|
||||
background-color: lighten(@green, 40%);
|
||||
border: 1px solid lighten(@green, 20%);
|
||||
border-radius: 6px;
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
line-height: 110%;
|
||||
}
|
||||
|
||||
// stats shown on homepage. eg. "999 members..."
|
||||
p.stats {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.homepage-members {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.homepage-members:nth-child(odd) {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
#map {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.member-location {
|
||||
font-size: small;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.member-location a {
|
||||
color: @brown;
|
||||
}
|
||||
|
||||
// Overrides applying only to mobile view
|
||||
|
||||
@media only screen and (max-width: 767px) {
|
||||
.sidebar {
|
||||
margin-left: 0;
|
||||
border-left: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
#map {
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
li.crop-hierarchy {
|
||||
list-style-type: disc;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
// !!! AUTOMATICALLY GENERATED FILE. DO NOT MODIFY !!!
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
// Core variables and mixins
|
||||
@import "twitter/bootstrap/variables.less";
|
||||
@import "custom_bootstrap/variables.less"; // Modify this for custom colors, font-sizes, etc
|
||||
@import "twitter/bootstrap/mixins.less";
|
||||
@import "custom_bootstrap/mixins.less"; // Modify this for custom mixins
|
||||
|
||||
// Reset and dependencies
|
||||
@import "twitter/bootstrap/normalize.less";
|
||||
@import "twitter/bootstrap/print.less";
|
||||
@import "twitter/bootstrap/glyphicons.less";
|
||||
|
||||
// Core CSS
|
||||
@import "twitter/bootstrap/scaffolding.less";
|
||||
@import "twitter/bootstrap/type.less";
|
||||
@import "twitter/bootstrap/code.less";
|
||||
@import "twitter/bootstrap/grid.less";
|
||||
@import "twitter/bootstrap/tables.less";
|
||||
@import "twitter/bootstrap/forms.less";
|
||||
@import "twitter/bootstrap/buttons.less";
|
||||
|
||||
// Components
|
||||
@import "twitter/bootstrap/component-animations.less";
|
||||
@import "twitter/bootstrap/dropdowns.less";
|
||||
@import "twitter/bootstrap/button-groups.less";
|
||||
@import "twitter/bootstrap/input-groups.less";
|
||||
@import "twitter/bootstrap/navs.less";
|
||||
@import "twitter/bootstrap/navbar.less";
|
||||
@import "twitter/bootstrap/breadcrumbs.less";
|
||||
@import "twitter/bootstrap/pagination.less";
|
||||
@import "twitter/bootstrap/pager.less";
|
||||
@import "twitter/bootstrap/labels.less";
|
||||
@import "twitter/bootstrap/badges.less";
|
||||
@import "twitter/bootstrap/jumbotron.less";
|
||||
@import "twitter/bootstrap/thumbnails.less";
|
||||
@import "twitter/bootstrap/alerts.less";
|
||||
@import "twitter/bootstrap/progress-bars.less";
|
||||
@import "twitter/bootstrap/media.less";
|
||||
@import "twitter/bootstrap/list-group.less";
|
||||
@import "twitter/bootstrap/panels.less";
|
||||
@import "twitter/bootstrap/responsive-embed.less";
|
||||
@import "twitter/bootstrap/wells.less";
|
||||
@import "twitter/bootstrap/close.less";
|
||||
|
||||
// Components w/ JavaScript
|
||||
@import "twitter/bootstrap/modals.less";
|
||||
@import "twitter/bootstrap/tooltip.less";
|
||||
@import "twitter/bootstrap/popovers.less";
|
||||
@import "twitter/bootstrap/carousel.less";
|
||||
|
||||
// Utility classes
|
||||
@import "twitter/bootstrap/utilities.less";
|
||||
@import "twitter/bootstrap/responsive-utilities.less";
|
||||
1
app/assets/stylesheets/custom_bootstrap/mixins.less
Normal file
1
app/assets/stylesheets/custom_bootstrap/mixins.less
Normal file
@@ -0,0 +1 @@
|
||||
// Use this file to override Twitter Bootstrap mixins or define own mixins.
|
||||
54
app/assets/stylesheets/custom_bootstrap/variables.less
Normal file
54
app/assets/stylesheets/custom_bootstrap/variables.less
Normal file
@@ -0,0 +1,54 @@
|
||||
// Use this file to override Twitter Bootstrap variables or define own variables.
|
||||
|
||||
// Import original variables so they can be used in overrides
|
||||
@import "twitter/bootstrap/variables.less";
|
||||
|
||||
// Base colours
|
||||
|
||||
@beige: #f3f1ee;
|
||||
@brown: #413f3b;
|
||||
|
||||
@green: #5f8e43;
|
||||
@blue: #2f4365;
|
||||
@red: #8e4d43;
|
||||
@orange: #b2685c;
|
||||
@yellow: #b2935c;
|
||||
|
||||
@body-bg: @beige;
|
||||
@text-color: @brown;
|
||||
@link-color: @green;
|
||||
|
||||
@brand-primary: @green;
|
||||
|
||||
@font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
@font-family-serif: Georgia, "Times New Roman", Times, serif;
|
||||
@font-family-mono: Monaco, Menlo, Consolas, "Courier New", monospace;
|
||||
|
||||
@font-size-base: 14px;
|
||||
@font-family-base: @font-family-sans-serif;
|
||||
@line-height-base: 1.5;
|
||||
@alt-font-family: @font-family-serif;
|
||||
|
||||
@headings-font-family: @font-family-sans-serif;
|
||||
@headings-font-weight: bold; // instead of browser default, bold
|
||||
@headings-color: inherit; // empty to use BS default, @textColor
|
||||
|
||||
// Hero unit
|
||||
@jumbotron-bg: darken(@body-bg, 10%);
|
||||
|
||||
// Nav bar
|
||||
@navbar-default-bg: @brown;
|
||||
@navbar-default-bg-highlight: @brown;
|
||||
@navbar-default-color: @beige;
|
||||
@navbar-default-link-color: darken(@beige, 20%);
|
||||
@navbar-default-link-hover-color: @beige;
|
||||
@navbar-default-link-active-color: @beige;
|
||||
@navbar-default-brand-color: lighten(@green, 20%);
|
||||
|
||||
// Top nav collapse threshold
|
||||
@grid-float-breakpoint: @screen-md-min;
|
||||
|
||||
@dropdown-bg: lighten(@beige, 10%);
|
||||
@dropdown-link-color: @brown;
|
||||
@dropdown-link-hover-color: @brown;
|
||||
@dropdown-link-hover-bg: lighten(@green, 50%);
|
||||
263
app/assets/stylesheets/overrides.css.less
Normal file
263
app/assets/stylesheets/overrides.css.less
Normal file
@@ -0,0 +1,263 @@
|
||||
@import "twitter/bootstrap/bootstrap";
|
||||
@import "custom_bootstrap/variables";
|
||||
// this padding needs to be done before the responsive stuff is imported
|
||||
body {
|
||||
// modifying this for our promotional banner. can be replaced after if
|
||||
// needed.
|
||||
// padding-top: @navbar-height + 15px;
|
||||
padding-top: @navbar-height;
|
||||
}
|
||||
|
||||
//@import "twitter/bootstrap/responsive";
|
||||
|
||||
// Set the correct sprite paths
|
||||
@iconSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings");
|
||||
@iconWhiteSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings-white");
|
||||
|
||||
// Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines)
|
||||
@fontAwesomeEotPath: asset-url("fontawesome-webfont.eot");
|
||||
@fontAwesomeEotPath_iefix: asset-url("fontawesome-webfont.eot#iefix");
|
||||
@fontAwesomeWoffPath: asset-url("fontawesome-webfont.woff");
|
||||
@fontAwesomeTtfPath: asset-url("fontawesome-webfont.ttf");
|
||||
@fontAwesomeSvgPath: asset-url("fontawesome-webfont.svg#fontawesomeregular");
|
||||
|
||||
// Font Awesome
|
||||
//@import "fontawesome/font-awesome";
|
||||
|
||||
// Glyphicons
|
||||
//@import "twitter/bootstrap/sprites.less";
|
||||
|
||||
|
||||
.list-inline > li.first {
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
/*
|
||||
#subtitle {
|
||||
color: lighten(@brown, 30%);
|
||||
margin-top: 0px;
|
||||
padding-top: 0px;
|
||||
padding-left: 1em;
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
}
|
||||
*/
|
||||
|
||||
h3 {
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
.main {
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
margin-left: -1px;
|
||||
border-left: 1px solid darken(@beige, 10%);
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
// this is used for eg. crops and members index pages
|
||||
.six-across:nth-child(6n+1) {
|
||||
margin-left: 0px
|
||||
}
|
||||
|
||||
.three-across:nth-child(3n+1) {
|
||||
margin-left: 0px;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
// let's condense the hero unit a little
|
||||
.jumbotron {
|
||||
padding-top: 30px;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
// info under the main heading on homepage
|
||||
.jumbotron .info {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
// signup widget on homepage
|
||||
.jumbotron .signup {
|
||||
background-color: lighten(@green, 40%);
|
||||
border: 1px solid lighten(@green, 20%);
|
||||
border-radius: 6px;
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
line-height: 200%;
|
||||
}
|
||||
|
||||
// stats shown on homepage. eg. "999 members..."
|
||||
p.stats {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.homepage-members {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.homepage-members:nth-child(odd) {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
#placesmap, #cropmap {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
#membermap {
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
.member-location {
|
||||
font-size: small;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.member-location a {
|
||||
color: @brown;
|
||||
}
|
||||
|
||||
.crop-thumbnail {
|
||||
position:relative;
|
||||
padding:0;
|
||||
img {
|
||||
width:100%;
|
||||
}
|
||||
.text {
|
||||
display:none;
|
||||
color:#000;
|
||||
position:absolute;
|
||||
bottom:0;
|
||||
background:rgba(0, 0, 0, 0.8);
|
||||
width:100%;
|
||||
margin:0;
|
||||
}
|
||||
p {
|
||||
padding:5px;
|
||||
margin:0;
|
||||
color:#fff;
|
||||
}
|
||||
&:hover {
|
||||
.text {
|
||||
display:block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.member-thumbnail {
|
||||
img {
|
||||
height: 85px;
|
||||
width: 85px;
|
||||
max-width: 85px
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
margin-bottom: 1.5em;
|
||||
|
||||
.scientific-name small, .crop-name a {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1em;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.crop-name a {
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.scientific-name small {
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
li.crop-hierarchy {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.navbar-bottom {
|
||||
margin: 40px 0px 0px 0px !important;
|
||||
}
|
||||
|
||||
// navbar centering
|
||||
footer .navbar .nav {
|
||||
float: none;
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
/* ie7 fix */
|
||||
*zoom: 1;
|
||||
/* hasLayout ie7 trigger */
|
||||
vertical-align: top;
|
||||
> li {
|
||||
float: none;
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
/* ie7 fix */
|
||||
*zoom: 1;
|
||||
/* hasLayout ie7 trigger */
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-bottom.navbar {
|
||||
text-align: center;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.crop-image, .member-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// Autosuggest
|
||||
|
||||
.ui-autocomplete {
|
||||
z-index: @zindex-tooltip;
|
||||
}
|
||||
|
||||
// Crowdfunding campaign, Sep-Oct 2014
|
||||
|
||||
.crowdfunding-banner {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
background-color: lighten(@green, 30%);
|
||||
margin-top: 0px;
|
||||
margin-bottom: 5px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.crowdfunding-banner a {
|
||||
color: @brown;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
// Overrides applying only to mobile view. This must be at the end of the overrides file.
|
||||
|
||||
@media only screen and (max-width: 767px) {
|
||||
.sidebar {
|
||||
margin-left: 0;
|
||||
border-left: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
#map {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.navbar .nav > li {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
class AccountTypesController < ApplicationController
|
||||
before_filter :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /account_types
|
||||
def index
|
||||
@account_types = AccountType.all
|
||||
@@ -64,7 +66,7 @@ class AccountTypesController < ApplicationController
|
||||
@account_type.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to account_types_url }
|
||||
format.html { redirect_to account_types_url, notice: 'Account type was successfully deleted.' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
class AccountsController < ApplicationController
|
||||
before_filter :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /accounts
|
||||
def index
|
||||
@accounts = Account.all
|
||||
|
||||
91
app/controllers/alternate_names_controller.rb
Normal file
91
app/controllers/alternate_names_controller.rb
Normal file
@@ -0,0 +1,91 @@
|
||||
class AlternateNamesController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /alternate_names
|
||||
# GET /alternate_names.json
|
||||
def index
|
||||
@alternate_names = AlternateName.all
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.haml
|
||||
format.json { render json: @alternate_names }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /alternate_names/1
|
||||
# GET /alternate_names/1.json
|
||||
def show
|
||||
@alternate_name = AlternateName.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.haml
|
||||
format.json { render json: @alternate_name }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /alternate_names/new
|
||||
# GET /alternate_names/new.json
|
||||
def new
|
||||
@alternate_name = AlternateName.new
|
||||
@crop = Crop.find_by_id(params[:crop_id]) || Crop.new
|
||||
|
||||
respond_to do |format|
|
||||
format.html # new.html.haml
|
||||
format.json { render json: @alternate_name }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /alternate_names/1/edit
|
||||
def edit
|
||||
@alternate_name = AlternateName.find(params[:id])
|
||||
end
|
||||
|
||||
# POST /alternate_names
|
||||
# POST /alternate_names.json
|
||||
def create
|
||||
params[:alternate_name][:creator_id] = current_member.id
|
||||
@alternate_name = AlternateName.new(params[:alternate_name])
|
||||
|
||||
respond_to do |format|
|
||||
if @alternate_name.save
|
||||
format.html { redirect_to @alternate_name.crop, notice: 'Alternate name was successfully created.' }
|
||||
format.json { render json: @alternate_name, status: :created, location: @alternate_name }
|
||||
else
|
||||
format.html { render action: "new" }
|
||||
format.json { render json: @alternate_name.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# PUT /alternate_names/1
|
||||
# PUT /alternate_names/1.json
|
||||
def update
|
||||
@alternate_name = AlternateName.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @alternate_name.update_attributes(params[:alternate_name])
|
||||
format.html { redirect_to @alternate_name.crop, notice: 'Alternate name was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
format.html { render action: "edit" }
|
||||
format.json { render json: @alternate_name.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /alternate_names/1
|
||||
# DELETE /alternate_names/1.json
|
||||
def destroy
|
||||
@alternate_name = AlternateName.find(params[:id])
|
||||
@crop = @alternate_name.crop
|
||||
@alternate_name.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
redirect_to @crop, notice: 'Alternate name was successfully deleted.'
|
||||
}
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
end
|
||||
13
app/controllers/api/v1/crops_controller.rb
Normal file
13
app/controllers/api/v1/crops_controller.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
class Api::V1::CropsController < ApplicationController
|
||||
|
||||
# GET /api/v1/crops
|
||||
def index
|
||||
@crops = Crop.all
|
||||
end
|
||||
|
||||
# GET /crops/1
|
||||
def show
|
||||
@crop = Crop.find(params[:id])
|
||||
end
|
||||
|
||||
end
|
||||
@@ -4,17 +4,22 @@ class ApplicationController < ActionController::Base
|
||||
include ApplicationHelper
|
||||
|
||||
after_filter :store_location
|
||||
before_filter :set_locale
|
||||
|
||||
def store_location
|
||||
# store last url - this is needed for post-login redirect to whatever the user last visited.
|
||||
if (request.fullpath != new_member_session_path && \
|
||||
!request.xhr?) # don't store ajax calls
|
||||
session[:previous_url] = request.fullpath
|
||||
if (request.path != "/members/sign_in" &&
|
||||
request.path != "/members/sign_up" &&
|
||||
request.path != "/members/password/new" &&
|
||||
request.path != "/members/password/edit" &&
|
||||
request.path != "/members/confirmation" &&
|
||||
request.path != "/members/sign_out" &&
|
||||
!request.xhr?)
|
||||
store_location_for(:member, request.fullpath)
|
||||
end
|
||||
end
|
||||
|
||||
def after_sign_in_path_for(resource)
|
||||
session[:previous_url] || root_path
|
||||
stored_location_for(:member) || root_path
|
||||
end
|
||||
|
||||
# tweak CanCan defaults because we don't have a "current_user" method
|
||||
@@ -28,5 +33,14 @@ class ApplicationController < ActionController::Base
|
||||
rescue_from CanCan::AccessDenied do |exception|
|
||||
redirect_to request.referer || root_url, :alert => exception.message
|
||||
end
|
||||
|
||||
def set_locale
|
||||
I18n.locale = params[:locale] || extract_locale_from_subdomain || I18n.default_locale
|
||||
end
|
||||
|
||||
def extract_locale_from_subdomain
|
||||
parsed_locale = request.subdomains.first
|
||||
I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class AuthenticationsController < ApplicationController
|
||||
before_filter :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /authentications
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class CommentsController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
cache_sweeper :comment_sweeper
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
class CropsController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :hierarchy, :search, :show]
|
||||
load_and_authorize_resource
|
||||
skip_authorize_resource :only => :hierarchy
|
||||
skip_authorize_resource :only => [:hierarchy, :search]
|
||||
|
||||
cache_sweeper :crop_sweeper
|
||||
|
||||
# GET /crops
|
||||
# GET /crops.json
|
||||
def index
|
||||
@crops = Crop.includes(:scientific_names, {:plantings => :photos}).paginate(:page => params[:page])
|
||||
@sort = params[:sort]
|
||||
if @sort == 'alpha'
|
||||
# alphabetical order
|
||||
@crops = Crop.includes(:scientific_names, {:plantings => :photos}).paginate(:page => params[:page])
|
||||
else
|
||||
# default to sorting by popularity
|
||||
@crops = Crop.popular.includes(:scientific_names, {:plantings => :photos}).paginate(:page => params[:page])
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.html
|
||||
format.json { render :json => @crops }
|
||||
format.rss do
|
||||
@crops = Crop.recent.includes(:scientific_names, :creator)
|
||||
@@ -27,7 +35,7 @@ class CropsController < ApplicationController
|
||||
# GET /crops/wrangle
|
||||
def wrangle
|
||||
@crops = Crop.recent.paginate(:page => params[:page])
|
||||
|
||||
@crop_wranglers = Role.crop_wranglers
|
||||
respond_to do |format|
|
||||
format.html
|
||||
end
|
||||
@@ -41,14 +49,36 @@ class CropsController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
# 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.reject!{ |r| @exact_match && r.eql?(@exact_match) }
|
||||
|
||||
@fuzzy = Crop.search(params[:term])
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json { render :json => @fuzzy }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /crops/1
|
||||
# GET /crops/1.json
|
||||
def show
|
||||
@crop = Crop.includes(:scientific_names, {:plantings => :photos}).find(params[:id])
|
||||
@posts = @crop.posts.paginate(:page => params[:page])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.haml
|
||||
format.json { render json: @crop }
|
||||
format.json do
|
||||
render :json => @crop.to_json(:include => {
|
||||
:plantings => { :include => { :owner => { :only => [:id, :login_name, :location, :latitude, :longitude] }}}
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -56,7 +86,9 @@ class CropsController < ApplicationController
|
||||
# GET /crops/new.json
|
||||
def new
|
||||
@crop = Crop.new
|
||||
|
||||
3.times do
|
||||
@crop.scientific_names.build
|
||||
end
|
||||
respond_to do |format|
|
||||
format.html # new.html.haml
|
||||
format.json { render json: @crop }
|
||||
|
||||
25
app/controllers/follows_controller.rb
Normal file
25
app/controllers/follows_controller.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
class FollowsController < ApplicationController
|
||||
|
||||
# POST /follows
|
||||
def create
|
||||
@follow = current_member.follows.build(:followed_id => params[:followed_id])
|
||||
|
||||
if @follow.save
|
||||
flash[:notice] = "Followed #{ @follow.followed.login_name }"
|
||||
redirect_to :back
|
||||
else
|
||||
flash[:error] = "Already following or error while following."
|
||||
redirect_to :back
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /follows/1
|
||||
def destroy
|
||||
@follow = current_member.follows.find(params[:id])
|
||||
unfollowed_name = @follow.followed.login_name
|
||||
@follow.destroy
|
||||
|
||||
flash[:notice] = "Unfollowed #{ unfollowed_name }"
|
||||
redirect_to root_path
|
||||
end
|
||||
end
|
||||
@@ -80,7 +80,7 @@ class ForumsController < ApplicationController
|
||||
@forum.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to forums_url }
|
||||
format.html { redirect_to forums_url, notice: 'Forum was successfully deleted' }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
class GardensController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
|
||||
cache_sweeper :garden_sweeper
|
||||
|
||||
# GET /gardens
|
||||
@@ -85,7 +86,7 @@ class GardensController < ApplicationController
|
||||
@garden.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to @garden.owner, notice: 'Garden was successfully deleted.' }
|
||||
format.html { redirect_to gardens_by_owner_path(:owner => @garden.owner), notice: 'Garden was successfully deleted.' }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
class HarvestsController < ApplicationController
|
||||
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
|
||||
# GET /harvests
|
||||
# GET /harvests.json
|
||||
def index
|
||||
@owner = Member.find_by_slug(params[:owner])
|
||||
@crop = Crop.find_by_slug(params[:crop])
|
||||
if @owner
|
||||
@harvests = @owner.harvests.includes(:owner, :crop).paginate(:page => params[:page])
|
||||
@harvests = @owner.harvests.includes(:owner, :crop)
|
||||
elsif @crop
|
||||
@harvests = @crop.harvests.includes(:owner, :crop)
|
||||
else
|
||||
@harvests = Harvest.includes(:owner, :crop).paginate(:page => params[:page])
|
||||
@harvests = Harvest.includes(:owner, :crop)
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
format.html { @harvests = @harvests.paginate(:page => params[:page]) }
|
||||
format.json { render json: @harvests }
|
||||
format.csv do
|
||||
if @owner
|
||||
@filename = "Growstuff-#{@owner}-Harvests-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@harvests = @owner.harvests.includes(:owner, :crop)
|
||||
else
|
||||
@filename = "Growstuff-Harvests-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@harvests = Harvest.includes(:owner, :crop)
|
||||
end
|
||||
specifics = (@owner ? "#{@owner.name}-" : @crop ? "#{@crop.name}-" : nil)
|
||||
@filename = "Growstuff-#{specifics}Harvests-#{Time.zone.now.to_s(:number)}.csv"
|
||||
render :csv => @harvests
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,6 +10,7 @@ class MembersController < ApplicationController
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.haml
|
||||
format.json { render :json => @members.to_json(:only => [:id, :login_name, :slug, :bio, :created_at, :location, :latitude, :longitude]) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,9 +23,10 @@ class MembersController < ApplicationController
|
||||
# it requires a garden to be passed in @garden.
|
||||
# The new garden is not persisted unless Garden#save is called.
|
||||
@garden = Garden.new
|
||||
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.haml
|
||||
format.json { render :json => @member.to_json(:only => [:id, :login_name, :bio, :created_at, :slug, :location, :latitude, :longitude]) }
|
||||
format.rss { render(
|
||||
:layout => false,
|
||||
:locals => { :member => @member }
|
||||
@@ -32,4 +34,14 @@ class MembersController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
def view_follows
|
||||
@member = Member.confirmed.find(params[:login_name])
|
||||
@follows = @member.followed.paginate(:page => params[:page])
|
||||
end
|
||||
|
||||
def view_followers
|
||||
@member = Member.confirmed.find(params[:login_name])
|
||||
@followers = @member.followers.paginate(:page => params[:page])
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
class NotificationsController < ApplicationController
|
||||
include NotificationsHelper
|
||||
before_filter :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /notifications
|
||||
def index
|
||||
@notifications = Notification.find_all_by_recipient_id(current_member)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class OrderItemsController < ApplicationController
|
||||
before_filter :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
|
||||
# POST /order_items
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class OrdersController < ApplicationController
|
||||
before_filter :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /orders
|
||||
|
||||
7
app/controllers/passwords_controller.rb
Normal file
7
app/controllers/passwords_controller.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
class PasswordsController < Devise::PasswordsController
|
||||
|
||||
protected
|
||||
def after_resetting_password_path_for(resource)
|
||||
root_path
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,7 @@
|
||||
class PhotosController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
|
||||
cache_sweeper :photo_sweeper
|
||||
|
||||
# GET /photos
|
||||
@@ -29,7 +30,8 @@ class PhotosController < ApplicationController
|
||||
# GET /photos/new.json
|
||||
def new
|
||||
@photo = Photo.new
|
||||
@planting_id = params[:planting_id]
|
||||
@type = params[:type]
|
||||
@id = params[:id]
|
||||
|
||||
page = params[:page] || 1
|
||||
|
||||
@@ -63,19 +65,35 @@ class PhotosController < ApplicationController
|
||||
@photo.owner_id = current_member.id
|
||||
@photo.set_flickr_metadata
|
||||
|
||||
if params[:planting_id]
|
||||
planting = Planting.find_by_id(params[:planting_id])
|
||||
if planting
|
||||
if planting.owner.id == current_member.id
|
||||
@photo.plantings << planting unless @photo.plantings.include?(planting)
|
||||
# several models can have photos. we need to know what model and the id
|
||||
# for the entry to attach the photo to
|
||||
valid_models = ["planting", "harvest"]
|
||||
if params[:type]
|
||||
if valid_models.include?(params[:type])
|
||||
if params[:id]
|
||||
item = params[:type].camelcase.constantize.find_by_id(params[:id])
|
||||
if item
|
||||
if item.owner.id == current_member.id
|
||||
# This syntax is weird, so just know that it means this:
|
||||
# @photo.harvests << item unless @photo.harvests.include?(item)
|
||||
# but with the correct many-to-many relationship automatically referenced
|
||||
(@photo.send "#{params[:type]}s") << item unless (@photo.send "#{params[:type]}s").include?(item)
|
||||
else
|
||||
flash[:alert] = "You must own both the #{params[:type]} and the photo."
|
||||
end
|
||||
else
|
||||
flash[:alert] = "Couldn't find #{params[:type]} to connect to photo."
|
||||
end
|
||||
else
|
||||
flash[:alert] = "You must own both the planting and the photo."
|
||||
flash[:alert] = "Missing id parameter"
|
||||
end
|
||||
else
|
||||
flash[:alert] = "Couldn't find planting to connect to photo."
|
||||
flash[:alert] = "Cannot attach photos to #{params[:type]}"
|
||||
end
|
||||
else
|
||||
flash[:alert] = "Missing type parameter"
|
||||
end
|
||||
|
||||
|
||||
respond_to do |format|
|
||||
if @photo.save
|
||||
format.html { redirect_to @photo, notice: 'Photo was successfully added.' }
|
||||
|
||||
@@ -5,7 +5,7 @@ class PlacesController < ApplicationController
|
||||
respond_to do |format|
|
||||
format.html
|
||||
# json response is whatever we want to map here
|
||||
format.json { render :json => Member.located.to_json(:only => [:id, :login_name, :location, :latitude, :longitude]) }
|
||||
format.json { render :json => Member.located.to_json(:only => [:id, :login_name, :slug, :location, :latitude, :longitude]) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -16,7 +16,7 @@ class PlacesController < ApplicationController
|
||||
@nearby_members = Member.nearest_to(params[:place])
|
||||
respond_to do |format|
|
||||
format.html # show.html.haml
|
||||
format.json { render :json => @nearby_members.to_json(:only => [:id, :login_name, :location, :latitude, :longitude]) }
|
||||
format.json { render :json => @nearby_members.to_json(:only => [:id, :login_name, :slug, :location, :latitude, :longitude]) }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
85
app/controllers/plant_parts_controller.rb
Normal file
85
app/controllers/plant_parts_controller.rb
Normal file
@@ -0,0 +1,85 @@
|
||||
class PlantPartsController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /plant_parts
|
||||
# GET /plant_parts.json
|
||||
def index
|
||||
@plant_parts = PlantPart.all
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
format.json { render json: @plant_parts }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /plant_parts/1
|
||||
# GET /plant_parts/1.json
|
||||
def show
|
||||
@plant_part = PlantPart.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.erb
|
||||
format.json { render json: @plant_part }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /plant_parts/new
|
||||
# GET /plant_parts/new.json
|
||||
def new
|
||||
@plant_part = PlantPart.new
|
||||
|
||||
respond_to do |format|
|
||||
format.html # new.html.erb
|
||||
format.json { render json: @plant_part }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /plant_parts/1/edit
|
||||
def edit
|
||||
@plant_part = PlantPart.find(params[:id])
|
||||
end
|
||||
|
||||
# POST /plant_parts
|
||||
# POST /plant_parts.json
|
||||
def create
|
||||
@plant_part = PlantPart.new(params[:plant_part])
|
||||
|
||||
respond_to do |format|
|
||||
if @plant_part.save
|
||||
format.html { redirect_to @plant_part, notice: 'Plant part was successfully created.' }
|
||||
format.json { render json: @plant_part, status: :created, location: @plant_part }
|
||||
else
|
||||
format.html { render action: "new" }
|
||||
format.json { render json: @plant_part.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# PUT /plant_parts/1
|
||||
# PUT /plant_parts/1.json
|
||||
def update
|
||||
@plant_part = PlantPart.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @plant_part.update_attributes(params[:plant_part])
|
||||
format.html { redirect_to @plant_part, notice: 'Plant part was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
format.html { render action: "edit" }
|
||||
format.json { render json: @plant_part.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /plant_parts/1
|
||||
# DELETE /plant_parts/1.json
|
||||
def destroy
|
||||
@plant_part = PlantPart.find(params[:id])
|
||||
@plant_part.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to plant_parts_url }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,7 @@
|
||||
class PlantingsController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
|
||||
cache_sweeper :planting_sweeper
|
||||
|
||||
@@ -7,24 +9,22 @@ class PlantingsController < ApplicationController
|
||||
# GET /plantings.json
|
||||
def index
|
||||
@owner = Member.find_by_slug(params[:owner])
|
||||
@crop = Crop.find_by_slug(params[:crop])
|
||||
if @owner
|
||||
@plantings = @owner.plantings.includes(:owner, :crop, :garden).paginate(:page => params[:page])
|
||||
elsif @crop
|
||||
@plantings = @crop.plantings.includes(:owner, :crop, :garden).paginate(:page => params[:page])
|
||||
else
|
||||
@plantings = Planting.includes(:owner, :crop, :garden).paginate(:page => params[:page])
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
format.html { @plantings = @plantings.paginate(:page => params[:page]) }
|
||||
format.json { render json: @plantings }
|
||||
format.rss { render :layout => false } #index.rss.builder
|
||||
format.csv do
|
||||
if @owner
|
||||
@filename = "Growstuff-#{@owner}-Plantings-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@plantings = @owner.plantings.includes(:owner, :crop, :garden)
|
||||
else
|
||||
@filename = "Growstuff-Plantings-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@plantings = Planting.includes(:owner, :crop, :garden)
|
||||
end
|
||||
specifics = (@owner ? "#{@owner.name}-" : @crop ? "#{@crop.name}-" : nil)
|
||||
@filename = "Growstuff-#{specifics}Plantings-#{Time.zone.now.to_s(:number)}.csv"
|
||||
render :csv => @plantings
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class PostsController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
cache_sweeper :post_sweeper
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
class ProductsController < ApplicationController
|
||||
before_filter :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /products
|
||||
def index
|
||||
@products = Product.all
|
||||
|
||||
@@ -10,26 +10,37 @@ class RegistrationsController < Devise::RegistrationsController
|
||||
|
||||
# we need this subclassed method so that Devise doesn't force people to
|
||||
# change their password every time they want to edit their settings.
|
||||
# we also check that they give their current password to change their password.
|
||||
# Code copied from
|
||||
# https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-account-without-providing-a-password
|
||||
|
||||
def update
|
||||
# required for settings form to submit when password is left blank
|
||||
if params[:member][:password].blank?
|
||||
params[:member].delete("password")
|
||||
params[:member].delete("password_confirmation")
|
||||
params[:member].delete("current_password")
|
||||
end
|
||||
|
||||
@member = Member.find(current_member.id)
|
||||
|
||||
if @member.update_attributes(params[:member])
|
||||
successfully_updated = if needs_password?(@member, params)
|
||||
@member.update_with_password(devise_parameter_sanitizer.sanitize(:account_update))
|
||||
else
|
||||
# remove the virtual current_password attribute
|
||||
# update_without_password doesn't know how to ignore it
|
||||
params[:member].delete(:current_password)
|
||||
@member.update_without_password(devise_parameter_sanitizer.sanitize(:account_update))
|
||||
end
|
||||
|
||||
if successfully_updated
|
||||
set_flash_message :notice, :updated
|
||||
# Sign in the member bypassing validation in case his password changed
|
||||
# Sign in the member bypassing validation in case their password changed
|
||||
sign_in @member, :bypass => true
|
||||
redirect_to edit_member_registration_path
|
||||
else
|
||||
render "edit"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
# check if we need the current password to update fields
|
||||
def needs_password?(member, params)
|
||||
params[:member][:password].present? ||
|
||||
params[:member][:password_confirmation].present?
|
||||
end
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
class RolesController < ApplicationController
|
||||
before_filter :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /roles
|
||||
def index
|
||||
@roles = Role.all
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class ScientificNamesController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
cache_sweeper :scientific_name_sweeper
|
||||
@@ -83,7 +84,9 @@ class ScientificNamesController < ApplicationController
|
||||
@scientific_name.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to @crop }
|
||||
format.html {
|
||||
redirect_to @crop, notice: 'Scientific name was successfully deleted.'
|
||||
}
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class SeedsController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
cache_sweeper :seed_sweeper
|
||||
@@ -7,8 +8,11 @@ class SeedsController < ApplicationController
|
||||
# GET /seeds.json
|
||||
def index
|
||||
@owner = Member.find_by_slug(params[:owner])
|
||||
@crop = Crop.find_by_slug(params[:crop])
|
||||
if @owner
|
||||
@seeds = @owner.seeds.includes(:owner, :crop).paginate(:page => params[:page])
|
||||
elsif @crop
|
||||
@seeds = @crop.seeds.includes(:owner, :crop).paginate(:page => params[:page])
|
||||
else
|
||||
@seeds = Seed.includes(:owner, :crop).paginate(:page => params[:page])
|
||||
end
|
||||
|
||||
22
app/helpers/auto_suggest_helper.rb
Normal file
22
app/helpers/auto_suggest_helper.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
module AutoSuggestHelper
|
||||
|
||||
def auto_suggest(resource, source, options={})
|
||||
if options[:default] && !options[:default].new_record?
|
||||
default = options[:default]
|
||||
default_id = options[:default].try(:id)
|
||||
else
|
||||
default = resource.send(source)
|
||||
default_id = default.try(:id)
|
||||
end
|
||||
|
||||
resource = resource.class.name.downcase
|
||||
source_path = Rails.application.routes.url_helpers.send("#{source}s_search_path")
|
||||
|
||||
%Q{
|
||||
<input id="#{source}" class="auto-suggest #{options[:class]}" type="text" value="#{default}" data-source-url="#{source_path}", placeholder="e.g. lettuce">
|
||||
<noscript class="text-warning">Warning: Javascript must be available to search and match crops</noscript>
|
||||
<input id="#{resource}_#{source}_id" class="auto-suggest-id" type="hidden" name="#{resource}[#{source}_id]" value="#{default_id}">
|
||||
}.html_safe
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,2 +0,0 @@
|
||||
module SeedsHelper
|
||||
end
|
||||
@@ -9,4 +9,17 @@ class Notifier < ActionMailer::Base
|
||||
mail(:to => @notification.recipient.email,
|
||||
:subject => @notification.subject)
|
||||
end
|
||||
|
||||
def planting_reminder(member)
|
||||
@member = member
|
||||
|
||||
@plantings = @member.plantings.reorder.last(5)
|
||||
@harvests = @member.harvests.reorder.last(5)
|
||||
|
||||
if @member.send_planting_reminder
|
||||
mail(:to => @member.email,
|
||||
:subject => "What have you planted lately?")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -6,6 +6,8 @@ class Ability
|
||||
|
||||
# everyone can do these things, even non-logged in
|
||||
can :read, :all
|
||||
can :view_follows, Member
|
||||
can :view_followers, Member
|
||||
|
||||
# except these, which don't make sense if you're not logged in
|
||||
cannot :read, Notification
|
||||
@@ -40,6 +42,7 @@ class Ability
|
||||
can :wrangle, Crop
|
||||
can :manage, Crop
|
||||
can :manage, ScientificName
|
||||
can :manage, AlternateName
|
||||
end
|
||||
|
||||
# can create & destroy their own authentications against other sites.
|
||||
@@ -90,6 +93,11 @@ class Ability
|
||||
cannot :update, OrderItem, :order => { :member_id => member.id, :completed_at => nil }
|
||||
cannot :destroy, OrderItem, :order => { :member_id => member.id, :completed_at => nil }
|
||||
|
||||
# following/unfollowing permissions
|
||||
can :create, Follow do |f|
|
||||
!member.already_following?(f.followed) && f.followed_id != member.id
|
||||
end
|
||||
|
||||
if member.has_role? :admin
|
||||
|
||||
can :read, :all
|
||||
@@ -101,6 +109,12 @@ class Ability
|
||||
cannot :destroy, Order
|
||||
cannot :manage, OrderItem
|
||||
|
||||
# can't delete plant parts if they have harvests associated with them
|
||||
cannot :destroy, PlantPart
|
||||
can :destroy, PlantPart do |pp|
|
||||
pp.harvests.empty?
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
5
app/models/alternate_name.rb
Normal file
5
app/models/alternate_name.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class AlternateName < ActiveRecord::Base
|
||||
attr_accessible :crop_id, :name, :creator_id
|
||||
belongs_to :crop
|
||||
belongs_to :creator, :class_name => 'Member'
|
||||
end
|
||||
@@ -1,21 +1,31 @@
|
||||
class Crop < ActiveRecord::Base
|
||||
extend FriendlyId
|
||||
friendly_id :name, use: :slugged
|
||||
attr_accessible :en_wikipedia_url, :name, :parent_id, :creator_id
|
||||
attr_accessible :en_wikipedia_url, :name, :parent_id, :creator_id, :scientific_names_attributes
|
||||
|
||||
has_many :scientific_names
|
||||
accepts_nested_attributes_for :scientific_names,
|
||||
:allow_destroy => true,
|
||||
:reject_if => :all_blank
|
||||
|
||||
has_many :alternate_names
|
||||
has_many :plantings
|
||||
has_many :photos, :through => :plantings
|
||||
has_many :seeds
|
||||
has_many :harvests
|
||||
has_many :plant_parts, :through => :harvests, :uniq => :true
|
||||
belongs_to :creator, :class_name => 'Member'
|
||||
|
||||
belongs_to :parent, :class_name => 'Crop'
|
||||
has_many :varieties, :class_name => 'Crop', :foreign_key => 'parent_id'
|
||||
has_and_belongs_to_many :posts
|
||||
before_destroy {|crop| crop.posts.clear}
|
||||
|
||||
|
||||
default_scope order("lower(name) asc")
|
||||
scope :recent, reorder("created_at desc")
|
||||
scope :toplevel, where(:parent_id => nil)
|
||||
scope :popular, reorder("plantings_count desc, lower(name) asc")
|
||||
scope :randomized, reorder('random()') # ok on sqlite and psql, but not on mysql
|
||||
|
||||
validates :en_wikipedia_url,
|
||||
@@ -24,11 +34,6 @@ class Crop < ActiveRecord::Base
|
||||
:message => 'is not a valid English Wikipedia URL'
|
||||
}
|
||||
|
||||
def Crop.random
|
||||
@crop = Crop.offset(rand(Crop.count)).first
|
||||
return @crop
|
||||
end
|
||||
|
||||
def to_s
|
||||
return name
|
||||
end
|
||||
@@ -78,6 +83,20 @@ class Crop < ActiveRecord::Base
|
||||
return planted_from
|
||||
end
|
||||
|
||||
# crop.popular_plant_parts
|
||||
# returns a hash of most harvested plant parts (fruit, seed, etc)
|
||||
# key: plant part (eg. 'fruit')
|
||||
# value: count of how many times it's been used by harvests
|
||||
def popular_plant_parts
|
||||
popular_plant_parts = Hash.new(0)
|
||||
harvests.each do |h|
|
||||
if h.plant_part
|
||||
popular_plant_parts[h.plant_part] += 1
|
||||
end
|
||||
end
|
||||
return popular_plant_parts
|
||||
end
|
||||
|
||||
def interesting?
|
||||
min_plantings = 3 # needs this many plantings to be interesting
|
||||
min_photos = 3 # needs this many photos to be interesting
|
||||
@@ -103,48 +122,91 @@ class Crop < ActiveRecord::Base
|
||||
# used by db/seeds.rb and rake growstuff:import_crops
|
||||
# CSV fields:
|
||||
# - name (required)
|
||||
# - scientific name (optional, can be picked up from parent if it has one)
|
||||
# - en_wikipedia_url (required)
|
||||
# - parent (name, optional)
|
||||
# - scientific name (optional, can be picked up from parent if it has one)
|
||||
|
||||
def Crop.create_from_csv(row)
|
||||
name,scientific_name,en_wikipedia_url,parent = row
|
||||
name,en_wikipedia_url,parent,scientific_names,alternate_names = row
|
||||
|
||||
@cropbot = Member.find_by_login_name('cropbot')
|
||||
raise "cropbot account not found: run rake db:seed" unless @cropbot
|
||||
cropbot = Member.find_by_login_name('cropbot')
|
||||
raise "cropbot account not found: run rake db:seed" unless cropbot
|
||||
|
||||
@crop = Crop.find_or_create_by_name(name)
|
||||
@crop.update_attributes(
|
||||
crop = Crop.find_or_create_by_name(name)
|
||||
crop.update_attributes(
|
||||
:en_wikipedia_url => en_wikipedia_url,
|
||||
:creator_id => @cropbot.id
|
||||
:creator_id => cropbot.id
|
||||
)
|
||||
|
||||
if parent
|
||||
@parent = Crop.find_by_name(parent)
|
||||
if @parent
|
||||
@crop.update_attributes(:parent_id => @parent.id)
|
||||
parent = Crop.find_by_name(parent)
|
||||
if parent
|
||||
crop.update_attributes(:parent_id => parent.id)
|
||||
else
|
||||
logger.warn("Warning: parent crop #{parent} not found")
|
||||
end
|
||||
end
|
||||
|
||||
unless @crop.scientific_names.exists?(:scientific_name => scientific_name)
|
||||
@sn = ''
|
||||
if scientific_name
|
||||
@sn = scientific_name
|
||||
elsif @crop.parent
|
||||
@sn = @crop.parent.scientific_names.first.scientific_name
|
||||
end
|
||||
crop.add_scientific_names_from_csv(scientific_names)
|
||||
crop.add_alternate_names_from_csv(alternate_names)
|
||||
|
||||
if @sn
|
||||
@crop.scientific_names.create(
|
||||
:scientific_name => @sn,
|
||||
:creator_id => @cropbot.id
|
||||
)
|
||||
else
|
||||
logger.warn("Warning: no scientific name (not even on parent crop) for #{@crop}")
|
||||
end
|
||||
|
||||
def add_scientific_names_from_csv(scientific_names)
|
||||
names_to_add = []
|
||||
if ! scientific_names.blank? # i.e. we actually passed something in, which isn't a given
|
||||
names_to_add = scientific_names.split(%r{,\s*})
|
||||
elsif parent && parent.scientific_names.size > 0 # pick up from parent
|
||||
names_to_add = parent.scientific_names.map{|s| s.scientific_name}
|
||||
else
|
||||
logger.warn("Warning: no scientific name (not even on parent crop) for #{self}")
|
||||
end
|
||||
|
||||
if names_to_add.size > 0
|
||||
cropbot = Member.find_by_login_name('cropbot')
|
||||
raise "cropbot account not found: run rake db:seed" unless cropbot
|
||||
|
||||
names_to_add.each do |n|
|
||||
if self.scientific_names.exists?(:scientific_name => n)
|
||||
logger.warn("Warning: skipping duplicate scientific name #{n} for #{self}")
|
||||
else
|
||||
|
||||
self.scientific_names.create(
|
||||
:scientific_name => n,
|
||||
:creator_id => cropbot.id
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_alternate_names_from_csv(alternate_names)
|
||||
names_to_add = []
|
||||
if ! alternate_names.blank? # i.e. we actually passed something in, which isn't a given
|
||||
cropbot = Member.find_by_login_name('cropbot')
|
||||
raise "cropbot account not found: run rake db:seed" unless cropbot
|
||||
|
||||
names_to_add = alternate_names.split(%r{,\s*})
|
||||
|
||||
names_to_add.each do |n|
|
||||
if self.alternate_names.exists?(:name => n)
|
||||
logger.warn("Warning: skipping duplicate alternate name #{n} for #{self}")
|
||||
else
|
||||
self.alternate_names.create(
|
||||
:name => n,
|
||||
:creator_id => cropbot.id
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
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}%")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
17
app/models/follow.rb
Normal file
17
app/models/follow.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
class Follow < ActiveRecord::Base
|
||||
attr_accessible :followed_id, :follower_id
|
||||
belongs_to :follower, class_name: "Member"
|
||||
belongs_to :followed, class_name: "Member"
|
||||
validates :follower_id, uniqueness: { :scope => :followed_id }
|
||||
|
||||
after_create do
|
||||
Notification.create(
|
||||
:recipient_id => self.followed_id,
|
||||
:sender_id => self.follower_id,
|
||||
:subject => "#{self.follower.login_name} is now following you",
|
||||
:body => "#{self.follower.login_name} just followed you on #{ENV["GROWSTUFF_SITE_NAME"]}. "
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
@@ -14,6 +14,7 @@ class Garden < ActiveRecord::Base
|
||||
geocoded_by :location
|
||||
after_validation :geocode
|
||||
after_validation :empty_unwanted_geocodes
|
||||
after_save :mark_inactive_garden_plantings_as_finished
|
||||
|
||||
default_scope order("lower(name) asc")
|
||||
scope :active, where(:active => true)
|
||||
@@ -25,7 +26,7 @@ class Garden < ActiveRecord::Base
|
||||
}
|
||||
|
||||
validates :area,
|
||||
:numericality => { :only_integer => false },
|
||||
:numericality => { :only_integer => false, :greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
|
||||
AREA_UNITS_VALUES = {
|
||||
@@ -74,4 +75,15 @@ class Garden < ActiveRecord::Base
|
||||
name
|
||||
end
|
||||
|
||||
# When you mark a garden as inactive, all the plantings in it should be
|
||||
# marked as finished. This automates that.
|
||||
def mark_inactive_garden_plantings_as_finished
|
||||
if (active == false)
|
||||
plantings.current.each do |p|
|
||||
p.finished = true
|
||||
p.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,15 +1,30 @@
|
||||
class Harvest < ActiveRecord::Base
|
||||
include ActionView::Helpers::NumberHelper
|
||||
extend FriendlyId
|
||||
friendly_id :harvest_slug, use: :slugged
|
||||
|
||||
attr_accessible :crop_id, :harvested_at, :description, :owner_id,
|
||||
:quantity, :unit, :weight_quantity, :weight_unit, :slug
|
||||
:quantity, :unit, :weight_quantity, :weight_unit, :plant_part_id, :slug
|
||||
|
||||
belongs_to :crop
|
||||
belongs_to :owner, :class_name => 'Member'
|
||||
belongs_to :plant_part
|
||||
|
||||
has_and_belongs_to_many :photos
|
||||
|
||||
before_destroy do |harvest|
|
||||
photolist = harvest.photos.to_a # save a temp copy of the photo list
|
||||
harvest.photos.clear # clear relationship b/w harvest and photo
|
||||
|
||||
photolist.each do |photo|
|
||||
photo.destroy_if_unused
|
||||
end
|
||||
end
|
||||
|
||||
default_scope order('created_at DESC')
|
||||
|
||||
validates :crop, :presence => {:message => "must be present and exist in our database"}
|
||||
|
||||
validates :quantity,
|
||||
:numericality => { :only_integer => false },
|
||||
:allow_nil => true
|
||||
@@ -20,16 +35,16 @@ class Harvest < ActiveRecord::Base
|
||||
"sprigs" => "sprig",
|
||||
"handfuls" => "handful",
|
||||
"litres" => "litre",
|
||||
"pints" => "ping",
|
||||
"pints" => "pint",
|
||||
"quarts" => "quart",
|
||||
"buckets" => "bucket",
|
||||
"baskets" => "basket",
|
||||
"bushels" => "bushel"
|
||||
}
|
||||
validates :unit, :inclusion => { :in => UNITS_VALUES.values,
|
||||
:message => "%{value} is not a valid unit" },
|
||||
:allow_nil => true,
|
||||
:allow_blank => true
|
||||
:message => "%{value} is not a valid unit" },
|
||||
:allow_nil => true,
|
||||
:allow_blank => true
|
||||
|
||||
validates :weight_quantity,
|
||||
:numericality => { :only_integer => false },
|
||||
@@ -37,12 +52,13 @@ class Harvest < ActiveRecord::Base
|
||||
|
||||
WEIGHT_UNITS_VALUES = {
|
||||
"kg" => "kg",
|
||||
"lb" => "lb"
|
||||
"lb" => "lb",
|
||||
"oz" => "oz"
|
||||
}
|
||||
validates :weight_unit, :inclusion => { :in => WEIGHT_UNITS_VALUES.values,
|
||||
:message => "%{value} is not a valid unit" },
|
||||
:allow_nil => true,
|
||||
:allow_blank => true
|
||||
:message => "%{value} is not a valid unit" },
|
||||
:allow_nil => true,
|
||||
:allow_blank => true
|
||||
|
||||
after_validation :cleanup_quantities
|
||||
|
||||
@@ -68,4 +84,41 @@ class Harvest < ActiveRecord::Base
|
||||
"#{owner.login_name}-#{crop}".downcase.gsub(' ', '-')
|
||||
end
|
||||
|
||||
# stringify as "beet in Skud's backyard" or similar
|
||||
def to_s
|
||||
# 50 individual apples, weighing 3lb
|
||||
# 2 buckets of apricots, weighing 10kg
|
||||
string = ''
|
||||
if self.quantity
|
||||
string += "#{number_to_human(self.quantity.to_s, :strip_insignificant_zeros => true)} "
|
||||
if self.unit == 'individual'
|
||||
string += 'individual '
|
||||
else
|
||||
if self.quantity == 1
|
||||
string += "#{self.unit} of "
|
||||
else
|
||||
string += "#{self.unit.pluralize} of "
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.unit != 'individual' # buckets of apricot*s*
|
||||
string += "#{self.crop.name.pluralize}"
|
||||
elsif self.quantity == 1
|
||||
string += "#{self.crop.name}"
|
||||
else
|
||||
string += "#{self.crop.name.pluralize}"
|
||||
end
|
||||
|
||||
if self.weight_quantity
|
||||
string += " weighing #{number_to_human(self.weight_quantity, :strip_insignificant_zeros => true)} #{self.weight_unit}"
|
||||
end
|
||||
|
||||
return string
|
||||
end
|
||||
|
||||
def default_photo
|
||||
return photos.first
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -27,6 +27,12 @@ class Member < ActiveRecord::Base
|
||||
|
||||
has_many :photos
|
||||
|
||||
has_many :follows, :class_name => "Follow", :foreign_key => "follower_id"
|
||||
has_many :followed, :through => :follows
|
||||
|
||||
has_many :inverse_follows, :class_name => "Follow", :foreign_key => "followed_id"
|
||||
has_many :followers, :through => :inverse_follows, :source => :follower
|
||||
|
||||
default_scope order("lower(login_name) asc")
|
||||
scope :confirmed, where('confirmed_at IS NOT NULL')
|
||||
scope :located, where("location <> '' and latitude IS NOT NULL and longitude IS NOT NULL")
|
||||
@@ -42,8 +48,13 @@ class Member < ActiveRecord::Base
|
||||
|
||||
# Setup accessible (or protected) attributes for your model
|
||||
attr_accessible :login_name, :email, :password, :password_confirmation,
|
||||
:remember_me, :login, :tos_agreement, :show_email, :newsletter,
|
||||
:location, :latitude, :longitude, :send_notification_email, :bio
|
||||
:remember_me, :login,
|
||||
# terms of service
|
||||
:tos_agreement,
|
||||
# profile stuff
|
||||
:bio, :location, :latitude, :longitude,
|
||||
# email settings
|
||||
:show_email, :newsletter, :send_notification_email, :send_planting_reminder
|
||||
|
||||
# set up geocoding
|
||||
geocoded_by :location
|
||||
@@ -143,8 +154,8 @@ class Member < ActiveRecord::Base
|
||||
if @flickr.nil?
|
||||
flickr_auth = auth('flickr')
|
||||
if flickr_auth
|
||||
FlickRaw.api_key = ENV['FLICKR_KEY']
|
||||
FlickRaw.shared_secret = ENV['FLICKR_SECRET']
|
||||
FlickRaw.api_key = ENV['GROWSTUFF_FLICKR_KEY']
|
||||
FlickRaw.shared_secret = ENV['GROWSTUFF_FLICKR_SECRET']
|
||||
@flickr = FlickRaw::Flickr.new
|
||||
@flickr.access_token = flickr_auth.token
|
||||
@flickr.access_secret = flickr_auth.secret
|
||||
@@ -235,7 +246,7 @@ class Member < ActiveRecord::Base
|
||||
def newsletter_subscribe
|
||||
gb = Gibbon::API.new
|
||||
res = gb.lists.subscribe({
|
||||
:id => ENV['MAILCHIMP_NEWSLETTER_ID'],
|
||||
:id => Gibbon::API.api_key,
|
||||
:email => { :email => email },
|
||||
:merge_vars => { :login_name => login_name },
|
||||
:double_optin => false # they alredy confirmed their email with us
|
||||
@@ -245,9 +256,17 @@ class Member < ActiveRecord::Base
|
||||
def newsletter_unsubscribe
|
||||
gb = Gibbon::API.new
|
||||
res = gb.lists.unsubscribe({
|
||||
:id => ENV['MAILCHIMP_NEWSLETTER_ID'],
|
||||
:id => ENV['GROWSTUFF_MAILCHIMP_NEWSLETTER_ID'],
|
||||
:email => { :email => email }
|
||||
})
|
||||
end
|
||||
|
||||
def already_following?(member)
|
||||
self.follows.exists?(:followed_id => member.id)
|
||||
end
|
||||
|
||||
def get_follow(member)
|
||||
self.follows.where(:followed_id => member.id).first if already_following?(member)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -4,10 +4,21 @@ class Photo < ActiveRecord::Base
|
||||
belongs_to :owner, :class_name => 'Member'
|
||||
|
||||
has_and_belongs_to_many :plantings
|
||||
before_destroy {|photo| photo.plantings.clear}
|
||||
has_and_belongs_to_many :harvests
|
||||
before_destroy do |photo|
|
||||
photo.plantings.clear
|
||||
photo.harvests.clear
|
||||
end
|
||||
|
||||
default_scope order("created_at desc")
|
||||
|
||||
# remove photos that aren't used by anything
|
||||
def destroy_if_unused
|
||||
unless plantings.size > 0 or harvests.size > 0
|
||||
self.destroy
|
||||
end
|
||||
end
|
||||
|
||||
# This is split into a side-effect free method and a side-effecting method
|
||||
# for easier stubbing and testing.
|
||||
def flickr_metadata
|
||||
|
||||
26
app/models/plant_part.rb
Normal file
26
app/models/plant_part.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
class PlantPart < ActiveRecord::Base
|
||||
extend FriendlyId
|
||||
friendly_id :name, :use => :slugged
|
||||
|
||||
has_many :harvests
|
||||
has_many :crops, :through => :harvests, :uniq => true
|
||||
|
||||
attr_accessible :name, :slug
|
||||
|
||||
def to_s
|
||||
return name
|
||||
end
|
||||
|
||||
# Postgres complains if the ORDER BY clause of a SELECT DISTINCT query is
|
||||
# not precisely one of the SELECTed fields. The default sort order on
|
||||
# crops is lower(name), and Postgres is not smart enough to notice that it
|
||||
# can calculate this from fields which are selected. The solution is to
|
||||
# override PlantParts#crops to remove the ORDER BY clause, and replace it
|
||||
# with `ORDER BY name`. This is not perfect, because it means the crops
|
||||
# associated to plant parts will not be sorted in the same order as crops
|
||||
# on the rest of the site.
|
||||
def crops
|
||||
return super.reorder('name')
|
||||
end
|
||||
|
||||
end
|
||||
@@ -3,16 +3,27 @@ class Planting < ActiveRecord::Base
|
||||
friendly_id :planting_slug, use: :slugged
|
||||
|
||||
attr_accessible :crop_id, :description, :garden_id, :planted_at,
|
||||
:quantity, :sunniness, :planted_from, :owner_id
|
||||
:quantity, :sunniness, :planted_from, :owner_id, :finished,
|
||||
:finished_at
|
||||
|
||||
belongs_to :garden
|
||||
belongs_to :owner, :class_name => 'Member', :counter_cache => true
|
||||
belongs_to :crop, :counter_cache => true
|
||||
|
||||
has_and_belongs_to_many :photos
|
||||
before_destroy {|planting| planting.photos.clear}
|
||||
|
||||
before_destroy do |planting|
|
||||
photolist = planting.photos.to_a # save a temp copy of the photo list
|
||||
planting.photos.clear # clear relationship b/w planting and photo
|
||||
|
||||
photolist.each do |photo|
|
||||
photo.destroy_if_unused
|
||||
end
|
||||
end
|
||||
|
||||
default_scope order("created_at desc")
|
||||
scope :finished, where(:finished => true)
|
||||
scope :current, where(:finished => false)
|
||||
|
||||
delegate :name,
|
||||
:en_wikipedia_url,
|
||||
@@ -23,6 +34,8 @@ class Planting < ActiveRecord::Base
|
||||
|
||||
default_scope order("created_at desc")
|
||||
|
||||
validates :crop_id, :presence => {:message => "must be present and exist in our database"}
|
||||
|
||||
validates :quantity,
|
||||
:numericality => { :only_integer => true },
|
||||
:allow_nil => true
|
||||
@@ -40,6 +53,7 @@ class Planting < ActiveRecord::Base
|
||||
'root division',
|
||||
'runner',
|
||||
'bulb',
|
||||
'root/tuber',
|
||||
'bare root plant',
|
||||
'advanced plant',
|
||||
'graft',
|
||||
@@ -50,6 +64,14 @@ class Planting < ActiveRecord::Base
|
||||
:allow_nil => true,
|
||||
:allow_blank => true
|
||||
|
||||
validate :finished_must_be_after_planted
|
||||
|
||||
# check that any finished_at date occurs after planted_at
|
||||
def finished_must_be_after_planted
|
||||
return unless planted_at and finished_at # only check if we have both
|
||||
errors.add(:finished_at, "must be after the planting date") unless planted_at < finished_at
|
||||
end
|
||||
|
||||
def planting_slug
|
||||
"#{owner.login_name}-#{garden}-#{crop}".downcase.gsub(' ', '-')
|
||||
end
|
||||
@@ -75,15 +97,15 @@ class Planting < ActiveRecord::Base
|
||||
# return a list of interesting plantings, for the homepage etc.
|
||||
# we can't do this via a scope (as far as we know) so sadly we have to
|
||||
# do it this way.
|
||||
def Planting.interesting
|
||||
howmany = 12 # max amount to collect
|
||||
|
||||
def Planting.interesting(howmany=12, require_photo=true)
|
||||
interesting_plantings = Array.new
|
||||
seen_owners = Hash.new(false) # keep track of which owners we've seen already
|
||||
|
||||
Planting.all.each do |p|
|
||||
break if interesting_plantings.count == howmany # got enough yet?
|
||||
next unless p.interesting? # skip those that don't have photos
|
||||
if require_photo
|
||||
next unless p.photos.present? # skip those without photos, if required
|
||||
end
|
||||
next if seen_owners[p.owner] # skip if we already have one from this owner
|
||||
seen_owners[p.owner] = true # we've seen this owner
|
||||
interesting_plantings.push(p)
|
||||
|
||||
@@ -5,6 +5,9 @@ class Post < ActiveRecord::Base
|
||||
belongs_to :author, :class_name => 'Member'
|
||||
belongs_to :forum
|
||||
has_many :comments, :dependent => :destroy
|
||||
has_and_belongs_to_many :crops
|
||||
before_destroy {|post| post.crops.clear}
|
||||
after_save :update_crops_posts_association
|
||||
# also has_many notifications, but kinda meaningless to get at them
|
||||
# from this direction, so we won't set up an association for now.
|
||||
|
||||
@@ -39,4 +42,15 @@ class Post < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def update_crops_posts_association
|
||||
self.crops.destroy_all
|
||||
# look for crops mentioned in the post. eg. [tomato](crop)
|
||||
self.body.scan(Haml::Filters::GrowstuffMarkdown::CROP_REGEX) do |m|
|
||||
# find crop case-insensitively
|
||||
crop = Crop.where('lower(name) = ?', $1.downcase).first
|
||||
# create association
|
||||
self.crops << crop if crop and not self.crops.include?(crop)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,4 +3,13 @@ class Role < ActiveRecord::Base
|
||||
friendly_id :name, use: :slugged
|
||||
attr_accessible :description, :name, :members, :slug
|
||||
has_and_belongs_to_many :members
|
||||
|
||||
class << self
|
||||
[:crop_wranglers, :admins].each do |method|
|
||||
define_method method do
|
||||
slug = method.to_s.singularize.dasherize
|
||||
Role.where(slug: slug).try(:first).try(:members)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@ class Seed < ActiveRecord::Base
|
||||
extend FriendlyId
|
||||
friendly_id :seed_slug, use: :slugged
|
||||
|
||||
attr_accessible :owner_id, :crop_id, :description, :quantity, :plant_before,
|
||||
attr_accessible :owner_id, :crop_id, :description, :quantity, :plant_before,
|
||||
:tradable_to, :slug
|
||||
|
||||
belongs_to :crop
|
||||
@@ -10,6 +10,7 @@ class Seed < ActiveRecord::Base
|
||||
|
||||
default_scope order("created_at desc")
|
||||
|
||||
validates :crop, :presence => {:message => "must be present and exist in our database"}
|
||||
validates :quantity,
|
||||
:numericality => { :only_integer => true },
|
||||
:allow_nil => true
|
||||
@@ -55,6 +56,6 @@ class Seed < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def seed_slug
|
||||
"#{owner.login_name}-#{crop.name}".downcase.gsub(' ', '-')
|
||||
"#{owner.login_name}-#{crop}".downcase.gsub(' ', '-')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
%dd= link_to 'media@growstuff.org', 'mailto:media@growstuff.org'
|
||||
%dl
|
||||
%dt Twitter
|
||||
%dd= link_to '@Growstuff', 'http:/twitter.com/Growstuff'
|
||||
%dd= link_to '@growstufforg', 'http://twitter.com/growstufforg'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%h1 Editing account_type
|
||||
-content_for :title, "Editing account type"
|
||||
|
||||
= render 'form'
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%h1 Listing account_types
|
||||
- content_for :title, "Listing account types"
|
||||
|
||||
%table
|
||||
%tr
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%h1 New account_type
|
||||
- content_for :title, "New account type"
|
||||
|
||||
= render 'form'
|
||||
|
||||
|
||||
@@ -12,4 +12,6 @@
|
||||
|
||||
= link_to 'Edit', edit_account_type_path(@account_type)
|
||||
\|
|
||||
= link_to 'Delete', @account_type, :method => :delete, :data => { :confirm => 'Are you sure?' }
|
||||
\|
|
||||
= link_to 'Back', account_types_path
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%h1 Editing account
|
||||
- content_for :title, "Editing account"
|
||||
|
||||
= render 'form'
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%h1 Listing accounts
|
||||
- content_for :title, "Listing accounts"
|
||||
|
||||
%table
|
||||
%tr
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%h1 New account
|
||||
- content_for :title, "New account"
|
||||
|
||||
= render 'form'
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
%h2 Manage
|
||||
|
||||
%ul
|
||||
%ul#admin_links
|
||||
%li= link_to "Account types", account_types_path
|
||||
%li= link_to "Products", products_path
|
||||
%li= link_to "Roles", roles_path
|
||||
|
||||
@@ -36,4 +36,4 @@
|
||||
@
|
||||
= price_with_currency(o.price)
|
||||
%br/
|
||||
%td= link_to 'Details', order, :class => 'btn btn-mini'
|
||||
%td= link_to 'Details', order, :class => 'btn btn-default btn-xs'
|
||||
|
||||
25
app/views/alternate_names/_form.html.haml
Normal file
25
app/views/alternate_names/_form.html.haml
Normal file
@@ -0,0 +1,25 @@
|
||||
= form_for @alternate_name, :html => {:class => 'form-horizontal', :role => "form"} do |f|
|
||||
- if @alternate_name.errors.any?
|
||||
#error_explanation
|
||||
%h2= "#{pluralize(@alternate_name.errors.count, "error")} prohibited this alternate_name from being saved:"
|
||||
%ul
|
||||
- @alternate_name.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
||||
%p
|
||||
%span.help-block
|
||||
For detailed crop wrangling guidelines, please consult the
|
||||
=link_to "crop wrangling guide", "http://wiki.growstuff.org/index.php/Crop_wrangling"
|
||||
on the Growstuff wiki.
|
||||
|
||||
.form-group
|
||||
= f.label :crop_id, :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= collection_select(:alternate_name, :crop_id, Crop.all, :id, :name, { :selected => @alternate_name.crop_id || @crop.id }, :class => 'form-control')
|
||||
.form-group
|
||||
= f.label :name, :class => 'control-label col-md-2'
|
||||
.col-md-8
|
||||
= f.text_field :name, :class => 'form-control'
|
||||
.form-group
|
||||
.form-actions.col-md-offset-2.col-md-8
|
||||
= f.submit 'Save', :class => 'btn btn-primary'
|
||||
9
app/views/alternate_names/edit.html.haml
Normal file
9
app/views/alternate_names/edit.html.haml
Normal file
@@ -0,0 +1,9 @@
|
||||
- content_for :title, "Edit alternate name"
|
||||
|
||||
%p
|
||||
Added by
|
||||
= @alternate_name.creator
|
||||
= distance_of_time_in_words(@alternate_name.created_at, Time.zone.now)
|
||||
ago.
|
||||
|
||||
= render 'form'
|
||||
23
app/views/alternate_names/index.html.haml
Normal file
23
app/views/alternate_names/index.html.haml
Normal file
@@ -0,0 +1,23 @@
|
||||
- content_for :title, "Listing alternate names"
|
||||
|
||||
- if can? :create, AlternateName
|
||||
%p= link_to 'New alternate name', new_alternate_name_path, :class => 'btn btn-primary'
|
||||
|
||||
%table
|
||||
%tr
|
||||
%th Alternate name
|
||||
%th Crop
|
||||
%th
|
||||
%th
|
||||
|
||||
- @alternate_names.each do |alternate_name|
|
||||
%tr
|
||||
%td= link_to alternate_name.name, alternate_name
|
||||
%td= alternate_name.crop
|
||||
%td= link_to 'Show', alternate_name
|
||||
%td
|
||||
- if can? :edit, alternate_name
|
||||
= link_to 'Edit', edit_alternate_name_path(alternate_name), :class => 'btn btn-default btn-xs'
|
||||
%td
|
||||
- if can? :destroy, alternate_name
|
||||
= link_to 'Delete', alternate_name, method: :delete, data: { confirm: 'Are you sure?' }, :class => 'btn btn-default btn-xs'
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user