mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-05-25 09:19:15 -04:00
Compare commits
896 Commits
2013-08-30
...
release7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a7fc57f66 | ||
|
|
175996ce90 | ||
|
|
f70f82014f | ||
|
|
8756808444 | ||
|
|
bb6e798bce | ||
|
|
a5c226d128 | ||
|
|
c7bcbf6323 | ||
|
|
f3302a9e30 | ||
|
|
1e331fc19d | ||
|
|
31a7011260 | ||
|
|
181c36cdc9 | ||
|
|
36ac0b75e7 | ||
|
|
d5aef16860 | ||
|
|
55ba535353 | ||
|
|
a510489570 | ||
|
|
fcbc6002d1 | ||
|
|
eeb48e2ad6 | ||
|
|
b114ed2414 | ||
|
|
1064f39020 | ||
|
|
b5a460a1b7 | ||
|
|
9a961b80e5 | ||
|
|
7bc05fa118 | ||
|
|
f7af7e649d | ||
|
|
4efb93ea45 | ||
|
|
a5edb6497c | ||
|
|
d6314ea9a1 | ||
|
|
c244ca672f | ||
|
|
401777cc4c | ||
|
|
7ef8afb1ba | ||
|
|
e25bb5578a | ||
|
|
401f3e176e | ||
|
|
10ae117914 | ||
|
|
86cea2ad9f | ||
|
|
ccef46a5cf | ||
|
|
c803da3dfa | ||
|
|
02ff45f705 | ||
|
|
8a05a959c3 | ||
|
|
e5d407b1ce | ||
|
|
5cd53be5be | ||
|
|
c6cc8ee6bb | ||
|
|
eaf4ea04ac | ||
|
|
007f7112a7 | ||
|
|
9d49a83918 | ||
|
|
901fed7d35 | ||
|
|
6deb0c659a | ||
|
|
f62fd8484a | ||
|
|
849bdac81b | ||
|
|
c1d2f2c9cc | ||
|
|
d92d22d99c | ||
|
|
8829acba3f | ||
|
|
66c4a19ad6 | ||
|
|
a65b78084f | ||
|
|
cdaa9c03a2 | ||
|
|
a35a573b2b | ||
|
|
0afd9dfbe2 | ||
|
|
c446ea9232 | ||
|
|
43c241219d | ||
|
|
77e424c412 | ||
|
|
82b70cf893 | ||
|
|
6b0a298fd1 | ||
|
|
78c6220262 | ||
|
|
6f2fa1b72b | ||
|
|
314cdc687a | ||
|
|
df3b300967 | ||
|
|
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 | ||
|
|
d174f25d03 | ||
|
|
e7bb285b8e | ||
|
|
b06d48091e | ||
|
|
844791ead9 | ||
|
|
73d6b5065b | ||
|
|
7ddaca6e11 | ||
|
|
2036a0f2e9 | ||
|
|
27ad791f67 | ||
|
|
13c98eac89 | ||
|
|
6d2d6a51d5 | ||
|
|
4e19da56a0 | ||
|
|
cf7ead3d84 | ||
|
|
969f7e7c23 | ||
|
|
14e8c367e1 | ||
|
|
924efe6af5 | ||
|
|
23f524a236 | ||
|
|
39b47b0d08 | ||
|
|
18c40809a6 | ||
|
|
81d1189203 | ||
|
|
2111cddaef | ||
|
|
eb4fae463f | ||
|
|
8c55a6101d | ||
|
|
2ca42bbae3 | ||
|
|
5de6759917 | ||
|
|
aeb7192f48 | ||
|
|
0ba6562d79 | ||
|
|
996043d8fb | ||
|
|
0eb5ae58aa | ||
|
|
6aea16028e | ||
|
|
5f2ca6bc9d | ||
|
|
9861f8cf51 | ||
|
|
284c28ce9a | ||
|
|
c674f30eb8 | ||
|
|
02994bd5cc | ||
|
|
53d782bc83 | ||
|
|
c83b01bbb7 | ||
|
|
fb595de457 | ||
|
|
acbd3f9a9e | ||
|
|
98c1680138 | ||
|
|
0ffe9adc86 | ||
|
|
56b4f8a221 | ||
|
|
f1945151c7 | ||
|
|
cc31e864d5 | ||
|
|
868e5c2079 | ||
|
|
70185156aa | ||
|
|
7fc886f43c | ||
|
|
93e468876d | ||
|
|
52cfa9f676 | ||
|
|
8e79199766 | ||
|
|
40a88b5b09 | ||
|
|
65706f560b | ||
|
|
7d27251be3 | ||
|
|
bd8be01ec7 | ||
|
|
3dbb585467 | ||
|
|
bfce66a8f7 | ||
|
|
f56c184033 | ||
|
|
68dfdbeba1 | ||
|
|
0ffa3a6d0f | ||
|
|
c6f56a23d6 | ||
|
|
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 | ||
|
|
7b80db9596 | ||
|
|
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 | ||
|
|
06b8740f58 | ||
|
|
21c10e09ab | ||
|
|
9da92c8f71 | ||
|
|
11e7055497 | ||
|
|
9c899e80a3 | ||
|
|
076db6dd07 | ||
|
|
8620236cb0 | ||
|
|
ce7bf82336 | ||
|
|
3a162e9b31 | ||
|
|
4add2f3dcf | ||
|
|
f3779cd2c9 | ||
|
|
c575638962 | ||
|
|
5afaea90b9 | ||
|
|
60eb4c7296 | ||
|
|
838f3c6e36 | ||
|
|
a1ea1fd9c1 | ||
|
|
435cb4aed9 | ||
|
|
ba27bb6490 | ||
|
|
ac5027ec27 | ||
|
|
084bdc4f40 | ||
|
|
1b85326549 | ||
|
|
f723fea4e2 | ||
|
|
e9a3c0f4ee | ||
|
|
23d9229d73 | ||
|
|
94cb16acbd | ||
|
|
eb8497e1fb | ||
|
|
315da26542 | ||
|
|
26bd744d31 | ||
|
|
85f132edff | ||
|
|
91e0ba92de | ||
|
|
40eac92013 | ||
|
|
5302dd58b1 | ||
|
|
f90da16cb1 | ||
|
|
7a5bfa5e07 | ||
|
|
721692277d | ||
|
|
f89c5e5b86 | ||
|
|
9917b3a489 | ||
|
|
8dcd3932fa | ||
|
|
0ae0e54369 | ||
|
|
3494651685 | ||
|
|
795dc0923f | ||
|
|
faf0ed39d6 | ||
|
|
d49a9e28e4 | ||
|
|
e26544f5ad | ||
|
|
b63c854b16 | ||
|
|
86aae6534b | ||
|
|
1798696a7b | ||
|
|
60392c30ce | ||
|
|
dcf5855643 | ||
|
|
aee0adde43 | ||
|
|
96b5a6cd94 | ||
|
|
aee35aeeca | ||
|
|
803f8244c9 | ||
|
|
0aa0c38f1f | ||
|
|
52716df6e9 | ||
|
|
a25ba32476 | ||
|
|
22df0a43fd | ||
|
|
3b29efb828 | ||
|
|
f1368d39bb | ||
|
|
2c8f0687e5 | ||
|
|
bc03252ffa | ||
|
|
142df2ebad | ||
|
|
9c5b6f6192 | ||
|
|
afa0dbcfa9 | ||
|
|
ed01c6fb29 | ||
|
|
c46e19e43e | ||
|
|
a98cb31161 | ||
|
|
58bd464bca | ||
|
|
bc3a31a6a3 | ||
|
|
219b6e3bbc | ||
|
|
3f71605572 | ||
|
|
7d2cadce83 | ||
|
|
97065314f2 | ||
|
|
294a163918 | ||
|
|
668cb33f15 | ||
|
|
233d740df8 | ||
|
|
bc6adc8840 | ||
|
|
c23f27e8b0 | ||
|
|
9a48edc187 | ||
|
|
9cf770a200 | ||
|
|
013234c882 | ||
|
|
0116547c96 | ||
|
|
9cef31e646 | ||
|
|
c452d38717 | ||
|
|
92ebf1a43c | ||
|
|
1202a48131 | ||
|
|
a881ec2250 | ||
|
|
c5e8e4298f | ||
|
|
00ef1b03d4 | ||
|
|
4ba7a3a48e | ||
|
|
c3c3c435d5 | ||
|
|
78ee229205 | ||
|
|
9af9d2ac04 | ||
|
|
2ee11e19e5 | ||
|
|
e425a3f7f4 | ||
|
|
9667f43874 | ||
|
|
c63fc97736 | ||
|
|
40ce4ab77b | ||
|
|
ebf6034659 | ||
|
|
ccb0e42c95 | ||
|
|
67b88e8e18 | ||
|
|
2196bc8373 | ||
|
|
37383ba451 | ||
|
|
3d42e4bce9 | ||
|
|
4e03e5abbd | ||
|
|
24255411c9 | ||
|
|
d014343667 | ||
|
|
9e374779c0 | ||
|
|
df3254135f | ||
|
|
ab588bd21b | ||
|
|
a250bc162f | ||
|
|
fef92f0d14 | ||
|
|
faea1904c2 | ||
|
|
1adcedb534 | ||
|
|
0ce821aacf | ||
|
|
2422182aa8 | ||
|
|
065005395f | ||
|
|
950a333ac7 | ||
|
|
8994b5f5fc | ||
|
|
22cb6d2ad5 | ||
|
|
dbe542f3d2 | ||
|
|
c64499881d | ||
|
|
b02091bc00 | ||
|
|
32e49355ff | ||
|
|
f753211096 | ||
|
|
32614da4ea | ||
|
|
d345a082c8 | ||
|
|
191c86f0d5 | ||
|
|
35f3ee8948 | ||
|
|
e3c736927f | ||
|
|
8f15b30eb7 | ||
|
|
f6c6aa1051 | ||
|
|
e7436de1e1 | ||
|
|
7e65af532d | ||
|
|
a75ed5090a | ||
|
|
9cd9fa02f6 | ||
|
|
8a203deb19 | ||
|
|
f929f33844 | ||
|
|
8b65ad5b8f | ||
|
|
75d8b472d1 | ||
|
|
90f3f0e17c | ||
|
|
3582abab73 | ||
|
|
481ef2c4d1 | ||
|
|
541031707f | ||
|
|
67a0af3d87 | ||
|
|
af9023f87e | ||
|
|
c58a24f851 | ||
|
|
b39df447b6 | ||
|
|
7a57b4818f | ||
|
|
80b9285727 | ||
|
|
138318e5f4 | ||
|
|
7d5643e30d | ||
|
|
3cd48f751e | ||
|
|
f201f357da | ||
|
|
c02772d219 | ||
|
|
658d46ccf0 | ||
|
|
0d922acd56 | ||
|
|
0bb482d181 | ||
|
|
d6972da84f | ||
|
|
67ed1e2170 | ||
|
|
be6d6fd6bd | ||
|
|
9e190d7345 | ||
|
|
cf9c1d7aa3 | ||
|
|
995f9b2d60 | ||
|
|
4dbfecd315 | ||
|
|
772eec77a7 | ||
|
|
a0e81b4570 | ||
|
|
3beb2e27f2 | ||
|
|
71ea2b0485 | ||
|
|
4701bd1b2e | ||
|
|
b49d548240 | ||
|
|
fd24680a16 | ||
|
|
d9cb1cba89 | ||
|
|
087c2c5834 | ||
|
|
b211e34cd6 | ||
|
|
ffeda3007a | ||
|
|
0c45fde749 | ||
|
|
310b40aace | ||
|
|
5c4ee581cc | ||
|
|
2c6e2ee2d7 | ||
|
|
ca236ce7fd | ||
|
|
24f306e8ef | ||
|
|
c955504ad7 | ||
|
|
af3d6a50a3 | ||
|
|
bce0fe23c0 | ||
|
|
ef73e712ba | ||
|
|
aa73a07e5f | ||
|
|
dfa3c410fc | ||
|
|
60ddc82d9f | ||
|
|
8ce2798fb3 | ||
|
|
89fa16f5b3 | ||
|
|
251a45a441 | ||
|
|
7857bfd0df | ||
|
|
4e62991f22 | ||
|
|
57fe663675 | ||
|
|
e54697189f | ||
|
|
afc57db8d3 | ||
|
|
5cc1ab2ab5 | ||
|
|
d30f8e443e | ||
|
|
be9bc4ba4d | ||
|
|
32f2830df5 | ||
|
|
23f8848773 | ||
|
|
fe00146f22 | ||
|
|
a43d3a1ce7 |
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.
|
||||
|
||||
@@ -11,10 +11,11 @@ submit the change with your pull request.
|
||||
- Alex Bayley / [Skud](https://github.com/Skud)
|
||||
- Cesy / [cesy](https://github.com/cesy)
|
||||
- Miles Gould / [pozorvlak](https://github.com/pozorvlak)
|
||||
- Joseph Caudle / [jcaudle](https://github.com/jcaudle)
|
||||
- Taylor Griffin / [tygriffin](https://github.com/tygriffin)
|
||||
|
||||
## Contributors
|
||||
|
||||
- Joseph Caudle / [jcaudle](https://github.com/jcaudle)
|
||||
- Ricky Amianym / [amianym](https://github.com/amianym)
|
||||
- Juliet Kemp / [julietk](https://github.com/julietk)
|
||||
- Federico Mena Quintero / [federicomenaquintero](https://github.com/federicomenaquintero)
|
||||
@@ -38,3 +39,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)
|
||||
- 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)
|
||||
- Rocky Jaiswal / [rocky-jaiswal](https://github.com/rocky-jaiswal)
|
||||
|
||||
|
||||
139
Gemfile
139
Gemfile
@@ -1,75 +1,52 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
ruby '2.1.5'
|
||||
|
||||
gem 'rails', '4.1.9'
|
||||
|
||||
gem 'bundler', '>=1.1.5'
|
||||
|
||||
gem 'rails', '3.2.13'
|
||||
gem 'rack', '~>1.4.5'
|
||||
gem 'json', '~>1.7.7'
|
||||
gem 'sass-rails', '~> 4.0.4'
|
||||
gem 'coffee-rails', '~> 4.1.0'
|
||||
gem 'haml'
|
||||
gem 'unicorn' # http server
|
||||
|
||||
gem 'cancan' # for checking member privileges
|
||||
# Another CSS preprocessor, used for Bootstrap overrides
|
||||
gem 'less', '~>2.5.0'
|
||||
gem 'less-rails', '~> 2.5.0'
|
||||
# CSS framework
|
||||
gem 'less-rails-bootstrap', '~> 3.2.0'
|
||||
|
||||
gem 'gibbon' # for Mailchimp newsletter subscriptions
|
||||
gem 'uglifier', '~> 2.5.3' # JavaScript compressor
|
||||
|
||||
gem 'jquery-rails'
|
||||
gem 'jquery-ui-rails', '~> 5.0.2'
|
||||
gem 'js-routes' # provides access to Rails routes in Javascript
|
||||
gem 'flickraw'
|
||||
|
||||
gem 'leaflet-rails'
|
||||
gem 'leaflet-markercluster-rails'
|
||||
gem 'unicorn' # http server
|
||||
gem 'pg'
|
||||
gem 'figaro' # for handling config via ENV variables
|
||||
gem 'cancancan', '~> 1.9' # for checking member privileges
|
||||
gem 'gibbon' # for Mailchimp newsletter subscriptions
|
||||
gem 'csv_shaper' # CSV export
|
||||
|
||||
# vendored activemerchant for testing- needed for bogus paypal
|
||||
# gateway monkeypatch
|
||||
gem 'activemerchant', '1.33.0',
|
||||
:path => 'vendor/gems/activemerchant-1.33.0',
|
||||
:path => 'vendor/gems/activemerchant-1.33.0',
|
||||
:require => 'active_merchant'
|
||||
gem 'active_utils', '1.0.5',
|
||||
:path => 'vendor/gems/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'
|
||||
end
|
||||
|
||||
# Gems used only for assets and not required
|
||||
# in production environments by default.
|
||||
group :assets do
|
||||
# CSS preprocessor, used for app/assets/stylesheets/application.css
|
||||
gem 'sass-rails', '~> 3.2.3'
|
||||
# CoffeeScript is a Python-like language that compiles to JavaScript
|
||||
gem 'coffee-rails', '~> 3.2.1'
|
||||
|
||||
# less-rails depends on a JavaScript engine; we use therubyracer.
|
||||
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
||||
# long term, we'll probably want node.js for performance, but this will do
|
||||
# for now as it's easier for new people to install
|
||||
gem 'therubyracer', '~> 0.10.2', :platforms => :ruby
|
||||
# Another CSS preprocessor, used for Bootstrap overrides
|
||||
gem "less-rails"
|
||||
# CSS framework
|
||||
gem "twitter-bootstrap-rails",
|
||||
:git => 'https://github.com/seyhunak/twitter-bootstrap-rails.git',
|
||||
:ref => '2c7c52'
|
||||
|
||||
gem 'uglifier', '>= 1.0.3' # JavaScript compressor
|
||||
|
||||
gem 'compass-rails', '~> 1.0.3' # Yet Another CSS framework
|
||||
end
|
||||
|
||||
gem 'jquery-rails'
|
||||
gem 'flickraw'
|
||||
|
||||
# To use ActiveModel has_secure_password
|
||||
# gem 'bcrypt-ruby', '~> 3.0.0'
|
||||
|
||||
# To use Jbuilder templates for JSON
|
||||
# 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'
|
||||
end
|
||||
# less-rails depends on a JavaScript engine; we use therubyracer.
|
||||
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
||||
# long term, we'll probably want node.js for performance, but this will do
|
||||
# for now as it's easier for new people to install
|
||||
gem 'therubyracer', '~> 0.12', :platforms => :ruby
|
||||
# libv8 is needed by therubyracer and is a bit finicky
|
||||
gem 'libv8', '3.16.14.7'
|
||||
|
||||
# Markdown formatting for updates etc
|
||||
gem 'bluecloth'
|
||||
@@ -78,16 +55,18 @@ gem 'bluecloth'
|
||||
gem 'will_paginate', '~> 3.0'
|
||||
|
||||
# user signup/login/etc
|
||||
gem 'devise'
|
||||
gem 'devise', '~> 3.4.1'
|
||||
|
||||
# nicely formatted URLs
|
||||
gem 'friendly_id'
|
||||
gem 'friendly_id', '~> 5.0.4'
|
||||
|
||||
# gravatars
|
||||
gem 'gravatar-ultimate'
|
||||
|
||||
# For geolocation
|
||||
gem 'geocoder'
|
||||
gem 'geocoder',
|
||||
:git => 'https://github.com/alexreisner/geocoder.git',
|
||||
:ref => '104d46'
|
||||
|
||||
# For easy calendar selection
|
||||
gem 'bootstrap-datepicker-rails'
|
||||
@@ -95,18 +74,38 @@ 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
|
||||
group :production, :staging do
|
||||
gem 'newrelic_rpm'
|
||||
gem 'dalli'
|
||||
gem 'memcachier'
|
||||
gem 'rails_12factor' # supresses heroku plugin injection
|
||||
end
|
||||
|
||||
group :development, :test, :travis do
|
||||
gem 'haml-rails' # HTML templating language
|
||||
gem 'rspec-rails', '~> 2.12.1' # unit testing framework
|
||||
gem 'webrat' # provides HTML matchers for view tests
|
||||
gem 'factory_girl_rails', '~> 4.0' # for creating test data
|
||||
gem 'coveralls', require: false # coverage analysis
|
||||
group :development do
|
||||
# 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
|
||||
|
||||
group :development, :test do
|
||||
gem 'haml-rails' # HTML templating language
|
||||
gem 'rspec-rails', '~> 3.1.0' # unit testing framework
|
||||
gem 'rspec-activemodel-mocks'
|
||||
gem 'byebug' # debugging
|
||||
gem 'database_cleaner', '~> 1.3.0'
|
||||
gem 'webrat' # provides HTML matchers for view tests
|
||||
gem 'factory_girl_rails', '~> 4.5.0' # for creating test data
|
||||
gem 'coveralls', require: false # coverage analysis
|
||||
gem 'capybara' # integration tests
|
||||
gem 'capybara-email' # integration tests for email
|
||||
gem 'poltergeist', '~> 1.5.1' # for headless JS testing
|
||||
gem 'i18n-tasks' # adds tests for finding missing and unused translations
|
||||
end
|
||||
|
||||
473
Gemfile.lock
473
Gemfile.lock
@@ -1,13 +1,9 @@
|
||||
GIT
|
||||
remote: https://github.com/seyhunak/twitter-bootstrap-rails.git
|
||||
revision: 2c7c527c354d9068ce49346d4fd8389328d32ce6
|
||||
ref: 2c7c52
|
||||
remote: https://github.com/alexreisner/geocoder.git
|
||||
revision: 104d466ba7097b7dce5ba19f8e4091b7f69ccdf6
|
||||
ref: 104d46
|
||||
specs:
|
||||
twitter-bootstrap-rails (2.2.7)
|
||||
actionpack (>= 3.1)
|
||||
execjs
|
||||
rails (>= 3.1)
|
||||
railties (>= 3.1)
|
||||
geocoder (1.1.8)
|
||||
|
||||
PATH
|
||||
remote: vendor/gems/active_utils-1.0.5
|
||||
@@ -24,209 +20,296 @@ PATH
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actionmailer (3.2.13)
|
||||
actionpack (= 3.2.13)
|
||||
mail (~> 2.5.3)
|
||||
actionpack (3.2.13)
|
||||
activemodel (= 3.2.13)
|
||||
activesupport (= 3.2.13)
|
||||
builder (~> 3.0.0)
|
||||
actionmailer (4.1.9)
|
||||
actionpack (= 4.1.9)
|
||||
actionview (= 4.1.9)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
actionpack (4.1.9)
|
||||
actionview (= 4.1.9)
|
||||
activesupport (= 4.1.9)
|
||||
rack (~> 1.5.2)
|
||||
rack-test (~> 0.6.2)
|
||||
actionview (4.1.9)
|
||||
activesupport (= 4.1.9)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
journey (~> 1.0.4)
|
||||
rack (~> 1.4.5)
|
||||
rack-cache (~> 1.2)
|
||||
rack-test (~> 0.6.1)
|
||||
sprockets (~> 2.2.1)
|
||||
activemodel (3.2.13)
|
||||
activesupport (= 3.2.13)
|
||||
builder (~> 3.0.0)
|
||||
activerecord (3.2.13)
|
||||
activemodel (= 3.2.13)
|
||||
activesupport (= 3.2.13)
|
||||
arel (~> 3.0.2)
|
||||
tzinfo (~> 0.3.29)
|
||||
activeresource (3.2.13)
|
||||
activemodel (= 3.2.13)
|
||||
activesupport (= 3.2.13)
|
||||
activesupport (3.2.13)
|
||||
i18n (= 0.6.1)
|
||||
multi_json (~> 1.0)
|
||||
arel (3.0.2)
|
||||
bcrypt-ruby (3.1.1)
|
||||
activemodel (4.1.9)
|
||||
activesupport (= 4.1.9)
|
||||
builder (~> 3.1)
|
||||
activerecord (4.1.9)
|
||||
activemodel (= 4.1.9)
|
||||
activesupport (= 4.1.9)
|
||||
arel (~> 5.0.0)
|
||||
activesupport (4.1.9)
|
||||
i18n (~> 0.6, >= 0.6.9)
|
||||
json (~> 1.7, >= 1.7.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.1)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.3.6)
|
||||
arel (5.0.1.20140414130214)
|
||||
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)
|
||||
cancan (1.6.10)
|
||||
chunky_png (1.2.8)
|
||||
coffee-rails (3.2.2)
|
||||
builder (3.2.2)
|
||||
byebug (3.5.1)
|
||||
columnize (~> 0.8)
|
||||
debugger-linecache (~> 1.2)
|
||||
slop (~> 3.6)
|
||||
cancancan (1.9.2)
|
||||
capybara (2.4.4)
|
||||
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
|
||||
cliver (0.3.2)
|
||||
coderay (1.1.0)
|
||||
coffee-rails (4.1.0)
|
||||
coffee-script (>= 2.2.0)
|
||||
railties (~> 3.2.0)
|
||||
coffee-script (2.2.0)
|
||||
railties (>= 4.0.0, < 5.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)
|
||||
chunky_png (~> 1.2)
|
||||
fssm (>= 0.2.7)
|
||||
sass (~> 3.1)
|
||||
compass-rails (1.0.3)
|
||||
compass (>= 0.12.2, < 0.14)
|
||||
coveralls (0.6.7)
|
||||
colorize
|
||||
coffee-script-source (1.8.0)
|
||||
columnize (0.9.0)
|
||||
commonjs (0.2.7)
|
||||
coveralls (0.7.1)
|
||||
multi_json (~> 1.3)
|
||||
rest-client
|
||||
simplecov (>= 0.7)
|
||||
term-ansicolor
|
||||
thor
|
||||
dalli (2.6.4)
|
||||
debugger (1.6.1)
|
||||
columnize (>= 0.3.1)
|
||||
debugger-linecache (~> 1.2.0)
|
||||
debugger-ruby_core_source (~> 1.2.3)
|
||||
csv_shaper (1.1.1)
|
||||
activesupport (>= 3.0.0)
|
||||
dalli (2.7.2)
|
||||
database_cleaner (1.3.0)
|
||||
debug_inspector (0.0.2)
|
||||
debugger-linecache (1.2.0)
|
||||
debugger-ruby_core_source (1.2.3)
|
||||
devise (3.0.0)
|
||||
bcrypt-ruby (~> 3.0)
|
||||
devise (3.4.1)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 3.2.6, < 5)
|
||||
responders
|
||||
thread_safe (~> 0.1)
|
||||
warden (~> 1.2.3)
|
||||
diff-lcs (1.1.3)
|
||||
diff-lcs (1.2.5)
|
||||
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)
|
||||
fssm (0.2.10)
|
||||
geocoder (1.1.8)
|
||||
gibbon (1.0.0)
|
||||
figaro (1.0.0)
|
||||
thor (~> 0.14)
|
||||
flickraw (0.9.8)
|
||||
friendly_id (5.0.4)
|
||||
activerecord (>= 4.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.1.0.beta.1)
|
||||
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)
|
||||
haml-rails (0.6.0)
|
||||
actionpack (>= 4.0.1)
|
||||
activesupport (>= 4.0.1)
|
||||
haml (>= 3.1, < 5.0)
|
||||
html2haml (>= 1.0.1)
|
||||
railties (>= 4.0.1)
|
||||
hashie (3.3.2)
|
||||
highline (1.6.21)
|
||||
hike (1.2.3)
|
||||
httparty (0.11.0)
|
||||
multi_json (~> 1.0)
|
||||
hpricot (0.8.6)
|
||||
html2haml (1.0.1)
|
||||
erubis (~> 2.7.0)
|
||||
haml (>= 4.0.0.rc.1)
|
||||
hpricot (~> 0.8.6)
|
||||
ruby_parser (~> 3.1.1)
|
||||
httparty (0.13.3)
|
||||
json (~> 1.8)
|
||||
multi_xml (>= 0.5.2)
|
||||
i18n (0.6.1)
|
||||
journey (1.0.4)
|
||||
jquery-rails (3.0.4)
|
||||
i18n (0.7.0)
|
||||
i18n-tasks (0.7.8)
|
||||
activesupport
|
||||
easy_translate (>= 0.5.0)
|
||||
erubis
|
||||
highline
|
||||
i18n
|
||||
slop (>= 3.5.0)
|
||||
term-ansicolor
|
||||
terminal-table
|
||||
jquery-rails (3.1.2)
|
||||
railties (>= 3.0, < 5.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
json (1.7.7)
|
||||
kgio (2.8.0)
|
||||
less (2.3.2)
|
||||
commonjs (~> 0.2.6)
|
||||
less-rails (2.3.3)
|
||||
jquery-ui-rails (5.0.3)
|
||||
railties (>= 3.2.16)
|
||||
js-routes (0.9.9)
|
||||
railties (>= 3.2)
|
||||
sprockets-rails
|
||||
json (1.8.2)
|
||||
kgio (2.9.2)
|
||||
launchy (2.4.3)
|
||||
addressable (~> 2.3)
|
||||
leaflet-markercluster-rails (0.7.0)
|
||||
railties (>= 3.1)
|
||||
leaflet-rails (0.7.4)
|
||||
less (2.5.1)
|
||||
commonjs (~> 0.2.7)
|
||||
less-rails (2.5.0)
|
||||
actionpack (>= 3.1)
|
||||
less (~> 2.3.1)
|
||||
libv8 (3.3.10.4)
|
||||
mail (2.5.4)
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
less (~> 2.5.0)
|
||||
less-rails-bootstrap (3.2.0)
|
||||
less-rails (~> 2.5.0)
|
||||
letter_opener (1.3.0)
|
||||
launchy (~> 2.2)
|
||||
libv8 (3.16.14.7)
|
||||
mail (2.6.3)
|
||||
mime-types (>= 1.16, < 3)
|
||||
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 (2.4.3)
|
||||
mini_portile (0.6.1)
|
||||
minitest (5.5.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.10.0)
|
||||
newrelic_rpm (3.9.8.273)
|
||||
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)
|
||||
rack (1.4.5)
|
||||
rack-cache (1.2)
|
||||
rack (>= 0.4)
|
||||
rack-ssl (1.3.3)
|
||||
rack
|
||||
rack-test (0.6.2)
|
||||
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)
|
||||
pry (0.10.1)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.8.1)
|
||||
slop (~> 3.4)
|
||||
rack (1.5.2)
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rails (3.2.13)
|
||||
actionmailer (= 3.2.13)
|
||||
actionpack (= 3.2.13)
|
||||
activerecord (= 3.2.13)
|
||||
activeresource (= 3.2.13)
|
||||
activesupport (= 3.2.13)
|
||||
bundler (~> 1.0)
|
||||
railties (= 3.2.13)
|
||||
railties (3.2.13)
|
||||
actionpack (= 3.2.13)
|
||||
activesupport (= 3.2.13)
|
||||
rack-ssl (~> 1.3.2)
|
||||
rails (4.1.9)
|
||||
actionmailer (= 4.1.9)
|
||||
actionpack (= 4.1.9)
|
||||
actionview (= 4.1.9)
|
||||
activemodel (= 4.1.9)
|
||||
activerecord (= 4.1.9)
|
||||
activesupport (= 4.1.9)
|
||||
bundler (>= 1.3.0, < 2.0)
|
||||
railties (= 4.1.9)
|
||||
sprockets-rails (~> 2.0)
|
||||
rails_12factor (0.0.3)
|
||||
rails_serve_static_assets
|
||||
rails_stdout_logging
|
||||
rails_serve_static_assets (0.0.2)
|
||||
rails_stdout_logging (0.0.3)
|
||||
railties (4.1.9)
|
||||
actionpack (= 4.1.9)
|
||||
activesupport (= 4.1.9)
|
||||
rake (>= 0.8.7)
|
||||
rdoc (~> 3.4)
|
||||
thor (>= 0.14.6, < 2.0)
|
||||
raindrops (0.11.0)
|
||||
rake (10.1.0)
|
||||
rdoc (3.12.2)
|
||||
json (~> 1.4)
|
||||
rest-client (1.6.7)
|
||||
mime-types (>= 1.16)
|
||||
rspec-core (2.12.2)
|
||||
rspec-expectations (2.12.1)
|
||||
diff-lcs (~> 1.1.3)
|
||||
rspec-mocks (2.12.2)
|
||||
rspec-rails (2.12.2)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
raindrops (0.13.0)
|
||||
rake (10.4.2)
|
||||
ref (1.0.5)
|
||||
responders (1.1.2)
|
||||
railties (>= 3.2, < 4.2)
|
||||
rest-client (1.7.2)
|
||||
mime-types (>= 1.16, < 3.0)
|
||||
netrc (~> 0.7)
|
||||
rspec-activemodel-mocks (1.0.1)
|
||||
activemodel (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
rspec-mocks (>= 2.99, < 4.0)
|
||||
rspec-core (3.1.7)
|
||||
rspec-support (~> 3.1.0)
|
||||
rspec-expectations (3.1.2)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.1.0)
|
||||
rspec-mocks (3.1.3)
|
||||
rspec-support (~> 3.1.0)
|
||||
rspec-rails (3.1.0)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
rspec-core (~> 2.12.0)
|
||||
rspec-expectations (~> 2.12.0)
|
||||
rspec-mocks (~> 2.12.0)
|
||||
sass (3.2.9)
|
||||
sass-rails (3.2.6)
|
||||
railties (~> 3.2.0)
|
||||
sass (>= 3.1.10)
|
||||
tilt (~> 1.3)
|
||||
simplecov (0.7.1)
|
||||
rspec-core (~> 3.1.0)
|
||||
rspec-expectations (~> 3.1.0)
|
||||
rspec-mocks (~> 3.1.0)
|
||||
rspec-support (~> 3.1.0)
|
||||
rspec-support (3.1.2)
|
||||
ruby_parser (3.1.3)
|
||||
sexp_processor (~> 4.1)
|
||||
sass (3.2.19)
|
||||
sass-rails (4.0.5)
|
||||
railties (>= 4.0.0, < 5.0)
|
||||
sass (~> 3.2.2)
|
||||
sprockets (~> 2.8, < 3.0)
|
||||
sprockets-rails (~> 2.0)
|
||||
sexp_processor (4.4.4)
|
||||
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.12.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 (2.2.2)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
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)
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
tzinfo (0.3.37)
|
||||
uglifier (2.1.2)
|
||||
tins (1.3.3)
|
||||
tzinfo (1.2.2)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (2.5.3)
|
||||
execjs (>= 0.3.0)
|
||||
multi_json (~> 1.0, >= 1.0.2)
|
||||
unicorn (4.6.3)
|
||||
json (>= 1.8.0)
|
||||
unicorn (4.8.3)
|
||||
kgio (~> 2.6)
|
||||
rack
|
||||
raindrops (~> 0.7)
|
||||
@@ -236,7 +319,12 @@ GEM
|
||||
nokogiri (>= 1.2.0)
|
||||
rack (>= 1.0)
|
||||
rack-test (>= 0.5.3)
|
||||
will_paginate (3.0.4)
|
||||
websocket-driver (0.5.0)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.0)
|
||||
will_paginate (3.0.7)
|
||||
xpath (2.0.0)
|
||||
nokogiri (~> 1.3)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
@@ -244,42 +332,57 @@ PLATFORMS
|
||||
DEPENDENCIES
|
||||
active_utils (= 1.0.5)!
|
||||
activemerchant (= 1.33.0)!
|
||||
better_errors
|
||||
binding_of_caller
|
||||
bluecloth
|
||||
bootstrap-datepicker-rails
|
||||
bundler (>= 1.1.5)
|
||||
cancan
|
||||
coffee-rails (~> 3.2.1)
|
||||
compass-rails (~> 1.0.3)
|
||||
byebug
|
||||
cancancan (~> 1.9)
|
||||
capybara
|
||||
capybara-email
|
||||
coffee-rails (~> 4.1.0)
|
||||
coveralls
|
||||
csv_shaper
|
||||
dalli
|
||||
debugger
|
||||
devise
|
||||
factory_girl_rails (~> 4.0)
|
||||
database_cleaner (~> 1.3.0)
|
||||
devise (~> 3.4.1)
|
||||
factory_girl_rails (~> 4.5.0)
|
||||
figaro
|
||||
flickraw
|
||||
friendly_id
|
||||
geocoder
|
||||
friendly_id (~> 5.0.4)
|
||||
geocoder!
|
||||
gibbon
|
||||
gravatar-ultimate
|
||||
haml
|
||||
haml-rails
|
||||
i18n-tasks
|
||||
jquery-rails
|
||||
json (~> 1.7.7)
|
||||
less-rails
|
||||
jquery-ui-rails (~> 5.0.2)
|
||||
js-routes
|
||||
leaflet-markercluster-rails
|
||||
leaflet-rails
|
||||
less (~> 2.5.0)
|
||||
less-rails (~> 2.5.0)
|
||||
less-rails-bootstrap (~> 3.2.0)
|
||||
letter_opener
|
||||
libv8 (= 3.16.14.7)
|
||||
memcachier
|
||||
newrelic_rpm
|
||||
omniauth
|
||||
omniauth-flickr
|
||||
omniauth-flickr (>= 0.0.15)
|
||||
omniauth-twitter
|
||||
pg
|
||||
rack (~> 1.4.5)
|
||||
rails (= 3.2.13)
|
||||
poltergeist (~> 1.5.1)
|
||||
pry
|
||||
rails (= 4.1.9)
|
||||
rails_12factor
|
||||
rake (>= 10.0.0)
|
||||
rspec-rails (~> 2.12.1)
|
||||
sass-rails (~> 3.2.3)
|
||||
sqlite3
|
||||
therubyracer (~> 0.10.2)
|
||||
twitter-bootstrap-rails!
|
||||
uglifier (>= 1.0.3)
|
||||
rspec-activemodel-mocks
|
||||
rspec-rails (~> 3.1.0)
|
||||
sass-rails (~> 4.0.4)
|
||||
therubyracer (~> 0.12)
|
||||
uglifier (~> 2.5.3)
|
||||
unicorn
|
||||
webrat
|
||||
will_paginate (~> 3.0)
|
||||
|
||||
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: 613 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()
|
||||
@@ -10,8 +10,13 @@
|
||||
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
|
||||
// GO AFTER THE REQUIRES BELOW.
|
||||
//
|
||||
//= 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/
|
||||
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/
|
||||
50
app/assets/javascripts/places.js.erb
Normal file
50
app/assets/javascripts/places.js.erb
Normal file
@@ -0,0 +1,50 @@
|
||||
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'
|
||||
|
||||
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(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(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.slug + "'>" + m.login_name + "</a></p>";
|
||||
where = "<p><i>" + m.location + "</i></p>";
|
||||
marker.bindPopup(link + where).openPopup();
|
||||
markers.addLayer(marker);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
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,6 +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_tree .
|
||||
*= require jquery-ui/autocomplete
|
||||
*= require bootstrap-datepicker
|
||||
*= require leaflet
|
||||
*= require leaflet.markercluster
|
||||
*= require leaflet.markercluster.default
|
||||
*= require custom_bootstrap/custom_bootstrap
|
||||
*= require overrides.css
|
||||
*= require_tree .
|
||||
*/
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
@import "twitter/bootstrap/bootstrap";
|
||||
@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%);
|
||||
|
||||
body {
|
||||
padding-top: @navbarHeight + 10px;
|
||||
padding-bottom: @navbarHeight + 10px;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// Overrides applying only to mobile view
|
||||
|
||||
@media only screen and (max-width: 767px) {
|
||||
.sidebar {
|
||||
margin-left: 0;
|
||||
border-left: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
@@ -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%);
|
||||
3
app/assets/stylesheets/leaflet_overrides.css.less
Normal file
3
app/assets/stylesheets/leaflet_overrides.css.less
Normal file
@@ -0,0 +1,3 @@
|
||||
.leaflet-popup-content-wrapper, .leaflet-popup-tip {
|
||||
border: none;
|
||||
}
|
||||
276
app/assets/stylesheets/overrides.css.less
Normal file
276
app/assets/stylesheets/overrides.css.less
Normal file
@@ -0,0 +1,276 @@
|
||||
|
||||
|
||||
@import "twitter/bootstrap/bootstrap";
|
||||
@import "custom_bootstrap/variables";
|
||||
// this padding needs to be done before the responsive stuff is imported
|
||||
body {
|
||||
// modifying this for our promotional banner. can be replaced after if
|
||||
// needed.
|
||||
// padding-top: @navbar-height + 15px;
|
||||
padding-top: @navbar-height;
|
||||
}
|
||||
|
||||
//@import "twitter/bootstrap/responsive";
|
||||
|
||||
// Set the correct sprite paths
|
||||
@iconSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings");
|
||||
@iconWhiteSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings-white");
|
||||
|
||||
// Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines)
|
||||
@fontAwesomeEotPath: asset-url("fontawesome-webfont.eot");
|
||||
@fontAwesomeEotPath_iefix: asset-url("fontawesome-webfont.eot#iefix");
|
||||
@fontAwesomeWoffPath: asset-url("fontawesome-webfont.woff");
|
||||
@fontAwesomeTtfPath: asset-url("fontawesome-webfont.ttf");
|
||||
@fontAwesomeSvgPath: asset-url("fontawesome-webfont.svg#fontawesomeregular");
|
||||
|
||||
// Font Awesome
|
||||
//@import "fontawesome/font-awesome";
|
||||
|
||||
// Glyphicons
|
||||
//@import "twitter/bootstrap/sprites.less";
|
||||
|
||||
|
||||
.list-inline > li.first {
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
/*
|
||||
#subtitle {
|
||||
color: lighten(@brown, 30%);
|
||||
margin-top: 0px;
|
||||
padding-top: 0px;
|
||||
padding-left: 1em;
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
}
|
||||
*/
|
||||
|
||||
h3 {
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
.main {
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
margin-left: -1px;
|
||||
border-left: 1px solid darken(@beige, 10%);
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
// this is used for eg. crops and members index pages
|
||||
.six-across:nth-child(6n+1) {
|
||||
margin-left: 0px
|
||||
}
|
||||
|
||||
.three-across:nth-child(3n+1) {
|
||||
margin-left: 0px;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
// let's condense the hero unit a little
|
||||
.jumbotron {
|
||||
padding-top: 30px;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
// info under the main heading on homepage
|
||||
.jumbotron .info {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
// signup widget on homepage
|
||||
.jumbotron .signup {
|
||||
background-color: lighten(@green, 40%);
|
||||
border: 1px solid lighten(@green, 20%);
|
||||
border-radius: 6px;
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
line-height: 200%;
|
||||
}
|
||||
|
||||
// stats shown on homepage. eg. "999 members..."
|
||||
p.stats {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.homepage-members {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.homepage-members:nth-child(odd) {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
#placesmap, #cropmap {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
#membermap {
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
.member-location {
|
||||
font-size: small;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.member-location a {
|
||||
color: @brown;
|
||||
}
|
||||
|
||||
.photo-thumbnail {
|
||||
position:relative;
|
||||
padding:0;
|
||||
img {
|
||||
width:100%;
|
||||
}
|
||||
.text {
|
||||
display:none;
|
||||
color:#000;
|
||||
position:absolute;
|
||||
bottom:0;
|
||||
background:rgba(0, 0, 0, 0.8);
|
||||
width:100%;
|
||||
margin:0;
|
||||
}
|
||||
p {
|
||||
padding:5px;
|
||||
margin:0;
|
||||
color:#fff;
|
||||
}
|
||||
&:hover {
|
||||
.text {
|
||||
display:block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.thumbnail {
|
||||
border: none;
|
||||
text-align: center;
|
||||
margin-bottom: 1.5em;
|
||||
|
||||
.member-thumbnail {
|
||||
text-align: left;
|
||||
img {
|
||||
height: 85px;
|
||||
width: 85px;
|
||||
max-width: 85px
|
||||
}
|
||||
}
|
||||
|
||||
.crop-thumbnail {
|
||||
height: 220px;
|
||||
.scientific-name small, .crop-name a {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1em;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.crop-name a {
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.scientific-name small {
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
li.crop-hierarchy {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.navbar-bottom {
|
||||
margin: 40px 0px 0px 0px !important;
|
||||
}
|
||||
|
||||
footer {
|
||||
#contact, #about-growstuff, #policies {
|
||||
text-align: left;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 2em;
|
||||
ul {
|
||||
list-style-type: none;
|
||||
list-style-position: outside;
|
||||
padding-left: 0px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
a {
|
||||
color: @navbar-default-link-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
color: @navbar-default-link-hover-color;
|
||||
}
|
||||
a:active {
|
||||
color: @navbar-default-link-active-color;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-bottom.navbar {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.crop-image, .member-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// Autosuggest
|
||||
|
||||
.ui-autocomplete {
|
||||
z-index: @zindex-tooltip;
|
||||
}
|
||||
|
||||
// Crowdfunding campaign, Sep-Oct 2014
|
||||
|
||||
.crowdfunding-banner {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
background-color: lighten(@green, 30%);
|
||||
margin-top: 0px;
|
||||
margin-bottom: 5px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.crowdfunding-banner a {
|
||||
color: @brown;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
// Overrides applying only to mobile view. This must be at the end of the overrides file.
|
||||
|
||||
@media only screen and (max-width: 767px) {
|
||||
.sidebar {
|
||||
margin-left: 0;
|
||||
border-left: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
#map {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.navbar .nav > li {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
class AccountTypesController < ApplicationController
|
||||
before_filter :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /account_types
|
||||
def index
|
||||
@account_types = AccountType.all
|
||||
@@ -34,7 +36,7 @@ class AccountTypesController < ApplicationController
|
||||
|
||||
# POST /account_types
|
||||
def create
|
||||
@account_type = AccountType.new(params[:account_type])
|
||||
@account_type = AccountType.new(account_type_params)
|
||||
|
||||
respond_to do |format|
|
||||
if @account_type.save
|
||||
@@ -50,7 +52,7 @@ class AccountTypesController < ApplicationController
|
||||
@account_type = AccountType.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @account_type.update_attributes(params[:account_type])
|
||||
if @account_type.update(account_type_params)
|
||||
format.html { redirect_to @account_type, notice: 'Account type was successfully updated.' }
|
||||
else
|
||||
format.html { render action: "edit" }
|
||||
@@ -64,7 +66,13 @@ 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
|
||||
|
||||
private
|
||||
|
||||
def account_type_params
|
||||
params.require(:account_type).permit(:is_paid, :is_permanent_paid, :name)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
class AccountsController < ApplicationController
|
||||
before_filter :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /accounts
|
||||
def index
|
||||
@accounts = Account.all
|
||||
@@ -28,7 +30,7 @@ class AccountsController < ApplicationController
|
||||
@account = Account.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @account.update_attributes(params[:account])
|
||||
if @account.update(params[:account])
|
||||
format.html { redirect_to @account, notice: 'Account detail was successfully updated.' }
|
||||
else
|
||||
format.html { render action: "edit" }
|
||||
@@ -36,4 +38,10 @@ class AccountsController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def account_params
|
||||
params.require(:account).permit(:account_type_id, :member_id, :paid_until)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -8,34 +8,10 @@ class Admin::OrdersController < ApplicationController
|
||||
|
||||
def search
|
||||
authorize! :manage, :all
|
||||
@orders = []
|
||||
@orders = Order.search({:by => params[:search_by], :for => params[:search_text]})
|
||||
|
||||
if params[:search_text]
|
||||
case params[:search_by]
|
||||
when "member"
|
||||
member = Member.find_by_login_name(params[:search_text])
|
||||
if member
|
||||
@orders = member.orders
|
||||
end
|
||||
when "order_id"
|
||||
order = Order.find_by_id(params[:search_text])
|
||||
if order
|
||||
@orders = [order]
|
||||
end
|
||||
when "paypal_token"
|
||||
order = Order.find_by_paypal_express_token(params[:search_text])
|
||||
if order
|
||||
@orders = [order]
|
||||
end
|
||||
when "paypal_payer_id"
|
||||
order = Order.find_by_paypal_express_payer_id(params[:search_text])
|
||||
if order
|
||||
@orders = [order]
|
||||
end
|
||||
end
|
||||
if @orders.empty?
|
||||
flash[:alert] = "Couldn't find order with #{params[:search_by]} = #{params[:search_text]}"
|
||||
end
|
||||
if @orders.empty?
|
||||
flash[:alert] = "Couldn't find order with #{params[:search_by]} = #{params[:search_text]}"
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
|
||||
97
app/controllers/alternate_names_controller.rb
Normal file
97
app/controllers/alternate_names_controller.rb
Normal file
@@ -0,0 +1,97 @@
|
||||
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(alternate_name_params)
|
||||
|
||||
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(alternate_name_params)
|
||||
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
|
||||
|
||||
private
|
||||
|
||||
def alternate_name_params
|
||||
params.require(:alternate_name).permit(:crop_id, :name, :creator_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,46 @@ 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
|
||||
|
||||
before_action :configure_permitted_parameters, if: :devise_controller?
|
||||
|
||||
protected
|
||||
|
||||
def configure_permitted_parameters
|
||||
devise_parameter_sanitizer.for(:sign_up) do |member|
|
||||
member.permit(:login_name, :email, :password, :password_confirmation,
|
||||
: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
|
||||
)
|
||||
end
|
||||
|
||||
devise_parameter_sanitizer.for(:account_update) do |member|
|
||||
member.permit(:login_name, :email, :password, :password_confirmation,
|
||||
: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,
|
||||
#update password
|
||||
:current_password
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
class AuthenticationsController < ApplicationController
|
||||
before_filter :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /authentications
|
||||
def index
|
||||
@authentications = current_member.authentications if member_signed_in?
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
end
|
||||
end
|
||||
|
||||
# POST /authentications
|
||||
def create
|
||||
auth = request.env['omniauth.auth']
|
||||
@@ -26,12 +18,17 @@ class AuthenticationsController < ApplicationController
|
||||
name = auth['info']['name']
|
||||
end
|
||||
|
||||
@authentication = current_member.authentications.find_or_create_by_provider_and_uid(
|
||||
:provider => auth['provider'],
|
||||
:uid => auth['uid'],
|
||||
@authentication = current_member.authentications
|
||||
.create_with(
|
||||
:name => name,
|
||||
:token => auth['credentials']['token'],
|
||||
:secret => auth['credentials']['secret'])
|
||||
:secret => auth['credentials']['secret']
|
||||
)
|
||||
.find_or_create_by(
|
||||
:provider => auth['provider'],
|
||||
:uid => auth['uid'],
|
||||
:name => name)
|
||||
|
||||
flash[:notice] = "Authentication successful."
|
||||
else
|
||||
flash[:notice] = "Authentication failed."
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
class CommentsController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
cache_sweeper :comment_sweeper
|
||||
|
||||
# GET /comments
|
||||
# GET /comments.json
|
||||
def index
|
||||
@@ -54,11 +53,11 @@ class CommentsController < ApplicationController
|
||||
# POST /comments.json
|
||||
def create
|
||||
params[:comment][:author_id] = current_member.id
|
||||
@comment = Comment.new(params[:comment])
|
||||
@comment = Comment.new(comment_params)
|
||||
|
||||
respond_to do |format|
|
||||
if @comment.save
|
||||
format.html { redirect_to @comment.post, notice: 'Comment was successfully created.' }
|
||||
format.html { redirect_to @comment.post, notice: "Comment was successfully created." }
|
||||
format.json { render json: @comment, status: :created, location: @comment }
|
||||
else
|
||||
format.html { render action: "new" }
|
||||
@@ -78,7 +77,7 @@ class CommentsController < ApplicationController
|
||||
params[:comment].delete("author_id")
|
||||
|
||||
respond_to do |format|
|
||||
if @comment.update_attributes(params[:comment])
|
||||
if @comment.update(comment_params)
|
||||
format.html { redirect_to @comment.post, notice: 'Comment was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
@@ -100,4 +99,10 @@ class CommentsController < ApplicationController
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def comment_params
|
||||
params.require(:comment).permit(:author_id, :body, :post_id)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,26 +1,66 @@
|
||||
class CropsController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :hierarchy, :search, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
cache_sweeper :crop_sweeper
|
||||
skip_authorize_resource :only => [:hierarchy, :search]
|
||||
|
||||
# GET /crops
|
||||
# GET /crops.json
|
||||
def index
|
||||
@crops = Crop.includes(:scientific_names, {:plantings => :photos}).paginate(:page => params[:page])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.haml
|
||||
format.json { render json: @crops }
|
||||
format.rss { render :layout => false }
|
||||
@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
|
||||
end
|
||||
|
||||
# GET /wrangle
|
||||
def wrangle
|
||||
@crops = Crop.recent.paginate(:page => params[:page])
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json { render :json => @crops }
|
||||
format.rss do
|
||||
@crops = Crop.recent.includes(:scientific_names, :creator)
|
||||
render :rss => @crops
|
||||
end
|
||||
format.csv do
|
||||
@filename = "Growstuff-Crops-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@crops = Crop.includes(:scientific_names, :plantings, :seeds, :creator)
|
||||
render :csv => @crops
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# GET /crops/wrangle
|
||||
def wrangle
|
||||
@crops = Crop.recent.paginate(:page => params[:page])
|
||||
@crop_wranglers = Role.crop_wranglers
|
||||
respond_to do |format|
|
||||
format.html
|
||||
end
|
||||
end
|
||||
|
||||
# GET /crops/hierarchy
|
||||
def hierarchy
|
||||
@crops = Crop.toplevel
|
||||
respond_to do |format|
|
||||
format.html
|
||||
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 = @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
|
||||
|
||||
@@ -28,10 +68,15 @@ class CropsController < ApplicationController
|
||||
# GET /crops/1.json
|
||||
def show
|
||||
@crop = Crop.includes(:scientific_names, {:plantings => :photos}).find(params[:id])
|
||||
@posts = @crop.posts.paginate(:page => params[:page])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.haml
|
||||
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
|
||||
|
||||
@@ -39,7 +84,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 }
|
||||
@@ -55,7 +102,7 @@ class CropsController < ApplicationController
|
||||
# POST /crops.json
|
||||
def create
|
||||
params[:crop][:creator_id] = current_member.id
|
||||
@crop = Crop.new(params[:crop])
|
||||
@crop = Crop.new(crop_params)
|
||||
|
||||
respond_to do |format|
|
||||
if @crop.save
|
||||
@@ -74,7 +121,7 @@ class CropsController < ApplicationController
|
||||
@crop = Crop.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @crop.update_attributes(params[:crop])
|
||||
if @crop.update(crop_params)
|
||||
format.html { redirect_to @crop, notice: 'Crop was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
@@ -95,4 +142,10 @@ class CropsController < ApplicationController
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def crop_params
|
||||
params.require(:crop).permit(:en_wikipedia_url, :name, :parent_id, :creator_id, :scientific_names_attributes)
|
||||
end
|
||||
end
|
||||
|
||||
32
app/controllers/follows_controller.rb
Normal file
32
app/controllers/follows_controller.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
class FollowsController < ApplicationController
|
||||
|
||||
# POST /follows
|
||||
def create
|
||||
|
||||
@follow = current_member.follows.build(:followed_id => follow_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(follow_params[:id])
|
||||
unfollowed_name = @follow.followed.login_name
|
||||
@follow.destroy
|
||||
|
||||
flash[:notice] = "Unfollowed #{ unfollowed_name }"
|
||||
redirect_to root_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def follow_params
|
||||
params.permit(:id, :followed_id, :follower_id, :authenticity_token, :_method)
|
||||
end
|
||||
end
|
||||
@@ -1,8 +1,6 @@
|
||||
class ForumsController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
|
||||
cache_sweeper :forum_sweeper
|
||||
|
||||
# GET /forums
|
||||
# GET /forums.json
|
||||
def index
|
||||
@@ -44,7 +42,7 @@ class ForumsController < ApplicationController
|
||||
# POST /forums
|
||||
# POST /forums.json
|
||||
def create
|
||||
@forum = Forum.new(params[:forum])
|
||||
@forum = Forum.new(forum_params)
|
||||
|
||||
respond_to do |format|
|
||||
if @forum.save
|
||||
@@ -63,7 +61,7 @@ class ForumsController < ApplicationController
|
||||
@forum = Forum.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @forum.update_attributes(params[:forum])
|
||||
if @forum.update(forum_params)
|
||||
format.html { redirect_to @forum, notice: 'Forum was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
@@ -80,8 +78,14 @@ 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
|
||||
|
||||
private
|
||||
|
||||
def forum_params
|
||||
params.require(:forum).permit(:description, :name, :owner_id, :slug)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class GardensController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
cache_sweeper :garden_sweeper
|
||||
|
||||
# GET /gardens
|
||||
# GET /gardens.json
|
||||
@@ -49,12 +49,13 @@ class GardensController < ApplicationController
|
||||
# POST /gardens.json
|
||||
def create
|
||||
params[:garden][:owner_id] = current_member.id
|
||||
@garden = Garden.new(params[:garden])
|
||||
@garden = Garden.new(garden_params)
|
||||
|
||||
respond_to do |format|
|
||||
if @garden.save
|
||||
format.html { redirect_to @garden, notice: 'Garden was successfully created.' }
|
||||
format.json { render json: @garden, status: :created, location: @garden }
|
||||
expire_fragment("homepage_stats")
|
||||
else
|
||||
format.html { render action: "new" }
|
||||
format.json { render json: @garden.errors, status: :unprocessable_entity }
|
||||
@@ -68,7 +69,7 @@ class GardensController < ApplicationController
|
||||
@garden = Garden.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @garden.update_attributes(params[:garden])
|
||||
if @garden.update(garden_params)
|
||||
format.html { redirect_to @garden, notice: 'Garden was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
@@ -83,10 +84,18 @@ class GardensController < ApplicationController
|
||||
def destroy
|
||||
@garden = Garden.find(params[:id])
|
||||
@garden.destroy
|
||||
expire_fragment("homepage_stats")
|
||||
|
||||
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
|
||||
|
||||
private
|
||||
|
||||
def garden_params
|
||||
params.require(:garden).permit(:name, :slug, :owner_id, :description, :active,
|
||||
:location, :latitude, :longitude, :area, :area_unit)
|
||||
end
|
||||
end
|
||||
|
||||
112
app/controllers/harvests_controller.rb
Normal file
112
app/controllers/harvests_controller.rb
Normal file
@@ -0,0 +1,112 @@
|
||||
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)
|
||||
elsif @crop
|
||||
@harvests = @crop.harvests.includes(:owner, :crop)
|
||||
else
|
||||
@harvests = Harvest.includes(:owner, :crop)
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html { @harvests = @harvests.paginate(:page => params[:page]) }
|
||||
format.json { render json: @harvests }
|
||||
format.csv do
|
||||
specifics = (@owner ? "#{@owner.name}-" : @crop ? "#{@crop.name}-" : nil)
|
||||
@filename = "Growstuff-#{specifics}Harvests-#{Time.zone.now.to_s(:number)}.csv"
|
||||
render :csv => @harvests
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# GET /harvests/1
|
||||
# GET /harvests/1.json
|
||||
def show
|
||||
@harvest = Harvest.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.erb
|
||||
format.json { render json: @harvest }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /harvests/new
|
||||
# GET /harvests/new.json
|
||||
def new
|
||||
@harvest = Harvest.new('harvested_at' => Date.today)
|
||||
|
||||
# using find_by_id here because it returns nil, unlike find
|
||||
@crop = Crop.find_by_id(params[:crop_id]) || Crop.new
|
||||
|
||||
respond_to do |format|
|
||||
format.html # new.html.erb
|
||||
format.json { render json: @harvest }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /harvests/1/edit
|
||||
def edit
|
||||
@harvest = Harvest.find(params[:id])
|
||||
end
|
||||
|
||||
# POST /harvests
|
||||
# POST /harvests.json
|
||||
def create
|
||||
params[:harvest][:owner_id] = current_member.id
|
||||
params[:harvested_at] = parse_date(params[:harvested_at])
|
||||
@harvest = Harvest.new(harvest_params)
|
||||
|
||||
respond_to do |format|
|
||||
if @harvest.save
|
||||
format.html { redirect_to @harvest, notice: 'Harvest was successfully created.' }
|
||||
format.json { render json: @harvest, status: :created, location: @harvest }
|
||||
else
|
||||
format.html { render action: "new" }
|
||||
format.json { render json: @harvest.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# PUT /harvests/1
|
||||
# PUT /harvests/1.json
|
||||
def update
|
||||
@harvest = Harvest.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @harvest.update(harvest_params)
|
||||
format.html { redirect_to @harvest, notice: 'Harvest was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
format.html { render action: "edit" }
|
||||
format.json { render json: @harvest.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /harvests/1
|
||||
# DELETE /harvests/1.json
|
||||
def destroy
|
||||
@harvest = Harvest.find(params[:id])
|
||||
@harvest.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to harvests_url }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def harvest_params
|
||||
params.require(:harvest).permit(:crop_id, :harvested_at, :description, :owner_id,
|
||||
:quantity, :unit, :weight_quantity, :weight_unit, :plant_part_id, :slug)
|
||||
end
|
||||
end
|
||||
@@ -1,15 +1,16 @@
|
||||
class MembersController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
|
||||
cache_sweeper :member_sweeper
|
||||
|
||||
skip_authorize_resource :only => :nearby
|
||||
|
||||
after_action :expire_cache_fragments, :only => :create
|
||||
|
||||
def index
|
||||
@members = Member.confirmed.paginate(:page => params[:page])
|
||||
|
||||
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,31 +34,20 @@ class MembersController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
def nearby
|
||||
if !params[:location].blank?
|
||||
@location = params[:location]
|
||||
elsif current_member
|
||||
@location = current_member.location
|
||||
else
|
||||
@location = nil
|
||||
end
|
||||
def view_follows
|
||||
@member = Member.confirmed.find(params[:login_name])
|
||||
@follows = @member.followed.paginate(:page => params[:page])
|
||||
end
|
||||
|
||||
if !params[:distance].blank?
|
||||
@distance = params[:distance]
|
||||
else
|
||||
@distance = 100
|
||||
end
|
||||
def view_followers
|
||||
@member = Member.confirmed.find(params[:login_name])
|
||||
@followers = @member.followers.paginate(:page => params[:page])
|
||||
end
|
||||
|
||||
if params[:units] == "mi"
|
||||
@units = :mi
|
||||
else
|
||||
@units = :km
|
||||
end
|
||||
private
|
||||
|
||||
@nearby_members = @location ? Member.near(@location, @distance, :units => @units) : []
|
||||
respond_to do |format|
|
||||
format.html # nearby.html.haml
|
||||
end
|
||||
def expire_cache_fragments
|
||||
expire_fragment("homepage_stats")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
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)
|
||||
@notifications = Notification.where(recipient_id: current_member)
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
@@ -47,7 +49,7 @@ class NotificationsController < ApplicationController
|
||||
# POST /notifications
|
||||
def create
|
||||
params[:notification][:sender_id] = current_member.id
|
||||
@notification = Notification.new(params[:notification])
|
||||
@notification = Notification.new(notification_params)
|
||||
@recipient = Member.find_by_id(params[:notification][:recipient_id])
|
||||
|
||||
respond_to do |format|
|
||||
@@ -58,4 +60,10 @@ class NotificationsController < ApplicationController
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def notification_params
|
||||
params.require(:notification).permit(:sender_id, :recipient_id, :subject, :body, :post_id, :read)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class OrderItemsController < ApplicationController
|
||||
before_filter :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
|
||||
# POST /order_items
|
||||
@@ -6,7 +7,7 @@ class OrderItemsController < ApplicationController
|
||||
if params[:order_item][:price]
|
||||
params[:order_item][:price] = params[:order_item][:price].to_f * 100 # convert to cents
|
||||
end
|
||||
@order_item = OrderItem.new(params[:order_item])
|
||||
@order_item = OrderItem.new(order_item_params)
|
||||
@order_item.order = current_member.current_order || Order.create(:member_id => current_member.id)
|
||||
|
||||
respond_to do |format|
|
||||
@@ -19,4 +20,10 @@ class OrderItemsController < ApplicationController
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def order_item_params
|
||||
params.require(:order_item).permit(:order_id, :price, :product_id, :quantity)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
class OrdersController < ApplicationController
|
||||
before_filter :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /orders
|
||||
def index
|
||||
@orders = Order.find_all_by_member_id(current_member.id)
|
||||
@orders = Order.where(member_id: current_member.id)
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
@@ -32,24 +33,28 @@ class OrdersController < ApplicationController
|
||||
def checkout
|
||||
@order = Order.find(params[:id])
|
||||
|
||||
response = EXPRESS_GATEWAY.setup_purchase(
|
||||
@order.total,
|
||||
:items => @order.activemerchant_items,
|
||||
:currency => Growstuff::Application.config.currency,
|
||||
:no_shipping => true,
|
||||
:ip => request.remote_ip,
|
||||
:return_url => complete_order_url,
|
||||
:cancel_return_url => shop_url
|
||||
)
|
||||
redirect_to EXPRESS_GATEWAY.redirect_url_for(response.token)
|
||||
respond_to do |format|
|
||||
if @order.update_attributes(:referral_code => params[:referral_code])
|
||||
response = EXPRESS_GATEWAY.setup_purchase(
|
||||
@order.total,
|
||||
:items => @order.activemerchant_items,
|
||||
:currency => Growstuff::Application.config.currency,
|
||||
:no_shipping => true,
|
||||
:ip => request.remote_ip,
|
||||
:return_url => complete_order_url,
|
||||
:cancel_return_url => shop_url
|
||||
)
|
||||
format.html { redirect_to EXPRESS_GATEWAY.redirect_url_for(response.token) }
|
||||
else
|
||||
format.html { render action: "show" }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def complete
|
||||
@order = Order.find(params[:id])
|
||||
|
||||
@order.save
|
||||
|
||||
if (params[:token] && params['PayerID'])
|
||||
purchase = EXPRESS_GATEWAY.purchase(
|
||||
@order.total,
|
||||
|
||||
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,8 +1,7 @@
|
||||
class PhotosController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
cache_sweeper :photo_sweeper
|
||||
|
||||
# GET /photos
|
||||
# GET /photos.json
|
||||
def index
|
||||
@@ -29,7 +28,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
|
||||
|
||||
@@ -59,23 +59,39 @@ class PhotosController < ApplicationController
|
||||
# POST /photos.json
|
||||
def create
|
||||
@photo = Photo.find_by_flickr_photo_id(params[:photo][:flickr_photo_id]) ||
|
||||
Photo.new(params[:photo])
|
||||
Photo.new(photo_params)
|
||||
@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.' }
|
||||
@@ -93,7 +109,7 @@ class PhotosController < ApplicationController
|
||||
@photo = Photo.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @photo.update_attributes(params[:photo])
|
||||
if @photo.update(photo_params)
|
||||
format.html { redirect_to @photo, notice: 'Photo was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
@@ -114,4 +130,11 @@ class PhotosController < ApplicationController
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def photo_params
|
||||
params.require(:photo).permit(:flickr_photo_id, :owner_id, :title, :license_name,
|
||||
:license_url, :thumbnail_url, :fullsize_url, :link_url)
|
||||
end
|
||||
end
|
||||
|
||||
31
app/controllers/places_controller.rb
Normal file
31
app/controllers/places_controller.rb
Normal file
@@ -0,0 +1,31 @@
|
||||
class PlacesController < ApplicationController
|
||||
skip_authorize_resource
|
||||
|
||||
def index
|
||||
respond_to do |format|
|
||||
format.html
|
||||
# json response is whatever we want to map here
|
||||
format.json { render :json => Member.located.to_json(:only => [:id, :login_name, :slug, :location, :latitude, :longitude]) }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /places/london
|
||||
# GET /places/london.json
|
||||
def show
|
||||
@place = params[:place] # used for page title
|
||||
@nearby_members = Member.nearest_to(params[:place])
|
||||
respond_to do |format|
|
||||
format.html # show.html.haml
|
||||
format.json { render :json => @nearby_members.to_json(:only => [:id, :login_name, :slug, :location, :latitude, :longitude]) }
|
||||
end
|
||||
end
|
||||
|
||||
def search
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
redirect_to place_path(params[:new_place])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
91
app/controllers/plant_parts_controller.rb
Normal file
91
app/controllers/plant_parts_controller.rb
Normal file
@@ -0,0 +1,91 @@
|
||||
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(plant_part_params)
|
||||
|
||||
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(plant_part_params)
|
||||
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
|
||||
|
||||
private
|
||||
|
||||
def plant_part_params
|
||||
params.require(:plant_part).permit(:name, :slug)
|
||||
end
|
||||
end
|
||||
@@ -1,29 +1,36 @@
|
||||
class PlantingsController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
cache_sweeper :planting_sweeper
|
||||
|
||||
# GET /plantings
|
||||
# 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
|
||||
specifics = (@owner ? "#{@owner.name}-" : @crop ? "#{@crop.name}-" : nil)
|
||||
@filename = "Growstuff-#{specifics}Plantings-#{Time.zone.now.to_s(:number)}.csv"
|
||||
render :csv => @plantings
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# GET /plantings/1
|
||||
# GET /plantings/1.json
|
||||
def show
|
||||
@planting = Planting.includes(:owner, :crop, :garden, :photos).find(params[:id])
|
||||
@planting = Planting.includes(:owner, :crop, :garden, :photos).friendly.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.erb
|
||||
@@ -58,14 +65,15 @@ class PlantingsController < ApplicationController
|
||||
# POST /plantings
|
||||
# POST /plantings.json
|
||||
def create
|
||||
params[:planting][:owner_id] = current_member.id
|
||||
params[:planted_at] = parse_date(params[:planted_at])
|
||||
@planting = Planting.new(params[:planting])
|
||||
@planting = Planting.new(planting_params)
|
||||
@planting.owner = current_member
|
||||
|
||||
respond_to do |format|
|
||||
if @planting.save
|
||||
format.html { redirect_to @planting, notice: 'Planting was successfully created.' }
|
||||
format.json { render json: @planting, status: :created, location: @planting }
|
||||
expire_fragment("homepage_stats")
|
||||
else
|
||||
format.html { render action: "new" }
|
||||
format.json { render json: @planting.errors, status: :unprocessable_entity }
|
||||
@@ -80,7 +88,7 @@ class PlantingsController < ApplicationController
|
||||
params[:planted_at] = parse_date(params[:planted_at])
|
||||
|
||||
respond_to do |format|
|
||||
if @planting.update_attributes(params[:planting])
|
||||
if @planting.update(planting_params)
|
||||
format.html { redirect_to @planting, notice: 'Planting was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
@@ -96,10 +104,19 @@ class PlantingsController < ApplicationController
|
||||
@planting = Planting.find(params[:id])
|
||||
@garden = @planting.garden
|
||||
@planting.destroy
|
||||
expire_fragment("homepage_stats")
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to @garden }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def planting_params
|
||||
params.require(:planting).permit(:crop_id, :description, :garden_id, :planted_at,
|
||||
:quantity, :sunniness, :planted_from, :owner_id, :finished,
|
||||
:finished_at)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
class PostsController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
cache_sweeper :post_sweeper
|
||||
|
||||
# GET /posts
|
||||
# GET /posts.json
|
||||
|
||||
@@ -57,7 +56,7 @@ class PostsController < ApplicationController
|
||||
# POST /posts.json
|
||||
def create
|
||||
params[:post][:author_id] = current_member.id
|
||||
@post = Post.new(params[:post])
|
||||
@post = Post.new(post_params)
|
||||
|
||||
respond_to do |format|
|
||||
if @post.save
|
||||
@@ -76,7 +75,7 @@ class PostsController < ApplicationController
|
||||
@post = Post.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @post.update_attributes(params[:post])
|
||||
if @post.update(post_params)
|
||||
format.html { redirect_to @post, notice: 'Post was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
@@ -97,4 +96,10 @@ class PostsController < ApplicationController
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def post_params
|
||||
params.require(:post).permit(:body, :subject, :author_id, :forum_id)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
class ProductsController < ApplicationController
|
||||
before_filter :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /products
|
||||
def index
|
||||
@products = Product.all
|
||||
@@ -34,7 +36,7 @@ class ProductsController < ApplicationController
|
||||
|
||||
# POST /products
|
||||
def create
|
||||
@product = Product.new(params[:product])
|
||||
@product = Product.new(product_params)
|
||||
|
||||
respond_to do |format|
|
||||
if @product.save
|
||||
@@ -50,7 +52,7 @@ class ProductsController < ApplicationController
|
||||
@product = Product.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @product.update_attributes(params[:product])
|
||||
if @product.update(product_params)
|
||||
format.html { redirect_to @product, notice: 'Product was successfully updated.' }
|
||||
else
|
||||
format.html { render action: "edit" }
|
||||
@@ -67,4 +69,11 @@ class ProductsController < ApplicationController
|
||||
format.html { redirect_to products_url }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def product_params
|
||||
params.require(:product).permit(:description, :min_price, :recommended_price, :name,
|
||||
:account_type_id, :paid_months)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
class RegistrationsController < Devise::RegistrationsController
|
||||
|
||||
cache_sweeper :member_sweeper
|
||||
|
||||
def edit
|
||||
@twitter_auth = current_member.auth('twitter')
|
||||
@flickr_auth = current_member.auth('flickr')
|
||||
@@ -10,26 +8,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
|
||||
@@ -34,7 +36,7 @@ class RolesController < ApplicationController
|
||||
|
||||
# POST /roles
|
||||
def create
|
||||
@role = Role.new(params[:role])
|
||||
@role = Role.new(role_params)
|
||||
|
||||
respond_to do |format|
|
||||
if @role.save
|
||||
@@ -50,7 +52,7 @@ class RolesController < ApplicationController
|
||||
@role = Role.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @role.update_attributes(params[:role])
|
||||
if @role.update(role_params)
|
||||
format.html { redirect_to @role, notice: 'Role was successfully updated.' }
|
||||
else
|
||||
format.html { render action: "edit" }
|
||||
@@ -67,4 +69,10 @@ class RolesController < ApplicationController
|
||||
format.html { redirect_to roles_url }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def role_params
|
||||
params.require(:role).permit(:description, :name, :members, :slug)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
class ScientificNamesController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
cache_sweeper :scientific_name_sweeper
|
||||
|
||||
# GET /scientific_names
|
||||
# GET /scientific_names.json
|
||||
def index
|
||||
@@ -46,7 +45,7 @@ class ScientificNamesController < ApplicationController
|
||||
# POST /scientific_names.json
|
||||
def create
|
||||
params[:scientific_name][:creator_id] = current_member.id
|
||||
@scientific_name = ScientificName.new(params[:scientific_name])
|
||||
@scientific_name = ScientificName.new(scientific_name_params)
|
||||
|
||||
respond_to do |format|
|
||||
if @scientific_name.save
|
||||
@@ -65,7 +64,7 @@ class ScientificNamesController < ApplicationController
|
||||
@scientific_name = ScientificName.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @scientific_name.update_attributes(params[:scientific_name])
|
||||
if @scientific_name.update(scientific_name_params)
|
||||
format.html { redirect_to @scientific_name.crop, notice: 'Scientific name was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
@@ -83,8 +82,16 @@ 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
|
||||
|
||||
private
|
||||
|
||||
def scientific_name_params
|
||||
params.require(:scientific_name).permit(:crop_id, :scientific_name, :creator_id)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
class SeedsController < ApplicationController
|
||||
before_filter :authenticate_member!, :except => [:index, :show]
|
||||
load_and_authorize_resource
|
||||
|
||||
cache_sweeper :seed_sweeper
|
||||
|
||||
# GET /seeds
|
||||
# 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
|
||||
@@ -17,6 +19,16 @@ class SeedsController < ApplicationController
|
||||
format.html # index.html.erb
|
||||
format.json { render json: @seeds }
|
||||
format.rss { render :layout => false } #index.rss.builder
|
||||
format.csv do
|
||||
if @owner
|
||||
@filename = "Growstuff-#{@owner}-Seeds-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@seeds = @owner.seeds.includes(:owner, :crop)
|
||||
else
|
||||
@filename = "Growstuff-Seeds-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@seeds = Seed.includes(:owner, :crop)
|
||||
end
|
||||
render :csv => @seeds
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -54,7 +66,7 @@ class SeedsController < ApplicationController
|
||||
# POST /seeds.json
|
||||
def create
|
||||
params[:seed][:owner_id] = current_member.id
|
||||
@seed = Seed.new(params[:seed])
|
||||
@seed = Seed.new(seed_params)
|
||||
|
||||
respond_to do |format|
|
||||
if @seed.save
|
||||
@@ -73,7 +85,7 @@ class SeedsController < ApplicationController
|
||||
@seed = Seed.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @seed.update_attributes(params[:seed])
|
||||
if @seed.update(seed_params)
|
||||
format.html { redirect_to @seed, notice: 'Seed was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
@@ -94,4 +106,11 @@ class SeedsController < ApplicationController
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def seed_params
|
||||
params.require(:seed).permit(:owner_id, :crop_id, :description, :quantity, :plant_before,
|
||||
:tradable_to, :slug)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,5 +24,12 @@ module ApplicationHelper
|
||||
:target => "_blank"
|
||||
end
|
||||
|
||||
# Produces a cache key for uniquely identifying cached fragments.
|
||||
def cache_key_for(klass, identifier="all")
|
||||
count = klass.count
|
||||
max_updated_at = klass.maximum(:updated_at).try(:utc).try(:to_s, :number)
|
||||
"#{klass.name.downcase.pluralize}/#{identifier}-#{count}-#{max_updated_at}"
|
||||
end
|
||||
|
||||
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
|
||||
40
app/helpers/harvests_helper.rb
Normal file
40
app/helpers/harvests_helper.rb
Normal file
@@ -0,0 +1,40 @@
|
||||
module HarvestsHelper
|
||||
|
||||
def display_quantity(harvest)
|
||||
human_quantity = display_human_quantity(harvest)
|
||||
weight = display_weight(harvest)
|
||||
|
||||
if human_quantity && weight
|
||||
return "#{human_quantity}, weighing #{weight}"
|
||||
elsif human_quantity
|
||||
return human_quantity
|
||||
elsif weight
|
||||
return weight
|
||||
else
|
||||
return 'not specified'
|
||||
end
|
||||
end
|
||||
|
||||
def display_human_quantity(harvest)
|
||||
if ! harvest.quantity.blank? && harvest.quantity > 0
|
||||
if harvest.unit == 'individual' # just the number
|
||||
number_to_human(harvest.quantity, :strip_insignificant_zeros => true)
|
||||
elsif ! harvest.unit.blank? # pluralize anything else
|
||||
return pluralize(number_to_human(harvest.quantity, :strip_insignificant_zeros => true), harvest.unit)
|
||||
else
|
||||
return "#{number_to_human(harvest.quantity, :strip_insignificant_zeros => true)} #{harvest.unit}"
|
||||
end
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
def display_weight(harvest)
|
||||
if ! harvest.weight_quantity.blank? && harvest.weight_quantity > 0
|
||||
return "#{number_to_human(harvest.weight_quantity, :strip_insignificant_zeros => true)} #{harvest.weight_unit}"
|
||||
else
|
||||
return nil
|
||||
end
|
||||
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.first(5)
|
||||
@harvests = @member.harvests.first(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.
|
||||
@@ -64,6 +67,10 @@ class Ability
|
||||
can :update, Planting, :garden => { :owner_id => member.id }
|
||||
can :destroy, Planting, :garden => { :owner_id => member.id }
|
||||
|
||||
can :create, Harvest
|
||||
can :update, Harvest, :owner_id => member.id
|
||||
can :destroy, Harvest, :owner_id => member.id
|
||||
|
||||
can :create, Photo
|
||||
can :update, Photo, :owner_id => member.id
|
||||
can :destroy, Photo, :owner_id => member.id
|
||||
@@ -86,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
|
||||
@@ -97,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
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
class Account < ActiveRecord::Base
|
||||
attr_accessible :account_type_id, :member_id, :paid_until
|
||||
belongs_to :member
|
||||
belongs_to :account_type
|
||||
|
||||
@@ -9,7 +8,7 @@ class Account < ActiveRecord::Base
|
||||
|
||||
before_create do |account|
|
||||
unless account.account_type
|
||||
account.account_type = AccountType.find_or_create_by_name(
|
||||
account.account_type = AccountType.find_or_create_by(name:
|
||||
Growstuff::Application.config.default_account_type
|
||||
)
|
||||
end
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
class AccountType < ActiveRecord::Base
|
||||
attr_accessible :is_paid, :is_permanent_paid, :name
|
||||
has_many :products
|
||||
|
||||
def to_s
|
||||
|
||||
4
app/models/alternate_name.rb
Normal file
4
app/models/alternate_name.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
class AlternateName < ActiveRecord::Base
|
||||
belongs_to :crop
|
||||
belongs_to :creator, :class_name => 'Member'
|
||||
end
|
||||
@@ -1,4 +1,3 @@
|
||||
class Authentication < ActiveRecord::Base
|
||||
belongs_to :member
|
||||
attr_accessible :provider, :uid, :token, :secret, :name
|
||||
end
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
class Comment < ActiveRecord::Base
|
||||
attr_accessible :author_id, :body, :post_id
|
||||
belongs_to :author, :class_name => 'Member'
|
||||
belongs_to :post
|
||||
|
||||
default_scope order("created_at DESC")
|
||||
scope :post_order, reorder("created_at ASC") # for display on post page
|
||||
default_scope { order("created_at DESC") }
|
||||
scope :post_order, -> { reorder("created_at ASC") } # for display on post page
|
||||
|
||||
after_create do
|
||||
recipient = self.post.author.id
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
class CommentSweeper < ActionController::Caching::Sweeper
|
||||
observe Comment
|
||||
|
||||
def after_create(comment)
|
||||
expire_fragment('recent_posts')
|
||||
end
|
||||
|
||||
def after_update(comment)
|
||||
end
|
||||
|
||||
def after_destroy(comment)
|
||||
expire_fragment('recent_posts')
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,34 +1,39 @@
|
||||
class Crop < ActiveRecord::Base
|
||||
extend FriendlyId
|
||||
friendly_id :system_name, use: :slugged
|
||||
attr_accessible :en_wikipedia_url, :system_name, :parent_id, :creator_id
|
||||
friendly_id :name, use: [:slugged, :finders]
|
||||
|
||||
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, -> { uniq }, :through => :harvests
|
||||
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(system_name) asc")
|
||||
scope :recent, reorder("created_at desc")
|
||||
scope :randomized, reorder('random()') # ok on sqlite and psql, but not on mysql
|
||||
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,
|
||||
:format => {
|
||||
:with => /^https?:\/\/en\.wikipedia\.org\/wiki/,
|
||||
:with => /\Ahttps?:\/\/en\.wikipedia\.org\/wiki/,
|
||||
: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 system_name
|
||||
return name
|
||||
end
|
||||
|
||||
def default_scientific_name
|
||||
@@ -76,6 +81,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
|
||||
@@ -97,4 +116,95 @@ class Crop < ActiveRecord::Base
|
||||
return interesting_crops
|
||||
end
|
||||
|
||||
# Crop.create_from_csv(row)
|
||||
# used by db/seeds.rb and rake growstuff:import_crops
|
||||
# CSV fields:
|
||||
# - name (required)
|
||||
# - 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,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
|
||||
|
||||
crop = Crop.find_or_create_by(name: name)
|
||||
crop.update_attributes(
|
||||
:en_wikipedia_url => en_wikipedia_url,
|
||||
:creator_id => cropbot.id
|
||||
)
|
||||
|
||||
if parent
|
||||
parent = Crop.find_by_name(parent)
|
||||
if parent
|
||||
crop.update_attributes(:parent_id => parent.id)
|
||||
else
|
||||
logger.warn("Warning: parent crop #{parent} not found")
|
||||
end
|
||||
end
|
||||
|
||||
crop.add_scientific_names_from_csv(scientific_names)
|
||||
crop.add_alternate_names_from_csv(alternate_names)
|
||||
|
||||
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
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
class CropSweeper < ActionController::Caching::Sweeper
|
||||
observe Crop
|
||||
|
||||
def after_create(crop)
|
||||
expire_fragment('homepage_stats')
|
||||
expire_fragment('recent_crops')
|
||||
end
|
||||
|
||||
def after_update(crop)
|
||||
expire_fragment("crop_image_#{crop.id}")
|
||||
end
|
||||
|
||||
def after_destroy(crop)
|
||||
expire_fragment('homepage_stats')
|
||||
expire_fragment('recent_crops')
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
16
app/models/follow.rb
Normal file
16
app/models/follow.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
class Follow < ActiveRecord::Base
|
||||
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
|
||||
@@ -1,7 +1,7 @@
|
||||
class Forum < ActiveRecord::Base
|
||||
extend FriendlyId
|
||||
friendly_id :name, use: :slugged
|
||||
attr_accessible :description, :name, :owner_id, :slug
|
||||
friendly_id :name, use: [:slugged, :finders]
|
||||
|
||||
has_many :posts
|
||||
belongs_to :owner, :class_name => "Member"
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
class ForumSweeper < ActionController::Caching::Sweeper
|
||||
observe Forum
|
||||
|
||||
def after_create(forum)
|
||||
expire_fragment('homepage_forums')
|
||||
end
|
||||
|
||||
def after_update(forum)
|
||||
expire_fragment('homepage_forums')
|
||||
end
|
||||
|
||||
def after_destroy(forum)
|
||||
expire_fragment('homepage_forums')
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,21 +1,53 @@
|
||||
class Garden < ActiveRecord::Base
|
||||
include Geocodable
|
||||
extend FriendlyId
|
||||
friendly_id :garden_slug, use: :slugged
|
||||
friendly_id :garden_slug, use: [:slugged, :finders]
|
||||
|
||||
attr_accessible :name, :slug, :owner_id, :description
|
||||
belongs_to :owner, :class_name => 'Member', :foreign_key => 'owner_id'
|
||||
has_many :plantings, :order => 'created_at DESC', :dependent => :destroy
|
||||
has_many :plantings, -> { order(created_at: :desc) }, :dependent => :destroy
|
||||
has_many :crops, :through => :plantings
|
||||
|
||||
# before_create :replace_blank_name
|
||||
# set up geocoding
|
||||
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")
|
||||
default_scope { order("lower(name) asc") }
|
||||
scope :active, -> { where(:active => true) }
|
||||
scope :inactive, -> { where(:active => false) }
|
||||
|
||||
validates :name,
|
||||
:format => {
|
||||
:with => /\S/
|
||||
}
|
||||
|
||||
validates :area,
|
||||
:numericality => { :only_integer => false, :greater_than_or_equal_to => 0 },
|
||||
:allow_nil => true
|
||||
|
||||
AREA_UNITS_VALUES = {
|
||||
"square metres" => "square metre",
|
||||
"square feet" => "square foot",
|
||||
"hectares" => "hectare",
|
||||
"acres" => "acre"
|
||||
}
|
||||
validates :area_unit, :inclusion => { :in => AREA_UNITS_VALUES.values,
|
||||
:message => "%{value} is not a valid area unit" },
|
||||
:allow_nil => true,
|
||||
:allow_blank => true
|
||||
|
||||
after_validation :cleanup_area
|
||||
|
||||
def cleanup_area
|
||||
if area == 0
|
||||
self.area = nil
|
||||
end
|
||||
if area.blank?
|
||||
self.area_unit = nil
|
||||
end
|
||||
end
|
||||
|
||||
def garden_slug
|
||||
"#{owner.login_name}-#{name}".downcase.gsub(' ', '-')
|
||||
end
|
||||
@@ -40,4 +72,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,16 +0,0 @@
|
||||
class GardenSweeper < ActionController::Caching::Sweeper
|
||||
observe Garden
|
||||
|
||||
def after_create(garden)
|
||||
expire_fragment('homepage_stats')
|
||||
expire_fragment('interesting_members') if garden.owner.interesting?
|
||||
expire_fragment("member_thumbnail_#{garden.owner.id}")
|
||||
end
|
||||
|
||||
def after_destroy(garden)
|
||||
expire_fragment('homepage_stats')
|
||||
expire_fragment('interesting_members') if garden.owner.interesting?
|
||||
expire_fragment("member_thumbnail_#{garden.owner.id}")
|
||||
end
|
||||
end
|
||||
|
||||
121
app/models/harvest.rb
Normal file
121
app/models/harvest.rb
Normal file
@@ -0,0 +1,121 @@
|
||||
class Harvest < ActiveRecord::Base
|
||||
include ActionView::Helpers::NumberHelper
|
||||
extend FriendlyId
|
||||
friendly_id :harvest_slug, use: [:slugged, :finders]
|
||||
|
||||
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
|
||||
|
||||
UNITS_VALUES = {
|
||||
"individual" => "individual",
|
||||
"bunches" => "bunch",
|
||||
"sprigs" => "sprig",
|
||||
"handfuls" => "handful",
|
||||
"litres" => "litre",
|
||||
"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
|
||||
|
||||
validates :weight_quantity,
|
||||
:numericality => { :only_integer => false },
|
||||
:allow_nil => true
|
||||
|
||||
WEIGHT_UNITS_VALUES = {
|
||||
"kg" => "kg",
|
||||
"lb" => "lb",
|
||||
"oz" => "oz"
|
||||
}
|
||||
validates :weight_unit, :inclusion => { :in => WEIGHT_UNITS_VALUES.values,
|
||||
:message => "%{value} is not a valid unit" },
|
||||
:allow_nil => true,
|
||||
:allow_blank => true
|
||||
|
||||
after_validation :cleanup_quantities
|
||||
|
||||
def cleanup_quantities
|
||||
if quantity == 0
|
||||
self.quantity = nil
|
||||
end
|
||||
|
||||
if quantity.blank?
|
||||
self.unit = nil
|
||||
end
|
||||
|
||||
if weight_quantity == 0
|
||||
self.weight_quantity = nil
|
||||
end
|
||||
|
||||
if weight_quantity.blank?
|
||||
self.weight_unit = nil
|
||||
end
|
||||
end
|
||||
|
||||
def harvest_slug
|
||||
"#{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
|
||||
@@ -1,6 +1,8 @@
|
||||
class Member < ActiveRecord::Base
|
||||
include Geocodable
|
||||
extend FriendlyId
|
||||
friendly_id :login_name, use: :slugged
|
||||
|
||||
friendly_id :login_name, use: [:slugged, :finders]
|
||||
|
||||
has_many :posts, :foreign_key => 'author_id'
|
||||
has_many :comments, :foreign_key => 'author_id'
|
||||
@@ -10,6 +12,7 @@ class Member < ActiveRecord::Base
|
||||
has_many :plantings, :foreign_key => 'owner_id'
|
||||
|
||||
has_many :seeds, :foreign_key => 'owner_id'
|
||||
has_many :harvests, :foreign_key => 'owner_id'
|
||||
|
||||
has_and_belongs_to_many :roles
|
||||
|
||||
@@ -24,11 +27,18 @@ class Member < ActiveRecord::Base
|
||||
|
||||
has_many :photos
|
||||
|
||||
default_scope order("lower(login_name) asc")
|
||||
scope :confirmed, where('confirmed_at IS NOT NULL')
|
||||
scope :located, where("location <> ''")
|
||||
scope :recently_signed_in, reorder('updated_at DESC')
|
||||
scope :wants_newsletter, where(:newsletter => true)
|
||||
|
||||
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") }
|
||||
scope :recently_signed_in, -> { reorder('updated_at DESC') }
|
||||
scope :wants_newsletter, -> { where(:newsletter => true) }
|
||||
|
||||
has_many :follows, :class_name => "Follow", :foreign_key => "follower_id"
|
||||
has_many :followed, :through => :follows
|
||||
|
||||
has_many :inverse_follows, :class_name => "Follow", :foreign_key => "followed_id"
|
||||
has_many :followers, :through => :inverse_follows, :source => :follower
|
||||
|
||||
# Include default devise modules. Others available are:
|
||||
# :token_authenticatable, :confirmable,
|
||||
@@ -37,11 +47,6 @@ class Member < ActiveRecord::Base
|
||||
:recoverable, :rememberable, :trackable, :validatable,
|
||||
:confirmable, :lockable, :timeoutable
|
||||
|
||||
# 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
|
||||
|
||||
# set up geocoding
|
||||
geocoded_by :location
|
||||
after_validation :geocode
|
||||
@@ -66,7 +71,7 @@ class Member < ActiveRecord::Base
|
||||
:message => "name is reserved"
|
||||
},
|
||||
:format => {
|
||||
:with => /^\w+$/,
|
||||
:with => /\A\w+\z/,
|
||||
:message => "may only include letters, numbers, or underscores"
|
||||
},
|
||||
:uniqueness => {
|
||||
@@ -79,7 +84,7 @@ class Member < ActiveRecord::Base
|
||||
# and an account record (for paid accounts etc)
|
||||
# we use find_or_create to avoid accidentally creating a second one,
|
||||
# which can happen sometimes especially with FactoryGirl associations
|
||||
after_create {|member| Account.find_or_create_by_member_id(:member_id => member.id) }
|
||||
after_create {|member| Account.find_or_create_by(:member_id => member.id) }
|
||||
|
||||
after_save :update_newsletter_subscription
|
||||
|
||||
@@ -140,8 +145,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
|
||||
@@ -204,13 +209,15 @@ class Member < ActiveRecord::Base
|
||||
return interesting_members
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def empty_unwanted_geocodes
|
||||
if self.location.to_s == ''
|
||||
self.latitude = nil
|
||||
self.longitude = nil
|
||||
def Member.nearest_to(place)
|
||||
nearby_members = []
|
||||
if place
|
||||
latitude, longitude = Geocoder.coordinates(place, params: {limit: 1})
|
||||
if latitude && longitude
|
||||
nearby_members = Member.located.sort_by { |x| x.distance_from([latitude, longitude]) }
|
||||
end
|
||||
end
|
||||
return nearby_members
|
||||
end
|
||||
|
||||
def update_newsletter_subscription
|
||||
@@ -227,22 +234,32 @@ class Member < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def newsletter_subscribe
|
||||
def newsletter_subscribe(testing=false)
|
||||
return true if (Rails.env.test? && !testing)
|
||||
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
|
||||
:double_optin => false # they already confirmed their email with us
|
||||
})
|
||||
end
|
||||
|
||||
def newsletter_unsubscribe
|
||||
def newsletter_unsubscribe(testing=false)
|
||||
return true if (Rails.env.test? && !testing)
|
||||
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
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
class MemberSweeper < ActionController::Caching::Sweeper
|
||||
observe Member
|
||||
|
||||
def after_create(member)
|
||||
expire_fragment('homepage_stats')
|
||||
end
|
||||
|
||||
def after_update(member)
|
||||
expire_fragment('interesting_members') if member.interesting?
|
||||
expire_fragment("interesting_seeds") if member.seeds.tradable.present?
|
||||
expire_fragment("member_thumbnail_#{member.id}")
|
||||
|
||||
if member.plantings.present?
|
||||
member.plantings.each do |p|
|
||||
expire_fragment("plantings_listitem_#{p.id}") if p.interesting?
|
||||
end
|
||||
expire_fragment('interesting_plantings')
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
class Notification < ActiveRecord::Base
|
||||
attr_accessible :sender_id, :recipient_id,
|
||||
:subject, :body, :post_id, :read
|
||||
|
||||
belongs_to :sender, :class_name => 'Member'
|
||||
belongs_to :recipient, :class_name => 'Member'
|
||||
belongs_to :post
|
||||
|
||||
default_scope order('created_at DESC')
|
||||
scope :unread, where(:read => false)
|
||||
default_scope { order('created_at DESC') }
|
||||
scope :unread, -> { where(:read => false) }
|
||||
|
||||
before_create :replace_blank_subject
|
||||
after_create :send_email
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
class Order < ActiveRecord::Base
|
||||
attr_accessible :member_id, :completed_at
|
||||
belongs_to :member
|
||||
|
||||
has_many :order_items, :dependent => :destroy
|
||||
|
||||
default_scope order('created_at DESC')
|
||||
default_scope { order('created_at DESC') }
|
||||
|
||||
validates :referral_code, :format => {
|
||||
:with => /\A[a-zA-Z0-9 ]*\z/,
|
||||
:message => "may only include letters and numbers"
|
||||
}
|
||||
|
||||
before_save :standardize_referral_code
|
||||
|
||||
# total price of an order
|
||||
def total
|
||||
@@ -45,4 +51,46 @@ class Order < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
# removes whitespace and forces to uppercase (we're somewhat liberal
|
||||
# in what we accept, but we clean it up anyway.)
|
||||
def standardize_referral_code
|
||||
if referral_code
|
||||
self.referral_code = referral_code.upcase.gsub /\s/, ''
|
||||
end
|
||||
end
|
||||
|
||||
# search orders (used by admin/orders)
|
||||
# usage: Order.search({ :by => 'member', :for => 'Skud' })
|
||||
# can search by: member, order_id, paypal_token, paypal_payer_id,
|
||||
def Order.search(args={})
|
||||
if args[:for]
|
||||
case args[:by]
|
||||
when "member"
|
||||
member = Member.find_by_login_name(args[:for])
|
||||
if member
|
||||
return member.orders
|
||||
end
|
||||
when "order_id"
|
||||
order = Order.find_by_id(args[:for])
|
||||
if order
|
||||
return [order]
|
||||
end
|
||||
when "paypal_token"
|
||||
order = Order.find_by_paypal_express_token(args[:for])
|
||||
if order
|
||||
return [order]
|
||||
end
|
||||
when "paypal_payer_id"
|
||||
order = Order.find_by_paypal_express_payer_id(args[:for])
|
||||
if order
|
||||
return [order]
|
||||
end
|
||||
when "referral_code"
|
||||
# coerce to uppercase
|
||||
return Order.where(:referral_code => args[:for].upcase)
|
||||
end
|
||||
end
|
||||
return []
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
class OrderItem < ActiveRecord::Base
|
||||
attr_accessible :order_id, :price, :product_id, :quantity
|
||||
|
||||
belongs_to :order
|
||||
belongs_to :product
|
||||
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
class Photo < ActiveRecord::Base
|
||||
attr_accessible :flickr_photo_id, :owner_id, :title, :license_name,
|
||||
:license_url, :thumbnail_url, :fullsize_url, :link_url
|
||||
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")
|
||||
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.
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
class PhotoSweeper < ActionController::Caching::Sweeper
|
||||
observe Photo
|
||||
|
||||
def after_create(photo)
|
||||
expire_fragment('interesting_plantings')
|
||||
end
|
||||
|
||||
def after_destroy(photo)
|
||||
expire_fragment('interesting_plantings')
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
24
app/models/plant_part.rb
Normal file
24
app/models/plant_part.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
class PlantPart < ActiveRecord::Base
|
||||
extend FriendlyId
|
||||
friendly_id :name, :use => [:slugged, :finders]
|
||||
|
||||
has_many :harvests
|
||||
has_many :crops, -> { uniq }, :through => :harvests
|
||||
|
||||
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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user