Compare commits
836
Commits
1.4.0
...
2.1-stable
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8cdd427a0e | ||
|
|
de2b7c2879 | ||
|
|
f0aad8c2a8 | ||
|
|
68b923640e | ||
|
|
4e2fbf440b | ||
|
|
5572b6c715 | ||
|
|
d63f0fa5b0 | ||
|
|
dff772b066 | ||
|
|
fb2cbdc6cc | ||
|
|
6062a0a89d | ||
|
|
0ebf95d819 | ||
|
|
14879e24c4 | ||
|
|
ca0b289479 | ||
|
|
e36a611f81 | ||
|
|
6c2eec59d8 | ||
|
|
905ed3aa75 | ||
|
|
bb4c530ba2 | ||
|
|
e3958ef577 | ||
|
|
c5f2dadb2c | ||
|
|
af8dcccb7b | ||
|
|
c0742ed9e6 | ||
|
|
8c7ae20402 | ||
|
|
06b68f1948 | ||
|
|
b51203bc83 | ||
|
|
f578a5c4f6 | ||
|
|
17d1907747 | ||
|
|
0e241bb857 | ||
|
|
9dcde53dab | ||
|
|
a44521527a | ||
|
|
f12590fa5c | ||
|
|
a1c6f710aa | ||
|
|
3ad82e4665 | ||
|
|
eef3b5b5ca | ||
|
|
ab69845ea2 | ||
|
|
af51a68b34 | ||
|
|
d5c08680f6 | ||
|
|
9b82b948e3 | ||
|
|
1c8510bc80 | ||
|
|
23861c7cc1 | ||
|
|
6550343cb9 | ||
|
|
a70cf2f833 | ||
|
|
0f219b973a | ||
|
|
d02e5e54a8 | ||
|
|
c050028102 | ||
|
|
012ffdf9e8 | ||
|
|
9e3aeeef88 | ||
|
|
21b162a624 | ||
|
|
b7e999d1aa | ||
|
|
284d03e1f8 | ||
|
|
3b4126e14f | ||
|
|
46a76ecb6e | ||
|
|
5a74edef89 | ||
|
|
2f9c2e6bd3 | ||
|
|
2df7c0ff3a | ||
|
|
f5d5077d2b | ||
|
|
94a1eb21a4 | ||
|
|
1f80a4b0d9 | ||
|
|
55220950d2 | ||
|
|
daea57a37c | ||
|
|
fe61739108 | ||
|
|
a4cd96e8b0 | ||
|
|
804302ce61 | ||
|
|
89ecbe9cdc | ||
|
|
aac3e1e51c | ||
|
|
595fef0d68 | ||
|
|
65a6e3985e | ||
|
|
f27a0eca5b | ||
|
|
2d34ab9235 | ||
|
|
03bff3308f | ||
|
|
006a1b4fd7 | ||
|
|
9095874a07 | ||
|
|
8ecfd1dcf8 | ||
|
|
35584bc53a | ||
|
|
21a16e0958 | ||
|
|
76d24e44bf | ||
|
|
efbdef9357 | ||
|
|
5eab4af70d | ||
|
|
96b2e00015 | ||
|
|
9939fbeef6 | ||
|
|
b369f82d15 | ||
|
|
0ad5dfaba0 | ||
|
|
d7d18689b7 | ||
|
|
4cd22bb53a | ||
|
|
7d8db59dae | ||
|
|
d1606fd3e3 | ||
|
|
097d9661c9 | ||
|
|
f8cedf00a8 | ||
|
|
2461d12388 | ||
|
|
bcd8ce2de2 | ||
|
|
0cd9149e7a | ||
|
|
69d0660760 | ||
|
|
3f7a7d9b14 | ||
|
|
66f881aad3 | ||
|
|
d3d719c0e7 | ||
|
|
03aa7037a5 | ||
|
|
ffb981255c | ||
|
|
3bbb2566c8 | ||
|
|
8cfec7ddf5 | ||
|
|
af177c3a59 | ||
|
|
25aeb4c3d3 | ||
|
|
b75afd49de | ||
|
|
0cd08ed08c | ||
|
|
ad71bffe34 | ||
|
|
a2edef9035 | ||
|
|
abcfbd9c75 | ||
|
|
d0ca8eb703 | ||
|
|
e637c15660 | ||
|
|
f9d0b4776f | ||
|
|
3b5fd45fae | ||
|
|
6b0e8fd82a | ||
|
|
972a991a92 | ||
|
|
62d5b4c710 | ||
|
|
197a14a82e | ||
|
|
ff86c37ed3 | ||
|
|
5e9320137b | ||
|
|
18ed4cf373 | ||
|
|
8303e7c168 | ||
|
|
eb58c62f26 | ||
|
|
b72d40a429 | ||
|
|
6a37151b5c | ||
|
|
5003927f13 | ||
|
|
ffcf1925e3 | ||
|
|
ad4ee1c8d8 | ||
|
|
d4fc347a8a | ||
|
|
c25d175623 | ||
|
|
47e375b271 | ||
|
|
eeedd287ad | ||
|
|
2edb8d597a | ||
|
|
23eae0bf3c | ||
|
|
822a627c44 | ||
|
|
3431b860ad | ||
|
|
3483938185 | ||
|
|
2af4bb6a86 | ||
|
|
b9586dd570 | ||
|
|
40df75243b | ||
|
|
0486ce9976 | ||
|
|
621b15c923 | ||
|
|
d1eb07dd8b | ||
|
|
9c5f339efb | ||
|
|
bbed770fa4 | ||
|
|
1f7a944d14 | ||
|
|
ef8c393290 | ||
|
|
bcb4c2cde4 | ||
|
|
3dfef8a00d | ||
|
|
ee2481a3b9 | ||
|
|
99913e80f0 | ||
|
|
48ac467981 | ||
|
|
6e21342c60 | ||
|
|
3f63b5ea93 | ||
|
|
ca7498c2d6 | ||
|
|
1b64e4be5e | ||
|
|
3a32edc3bd | ||
|
|
462c986452 | ||
|
|
c3170e9264 | ||
|
|
2a9c001625 | ||
|
|
94db7c0029 | ||
|
|
e1492f771c | ||
|
|
48b9b805b2 | ||
|
|
8b72710d7e | ||
|
|
265baa1b2c | ||
|
|
139ddd639f | ||
|
|
e4b7b7cc38 | ||
|
|
8eb39d682d | ||
|
|
4bca584024 | ||
|
|
935d8e594b | ||
|
|
374a0cc7cb | ||
|
|
53dd8dc11a | ||
|
|
444987ce91 | ||
|
|
43add0d306 | ||
|
|
3581782ec2 | ||
|
|
8d4c0e5ecd | ||
|
|
9a57cc18e3 | ||
|
|
564de36c08 | ||
|
|
6ea1bc725a | ||
|
|
a7be337a4c | ||
|
|
3a77d543fd | ||
|
|
d33fa1f8c8 | ||
|
|
50506ef621 | ||
|
|
c68ee7f545 | ||
|
|
ebc979e9b1 | ||
|
|
3cc6d5e815 | ||
|
|
a2e59cc956 | ||
|
|
5969df8142 | ||
|
|
2ed4364c8a | ||
|
|
70226f1833 | ||
|
|
a1d8cab6c6 | ||
|
|
3478a21c2f | ||
|
|
44c15c7560 | ||
|
|
d1a52fd301 | ||
|
|
377ef084ad | ||
|
|
665a331cc4 | ||
|
|
185ab2a020 | ||
|
|
02fb4c3b2c | ||
|
|
8376311ee1 | ||
|
|
ac5e3c2036 | ||
|
|
a5c4fcc8f0 | ||
|
|
29d54f5d50 | ||
|
|
fc3a09e49a | ||
|
|
73aece0baf | ||
|
|
af5a814f4c | ||
|
|
91a09bd474 | ||
|
|
3673fbd881 | ||
|
|
d79bcc4369 | ||
|
|
327660eb7f | ||
|
|
49e80c2cea | ||
|
|
3119d1996c | ||
|
|
11d4d8177c | ||
|
|
861ca78179 | ||
|
|
8fb1a7e3cc | ||
|
|
91c875437d | ||
|
|
60574bfb9c | ||
|
|
3e4cbec95e | ||
|
|
dbf41dba68 | ||
|
|
163659d0a5 | ||
|
|
ec02853141 | ||
|
|
2118f3a1fd | ||
|
|
145e5997b2 | ||
|
|
32fcdff69f | ||
|
|
c0c491dd61 | ||
|
|
0a6c1d9c13 | ||
|
|
e52219f09d | ||
|
|
02b2a61e15 | ||
|
|
8b12702ebe | ||
|
|
cbfafcd5e2 | ||
|
|
e06bf0c464 | ||
|
|
f1650b8ff4 | ||
|
|
f82a7a35b0 | ||
|
|
908b960e71 | ||
|
|
0ce0b52342 | ||
|
|
b907398788 | ||
|
|
2e4f3d5a60 | ||
|
|
e0faf5cf84 | ||
|
|
9553325ee3 | ||
|
|
e2d6f0af4e | ||
|
|
405bcc10c0 | ||
|
|
2e536a2c56 | ||
|
|
b6be9bff35 | ||
|
|
b418e27283 | ||
|
|
3676783052 | ||
|
|
599736aca7 | ||
|
|
1749fbf3e6 | ||
|
|
e9de6c1415 | ||
|
|
962134ab4f | ||
|
|
2e35a6800f | ||
|
|
0a5828ee4a | ||
|
|
1949f61d0c | ||
|
|
3f1c35b71e | ||
|
|
1a38434d5f | ||
|
|
f8183429a9 | ||
|
|
2275f46138 | ||
|
|
34cdac1669 | ||
|
|
c7b712067e | ||
|
|
6ca1c117ab | ||
|
|
b603aa74f9 | ||
|
|
c57a2974d1 | ||
|
|
77d94818a4 | ||
|
|
a53894dd22 | ||
|
|
d99dbe9f0c | ||
|
|
3f88fd44ef | ||
|
|
bc153cb61d | ||
|
|
9554f0133f | ||
|
|
f50da1593e | ||
|
|
96306f9dff | ||
|
|
8f0947e578 | ||
|
|
d048d86c50 | ||
|
|
95457c434e | ||
|
|
42570c4426 | ||
|
|
e4518af32a | ||
|
|
08f200c487 | ||
|
|
64f66daedd | ||
|
|
0c72347d61 | ||
|
|
15341bd844 | ||
|
|
7a4c6d2a19 | ||
|
|
1e6938a119 | ||
|
|
9ef719f15e | ||
|
|
2175e4a901 | ||
|
|
3692369584 | ||
|
|
75b0c01431 | ||
|
|
d252848b2e | ||
|
|
3a7207672e | ||
|
|
f8de723c46 | ||
|
|
a0b2ab338e | ||
|
|
308010c7c9 | ||
|
|
6918a8974c | ||
|
|
ba9f1c0388 | ||
|
|
45d9763090 | ||
|
|
62d5b359c0 | ||
|
|
33d6f9e7a1 | ||
|
|
c6106543e1 | ||
|
|
8ee2ae1846 | ||
|
|
a7caf3faf0 | ||
|
|
cc79feabe2 | ||
|
|
71c5d6c8ee | ||
|
|
65524cc1cc | ||
|
|
138f6736b2 | ||
|
|
0880096162 | ||
|
|
571d316b13 | ||
|
|
93d9d459c5 | ||
|
|
c544e9b9c6 | ||
|
|
5565a078f3 | ||
|
|
7306c733bf | ||
|
|
969a5be7da | ||
|
|
6851b0ca81 | ||
|
|
a7a0d47f2e | ||
|
|
93a65c3c82 | ||
|
|
1a088fb31e | ||
|
|
6e7507d580 | ||
|
|
42fdc1d54d | ||
|
|
afc8239bf7 | ||
|
|
8088749f10 | ||
|
|
3c3bdb8bb0 | ||
|
|
9b4d29dc0d | ||
|
|
9eb041fbc7 | ||
|
|
ed165f6716 | ||
|
|
8ed4620bb9 | ||
|
|
8e1f7607ec | ||
|
|
87f284dcb6 | ||
|
|
04f9a321b1 | ||
|
|
f03e21fb45 | ||
|
|
88efd302a7 | ||
|
|
28cdc8adfc | ||
|
|
3209c4c5e4 | ||
|
|
8fa787719a | ||
|
|
bdd29295b4 | ||
|
|
faab8678d4 | ||
|
|
44fcc8919d | ||
|
|
0b31c8ac85 | ||
|
|
46b1a49453 | ||
|
|
cd54e15a8d | ||
|
|
6a2ca5e034 | ||
|
|
387836f8aa | ||
|
|
1f84a8d83e | ||
|
|
dc393cc480 | ||
|
|
d41d493128 | ||
|
|
dadd294a25 | ||
|
|
bd85428b01 | ||
|
|
eb62603144 | ||
|
|
387f09d4f4 | ||
|
|
47e496f049 | ||
|
|
d3bfbb800c | ||
|
|
9c3045eeda | ||
|
|
3eaa998c28 | ||
|
|
e8469e2c5b | ||
|
|
3386008491 | ||
|
|
dee586e9eb | ||
|
|
5b6732cfaf | ||
|
|
b53f9c688b | ||
|
|
87da04c808 | ||
|
|
38060a2cf0 | ||
|
|
c178aded61 | ||
|
|
8553107016 | ||
|
|
21ee2e2cf2 | ||
|
|
035805fbd0 | ||
|
|
51d5a52029 | ||
|
|
6668d7ebd1 | ||
|
|
1c8d03c1e9 | ||
|
|
c790f1ca41 | ||
|
|
d4f7b4af6d | ||
|
|
690b0fb08c | ||
|
|
cc553764ba | ||
|
|
705f96ccde | ||
|
|
cd39b12f2c | ||
|
|
7e5bad993d | ||
|
|
c9a46950de | ||
|
|
ff68fff80e | ||
|
|
bb2af97dcf | ||
|
|
ccc5f64a99 | ||
|
|
6f3c339e5e | ||
|
|
b0757bbdf6 | ||
|
|
a49d50614f | ||
|
|
7b3aaca83c | ||
|
|
922f1d5243 | ||
|
|
ee3478f389 | ||
|
|
07546b4943 | ||
|
|
2b642c0662 | ||
|
|
3e3714c2d6 | ||
|
|
b5a4446f18 | ||
|
|
4fdbeea686 | ||
|
|
bbb14cb57c | ||
|
|
07ce80be2d | ||
|
|
c3c7f1f900 | ||
|
|
fe95692f10 | ||
|
|
537be80be2 | ||
|
|
5c2de4dfc9 | ||
|
|
570e1b1d62 | ||
|
|
7f1fb7ce81 | ||
|
|
5ac4b6670f | ||
|
|
898873a22d | ||
|
|
74ee5c078f | ||
|
|
185175c78e | ||
|
|
ba647689f4 | ||
|
|
471f631dbd | ||
|
|
54d2b07bff | ||
|
|
e23511076c | ||
|
|
072475b8a5 | ||
|
|
071cf55fc8 | ||
|
|
a7bdc5e0af | ||
|
|
712e5be29f | ||
|
|
5097f51784 | ||
|
|
8a3126e175 | ||
|
|
2f55caa1ba | ||
|
|
4c8c5e9187 | ||
|
|
7946f4a696 | ||
|
|
cb09055d60 | ||
|
|
9e06942ef0 | ||
|
|
02e7025685 | ||
|
|
88e1ae892c | ||
|
|
7f0bb136ad | ||
|
|
391d9b14fb | ||
|
|
e0fb60c6b5 | ||
|
|
6c1db9c3a8 | ||
|
|
7c6582009a | ||
|
|
b636695631 | ||
|
|
004a13b75c | ||
|
|
d7b669e50b | ||
|
|
54d55a360a | ||
|
|
18f693f9f7 | ||
|
|
6bf60e8c20 | ||
|
|
823ac010c5 | ||
|
|
0dbe234226 | ||
|
|
9c9b0a4150 | ||
|
|
7502ac484e | ||
|
|
e2851a8b2a | ||
|
|
c929ab5c16 | ||
|
|
165c2be523 | ||
|
|
b9d7a02b51 | ||
|
|
2984dc903c | ||
|
|
5d90228866 | ||
|
|
2dbabde7f4 | ||
|
|
e74d4ecf5f | ||
|
|
23a1ef543f | ||
|
|
7acd04fb87 | ||
|
|
133ca59edb | ||
|
|
1c825df549 | ||
|
|
c11f5a23fe | ||
|
|
ab96a29460 | ||
|
|
c02594af96 | ||
|
|
193b571e67 | ||
|
|
a1d0acd632 | ||
|
|
6adf61fd02 | ||
|
|
986ffb2434 | ||
|
|
efc6abea07 | ||
|
|
851fbaf750 | ||
|
|
7b40767428 | ||
|
|
56666a41b8 | ||
|
|
7a529b24ee | ||
|
|
b379397d95 | ||
|
|
a0c495b953 | ||
|
|
b0bd506201 | ||
|
|
3b207ee77c | ||
|
|
553066e804 | ||
|
|
76a4b81cf3 | ||
|
|
6086aa1be4 | ||
|
|
8a080d8e53 | ||
|
|
6351631ec6 | ||
|
|
5981aee06d | ||
|
|
232edc6237 | ||
|
|
d4dd35a78c | ||
|
|
f4a2d1f342 | ||
|
|
232d57450a | ||
|
|
196f96fff1 | ||
|
|
1d7c0eb7c0 | ||
|
|
bd927eea88 | ||
|
|
a1f17b982c | ||
|
|
cc2ee90f97 | ||
|
|
1eebd030ca | ||
|
|
4cecc1beed | ||
|
|
51a1bf90dd | ||
|
|
ad68830a1b | ||
|
|
1d1aef9d61 | ||
|
|
d00ba6d2bb | ||
|
|
86617fc437 | ||
|
|
65b4515e1a | ||
|
|
a8e5ba0007 | ||
|
|
df41255105 | ||
|
|
bf69f9af9f | ||
|
|
0c435ce630 | ||
|
|
3e5dfed5d6 | ||
|
|
a265e323ad | ||
|
|
56cf381357 | ||
|
|
eb8f455c13 | ||
|
|
6c9401b624 | ||
|
|
f9f5e9e7c6 | ||
|
|
6529035ee1 | ||
|
|
7f6ac407ef | ||
|
|
0ecab46229 | ||
|
|
c38c3c9436 | ||
|
|
2007543539 | ||
|
|
72e5b638e9 | ||
|
|
66390fd0e6 | ||
|
|
21ac4a3d0e | ||
|
|
2ceb6b8230 | ||
|
|
7fb2ddefde | ||
|
|
8334651c4d | ||
|
|
ac56c0c99c | ||
|
|
5961a1e70d | ||
|
|
1221b79e98 | ||
|
|
0cc817fba3 | ||
|
|
7ed2c481d8 | ||
|
|
26e75568cf | ||
|
|
37cbbcea71 | ||
|
|
585d08765e | ||
|
|
3b854bee59 | ||
|
|
6c6aae07a2 | ||
|
|
9c060fe33a | ||
|
|
d3aa9c6a7b | ||
|
|
a787325ed2 | ||
|
|
2311f80c5d | ||
|
|
f62507dae5 | ||
|
|
9e878a9384 | ||
|
|
e3eab40ec4 | ||
|
|
b93e040bb8 | ||
|
|
dbdc9b9da7 | ||
|
|
2c07b478bb | ||
|
|
542b355210 | ||
|
|
897e83a556 | ||
|
|
768e36caf0 | ||
|
|
76eeb64d1a | ||
|
|
c804151780 | ||
|
|
e199c4b823 | ||
|
|
9eef74f09a | ||
|
|
48a557a79e | ||
|
|
4fda1f6cd1 | ||
|
|
12ea96066b | ||
|
|
13dced9d76 | ||
|
|
667693b32c | ||
|
|
965ef33192 | ||
|
|
462d0ddfe8 | ||
|
|
9989ae5a06 | ||
|
|
89feb8a3fb | ||
|
|
fdd36ce713 | ||
|
|
21dac7287b | ||
|
|
74a2c5a521 | ||
|
|
96f5d315ea | ||
|
|
9b63117856 | ||
|
|
888d284136 | ||
|
|
f673027bc1 | ||
|
|
8a491dbae9 | ||
|
|
7c105ec9e9 | ||
|
|
9b60214b3a | ||
|
|
4d12bea397 | ||
|
|
490bfc2934 | ||
|
|
c47293edb1 | ||
|
|
be4ad60058 | ||
|
|
74645eb017 | ||
|
|
26ff9e1c26 | ||
|
|
c73823cd0f | ||
|
|
8d4e528f22 | ||
|
|
898a4164a0 | ||
|
|
d21bacb01d | ||
|
|
8b381e3bd4 | ||
|
|
9f788310b1 | ||
|
|
7fba8bc682 | ||
|
|
2314e41474 | ||
|
|
77bac4b14d | ||
|
|
b7a435f7a0 | ||
|
|
234e5d59ac | ||
|
|
44ea32afba | ||
|
|
c8226509fd | ||
|
|
327d5d2132 | ||
|
|
1c5b214056 | ||
|
|
5bfe80949c | ||
|
|
0523ac387b | ||
|
|
ef01cada8b | ||
|
|
182a215c7b | ||
|
|
2cbf9c9cc4 | ||
|
|
dd9c2cafa7 | ||
|
|
3142183b30 | ||
|
|
ba25d27e3a | ||
|
|
69b46ab1be | ||
|
|
dd1cb4a5bf | ||
|
|
d58e5a0a1e | ||
|
|
9f531a4380 | ||
|
|
733fef458c | ||
|
|
121ce2a390 | ||
|
|
fc7b790081 | ||
|
|
d915a5e36d | ||
|
|
4ee133e77e | ||
|
|
53a0cee57a | ||
|
|
251d62c2bf | ||
|
|
afbff44fb4 | ||
|
|
3ba6718805 | ||
|
|
e1268faa4a | ||
|
|
cfaa0fc23e | ||
|
|
cfddd2ff48 | ||
|
|
87783362cf | ||
|
|
9aef7a3242 | ||
|
|
189be55235 | ||
|
|
84084b0168 | ||
|
|
3a4a708d51 | ||
|
|
e4332ba35f | ||
|
|
d664fdcde9 | ||
|
|
aa18cd54c7 | ||
|
|
59cbc68dde | ||
|
|
221585c7b5 | ||
|
|
e7b9a9c6ff | ||
|
|
472d3a00a3 | ||
|
|
235238b583 | ||
|
|
73500a349b | ||
|
|
b587f693ab | ||
|
|
c77370a9eb | ||
|
|
6206c88dfa | ||
|
|
1ab9e42b9b | ||
|
|
ae8830ed61 | ||
|
|
1996af104d | ||
|
|
e68043e13c | ||
|
|
05481fe96b | ||
|
|
034539c988 | ||
|
|
48c28f717d | ||
|
|
23a06ec022 | ||
|
|
4f0aa2432c | ||
|
|
426b1d6fcd | ||
|
|
478a549356 | ||
|
|
69e55fb1ef | ||
|
|
46400e355e | ||
|
|
dc7569ecae | ||
|
|
7c97832002 | ||
|
|
46e48ad3f7 | ||
|
|
a59257dfb6 | ||
|
|
9382d856b4 | ||
|
|
30d2bed1fe | ||
|
|
a99c61b471 | ||
|
|
1d677ac968 | ||
|
|
1ccc975f3f | ||
|
|
dca52d6729 | ||
|
|
1831861eac | ||
|
|
5bd90548b0 | ||
|
|
0bb937152b | ||
|
|
804864beca | ||
|
|
2ed78d95f6 | ||
|
|
678469cf09 | ||
|
|
11746c44f8 | ||
|
|
411f0874ab | ||
|
|
0ba0a7bcc9 | ||
|
|
ddfb2315aa | ||
|
|
1ad003dcc7 | ||
|
|
7b6d11ed6a | ||
|
|
04fe0412e1 | ||
|
|
03e9c51776 | ||
|
|
3710bbfda1 | ||
|
|
df7ddf5c0e | ||
|
|
31d2d1284a | ||
|
|
f6882cd69e | ||
|
|
18852b765a | ||
|
|
ee8dcab9db | ||
|
|
02f704bb01 | ||
|
|
f08b648cce | ||
|
|
19e56045dd | ||
|
|
6fc909a402 | ||
|
|
7af8d7caf0 | ||
|
|
e876d1bfc0 | ||
|
|
ba5a052c8c | ||
|
|
6e7eadf9fb | ||
|
|
5ce2987ea9 | ||
|
|
a3c25c18d7 | ||
|
|
61a1fa1b6e | ||
|
|
1ab261dda6 | ||
|
|
255a84878d | ||
|
|
6bcbdd44fc | ||
|
|
66d62504ef | ||
|
|
17d19febc6 | ||
|
|
b0414ec1fb | ||
|
|
569e1b37cc | ||
|
|
bef28f7dab | ||
|
|
49fc255b48 | ||
|
|
6c6467879a | ||
|
|
9f396a6dfb | ||
|
|
ad8193dcde | ||
|
|
b4350ca91a | ||
|
|
2c28d6b5d1 | ||
|
|
36ee2b24bd | ||
|
|
d2278b63a1 | ||
|
|
d658ab22d6 | ||
|
|
1446c9b7f0 | ||
|
|
9be6dfbe10 | ||
|
|
38011c0fb1 | ||
|
|
d1372107e5 | ||
|
|
c085367bb6 | ||
|
|
bebe429472 | ||
|
|
0491488554 | ||
|
|
785825900a | ||
|
|
017d974dd8 | ||
|
|
5b680625c1 | ||
|
|
6fca028905 | ||
|
|
b097a1753e | ||
|
|
f54ecfc55f | ||
|
|
45093230a9 | ||
|
|
f4b9d2a7ee | ||
|
|
e1920cc9e4 | ||
|
|
f10fed295a | ||
|
|
0fcc82482e | ||
|
|
ee4928d17c | ||
|
|
8a67fe30af | ||
|
|
db9a2c1c58 | ||
|
|
767a057333 | ||
|
|
30ceec20a6 | ||
|
|
910cb8ff06 | ||
|
|
6a1a4e006d | ||
|
|
d2378ea641 | ||
|
|
8d17242b82 | ||
|
|
1e491c0bfd | ||
|
|
fb8ca0d2f8 | ||
|
|
d8c70d7a11 | ||
|
|
253b02bd22 | ||
|
|
99cfca6f83 | ||
|
|
6143d119fe | ||
|
|
0162222bb2 | ||
|
|
ab92280a7f | ||
|
|
c0891e8a78 | ||
|
|
f624d01fd8 | ||
|
|
4b2fc90431 | ||
|
|
49b28fca68 | ||
|
|
1e3e23d756 | ||
|
|
68d5af3dd8 | ||
|
|
bceaf8be94 | ||
|
|
52986e8cd1 | ||
|
|
10c0634413 | ||
|
|
e4a9ce6db0 | ||
|
|
584fbc8f8b | ||
|
|
5985713335 | ||
|
|
68f8470d4a | ||
|
|
da43f785be | ||
|
|
03335d014c | ||
|
|
f12942ff40 | ||
|
|
c7d78ef094 | ||
|
|
5909b00a46 | ||
|
|
ee0d4a12a3 | ||
|
|
705b6b16a5 | ||
|
|
301e0386f5 | ||
|
|
a97e2593af | ||
|
|
f2ec23d80c | ||
|
|
d940797aa7 | ||
|
|
d2f779d6e8 | ||
|
|
8047a8cd28 | ||
|
|
dd23e07111 | ||
|
|
d7e14cb7b7 | ||
|
|
882d46661e | ||
|
|
6fcea5ef9d | ||
|
|
d19a7dee42 | ||
|
|
6f8d7d6e76 | ||
|
|
bf4d779ea2 | ||
|
|
d0d01d4e70 | ||
|
|
71649ba2f1 | ||
|
|
79091382cc | ||
|
|
cb16661d36 | ||
|
|
8d73ddf73f | ||
|
|
26868d8b14 | ||
|
|
abdcd7d705 | ||
|
|
6d41de61a5 | ||
|
|
d15ab86459 | ||
|
|
5e57a1a9d9 | ||
|
|
34e20c4373 | ||
|
|
70d98acf9a | ||
|
|
215f4adcd7 | ||
|
|
ccf37f59a9 | ||
|
|
0fad78f53f | ||
|
|
2e3bf71e9a | ||
|
|
95f9246a46 | ||
|
|
b960470d78 | ||
|
|
4a3f038595 | ||
|
|
1c31e32df7 | ||
|
|
4598c64ebd | ||
|
|
9305c84093 | ||
|
|
72ab3e22b1 | ||
|
|
e95ab31983 | ||
|
|
fcbeee78c1 | ||
|
|
306af1d017 | ||
|
|
5a35c5b2e2 | ||
|
|
b3bddcd31f | ||
|
|
b0f35a3cde | ||
|
|
62eb536e32 | ||
|
|
67f90df175 | ||
|
|
c88fbfbdad | ||
|
|
424a70978e | ||
|
|
24e6dbc709 | ||
|
|
d856e62ccf | ||
|
|
143c29d8e1 | ||
|
|
f79961f1c5 | ||
|
|
327a74b406 | ||
|
|
864054c005 | ||
|
|
6f71a508eb | ||
|
|
7366fb23bb | ||
|
|
0a4a49992e | ||
|
|
2cc294aa27 | ||
|
|
ee559c6820 | ||
|
|
2581d00c69 | ||
|
|
41b986db06 | ||
|
|
b949d49ed3 | ||
|
|
f529cdddb3 | ||
|
|
147f717c86 | ||
|
|
0cb633c688 | ||
|
|
c7149f4184 | ||
|
|
24c361eb0a | ||
|
|
9817e1d744 | ||
|
|
28f0c4f131 | ||
|
|
638583012a | ||
|
|
55ea557fe8 | ||
|
|
552ed9693e | ||
|
|
231282ddcb | ||
|
|
09375960d6 | ||
|
|
354e09811b | ||
|
|
43c677b489 | ||
|
|
4edc30d157 | ||
|
|
dd8d8e0560 | ||
|
|
de0689d4f4 | ||
|
|
1511b1435a | ||
|
|
12b71ebc8e | ||
|
|
04e7b18869 | ||
|
|
d88ffd11e2 | ||
|
|
e4ba838c6b | ||
|
|
269e9057dd | ||
|
|
ea307619be | ||
|
|
6d62ab64e6 | ||
|
|
78dd564a82 | ||
|
|
d2cb9d5289 | ||
|
|
d427d8e567 | ||
|
|
e436285b6f | ||
|
|
1334cbf78c | ||
|
|
fde9c7315a | ||
|
|
cfb06a2607 | ||
|
|
5e4bb16a7f | ||
|
|
2e240103f3 | ||
|
|
18270ee587 | ||
|
|
eabbab6e2b | ||
|
|
d25defe276 | ||
|
|
ab28a55b4e | ||
|
|
dd1163b58a | ||
|
|
051794dffc | ||
|
|
54bb145c76 | ||
|
|
a7bacf70fb | ||
|
|
07d20cc5f7 | ||
|
|
dee17f4677 | ||
|
|
2658730302 | ||
|
|
e06b60d1b9 | ||
|
|
7d458e0b27 | ||
|
|
725ff27c37 |
@@ -5,6 +5,7 @@
|
|||||||
/config/database.yml
|
/config/database.yml
|
||||||
/config/email.yml
|
/config/email.yml
|
||||||
/config/initializers/session_store.rb
|
/config/initializers/session_store.rb
|
||||||
|
/config/initializers/secret_token.rb
|
||||||
/coverage
|
/coverage
|
||||||
/db/*.db
|
/db/*.db
|
||||||
/db/*.sqlite3
|
/db/*.sqlite3
|
||||||
@@ -18,6 +19,7 @@
|
|||||||
/public/plugin_assets
|
/public/plugin_assets
|
||||||
/tmp/*
|
/tmp/*
|
||||||
/tmp/cache/*
|
/tmp/cache/*
|
||||||
|
/tmp/pdf/*
|
||||||
/tmp/sessions/*
|
/tmp/sessions/*
|
||||||
/tmp/sockets/*
|
/tmp/sockets/*
|
||||||
/tmp/test/*
|
/tmp/test/*
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ config/configuration.yml
|
|||||||
config/database.yml
|
config/database.yml
|
||||||
config/email.yml
|
config/email.yml
|
||||||
config/initializers/session_store.rb
|
config/initializers/session_store.rb
|
||||||
|
config/initializers/secret_token.rb
|
||||||
coverage
|
coverage
|
||||||
db/*.db
|
db/*.db
|
||||||
db/*.sqlite3
|
db/*.sqlite3
|
||||||
@@ -20,6 +21,7 @@ public/dispatch.*
|
|||||||
public/plugin_assets
|
public/plugin_assets
|
||||||
tmp/*
|
tmp/*
|
||||||
tmp/cache/*
|
tmp/cache/*
|
||||||
|
tmp/pdf/*
|
||||||
tmp/sessions/*
|
tmp/sessions/*
|
||||||
tmp/sockets/*
|
tmp/sockets/*
|
||||||
tmp/test/*
|
tmp/test/*
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
source :rubygems
|
source 'http://rubygems.org'
|
||||||
|
|
||||||
gem "rails", "2.3.14"
|
gem 'rails', '3.2.11'
|
||||||
gem "i18n", "~> 0.4.2"
|
gem "jquery-rails", "~> 2.0.2"
|
||||||
gem "coderay", "~> 1.0.0"
|
gem "i18n", "~> 0.6.0"
|
||||||
|
gem "coderay", "~> 1.0.6"
|
||||||
gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]
|
gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]
|
||||||
gem "tzinfo", "~> 0.3.31"
|
gem "builder", "3.0.0"
|
||||||
|
|
||||||
# Optional gem for LDAP authentication
|
# Optional gem for LDAP authentication
|
||||||
group :ldap do
|
group :ldap do
|
||||||
@@ -14,20 +15,23 @@ end
|
|||||||
# Optional gem for OpenID authentication
|
# Optional gem for OpenID authentication
|
||||||
group :openid do
|
group :openid do
|
||||||
gem "ruby-openid", "~> 2.1.4", :require => "openid"
|
gem "ruby-openid", "~> 2.1.4", :require => "openid"
|
||||||
|
gem "rack-openid"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Optional gem for exporting the gantt to a PNG file
|
# Optional gem for exporting the gantt to a PNG file, not supported with jruby
|
||||||
group :rmagick do
|
platforms :mri, :mingw do
|
||||||
# RMagick 2 supports ruby 1.9
|
group :rmagick do
|
||||||
# RMagick 1 would be fine for ruby 1.8 but Bundler does not support
|
# RMagick 2 supports ruby 1.9
|
||||||
# different requirements for the same gem on different platforms
|
# RMagick 1 would be fine for ruby 1.8 but Bundler does not support
|
||||||
gem "rmagick", ">= 2.0.0"
|
# different requirements for the same gem on different platforms
|
||||||
|
gem "rmagick", ">= 2.0.0"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Database gems
|
# Database gems
|
||||||
platforms :mri, :mingw do
|
platforms :mri, :mingw do
|
||||||
group :postgresql do
|
group :postgresql do
|
||||||
gem "pg", "~> 0.9.0"
|
gem "pg", ">= 0.11.0"
|
||||||
end
|
end
|
||||||
|
|
||||||
group :sqlite do
|
group :sqlite do
|
||||||
@@ -37,13 +41,13 @@ end
|
|||||||
|
|
||||||
platforms :mri_18, :mingw_18 do
|
platforms :mri_18, :mingw_18 do
|
||||||
group :mysql do
|
group :mysql do
|
||||||
gem "mysql"
|
gem "mysql", "~> 2.8.1"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
platforms :mri_19, :mingw_19 do
|
platforms :mri_19, :mingw_19 do
|
||||||
group :mysql do
|
group :mysql do
|
||||||
gem "mysql2", "~> 0.2.7"
|
gem "mysql2", "~> 0.3.11"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -65,16 +69,24 @@ end
|
|||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
gem "rdoc", ">= 2.4.2"
|
gem "rdoc", ">= 2.4.2"
|
||||||
|
gem "yard"
|
||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem "shoulda", "~> 2.10.3"
|
gem "shoulda", "~> 2.11"
|
||||||
gem "edavis10-object_daddy", :require => "object_daddy"
|
# Shoulda does not work nice on Ruby 1.9.3 and seems to need test-unit explicitely.
|
||||||
gem "mocha"
|
gem "test-unit", :platforms => [:mri_19]
|
||||||
|
gem "mocha", "0.12.3"
|
||||||
|
end
|
||||||
|
|
||||||
|
local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
|
||||||
|
if File.exists?(local_gemfile)
|
||||||
|
puts "Loading Gemfile.local ..." if $DEBUG # `ruby -d` or `bundle -v`
|
||||||
|
instance_eval File.read(local_gemfile)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Load plugins' Gemfiles
|
# Load plugins' Gemfiles
|
||||||
Dir.glob File.expand_path("../vendor/plugins/*/Gemfile", __FILE__) do |file|
|
Dir.glob File.expand_path("../plugins/*/Gemfile", __FILE__) do |file|
|
||||||
puts "Loading #{file} ..." if $DEBUG # `ruby -d` or `bundle -v`
|
puts "Loading #{file} ..." if $DEBUG # `ruby -d` or `bundle -v`
|
||||||
instance_eval File.read(file)
|
instance_eval File.read(file)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,15 +1,7 @@
|
|||||||
|
#!/usr/bin/env rake
|
||||||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||||
# for example lib/tasks/switchtower.rake, and they will automatically be available to Rake.
|
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||||
|
|
||||||
require(File.join(File.dirname(__FILE__), 'config', 'boot'))
|
require File.expand_path('../config/application', __FILE__)
|
||||||
|
|
||||||
require 'rake'
|
RedmineApp::Application.load_tasks
|
||||||
require 'rake/testtask'
|
|
||||||
|
|
||||||
begin
|
|
||||||
require 'rdoc/task'
|
|
||||||
rescue LoadError
|
|
||||||
# RDoc is not available
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'tasks/rails'
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -40,19 +40,26 @@ class AccountController < ApplicationController
|
|||||||
redirect_to home_url
|
redirect_to home_url
|
||||||
end
|
end
|
||||||
|
|
||||||
# Enable user to choose a new password
|
# Lets user choose a new password
|
||||||
def lost_password
|
def lost_password
|
||||||
redirect_to(home_url) && return unless Setting.lost_password?
|
redirect_to(home_url) && return unless Setting.lost_password?
|
||||||
if params[:token]
|
if params[:token]
|
||||||
@token = Token.find_by_action_and_value("recovery", params[:token])
|
@token = Token.find_by_action_and_value("recovery", params[:token].to_s)
|
||||||
redirect_to(home_url) && return unless @token and !@token.expired?
|
if @token.nil? || @token.expired?
|
||||||
|
redirect_to home_url
|
||||||
|
return
|
||||||
|
end
|
||||||
@user = @token.user
|
@user = @token.user
|
||||||
|
unless @user && @user.active?
|
||||||
|
redirect_to home_url
|
||||||
|
return
|
||||||
|
end
|
||||||
if request.post?
|
if request.post?
|
||||||
@user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
|
@user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
|
||||||
if @user.save
|
if @user.save
|
||||||
@token.destroy
|
@token.destroy
|
||||||
flash[:notice] = l(:notice_account_password_updated)
|
flash[:notice] = l(:notice_account_password_updated)
|
||||||
redirect_to :action => 'login'
|
redirect_to signin_path
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -60,17 +67,23 @@ class AccountController < ApplicationController
|
|||||||
return
|
return
|
||||||
else
|
else
|
||||||
if request.post?
|
if request.post?
|
||||||
user = User.find_by_mail(params[:mail])
|
user = User.find_by_mail(params[:mail].to_s)
|
||||||
# user not found in db
|
# user not found or not active
|
||||||
(flash.now[:error] = l(:notice_account_unknown_email); return) unless user
|
unless user && user.active?
|
||||||
# user uses an external authentification
|
flash.now[:error] = l(:notice_account_unknown_email)
|
||||||
(flash.now[:error] = l(:notice_can_t_change_password); return) if user.auth_source_id
|
return
|
||||||
|
end
|
||||||
|
# user cannot change its password
|
||||||
|
unless user.change_password_allowed?
|
||||||
|
flash.now[:error] = l(:notice_can_t_change_password)
|
||||||
|
return
|
||||||
|
end
|
||||||
# create a new token for password recovery
|
# create a new token for password recovery
|
||||||
token = Token.new(:user => user, :action => "recovery")
|
token = Token.new(:user => user, :action => "recovery")
|
||||||
if token.save
|
if token.save
|
||||||
Mailer.deliver_lost_password(token)
|
Mailer.lost_password(token).deliver
|
||||||
flash[:notice] = l(:notice_account_lost_email_sent)
|
flash[:notice] = l(:notice_account_lost_email_sent)
|
||||||
redirect_to :action => 'login'
|
redirect_to signin_path
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -84,8 +97,9 @@ class AccountController < ApplicationController
|
|||||||
session[:auth_source_registration] = nil
|
session[:auth_source_registration] = nil
|
||||||
@user = User.new(:language => Setting.default_language)
|
@user = User.new(:language => Setting.default_language)
|
||||||
else
|
else
|
||||||
|
user_params = params[:user] || {}
|
||||||
@user = User.new
|
@user = User.new
|
||||||
@user.safe_attributes = params[:user]
|
@user.safe_attributes = user_params
|
||||||
@user.admin = false
|
@user.admin = false
|
||||||
@user.register
|
@user.register
|
||||||
if session[:auth_source_registration]
|
if session[:auth_source_registration]
|
||||||
@@ -100,7 +114,9 @@ class AccountController < ApplicationController
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
@user.login = params[:user][:login]
|
@user.login = params[:user][:login]
|
||||||
@user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation]
|
unless user_params[:identity_url].present? && user_params[:password].blank? && user_params[:password_confirmation].blank?
|
||||||
|
@user.password, @user.password_confirmation = user_params[:password], user_params[:password_confirmation]
|
||||||
|
end
|
||||||
|
|
||||||
case Setting.self_registration
|
case Setting.self_registration
|
||||||
when '1'
|
when '1'
|
||||||
@@ -126,19 +142,11 @@ class AccountController < ApplicationController
|
|||||||
token.destroy
|
token.destroy
|
||||||
flash[:notice] = l(:notice_account_activated)
|
flash[:notice] = l(:notice_account_activated)
|
||||||
end
|
end
|
||||||
redirect_to :action => 'login'
|
redirect_to signin_path
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def logout_user
|
|
||||||
if User.current.logged?
|
|
||||||
cookies.delete :autologin
|
|
||||||
Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin'])
|
|
||||||
self.logged_user = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def authenticate_user
|
def authenticate_user
|
||||||
if Setting.openid? && using_open_id?
|
if Setting.openid? && using_open_id?
|
||||||
open_id_authenticate(params[:openid_url])
|
open_id_authenticate(params[:openid_url])
|
||||||
@@ -161,7 +169,7 @@ class AccountController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def open_id_authenticate(openid_url)
|
def open_id_authenticate(openid_url)
|
||||||
authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url) do |result, identity_url, registration|
|
authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url, :method => :post) do |result, identity_url, registration|
|
||||||
if result.successful?
|
if result.successful?
|
||||||
user = User.find_or_initialize_by_identity_url(identity_url)
|
user = User.find_or_initialize_by_identity_url(identity_url)
|
||||||
if user.new_record?
|
if user.new_record?
|
||||||
@@ -202,6 +210,7 @@ class AccountController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def successful_authentication(user)
|
def successful_authentication(user)
|
||||||
|
logger.info "Successful authentication for '#{user.login}' from #{request.remote_ip} at #{Time.now.utc}"
|
||||||
# Valid user
|
# Valid user
|
||||||
self.logged_user = user
|
self.logged_user = user
|
||||||
# generate a key and set cookie if autologin
|
# generate a key and set cookie if autologin
|
||||||
@@ -243,9 +252,9 @@ class AccountController < ApplicationController
|
|||||||
def register_by_email_activation(user, &block)
|
def register_by_email_activation(user, &block)
|
||||||
token = Token.new(:user => user, :action => "register")
|
token = Token.new(:user => user, :action => "register")
|
||||||
if user.save and token.save
|
if user.save and token.save
|
||||||
Mailer.deliver_register(token)
|
Mailer.register(token).deliver
|
||||||
flash[:notice] = l(:notice_account_register_done)
|
flash[:notice] = l(:notice_account_register_done)
|
||||||
redirect_to :action => 'login'
|
redirect_to signin_path
|
||||||
else
|
else
|
||||||
yield if block_given?
|
yield if block_given?
|
||||||
end
|
end
|
||||||
@@ -273,7 +282,7 @@ class AccountController < ApplicationController
|
|||||||
def register_manually_by_administrator(user, &block)
|
def register_manually_by_administrator(user, &block)
|
||||||
if user.save
|
if user.save
|
||||||
# Sends an email to the administrators
|
# Sends an email to the administrators
|
||||||
Mailer.deliver_account_activation_request(user)
|
Mailer.account_activation_request(user).deliver
|
||||||
account_pending
|
account_pending
|
||||||
else
|
else
|
||||||
yield if block_given?
|
yield if block_given?
|
||||||
@@ -282,6 +291,6 @@ class AccountController < ApplicationController
|
|||||||
|
|
||||||
def account_pending
|
def account_pending
|
||||||
flash[:notice] = l(:notice_account_pending)
|
flash[:notice] = l(:notice_account_pending)
|
||||||
redirect_to :action => 'login'
|
redirect_to signin_path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -40,10 +40,10 @@ class ActivitiesController < ApplicationController
|
|||||||
|
|
||||||
events = @activity.events(@date_from, @date_to)
|
events = @activity.events(@date_from, @date_to)
|
||||||
|
|
||||||
if events.empty? || stale?(:etag => [@activity.scope, @date_to, @date_from, @with_subprojects, @author, events.first, User.current, current_language])
|
if events.empty? || stale?(:etag => [@activity.scope, @date_to, @date_from, @with_subprojects, @author, events.first, events.size, User.current, current_language])
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html {
|
format.html {
|
||||||
@events_by_day = events.group_by(&:event_date)
|
@events_by_day = events.group_by {|event| User.current.time_to_date(event.event_datetime)}
|
||||||
render :layout => false if request.xhr?
|
render :layout => false if request.xhr?
|
||||||
}
|
}
|
||||||
format.atom {
|
format.atom {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -63,7 +63,7 @@ class AdminController < ApplicationController
|
|||||||
# Force ActionMailer to raise delivery errors so we can catch it
|
# Force ActionMailer to raise delivery errors so we can catch it
|
||||||
ActionMailer::Base.raise_delivery_errors = true
|
ActionMailer::Base.raise_delivery_errors = true
|
||||||
begin
|
begin
|
||||||
@test = Mailer.deliver_test_email(User.current)
|
@test = Mailer.test_email(User.current).deliver
|
||||||
flash[:notice] = l(:notice_email_sent, User.current.mail)
|
flash[:notice] = l(:notice_email_sent, User.current.mail)
|
||||||
rescue Exception => e
|
rescue Exception => e
|
||||||
flash[:error] = l(:notice_email_error, e.message)
|
flash[:error] = l(:notice_email_error, e.message)
|
||||||
@@ -75,9 +75,7 @@ class AdminController < ApplicationController
|
|||||||
def info
|
def info
|
||||||
@db_adapter_name = ActiveRecord::Base.connection.adapter_name
|
@db_adapter_name = ActiveRecord::Base.connection.adapter_name
|
||||||
@checklist = [
|
@checklist = [
|
||||||
[:text_default_administrator_account_changed,
|
[:text_default_administrator_account_changed, User.default_admin_account_changed?],
|
||||||
User.find(:first,
|
|
||||||
:conditions => ["login=? and hashed_password=?", 'admin', User.hash_password('admin')]).nil?],
|
|
||||||
[:text_file_repository_writable, File.writable?(Attachment.storage_path)],
|
[:text_file_repository_writable, File.writable?(Attachment.storage_path)],
|
||||||
[:text_plugin_assets_writable, File.writable?(Redmine::Plugin.public_directory)],
|
[:text_plugin_assets_writable, File.writable?(Redmine::Plugin.public_directory)],
|
||||||
[:text_rmagick_available, Object.const_defined?(:Magick)]
|
[:text_rmagick_available, Object.const_defined?(:Magick)]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -22,63 +22,59 @@ class Unauthorized < Exception; end
|
|||||||
|
|
||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
include Redmine::I18n
|
include Redmine::I18n
|
||||||
|
|
||||||
|
class_attribute :accept_api_auth_actions
|
||||||
|
class_attribute :accept_rss_auth_actions
|
||||||
|
class_attribute :model_object
|
||||||
|
|
||||||
layout 'base'
|
layout 'base'
|
||||||
exempt_from_layout 'builder', 'rsb'
|
|
||||||
|
|
||||||
protect_from_forgery
|
protect_from_forgery
|
||||||
def handle_unverified_request
|
def handle_unverified_request
|
||||||
super
|
super
|
||||||
cookies.delete(:autologin)
|
cookies.delete(:autologin)
|
||||||
end
|
end
|
||||||
# Remove broken cookie after upgrade from 0.8.x (#4292)
|
|
||||||
# See https://rails.lighthouseapp.com/projects/8994/tickets/3360
|
|
||||||
# TODO: remove it when Rails is fixed
|
|
||||||
before_filter :delete_broken_cookies
|
|
||||||
def delete_broken_cookies
|
|
||||||
if cookies['_redmine_session'] && cookies['_redmine_session'] !~ /--/
|
|
||||||
cookies.delete '_redmine_session'
|
|
||||||
redirect_to home_path
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# FIXME: Remove this when all of Rack and Rails have learned how to
|
before_filter :session_expiration, :user_setup, :check_if_login_required, :set_localization
|
||||||
# properly use encodings
|
|
||||||
before_filter :params_filter
|
|
||||||
|
|
||||||
def params_filter
|
|
||||||
if RUBY_VERSION >= '1.9' && defined?(Rails) && Rails::VERSION::MAJOR < 3
|
|
||||||
self.utf8nize!(params)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def utf8nize!(obj)
|
|
||||||
if obj.frozen?
|
|
||||||
obj
|
|
||||||
elsif obj.is_a? String
|
|
||||||
obj.respond_to?(:force_encoding) ? obj.force_encoding("UTF-8") : obj
|
|
||||||
elsif obj.is_a? Hash
|
|
||||||
obj.each {|k, v| obj[k] = self.utf8nize!(v)}
|
|
||||||
elsif obj.is_a? Array
|
|
||||||
obj.each {|v| self.utf8nize!(v)}
|
|
||||||
else
|
|
||||||
obj
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
before_filter :user_setup, :check_if_login_required, :set_localization
|
|
||||||
filter_parameter_logging :password
|
|
||||||
|
|
||||||
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
|
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
|
||||||
rescue_from ::Unauthorized, :with => :deny_access
|
rescue_from ::Unauthorized, :with => :deny_access
|
||||||
|
rescue_from ::ActionView::MissingTemplate, :with => :missing_template
|
||||||
|
|
||||||
include Redmine::Search::Controller
|
include Redmine::Search::Controller
|
||||||
include Redmine::MenuManager::MenuController
|
include Redmine::MenuManager::MenuController
|
||||||
helper Redmine::MenuManager::MenuHelper
|
helper Redmine::MenuManager::MenuHelper
|
||||||
|
|
||||||
Redmine::Scm::Base.all.each do |scm|
|
def session_expiration
|
||||||
require_dependency "repository/#{scm.underscore}"
|
if session[:user_id]
|
||||||
|
if session_expired? && !try_to_autologin
|
||||||
|
reset_session
|
||||||
|
flash[:error] = l(:error_session_expired)
|
||||||
|
redirect_to signin_url
|
||||||
|
else
|
||||||
|
session[:atime] = Time.now.utc.to_i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def session_expired?
|
||||||
|
if Setting.session_lifetime?
|
||||||
|
unless session[:ctime] && (Time.now.utc.to_i - session[:ctime].to_i <= Setting.session_lifetime.to_i * 60)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if Setting.session_timeout?
|
||||||
|
unless session[:atime] && (Time.now.utc.to_i - session[:atime].to_i <= Setting.session_timeout.to_i * 60)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def start_user_session(user)
|
||||||
|
session[:user_id] = user.id
|
||||||
|
session[:ctime] = Time.now.utc.to_i
|
||||||
|
session[:atime] = Time.now.utc.to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_setup
|
def user_setup
|
||||||
@@ -86,33 +82,48 @@ class ApplicationController < ActionController::Base
|
|||||||
Setting.check_cache
|
Setting.check_cache
|
||||||
# Find the current user
|
# Find the current user
|
||||||
User.current = find_current_user
|
User.current = find_current_user
|
||||||
|
logger.info(" Current user: " + (User.current.logged? ? "#{User.current.login} (id=#{User.current.id})" : "anonymous")) if logger
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the current user or nil if no user is logged in
|
# Returns the current user or nil if no user is logged in
|
||||||
# and starts a session if needed
|
# and starts a session if needed
|
||||||
def find_current_user
|
def find_current_user
|
||||||
if session[:user_id]
|
user = nil
|
||||||
# existing session
|
unless api_request?
|
||||||
(User.active.find(session[:user_id]) rescue nil)
|
if session[:user_id]
|
||||||
elsif cookies[:autologin] && Setting.autologin?
|
# existing session
|
||||||
# auto-login feature starts a new session
|
user = (User.active.find(session[:user_id]) rescue nil)
|
||||||
user = User.try_to_autologin(cookies[:autologin])
|
elsif autologin_user = try_to_autologin
|
||||||
session[:user_id] = user.id if user
|
user = autologin_user
|
||||||
user
|
elsif params[:format] == 'atom' && params[:key] && request.get? && accept_rss_auth?
|
||||||
elsif params[:format] == 'atom' && params[:key] && request.get? && accept_rss_auth?
|
# RSS key authentication does not start a session
|
||||||
# RSS key authentication does not start a session
|
user = User.find_by_rss_key(params[:key])
|
||||||
User.find_by_rss_key(params[:key])
|
end
|
||||||
elsif Setting.rest_api_enabled? && accept_api_auth?
|
end
|
||||||
|
if user.nil? && Setting.rest_api_enabled? && accept_api_auth?
|
||||||
if (key = api_key_from_request)
|
if (key = api_key_from_request)
|
||||||
# Use API key
|
# Use API key
|
||||||
User.find_by_api_key(key)
|
user = User.find_by_api_key(key)
|
||||||
else
|
else
|
||||||
# HTTP Basic, either username/password or API key/random
|
# HTTP Basic, either username/password or API key/random
|
||||||
authenticate_with_http_basic do |username, password|
|
authenticate_with_http_basic do |username, password|
|
||||||
User.try_to_login(username, password) || User.find_by_api_key(username)
|
user = User.try_to_login(username, password) || User.find_by_api_key(username)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
user
|
||||||
|
end
|
||||||
|
|
||||||
|
def try_to_autologin
|
||||||
|
if cookies[:autologin] && Setting.autologin?
|
||||||
|
# auto-login feature starts a new session
|
||||||
|
user = User.try_to_autologin(cookies[:autologin])
|
||||||
|
if user
|
||||||
|
reset_session
|
||||||
|
start_user_session(user)
|
||||||
|
end
|
||||||
|
user
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sets the logged in user
|
# Sets the logged in user
|
||||||
@@ -120,12 +131,21 @@ class ApplicationController < ActionController::Base
|
|||||||
reset_session
|
reset_session
|
||||||
if user && user.is_a?(User)
|
if user && user.is_a?(User)
|
||||||
User.current = user
|
User.current = user
|
||||||
session[:user_id] = user.id
|
start_user_session(user)
|
||||||
else
|
else
|
||||||
User.current = User.anonymous
|
User.current = User.anonymous
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Logs out current user
|
||||||
|
def logout_user
|
||||||
|
if User.current.logged?
|
||||||
|
cookies.delete :autologin
|
||||||
|
Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin'])
|
||||||
|
self.logged_user = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# check if login is globally required to access the application
|
# check if login is globally required to access the application
|
||||||
def check_if_login_required
|
def check_if_login_required
|
||||||
# no check needed if user is already logged in
|
# no check needed if user is already logged in
|
||||||
@@ -233,7 +253,7 @@ class ApplicationController < ActionController::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def find_model_object
|
def find_model_object
|
||||||
model = self.class.read_inheritable_attribute('model_object')
|
model = self.class.model_object
|
||||||
if model
|
if model
|
||||||
@object = model.find(params[:id])
|
@object = model.find(params[:id])
|
||||||
self.instance_variable_set('@' + controller_name.singularize, @object) if @object
|
self.instance_variable_set('@' + controller_name.singularize, @object) if @object
|
||||||
@@ -243,7 +263,7 @@ class ApplicationController < ActionController::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.model_object(model)
|
def self.model_object(model)
|
||||||
write_inheritable_attribute('model_object', model)
|
self.model_object = model
|
||||||
end
|
end
|
||||||
|
|
||||||
# Filter for bulk issue operations
|
# Filter for bulk issue operations
|
||||||
@@ -263,7 +283,7 @@ class ApplicationController < ActionController::Base
|
|||||||
# make sure that the user is a member of the project (or admin) if project is private
|
# make sure that the user is a member of the project (or admin) if project is private
|
||||||
# used as a before_filter for actions that do not require any particular permission on the project
|
# used as a before_filter for actions that do not require any particular permission on the project
|
||||||
def check_project_privacy
|
def check_project_privacy
|
||||||
if @project && @project.active?
|
if @project && !@project.archived?
|
||||||
if @project.visible?
|
if @project.visible?
|
||||||
true
|
true
|
||||||
else
|
else
|
||||||
@@ -277,12 +297,16 @@ class ApplicationController < ActionController::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def back_url
|
def back_url
|
||||||
params[:back_url] || request.env['HTTP_REFERER']
|
url = params[:back_url]
|
||||||
|
if url.nil? && referer = request.env['HTTP_REFERER']
|
||||||
|
url = CGI.unescape(referer.to_s)
|
||||||
|
end
|
||||||
|
url
|
||||||
end
|
end
|
||||||
|
|
||||||
def redirect_back_or_default(default)
|
def redirect_back_or_default(default)
|
||||||
back_url = CGI.unescape(params[:back_url].to_s)
|
back_url = params[:back_url].to_s
|
||||||
if !back_url.blank?
|
if back_url.present?
|
||||||
begin
|
begin
|
||||||
uri = URI.parse(back_url)
|
uri = URI.parse(back_url)
|
||||||
# do not redirect user to another host or to the login or register page
|
# do not redirect user to another host or to the login or register page
|
||||||
@@ -291,6 +315,7 @@ class ApplicationController < ActionController::Base
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
rescue URI::InvalidURIError
|
rescue URI::InvalidURIError
|
||||||
|
logger.warn("Could not redirect to invalid URL #{back_url}")
|
||||||
# redirect to default
|
# redirect to default
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -298,6 +323,19 @@ class ApplicationController < ActionController::Base
|
|||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Redirects to the request referer if present, redirects to args or call block otherwise.
|
||||||
|
def redirect_to_referer_or(*args, &block)
|
||||||
|
redirect_to :back
|
||||||
|
rescue ::ActionController::RedirectBackError
|
||||||
|
if args.any?
|
||||||
|
redirect_to *args
|
||||||
|
elsif block_given?
|
||||||
|
block.call
|
||||||
|
else
|
||||||
|
raise "#redirect_to_referer_or takes arguments or a block"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def render_403(options={})
|
def render_403(options={})
|
||||||
@project = nil
|
@project = nil
|
||||||
render_error({:message => :notice_not_authorized, :status => 403}.merge(options))
|
render_error({:message => :notice_not_authorized, :status => 403}.merge(options))
|
||||||
@@ -321,13 +359,17 @@ class ApplicationController < ActionController::Base
|
|||||||
format.html {
|
format.html {
|
||||||
render :template => 'common/error', :layout => use_layout, :status => @status
|
render :template => 'common/error', :layout => use_layout, :status => @status
|
||||||
}
|
}
|
||||||
format.atom { head @status }
|
format.any { head @status }
|
||||||
format.xml { head @status }
|
|
||||||
format.js { head @status }
|
|
||||||
format.json { head @status }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Handler for ActionView::MissingTemplate exception
|
||||||
|
def missing_template
|
||||||
|
logger.warn "Missing template, responding with 404"
|
||||||
|
@project = nil
|
||||||
|
render_404
|
||||||
|
end
|
||||||
|
|
||||||
# Filter for actions that provide an API response
|
# Filter for actions that provide an API response
|
||||||
# but have no HTML representation for non admin users
|
# but have no HTML representation for non admin users
|
||||||
def require_admin_or_api_request
|
def require_admin_or_api_request
|
||||||
@@ -364,23 +406,11 @@ class ApplicationController < ActionController::Base
|
|||||||
:content_type => 'application/atom+xml'
|
:content_type => 'application/atom+xml'
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: remove in Redmine 1.4
|
|
||||||
def self.accept_key_auth(*actions)
|
|
||||||
ActiveSupport::Deprecation.warn "ApplicationController.accept_key_auth is deprecated and will be removed in Redmine 1.4. Use accept_rss_auth (or accept_api_auth) instead."
|
|
||||||
accept_rss_auth(*actions)
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: remove in Redmine 1.4
|
|
||||||
def accept_key_auth_actions
|
|
||||||
ActiveSupport::Deprecation.warn "ApplicationController.accept_key_auth_actions is deprecated and will be removed in Redmine 1.4. Use accept_rss_auth (or accept_api_auth) instead."
|
|
||||||
self.class.accept_rss_auth
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.accept_rss_auth(*actions)
|
def self.accept_rss_auth(*actions)
|
||||||
if actions.any?
|
if actions.any?
|
||||||
write_inheritable_attribute('accept_rss_auth_actions', actions)
|
self.accept_rss_auth_actions = actions
|
||||||
else
|
else
|
||||||
read_inheritable_attribute('accept_rss_auth_actions') || []
|
self.accept_rss_auth_actions || []
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -390,9 +420,9 @@ class ApplicationController < ActionController::Base
|
|||||||
|
|
||||||
def self.accept_api_auth(*actions)
|
def self.accept_api_auth(*actions)
|
||||||
if actions.any?
|
if actions.any?
|
||||||
write_inheritable_attribute('accept_api_auth_actions', actions)
|
self.accept_api_auth_actions = actions
|
||||||
else
|
else
|
||||||
read_inheritable_attribute('accept_api_auth_actions') || []
|
self.accept_api_auth_actions || []
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -472,9 +502,9 @@ class ApplicationController < ActionController::Base
|
|||||||
# Returns the API key present in the request
|
# Returns the API key present in the request
|
||||||
def api_key_from_request
|
def api_key_from_request
|
||||||
if params[:key].present?
|
if params[:key].present?
|
||||||
params[:key]
|
params[:key].to_s
|
||||||
elsif request.headers["X-Redmine-API-Key"].present?
|
elsif request.headers["X-Redmine-API-Key"].present?
|
||||||
request.headers["X-Redmine-API-Key"]
|
request.headers["X-Redmine-API-Key"].to_s
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -506,6 +536,12 @@ class ApplicationController < ActionController::Base
|
|||||||
render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
|
render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Renders a 200 response for successfull updates or deletions via the API
|
||||||
|
def render_api_ok
|
||||||
|
# head :ok would return a response body with one space
|
||||||
|
render :text => '', :status => :ok, :layout => nil
|
||||||
|
end
|
||||||
|
|
||||||
# Renders API response on validation failure
|
# Renders API response on validation failure
|
||||||
def render_validation_errors(objects)
|
def render_validation_errors(objects)
|
||||||
if objects.is_a?(Array)
|
if objects.is_a?(Array)
|
||||||
@@ -513,26 +549,12 @@ class ApplicationController < ActionController::Base
|
|||||||
else
|
else
|
||||||
@error_messages = objects.errors.full_messages
|
@error_messages = objects.errors.full_messages
|
||||||
end
|
end
|
||||||
render :template => 'common/error_messages.api', :status => :unprocessable_entity, :layout => false
|
render :template => 'common/error_messages.api', :status => :unprocessable_entity, :layout => nil
|
||||||
end
|
end
|
||||||
|
|
||||||
# Overrides #default_template so that the api template
|
# Overrides #_include_layout? so that #render with no arguments
|
||||||
# is used automatically if it exists
|
|
||||||
def default_template(action_name = self.action_name)
|
|
||||||
if api_request?
|
|
||||||
begin
|
|
||||||
return self.view_paths.find_template(default_template_name(action_name), 'api')
|
|
||||||
rescue ::ActionView::MissingTemplate
|
|
||||||
# the api template was not found
|
|
||||||
# fallback to the default behaviour
|
|
||||||
end
|
|
||||||
end
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
# Overrides #pick_layout so that #render with no arguments
|
|
||||||
# doesn't use the layout for api requests
|
# doesn't use the layout for api requests
|
||||||
def pick_layout(*args)
|
def _include_layout?(*args)
|
||||||
api_request? ? nil : super
|
api_request? ? false : super
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
class AttachmentsController < ApplicationController
|
class AttachmentsController < ApplicationController
|
||||||
before_filter :find_project, :except => :upload
|
before_filter :find_project, :except => :upload
|
||||||
before_filter :file_readable, :read_authorize, :only => [:show, :download]
|
before_filter :file_readable, :read_authorize, :only => [:show, :download, :thumbnail]
|
||||||
before_filter :delete_authorize, :only => :destroy
|
before_filter :delete_authorize, :only => :destroy
|
||||||
before_filter :authorize_global, :only => :upload
|
before_filter :authorize_global, :only => :upload
|
||||||
|
|
||||||
@@ -52,11 +52,26 @@ class AttachmentsController < ApplicationController
|
|||||||
@attachment.increment_download
|
@attachment.increment_download
|
||||||
end
|
end
|
||||||
|
|
||||||
# images are sent inline
|
if stale?(:etag => @attachment.digest)
|
||||||
send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
|
# images are sent inline
|
||||||
:type => detect_content_type(@attachment),
|
send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
|
||||||
:disposition => (@attachment.image? ? 'inline' : 'attachment')
|
:type => detect_content_type(@attachment),
|
||||||
|
:disposition => (@attachment.image? ? 'inline' : 'attachment')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def thumbnail
|
||||||
|
if @attachment.thumbnailable? && thumbnail = @attachment.thumbnail(:size => params[:size])
|
||||||
|
if stale?(:etag => thumbnail)
|
||||||
|
send_file thumbnail,
|
||||||
|
:filename => filename_for_content_disposition(@attachment.filename),
|
||||||
|
:type => detect_content_type(@attachment),
|
||||||
|
:disposition => 'inline'
|
||||||
|
end
|
||||||
|
else
|
||||||
|
# No thumbnail for the attachment or thumbnail could not be created
|
||||||
|
render :nothing => true, :status => 404
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def upload
|
def upload
|
||||||
@@ -67,7 +82,7 @@ class AttachmentsController < ApplicationController
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@attachment = Attachment.new(:file => request.body)
|
@attachment = Attachment.new(:file => request.raw_post)
|
||||||
@attachment.author = User.current
|
@attachment.author = User.current
|
||||||
@attachment.filename = Redmine::Utils.random_hex(16)
|
@attachment.filename = Redmine::Utils.random_hex(16)
|
||||||
|
|
||||||
@@ -88,9 +103,7 @@ class AttachmentsController < ApplicationController
|
|||||||
end
|
end
|
||||||
# Make sure association callbacks are called
|
# Make sure association callbacks are called
|
||||||
@attachment.container.attachments.delete(@attachment)
|
@attachment.container.attachments.delete(@attachment)
|
||||||
redirect_to :back
|
redirect_to_referer_or project_path(@project)
|
||||||
rescue ::ActionController::RedirectBackError
|
|
||||||
redirect_to :controller => 'projects', :action => 'show', :id => @project
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,27 +1,44 @@
|
|||||||
|
# Redmine - project management software
|
||||||
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
class AutoCompletesController < ApplicationController
|
class AutoCompletesController < ApplicationController
|
||||||
before_filter :find_project
|
before_filter :find_project
|
||||||
|
|
||||||
def issues
|
def issues
|
||||||
@issues = []
|
@issues = []
|
||||||
q = params[:q].to_s
|
q = (params[:q] || params[:term]).to_s.strip
|
||||||
query = (params[:scope] == "all" && Setting.cross_project_issue_relations?) ? Issue : @project.issues
|
if q.present?
|
||||||
if q.match(/^\d+$/)
|
scope = (params[:scope] == "all" || @project.nil? ? Issue : @project.issues).visible
|
||||||
@issues << query.visible.find_by_id(q.to_i)
|
if q.match(/^\d+$/)
|
||||||
|
@issues << scope.find_by_id(q.to_i)
|
||||||
|
end
|
||||||
|
@issues += scope.where("LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%").order("#{Issue.table_name}.id DESC").limit(10).all
|
||||||
|
@issues.compact!
|
||||||
end
|
end
|
||||||
unless q.blank?
|
|
||||||
@issues += query.visible.find(:all, :conditions => ["LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%"], :limit => 10)
|
|
||||||
end
|
|
||||||
@issues.compact!
|
|
||||||
render :layout => false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def find_project
|
def find_project
|
||||||
project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
|
if params[:project_id].present?
|
||||||
@project = Project.find(project_id)
|
@project = Project.find(params[:project_id])
|
||||||
|
end
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
render_404
|
render_404
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -25,7 +25,7 @@ class BoardsController < ApplicationController
|
|||||||
helper :watchers
|
helper :watchers
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@boards = @project.boards
|
@boards = @project.boards.includes(:last_message => :author).all
|
||||||
# show the board if there is only one
|
# show the board if there is only one
|
||||||
if @boards.size == 1
|
if @boards.size == 1
|
||||||
@board = @boards.first
|
@board = @boards.first
|
||||||
@@ -43,11 +43,11 @@ class BoardsController < ApplicationController
|
|||||||
|
|
||||||
@topic_count = @board.topics.count
|
@topic_count = @board.topics.count
|
||||||
@topic_pages = Paginator.new self, @topic_count, per_page_option, params['page']
|
@topic_pages = Paginator.new self, @topic_count, per_page_option, params['page']
|
||||||
@topics = @board.topics.find :all, :order => ["#{Message.table_name}.sticky DESC", sort_clause].compact.join(', '),
|
@topics = @board.topics.reorder("#{Message.table_name}.sticky DESC").order(sort_clause).all(
|
||||||
:include => [:author, {:last_reply => :author}],
|
:include => [:author, {:last_reply => :author}],
|
||||||
:limit => @topic_pages.items_per_page,
|
:limit => @topic_pages.items_per_page,
|
||||||
:offset => @topic_pages.current.offset
|
:offset => @topic_pages.current.offset)
|
||||||
@message = Message.new
|
@message = Message.new(:board => @board)
|
||||||
render :action => 'show', :layout => !request.xhr?
|
render :action => 'show', :layout => !request.xhr?
|
||||||
}
|
}
|
||||||
format.atom {
|
format.atom {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,3 +1,20 @@
|
|||||||
|
# Redmine - project management software
|
||||||
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
class ContextMenusController < ApplicationController
|
class ContextMenusController < ApplicationController
|
||||||
helper :watchers
|
helper :watchers
|
||||||
helper :issues
|
helper :issues
|
||||||
@@ -7,6 +24,7 @@ class ContextMenusController < ApplicationController
|
|||||||
if (@issues.size == 1)
|
if (@issues.size == 1)
|
||||||
@issue = @issues.first
|
@issue = @issues.first
|
||||||
end
|
end
|
||||||
|
@issue_ids = @issues.map(&:id).sort
|
||||||
|
|
||||||
@allowed_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
|
@allowed_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
|
||||||
@projects = @issues.collect(&:project).compact.uniq
|
@projects = @issues.collect(&:project).compact.uniq
|
||||||
@@ -31,6 +49,7 @@ class ContextMenusController < ApplicationController
|
|||||||
@assignables = @projects.map(&:assignable_users).reduce(:&)
|
@assignables = @projects.map(&:assignable_users).reduce(:&)
|
||||||
@trackers = @projects.map(&:trackers).reduce(:&)
|
@trackers = @projects.map(&:trackers).reduce(:&)
|
||||||
end
|
end
|
||||||
|
@versions = @projects.map {|p| p.shared_versions.open}.reduce(:&)
|
||||||
|
|
||||||
@priorities = IssuePriority.active.reverse
|
@priorities = IssuePriority.active.reverse
|
||||||
@back = back_url
|
@back = back_url
|
||||||
@@ -48,6 +67,7 @@ class ContextMenusController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
|
||||||
render :layout => false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -86,7 +86,9 @@ class DocumentsController < ApplicationController
|
|||||||
attachments = Attachment.attach_files(@document, params[:attachments])
|
attachments = Attachment.attach_files(@document, params[:attachments])
|
||||||
render_attachment_warning_if_needed(@document)
|
render_attachment_warning_if_needed(@document)
|
||||||
|
|
||||||
Mailer.deliver_attachments_added(attachments[:files]) if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added')
|
if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added')
|
||||||
|
Mailer.attachments_added(attachments[:files]).deliver
|
||||||
|
end
|
||||||
redirect_to :action => 'show', :id => @document
|
redirect_to :action => 'show', :id => @document
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,3 +1,20 @@
|
|||||||
|
# Redmine - project management software
|
||||||
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
class FilesController < ApplicationController
|
class FilesController < ApplicationController
|
||||||
menu_item :files
|
menu_item :files
|
||||||
|
|
||||||
@@ -29,7 +46,7 @@ class FilesController < ApplicationController
|
|||||||
render_attachment_warning_if_needed(container)
|
render_attachment_warning_if_needed(container)
|
||||||
|
|
||||||
if !attachments.empty? && !attachments[:files].blank? && Setting.notified_events.include?('file_added')
|
if !attachments.empty? && !attachments[:files].blank? && Setting.notified_events.include?('file_added')
|
||||||
Mailer.deliver_attachments_added(attachments[:files])
|
Mailer.attachments_added(attachments[:files]).deliver
|
||||||
end
|
end
|
||||||
redirect_to project_files_path(@project)
|
redirect_to project_files_path(@project)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -19,51 +19,34 @@ class GroupsController < ApplicationController
|
|||||||
layout 'admin'
|
layout 'admin'
|
||||||
|
|
||||||
before_filter :require_admin
|
before_filter :require_admin
|
||||||
|
before_filter :find_group, :except => [:index, :new, :create]
|
||||||
|
accept_api_auth :index, :show, :create, :update, :destroy, :add_users, :remove_user
|
||||||
|
|
||||||
helper :custom_fields
|
helper :custom_fields
|
||||||
|
|
||||||
# GET /groups
|
|
||||||
# GET /groups.xml
|
|
||||||
def index
|
def index
|
||||||
@groups = Group.find(:all, :order => 'lastname')
|
@groups = Group.sorted.all
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html # index.html.erb
|
format.html
|
||||||
format.xml { render :xml => @groups }
|
format.api
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /groups/1
|
|
||||||
# GET /groups/1.xml
|
|
||||||
def show
|
def show
|
||||||
@group = Group.find(params[:id])
|
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html # show.html.erb
|
format.html
|
||||||
format.xml { render :xml => @group }
|
format.api
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /groups/new
|
|
||||||
# GET /groups/new.xml
|
|
||||||
def new
|
def new
|
||||||
@group = Group.new
|
@group = Group.new
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
format.html # new.html.erb
|
|
||||||
format.xml { render :xml => @group }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /groups/1/edit
|
|
||||||
def edit
|
|
||||||
@group = Group.find(params[:id], :include => :projects)
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST /groups
|
|
||||||
# POST /groups.xml
|
|
||||||
def create
|
def create
|
||||||
@group = Group.new(params[:group])
|
@group = Group.new
|
||||||
|
@group.safe_attributes = params[:group]
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @group.save
|
if @group.save
|
||||||
@@ -71,102 +54,87 @@ class GroupsController < ApplicationController
|
|||||||
flash[:notice] = l(:notice_successful_create)
|
flash[:notice] = l(:notice_successful_create)
|
||||||
redirect_to(params[:continue] ? new_group_path : groups_path)
|
redirect_to(params[:continue] ? new_group_path : groups_path)
|
||||||
}
|
}
|
||||||
format.xml { render :xml => @group, :status => :created, :location => @group }
|
format.api { render :action => 'show', :status => :created, :location => group_url(@group) }
|
||||||
else
|
else
|
||||||
format.html { render :action => "new" }
|
format.html { render :action => "new" }
|
||||||
format.xml { render :xml => @group.errors, :status => :unprocessable_entity }
|
format.api { render_validation_errors(@group) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# PUT /groups/1
|
def edit
|
||||||
# PUT /groups/1.xml
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@group = Group.find(params[:id])
|
@group.safe_attributes = params[:group]
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @group.update_attributes(params[:group])
|
if @group.save
|
||||||
flash[:notice] = l(:notice_successful_update)
|
flash[:notice] = l(:notice_successful_update)
|
||||||
format.html { redirect_to(groups_path) }
|
format.html { redirect_to(groups_path) }
|
||||||
format.xml { head :ok }
|
format.api { render_api_ok }
|
||||||
else
|
else
|
||||||
format.html { render :action => "edit" }
|
format.html { render :action => "edit" }
|
||||||
format.xml { render :xml => @group.errors, :status => :unprocessable_entity }
|
format.api { render_validation_errors(@group) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# DELETE /groups/1
|
|
||||||
# DELETE /groups/1.xml
|
|
||||||
def destroy
|
def destroy
|
||||||
@group = Group.find(params[:id])
|
|
||||||
@group.destroy
|
@group.destroy
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to(groups_url) }
|
format.html { redirect_to(groups_url) }
|
||||||
format.xml { head :ok }
|
format.api { render_api_ok }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_users
|
def add_users
|
||||||
@group = Group.find(params[:id])
|
@users = User.find_all_by_id(params[:user_id] || params[:user_ids])
|
||||||
users = User.find_all_by_id(params[:user_ids])
|
@group.users << @users if request.post?
|
||||||
@group.users << users if request.post?
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
|
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
|
||||||
format.js {
|
format.js
|
||||||
render(:update) {|page|
|
format.api { render_api_ok }
|
||||||
page.replace_html "tab-content-users", :partial => 'groups/users'
|
|
||||||
users.each {|user| page.visual_effect(:highlight, "user-#{user.id}") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_user
|
def remove_user
|
||||||
@group = Group.find(params[:id])
|
|
||||||
@group.users.delete(User.find(params[:user_id])) if request.delete?
|
@group.users.delete(User.find(params[:user_id])) if request.delete?
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
|
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
|
||||||
format.js { render(:update) {|page| page.replace_html "tab-content-users", :partial => 'groups/users'} }
|
format.js
|
||||||
|
format.api { render_api_ok }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def autocomplete_for_user
|
def autocomplete_for_user
|
||||||
@group = Group.find(params[:id])
|
|
||||||
@users = User.active.not_in_group(@group).like(params[:q]).all(:limit => 100)
|
@users = User.active.not_in_group(@group).like(params[:q]).all(:limit => 100)
|
||||||
render :layout => false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit_membership
|
def edit_membership
|
||||||
@group = Group.find(params[:id])
|
|
||||||
@membership = Member.edit_membership(params[:membership_id], params[:membership], @group)
|
@membership = Member.edit_membership(params[:membership_id], params[:membership], @group)
|
||||||
@membership.save if request.post?
|
@membership.save if request.post?
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @membership.valid?
|
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
|
||||||
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
|
format.js
|
||||||
format.js {
|
|
||||||
render(:update) {|page|
|
|
||||||
page.replace_html "tab-content-memberships", :partial => 'groups/memberships'
|
|
||||||
page.visual_effect(:highlight, "member-#{@membership.id}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
format.js {
|
|
||||||
render(:update) {|page|
|
|
||||||
page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', ')))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy_membership
|
def destroy_membership
|
||||||
@group = Group.find(params[:id])
|
|
||||||
Member.find(params[:membership_id]).destroy if request.post?
|
Member.find(params[:membership_id]).destroy if request.post?
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
|
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
|
||||||
format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'groups/memberships'} }
|
format.js
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def find_group
|
||||||
|
@group = Group.find(params[:id])
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
render_404
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -41,6 +41,11 @@ class IssueCategoriesController < ApplicationController
|
|||||||
def new
|
def new
|
||||||
@category = @project.issue_categories.build
|
@category = @project.issue_categories.build
|
||||||
@category.safe_attributes = params[:issue_category]
|
@category.safe_attributes = params[:issue_category]
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html
|
||||||
|
format.js
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@@ -52,20 +57,13 @@ class IssueCategoriesController < ApplicationController
|
|||||||
flash[:notice] = l(:notice_successful_create)
|
flash[:notice] = l(:notice_successful_create)
|
||||||
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
|
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
|
||||||
end
|
end
|
||||||
format.js do
|
format.js
|
||||||
# IE doesn't support the replace_html rjs method for select box options
|
|
||||||
render(:update) {|page| page.replace "issue_category_id",
|
|
||||||
content_tag('select', content_tag('option') + options_from_collection_for_select(@project.issue_categories, 'id', 'name', @category.id), :id => 'issue_category_id', :name => 'issue[category_id]')
|
|
||||||
}
|
|
||||||
end
|
|
||||||
format.api { render :action => 'show', :status => :created, :location => issue_category_path(@category) }
|
format.api { render :action => 'show', :status => :created, :location => issue_category_path(@category) }
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { render :action => 'new'}
|
format.html { render :action => 'new'}
|
||||||
format.js do
|
format.js { render :action => 'new'}
|
||||||
render(:update) {|page| page.alert(@category.errors.full_messages.join('\n')) }
|
|
||||||
end
|
|
||||||
format.api { render_validation_errors(@category) }
|
format.api { render_validation_errors(@category) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -82,7 +80,7 @@ class IssueCategoriesController < ApplicationController
|
|||||||
flash[:notice] = l(:notice_successful_update)
|
flash[:notice] = l(:notice_successful_update)
|
||||||
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
|
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
|
||||||
}
|
}
|
||||||
format.api { head :ok }
|
format.api { render_api_ok }
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
@@ -102,7 +100,7 @@ class IssueCategoriesController < ApplicationController
|
|||||||
@category.destroy(reassign_to)
|
@category.destroy(reassign_to)
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'categories' }
|
format.html { redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'categories' }
|
||||||
format.api { head :ok }
|
format.api { render_api_ok }
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -42,23 +42,16 @@ class IssueRelationsController < ApplicationController
|
|||||||
def create
|
def create
|
||||||
@relation = IssueRelation.new(params[:relation])
|
@relation = IssueRelation.new(params[:relation])
|
||||||
@relation.issue_from = @issue
|
@relation.issue_from = @issue
|
||||||
if params[:relation] && m = params[:relation][:issue_to_id].to_s.match(/^#?(\d+)$/)
|
if params[:relation] && m = params[:relation][:issue_to_id].to_s.strip.match(/^#?(\d+)$/)
|
||||||
@relation.issue_to = Issue.visible.find_by_id(m[1].to_i)
|
@relation.issue_to = Issue.visible.find_by_id(m[1].to_i)
|
||||||
end
|
end
|
||||||
saved = @relation.save
|
saved = @relation.save
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @issue }
|
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @issue }
|
||||||
format.js do
|
format.js {
|
||||||
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
|
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
|
||||||
render :update do |page|
|
}
|
||||||
page.replace_html "relations", :partial => 'issues/relations'
|
|
||||||
if @relation.errors.empty?
|
|
||||||
page << "$('relation_delay').value = ''"
|
|
||||||
page << "$('relation_issue_to_id').value = ''"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
format.api {
|
format.api {
|
||||||
if saved
|
if saved
|
||||||
render :action => 'show', :status => :created, :location => relation_url(@relation)
|
render :action => 'show', :status => :created, :location => relation_url(@relation)
|
||||||
@@ -75,8 +68,8 @@ class IssueRelationsController < ApplicationController
|
|||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to issue_path } # TODO : does this really work since @issue is always nil? What is it useful to?
|
format.html { redirect_to issue_path } # TODO : does this really work since @issue is always nil? What is it useful to?
|
||||||
format.js { render(:update) {|page| page.remove "relation-#{@relation.id}"} }
|
format.js
|
||||||
format.api { head :ok }
|
format.api { render_api_ok }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -50,7 +50,6 @@ class IssuesController < ApplicationController
|
|||||||
include SortHelper
|
include SortHelper
|
||||||
include IssuesHelper
|
include IssuesHelper
|
||||||
helper :timelog
|
helper :timelog
|
||||||
helper :gantt
|
|
||||||
include Redmine::Export::PDF
|
include Redmine::Export::PDF
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@@ -128,17 +127,7 @@ class IssuesController < ApplicationController
|
|||||||
def new
|
def new
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { render :action => 'new', :layout => !request.xhr? }
|
format.html { render :action => 'new', :layout => !request.xhr? }
|
||||||
format.js {
|
format.js { render :partial => 'update_form' }
|
||||||
render(:update) { |page|
|
|
||||||
if params[:project_change]
|
|
||||||
page.replace_html 'all_attributes', :partial => 'form'
|
|
||||||
else
|
|
||||||
page.replace_html 'attributes', :partial => 'attributes'
|
|
||||||
end
|
|
||||||
m = User.current.allowed_to?(:log_time, @issue.project) ? 'show' : 'hide'
|
|
||||||
page << "if ($('log_time')) {Element.#{m}('log_time');}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -150,7 +139,7 @@ class IssuesController < ApplicationController
|
|||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html {
|
format.html {
|
||||||
render_attachment_warning_if_needed(@issue)
|
render_attachment_warning_if_needed(@issue)
|
||||||
flash[:notice] = l(:notice_issue_successful_create, :id => "<a href='#{issue_path(@issue)}'>##{@issue.id}</a>")
|
flash[:notice] = l(:notice_issue_successful_create, :id => view_context.link_to("##{@issue.id}", issue_path(@issue), :title => @issue.subject))
|
||||||
redirect_to(params[:continue] ? { :action => 'new', :project_id => @issue.project, :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
|
redirect_to(params[:continue] ? { :action => 'new', :project_id => @issue.project, :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
|
||||||
{ :action => 'show', :id => @issue })
|
{ :action => 'show', :id => @issue })
|
||||||
}
|
}
|
||||||
@@ -183,12 +172,7 @@ class IssuesController < ApplicationController
|
|||||||
rescue ActiveRecord::StaleObjectError
|
rescue ActiveRecord::StaleObjectError
|
||||||
@conflict = true
|
@conflict = true
|
||||||
if params[:last_journal_id]
|
if params[:last_journal_id]
|
||||||
if params[:last_journal_id].present?
|
@conflict_journals = @issue.journals_after(params[:last_journal_id]).all
|
||||||
last_journal_id = params[:last_journal_id].to_i
|
|
||||||
@conflict_journals = @issue.journals.all(:conditions => ["#{Journal.table_name}.id > ?", last_journal_id])
|
|
||||||
else
|
|
||||||
@conflict_journals = @issue.journals.all
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -198,7 +182,7 @@ class IssuesController < ApplicationController
|
|||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
|
format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
|
||||||
format.api { head :ok }
|
format.api { render_api_ok }
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
@@ -225,12 +209,20 @@ class IssuesController < ApplicationController
|
|||||||
end
|
end
|
||||||
target_projects ||= @projects
|
target_projects ||= @projects
|
||||||
|
|
||||||
@available_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
|
if @copy
|
||||||
|
@available_statuses = [IssueStatus.default]
|
||||||
|
else
|
||||||
|
@available_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
|
||||||
|
end
|
||||||
@custom_fields = target_projects.map{|p|p.all_issue_custom_fields}.reduce(:&)
|
@custom_fields = target_projects.map{|p|p.all_issue_custom_fields}.reduce(:&)
|
||||||
@assignables = target_projects.map(&:assignable_users).reduce(:&)
|
@assignables = target_projects.map(&:assignable_users).reduce(:&)
|
||||||
@trackers = target_projects.map(&:trackers).reduce(:&)
|
@trackers = target_projects.map(&:trackers).reduce(:&)
|
||||||
@versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)
|
@versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)
|
||||||
@categories = target_projects.map {|p| p.issue_categories}.reduce(:&)
|
@categories = target_projects.map {|p| p.issue_categories}.reduce(:&)
|
||||||
|
if @copy
|
||||||
|
@attachments_present = @issues.detect {|i| i.attachments.any?}.present?
|
||||||
|
@subtasks_present = @issues.detect {|i| !i.leaf?}.present?
|
||||||
|
end
|
||||||
|
|
||||||
@safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
|
@safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
|
||||||
render :layout => false if request.xhr?
|
render :layout => false if request.xhr?
|
||||||
@@ -243,10 +235,20 @@ class IssuesController < ApplicationController
|
|||||||
|
|
||||||
unsaved_issue_ids = []
|
unsaved_issue_ids = []
|
||||||
moved_issues = []
|
moved_issues = []
|
||||||
|
|
||||||
|
if @copy && params[:copy_subtasks].present?
|
||||||
|
# Descendant issues will be copied with the parent task
|
||||||
|
# Don't copy them twice
|
||||||
|
@issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}}
|
||||||
|
end
|
||||||
|
|
||||||
@issues.each do |issue|
|
@issues.each do |issue|
|
||||||
issue.reload
|
issue.reload
|
||||||
if @copy
|
if @copy
|
||||||
issue = issue.copy
|
issue = issue.copy({},
|
||||||
|
:attachments => params[:copy_attachments].present?,
|
||||||
|
:subtasks => params[:copy_subtasks].present?
|
||||||
|
)
|
||||||
end
|
end
|
||||||
journal = issue.init_journal(User.current, params[:notes])
|
journal = issue.init_journal(User.current, params[:notes])
|
||||||
issue.safe_attributes = attributes
|
issue.safe_attributes = attributes
|
||||||
@@ -301,7 +303,7 @@ class IssuesController < ApplicationController
|
|||||||
end
|
end
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_back_or_default(:action => 'index', :project_id => @project) }
|
format.html { redirect_back_or_default(:action => 'index', :project_id => @project) }
|
||||||
format.api { head :ok }
|
format.api { render_api_ok }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -348,8 +350,6 @@ private
|
|||||||
# from the params
|
# from the params
|
||||||
# TODO: Refactor, not everything in here is needed by #edit
|
# TODO: Refactor, not everything in here is needed by #edit
|
||||||
def update_issue_from_params
|
def update_issue_from_params
|
||||||
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
|
|
||||||
@priorities = IssuePriority.active
|
|
||||||
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
|
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
|
||||||
@time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
|
@time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
|
||||||
@time_entry.attributes = params[:time_entry]
|
@time_entry.attributes = params[:time_entry]
|
||||||
@@ -371,6 +371,8 @@ private
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
@issue.safe_attributes = issue_attributes
|
@issue.safe_attributes = issue_attributes
|
||||||
|
@priorities = IssuePriority.active
|
||||||
|
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -383,7 +385,8 @@ private
|
|||||||
begin
|
begin
|
||||||
@copy_from = Issue.visible.find(params[:copy_from])
|
@copy_from = Issue.visible.find(params[:copy_from])
|
||||||
@copy_attachments = params[:copy_attachments].present? || request.get?
|
@copy_attachments = params[:copy_attachments].present? || request.get?
|
||||||
@issue.copy_from(@copy_from, :attachments => @copy_attachments)
|
@copy_subtasks = params[:copy_subtasks].present? || request.get?
|
||||||
|
@issue.copy_from(@copy_from, :attachments => @copy_attachments, :subtasks => @copy_subtasks)
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
render_404
|
render_404
|
||||||
return
|
return
|
||||||
@@ -395,7 +398,7 @@ private
|
|||||||
end
|
end
|
||||||
|
|
||||||
@issue.project = @project
|
@issue.project = @project
|
||||||
@issue.author = User.current
|
@issue.author ||= User.current
|
||||||
# Tracker must be set before custom field values
|
# Tracker must be set before custom field values
|
||||||
@issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
|
@issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
|
||||||
if @issue.tracker.nil?
|
if @issue.tracker.nil?
|
||||||
@@ -420,7 +423,16 @@ private
|
|||||||
def parse_params_for_bulk_issue_attributes(params)
|
def parse_params_for_bulk_issue_attributes(params)
|
||||||
attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
|
attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
|
||||||
attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
|
attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
|
||||||
attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
|
if custom = attributes[:custom_field_values]
|
||||||
|
custom.reject! {|k,v| v.blank?}
|
||||||
|
custom.keys.each do |k|
|
||||||
|
if custom[k].is_a?(Array)
|
||||||
|
custom[k] << '' if custom[k].delete('__none__')
|
||||||
|
else
|
||||||
|
custom[k] = '' if custom[k] == '__none__'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
attributes
|
attributes
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -67,16 +67,8 @@ class JournalsController < ApplicationController
|
|||||||
end
|
end
|
||||||
# Replaces pre blocks with [...]
|
# Replaces pre blocks with [...]
|
||||||
text = text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]')
|
text = text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]')
|
||||||
content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> "
|
@content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> "
|
||||||
content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
|
@content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
|
||||||
|
|
||||||
render(:update) { |page|
|
|
||||||
page.<< "$('notes').value = \"#{escape_javascript content}\";"
|
|
||||||
page.show 'update'
|
|
||||||
page << "Form.Element.focus('notes');"
|
|
||||||
page << "Element.scrollTo('update');"
|
|
||||||
page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;"
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -63,31 +63,16 @@ class MembersController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if members.present? && members.all? {|m| m.valid? }
|
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
|
||||||
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
|
format.js { @members = members }
|
||||||
format.js {
|
format.api {
|
||||||
render(:update) {|page|
|
@member = members.first
|
||||||
page.replace_html "tab-content-members", :partial => 'projects/settings/members'
|
if @member.valid?
|
||||||
page << 'hideOnLoad()'
|
|
||||||
members.each {|member| page.visual_effect(:highlight, "member-#{member.id}") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
format.api {
|
|
||||||
@member = members.first
|
|
||||||
render :action => 'show', :status => :created, :location => membership_url(@member)
|
render :action => 'show', :status => :created, :location => membership_url(@member)
|
||||||
}
|
else
|
||||||
else
|
render_validation_errors(@member)
|
||||||
format.js {
|
end
|
||||||
render(:update) {|page|
|
}
|
||||||
errors = members.collect {|m|
|
|
||||||
m.errors.full_messages
|
|
||||||
}.flatten.uniq
|
|
||||||
|
|
||||||
page.alert(l(:notice_failed_to_save_members, :errors => errors.join(', ')))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
format.api { render_validation_errors(members.first) }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -98,16 +83,10 @@ class MembersController < ApplicationController
|
|||||||
saved = @member.save
|
saved = @member.save
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
|
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
|
||||||
format.js {
|
format.js
|
||||||
render(:update) {|page|
|
|
||||||
page.replace_html "tab-content-members", :partial => 'projects/settings/members'
|
|
||||||
page << 'hideOnLoad()'
|
|
||||||
page.visual_effect(:highlight, "member-#{@member.id}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
format.api {
|
format.api {
|
||||||
if saved
|
if saved
|
||||||
head :ok
|
render_api_ok
|
||||||
else
|
else
|
||||||
render_validation_errors(@member)
|
render_validation_errors(@member)
|
||||||
end
|
end
|
||||||
@@ -121,14 +100,10 @@ class MembersController < ApplicationController
|
|||||||
end
|
end
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
|
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
|
||||||
format.js { render(:update) {|page|
|
format.js
|
||||||
page.replace_html "tab-content-members", :partial => 'projects/settings/members'
|
|
||||||
page << 'hideOnLoad()'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
format.api {
|
format.api {
|
||||||
if @member.destroyed?
|
if @member.destroyed?
|
||||||
head :ok
|
render_api_ok
|
||||||
else
|
else
|
||||||
head :unprocessable_entity
|
head :unprocessable_entity
|
||||||
end
|
end
|
||||||
@@ -140,5 +115,4 @@ class MembersController < ApplicationController
|
|||||||
@principals = Principal.active.not_member_of(@project).like(params[:q]).all(:limit => 100)
|
@principals = Principal.active.not_member_of(@project).like(params[:q]).all(:limit => 100)
|
||||||
render :layout => false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -22,6 +22,7 @@ class MessagesController < ApplicationController
|
|||||||
before_filter :find_message, :except => [:new, :preview]
|
before_filter :find_message, :except => [:new, :preview]
|
||||||
before_filter :authorize, :except => [:preview, :edit, :destroy]
|
before_filter :authorize, :except => [:preview, :edit, :destroy]
|
||||||
|
|
||||||
|
helper :boards
|
||||||
helper :watchers
|
helper :watchers
|
||||||
helper :attachments
|
helper :attachments
|
||||||
include AttachmentsHelper
|
include AttachmentsHelper
|
||||||
@@ -59,7 +60,7 @@ class MessagesController < ApplicationController
|
|||||||
if @message.save
|
if @message.save
|
||||||
call_hook(:controller_messages_new_after_save, { :params => params, :message => @message})
|
call_hook(:controller_messages_new_after_save, { :params => params, :message => @message})
|
||||||
render_attachment_warning_if_needed(@message)
|
render_attachment_warning_if_needed(@message)
|
||||||
redirect_to :action => 'show', :id => @message
|
redirect_to board_message_path(@board, @message)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -76,7 +77,7 @@ class MessagesController < ApplicationController
|
|||||||
attachments = Attachment.attach_files(@reply, params[:attachments])
|
attachments = Attachment.attach_files(@reply, params[:attachments])
|
||||||
render_attachment_warning_if_needed(@reply)
|
render_attachment_warning_if_needed(@reply)
|
||||||
end
|
end
|
||||||
redirect_to :action => 'show', :id => @topic, :r => @reply
|
redirect_to board_message_path(@board, @topic, :r => @reply)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Edit a message
|
# Edit a message
|
||||||
@@ -88,40 +89,35 @@ class MessagesController < ApplicationController
|
|||||||
render_attachment_warning_if_needed(@message)
|
render_attachment_warning_if_needed(@message)
|
||||||
flash[:notice] = l(:notice_successful_update)
|
flash[:notice] = l(:notice_successful_update)
|
||||||
@message.reload
|
@message.reload
|
||||||
redirect_to :action => 'show', :board_id => @message.board, :id => @message.root, :r => (@message.parent_id && @message.id)
|
redirect_to board_message_path(@message.board, @message.root, :r => (@message.parent_id && @message.id))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Delete a messages
|
# Delete a messages
|
||||||
def destroy
|
def destroy
|
||||||
(render_403; return false) unless @message.destroyable_by?(User.current)
|
(render_403; return false) unless @message.destroyable_by?(User.current)
|
||||||
|
r = @message.to_param
|
||||||
@message.destroy
|
@message.destroy
|
||||||
redirect_to @message.parent.nil? ?
|
if @message.parent
|
||||||
{ :controller => 'boards', :action => 'show', :project_id => @project, :id => @board } :
|
redirect_to board_message_path(@board, @message.parent, :r => r)
|
||||||
{ :action => 'show', :id => @message.parent, :r => @message }
|
else
|
||||||
|
redirect_to project_board_path(@project, @board)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def quote
|
def quote
|
||||||
user = @message.author
|
@subject = @message.subject
|
||||||
text = @message.content
|
@subject = "RE: #{@subject}" unless @subject.starts_with?('RE:')
|
||||||
subject = @message.subject.gsub('"', '\"')
|
|
||||||
subject = "RE: #{subject}" unless subject.starts_with?('RE:')
|
@content = "#{ll(Setting.default_language, :text_user_wrote, @message.author)}\n> "
|
||||||
content = "#{ll(Setting.default_language, :text_user_wrote, user)}\\n> "
|
@content << @message.content.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
|
||||||
content << text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub('"', '\"').gsub(/(\r?\n|\r\n?)/, "\\n> ") + "\\n\\n"
|
|
||||||
render(:update) { |page|
|
|
||||||
page << "$('message_subject').value = \"#{subject}\";"
|
|
||||||
page.<< "$('message_content').value = \"#{content}\";"
|
|
||||||
page.show 'reply'
|
|
||||||
page << "Form.Element.focus('message_content');"
|
|
||||||
page << "Element.scrollTo('reply');"
|
|
||||||
page << "$('message_content').scrollTop = $('message_content').scrollHeight - $('message_content').clientHeight;"
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def preview
|
def preview
|
||||||
message = @board.messages.find_by_id(params[:id])
|
message = @board.messages.find_by_id(params[:id])
|
||||||
@attachements = message.attachments if message
|
@attachements = message.attachments if message
|
||||||
@text = (params[:message] || params[:reply])[:content]
|
@text = (params[:message] || params[:reply])[:content]
|
||||||
|
@previewed = message
|
||||||
render :partial => 'common/preview'
|
render :partial => 'common/preview'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -65,6 +65,24 @@ class MyController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Destroys user's account
|
||||||
|
def destroy
|
||||||
|
@user = User.current
|
||||||
|
unless @user.own_account_deletable?
|
||||||
|
redirect_to :action => 'account'
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if request.post? && params[:confirm]
|
||||||
|
@user.destroy
|
||||||
|
if @user.destroyed?
|
||||||
|
logout_user
|
||||||
|
flash[:notice] = l(:notice_account_deleted)
|
||||||
|
end
|
||||||
|
redirect_to home_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Manage user's password
|
# Manage user's password
|
||||||
def password
|
def password
|
||||||
@user = User.current
|
@user = User.current
|
||||||
@@ -117,7 +135,11 @@ class MyController < ApplicationController
|
|||||||
@user = User.current
|
@user = User.current
|
||||||
@blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT.dup
|
@blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT.dup
|
||||||
@block_options = []
|
@block_options = []
|
||||||
BLOCKS.each {|k, v| @block_options << [l("my.blocks.#{v}", :default => [v, v.to_s.humanize]), k.dasherize]}
|
BLOCKS.each do |k, v|
|
||||||
|
unless %w(top left right).detect {|f| (@blocks[f] ||= []).include?(k)}
|
||||||
|
@block_options << [l("my.blocks.#{v}", :default => [v, v.to_s.humanize]), k.dasherize]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add a block to user's page
|
# Add a block to user's page
|
||||||
@@ -134,7 +156,7 @@ class MyController < ApplicationController
|
|||||||
layout['top'].unshift block
|
layout['top'].unshift block
|
||||||
@user.pref[:my_page_layout] = layout
|
@user.pref[:my_page_layout] = layout
|
||||||
@user.pref.save
|
@user.pref.save
|
||||||
render :partial => "block", :locals => {:user => @user, :block_name => block}
|
redirect_to :action => 'page_layout'
|
||||||
end
|
end
|
||||||
|
|
||||||
# Remove a block to user's page
|
# Remove a block to user's page
|
||||||
@@ -147,7 +169,7 @@ class MyController < ApplicationController
|
|||||||
%w(top left right).each {|f| (layout[f] ||= []).delete block }
|
%w(top left right).each {|f| (layout[f] ||= []).delete block }
|
||||||
@user.pref[:my_page_layout] = layout
|
@user.pref[:my_page_layout] = layout
|
||||||
@user.pref.save
|
@user.pref.save
|
||||||
render :nothing => true
|
redirect_to :action => 'page_layout'
|
||||||
end
|
end
|
||||||
|
|
||||||
# Change blocks order on user's page
|
# Change blocks order on user's page
|
||||||
@@ -157,7 +179,8 @@ class MyController < ApplicationController
|
|||||||
group = params[:group]
|
group = params[:group]
|
||||||
@user = User.current
|
@user = User.current
|
||||||
if group.is_a?(String)
|
if group.is_a?(String)
|
||||||
group_items = (params["list-#{group}"] || []).collect(&:underscore)
|
group_items = (params["blocks"] || []).collect(&:underscore)
|
||||||
|
group_items.each {|s| s.sub!(/^block_/, '')}
|
||||||
if group_items and group_items.is_a? Array
|
if group_items and group_items.is_a? Array
|
||||||
layout = @user.pref[:my_page_layout] || {}
|
layout = @user.pref[:my_page_layout] || {}
|
||||||
# remove group blocks if they are presents in other groups
|
# remove group blocks if they are presents in other groups
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -26,7 +26,8 @@ class PreviewsController < ApplicationController
|
|||||||
if @description && @description.gsub(/(\r?\n|\n\r?)/, "\n") == @issue.description.to_s.gsub(/(\r?\n|\n\r?)/, "\n")
|
if @description && @description.gsub(/(\r?\n|\n\r?)/, "\n") == @issue.description.to_s.gsub(/(\r?\n|\n\r?)/, "\n")
|
||||||
@description = nil
|
@description = nil
|
||||||
end
|
end
|
||||||
@notes = params[:notes]
|
# params[:notes] is useful for preview of notes in issue history
|
||||||
|
@notes = params[:notes] || (params[:issue] ? params[:issue][:notes] : nil)
|
||||||
else
|
else
|
||||||
@description = (params[:issue] ? params[:issue][:description] : nil)
|
@description = (params[:issue] ? params[:issue][:description] : nil)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,20 @@
|
|||||||
|
# Redmine - project management software
|
||||||
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
class ProjectEnumerationsController < ApplicationController
|
class ProjectEnumerationsController < ApplicationController
|
||||||
before_filter :find_project_by_project_id
|
before_filter :find_project_by_project_id
|
||||||
before_filter :authorize
|
before_filter :authorize
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -48,7 +48,11 @@ class ProjectsController < ApplicationController
|
|||||||
def index
|
def index
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html {
|
format.html {
|
||||||
@projects = Project.visible.find(:all, :order => 'lft')
|
scope = Project
|
||||||
|
unless params[:closed]
|
||||||
|
scope = scope.active
|
||||||
|
end
|
||||||
|
@projects = scope.visible.order('lft').all
|
||||||
}
|
}
|
||||||
format.api {
|
format.api {
|
||||||
@offset, @limit = api_offset_and_limit
|
@offset, @limit = api_offset_and_limit
|
||||||
@@ -65,14 +69,14 @@ class ProjectsController < ApplicationController
|
|||||||
|
|
||||||
def new
|
def new
|
||||||
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
|
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
|
||||||
@trackers = Tracker.all
|
@trackers = Tracker.sorted.all
|
||||||
@project = Project.new
|
@project = Project.new
|
||||||
@project.safe_attributes = params[:project]
|
@project.safe_attributes = params[:project]
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
|
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
|
||||||
@trackers = Tracker.all
|
@trackers = Tracker.sorted.all
|
||||||
@project = Project.new
|
@project = Project.new
|
||||||
@project.safe_attributes = params[:project]
|
@project.safe_attributes = params[:project]
|
||||||
|
|
||||||
@@ -105,7 +109,7 @@ class ProjectsController < ApplicationController
|
|||||||
|
|
||||||
def copy
|
def copy
|
||||||
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
|
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
|
||||||
@trackers = Tracker.all
|
@trackers = Tracker.sorted.all
|
||||||
@root_projects = Project.find(:all,
|
@root_projects = Project.find(:all,
|
||||||
:conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}",
|
:conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}",
|
||||||
:order => 'name')
|
:order => 'name')
|
||||||
@@ -152,12 +156,8 @@ class ProjectsController < ApplicationController
|
|||||||
|
|
||||||
cond = @project.project_condition(Setting.display_subprojects_issues?)
|
cond = @project.project_condition(Setting.display_subprojects_issues?)
|
||||||
|
|
||||||
@open_issues_by_tracker = Issue.visible.count(:group => :tracker,
|
@open_issues_by_tracker = Issue.visible.open.where(cond).count(:group => :tracker)
|
||||||
:include => [:project, :status, :tracker],
|
@total_issues_by_tracker = Issue.visible.where(cond).count(:group => :tracker)
|
||||||
:conditions => ["(#{cond}) AND #{IssueStatus.table_name}.is_closed=?", false])
|
|
||||||
@total_issues_by_tracker = Issue.visible.count(:group => :tracker,
|
|
||||||
:include => [:project, :status, :tracker],
|
|
||||||
:conditions => cond)
|
|
||||||
|
|
||||||
if User.current.allowed_to?(:view_time_entries, @project)
|
if User.current.allowed_to?(:view_time_entries, @project)
|
||||||
@total_hours = TimeEntry.visible.sum(:hours, :include => :project, :conditions => cond).to_f
|
@total_hours = TimeEntry.visible.sum(:hours, :include => :project, :conditions => cond).to_f
|
||||||
@@ -175,7 +175,7 @@ class ProjectsController < ApplicationController
|
|||||||
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
|
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
|
||||||
@issue_category ||= IssueCategory.new
|
@issue_category ||= IssueCategory.new
|
||||||
@member ||= @project.members.new
|
@member ||= @project.members.new
|
||||||
@trackers = Tracker.all
|
@trackers = Tracker.sorted.all
|
||||||
@wiki ||= @project.wiki
|
@wiki ||= @project.wiki
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -191,7 +191,7 @@ class ProjectsController < ApplicationController
|
|||||||
flash[:notice] = l(:notice_successful_update)
|
flash[:notice] = l(:notice_successful_update)
|
||||||
redirect_to :action => 'settings', :id => @project
|
redirect_to :action => 'settings', :id => @project
|
||||||
}
|
}
|
||||||
format.api { head :ok }
|
format.api { render_api_ok }
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
@@ -224,6 +224,16 @@ class ProjectsController < ApplicationController
|
|||||||
redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
|
redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def close
|
||||||
|
@project.close
|
||||||
|
redirect_to project_path(@project)
|
||||||
|
end
|
||||||
|
|
||||||
|
def reopen
|
||||||
|
@project.reopen
|
||||||
|
redirect_to project_path(@project)
|
||||||
|
end
|
||||||
|
|
||||||
# Delete @project
|
# Delete @project
|
||||||
def destroy
|
def destroy
|
||||||
@project_to_destroy = @project
|
@project_to_destroy = @project
|
||||||
@@ -231,7 +241,7 @@ class ProjectsController < ApplicationController
|
|||||||
@project_to_destroy.destroy
|
@project_to_destroy.destroy
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to :controller => 'admin', :action => 'projects' }
|
format.html { redirect_to :controller => 'admin', :action => 'projects' }
|
||||||
format.api { head :ok }
|
format.api { render_api_ok }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# hide project in layout
|
# hide project in layout
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -22,7 +22,7 @@ class ReportsController < ApplicationController
|
|||||||
def issue_report
|
def issue_report
|
||||||
@trackers = @project.trackers
|
@trackers = @project.trackers
|
||||||
@versions = @project.shared_versions.sort
|
@versions = @project.shared_versions.sort
|
||||||
@priorities = IssuePriority.all
|
@priorities = IssuePriority.all.reverse
|
||||||
@categories = @project.issue_categories
|
@categories = @project.issue_categories
|
||||||
@assignees = (Setting.issue_group_assignment? ? @project.principals : @project.users).sort
|
@assignees = (Setting.issue_group_assignment? ? @project.principals : @project.users).sort
|
||||||
@authors = @project.users.sort
|
@authors = @project.users.sort
|
||||||
@@ -53,7 +53,7 @@ class ReportsController < ApplicationController
|
|||||||
@report_title = l(:field_version)
|
@report_title = l(:field_version)
|
||||||
when "priority"
|
when "priority"
|
||||||
@field = "priority_id"
|
@field = "priority_id"
|
||||||
@rows = IssuePriority.all
|
@rows = IssuePriority.all.reverse
|
||||||
@data = Issue.by_priority(@project)
|
@data = Issue.by_priority(@project)
|
||||||
@report_title = l(:field_priority)
|
@report_title = l(:field_priority)
|
||||||
when "category"
|
when "category"
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
require 'SVG/Graph/Bar'
|
require 'SVG/Graph/Bar'
|
||||||
require 'SVG/Graph/BarHorizontal'
|
require 'SVG/Graph/BarHorizontal'
|
||||||
require 'digest/sha1'
|
require 'digest/sha1'
|
||||||
|
require 'redmine/scm/adapters/abstract_adapter'
|
||||||
|
|
||||||
class ChangesetNotFound < Exception; end
|
class ChangesetNotFound < Exception; end
|
||||||
class InvalidRevisionParam < Exception; end
|
class InvalidRevisionParam < Exception; end
|
||||||
@@ -41,11 +42,15 @@ class RepositoriesController < ApplicationController
|
|||||||
@repository = Repository.factory(scm)
|
@repository = Repository.factory(scm)
|
||||||
@repository.is_default = @project.repository.nil?
|
@repository.is_default = @project.repository.nil?
|
||||||
@repository.project = @project
|
@repository.project = @project
|
||||||
render :layout => !request.xhr?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@repository = Repository.factory(params[:repository_scm], params[:repository])
|
attrs = pickup_extra_info
|
||||||
|
@repository = Repository.factory(params[:repository_scm])
|
||||||
|
@repository.safe_attributes = params[:repository]
|
||||||
|
if attrs[:attrs_extra].keys.any?
|
||||||
|
@repository.merge_extra_info(attrs[:attrs_extra])
|
||||||
|
end
|
||||||
@repository.project = @project
|
@repository.project = @project
|
||||||
if request.post? && @repository.save
|
if request.post? && @repository.save
|
||||||
redirect_to settings_project_path(@project, :tab => 'repositories')
|
redirect_to settings_project_path(@project, :tab => 'repositories')
|
||||||
@@ -58,7 +63,11 @@ class RepositoriesController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@repository.attributes = params[:repository]
|
attrs = pickup_extra_info
|
||||||
|
@repository.safe_attributes = attrs[:attrs]
|
||||||
|
if attrs[:attrs_extra].keys.any?
|
||||||
|
@repository.merge_extra_info(attrs[:attrs_extra])
|
||||||
|
end
|
||||||
@repository.project = @project
|
@repository.project = @project
|
||||||
if request.put? && @repository.save
|
if request.put? && @repository.save
|
||||||
redirect_to settings_project_path(@project, :tab => 'repositories')
|
redirect_to settings_project_path(@project, :tab => 'repositories')
|
||||||
@@ -67,6 +76,20 @@ class RepositoriesController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pickup_extra_info
|
||||||
|
p = {}
|
||||||
|
p_extra = {}
|
||||||
|
params[:repository].each do |k, v|
|
||||||
|
if k =~ /^extra_/
|
||||||
|
p_extra[k] = v
|
||||||
|
else
|
||||||
|
p[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
{:attrs => p, :attrs_extra => p_extra}
|
||||||
|
end
|
||||||
|
private :pickup_extra_info
|
||||||
|
|
||||||
def committers
|
def committers
|
||||||
@committers = @repository.committers
|
@committers = @repository.committers
|
||||||
@users = @project.users
|
@users = @project.users
|
||||||
@@ -129,7 +152,15 @@ class RepositoriesController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def raw
|
||||||
|
entry_and_raw(true)
|
||||||
|
end
|
||||||
|
|
||||||
def entry
|
def entry
|
||||||
|
entry_and_raw(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def entry_and_raw(is_raw)
|
||||||
@entry = @repository.entry(@path, @rev)
|
@entry = @repository.entry(@path, @rev)
|
||||||
(show_error_not_found; return) unless @entry
|
(show_error_not_found; return) unless @entry
|
||||||
|
|
||||||
@@ -138,13 +169,14 @@ class RepositoriesController < ApplicationController
|
|||||||
|
|
||||||
@content = @repository.cat(@path, @rev)
|
@content = @repository.cat(@path, @rev)
|
||||||
(show_error_not_found; return) unless @content
|
(show_error_not_found; return) unless @content
|
||||||
if 'raw' == params[:format] ||
|
if is_raw ||
|
||||||
(@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) ||
|
(@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) ||
|
||||||
! is_entry_text_data?(@content, @path)
|
! is_entry_text_data?(@content, @path)
|
||||||
# Force the download
|
# Force the download
|
||||||
send_opt = { :filename => filename_for_content_disposition(@path.split('/').last) }
|
send_opt = { :filename => filename_for_content_disposition(@path.split('/').last) }
|
||||||
send_type = Redmine::MimeType.of(@path)
|
send_type = Redmine::MimeType.of(@path)
|
||||||
send_opt[:type] = send_type.to_s if send_type
|
send_opt[:type] = send_type.to_s if send_type
|
||||||
|
send_opt[:disposition] = (Redmine::MimeType.is_type?('image', @path) && !is_raw ? 'inline' : 'attachment')
|
||||||
send_data @content, send_opt
|
send_data @content, send_opt
|
||||||
else
|
else
|
||||||
# Prevent empty lines when displaying a file with Windows style eol
|
# Prevent empty lines when displaying a file with Windows style eol
|
||||||
@@ -154,6 +186,7 @@ class RepositoriesController < ApplicationController
|
|||||||
@changeset = @repository.find_changeset_by_name(@rev)
|
@changeset = @repository.find_changeset_by_name(@rev)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
private :entry_and_raw
|
||||||
|
|
||||||
def is_entry_text_data?(ent, path)
|
def is_entry_text_data?(ent, path)
|
||||||
# UTF-16 contains "\x00".
|
# UTF-16 contains "\x00".
|
||||||
@@ -202,22 +235,6 @@ class RepositoriesController < ApplicationController
|
|||||||
|
|
||||||
if @issue
|
if @issue
|
||||||
@changeset.issues << @issue
|
@changeset.issues << @issue
|
||||||
respond_to do |format|
|
|
||||||
format.js {
|
|
||||||
render :update do |page|
|
|
||||||
page.replace_html "related-issues", :partial => "related_issues"
|
|
||||||
page.visual_effect :highlight, "related-issue-#{@issue.id}"
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
respond_to do |format|
|
|
||||||
format.js {
|
|
||||||
render :update do |page|
|
|
||||||
page.alert(l(:label_issue) + ' ' + l('activerecord.errors.messages.invalid'))
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -228,14 +245,6 @@ class RepositoriesController < ApplicationController
|
|||||||
if @issue
|
if @issue
|
||||||
@changeset.issues.delete(@issue)
|
@changeset.issues.delete(@issue)
|
||||||
end
|
end
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
format.js {
|
|
||||||
render :update do |page|
|
|
||||||
page.remove "related-issue-#{@issue.id}"
|
|
||||||
end if @issue
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def diff
|
def diff
|
||||||
@@ -307,8 +316,7 @@ class RepositoriesController < ApplicationController
|
|||||||
@repository = @project.repository
|
@repository = @project.repository
|
||||||
end
|
end
|
||||||
(render_404; return false) unless @repository
|
(render_404; return false) unless @repository
|
||||||
@path = params[:path].join('/') unless params[:path].nil?
|
@path = params[:path].is_a?(Array) ? params[:path].join('/') : params[:path].to_s
|
||||||
@path ||= ''
|
|
||||||
@rev = params[:rev].blank? ? @repository.default_branch : params[:rev].to_s.strip
|
@rev = params[:rev].blank? ? @repository.default_branch : params[:rev].to_s.strip
|
||||||
@rev_to = params[:rev_to]
|
@rev_to = params[:rev_to]
|
||||||
|
|
||||||
@@ -343,17 +351,17 @@ class RepositoriesController < ApplicationController
|
|||||||
@date_to = Date.today
|
@date_to = Date.today
|
||||||
@date_from = @date_to << 11
|
@date_from = @date_to << 11
|
||||||
@date_from = Date.civil(@date_from.year, @date_from.month, 1)
|
@date_from = Date.civil(@date_from.year, @date_from.month, 1)
|
||||||
commits_by_day = repository.changesets.count(
|
commits_by_day = Changeset.count(
|
||||||
:all, :group => :commit_date,
|
:all, :group => :commit_date,
|
||||||
:conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
|
:conditions => ["repository_id = ? AND commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
|
||||||
commits_by_month = [0] * 12
|
commits_by_month = [0] * 12
|
||||||
commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
|
commits_by_day.each {|c| commits_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
|
||||||
|
|
||||||
changes_by_day = repository.changes.count(
|
changes_by_day = Change.count(
|
||||||
:all, :group => :commit_date,
|
:all, :group => :commit_date, :include => :changeset,
|
||||||
:conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
|
:conditions => ["#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
|
||||||
changes_by_month = [0] * 12
|
changes_by_month = [0] * 12
|
||||||
changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
|
changes_by_day.each {|c| changes_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
|
||||||
|
|
||||||
fields = []
|
fields = []
|
||||||
12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)}
|
12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)}
|
||||||
@@ -384,10 +392,10 @@ class RepositoriesController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def graph_commits_per_author(repository)
|
def graph_commits_per_author(repository)
|
||||||
commits_by_author = repository.changesets.count(:all, :group => :committer)
|
commits_by_author = Changeset.count(:all, :group => :committer, :conditions => ["repository_id = ?", repository.id])
|
||||||
commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}
|
commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}
|
||||||
|
|
||||||
changes_by_author = repository.changes.count(:all, :group => :committer)
|
changes_by_author = Change.count(:all, :group => :committer, :include => :changeset, :conditions => ["#{Changeset.table_name}.repository_id = ?", repository.id])
|
||||||
h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
|
h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
|
||||||
|
|
||||||
fields = commits_by_author.collect {|r| r.first}
|
fields = commits_by_author.collect {|r| r.first}
|
||||||
@@ -423,4 +431,3 @@ class RepositoriesController < ApplicationController
|
|||||||
graph.burn
|
graph.burn
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -36,8 +36,11 @@ class RolesController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
# Prefills the form with 'Non member' role permissions
|
# Prefills the form with 'Non member' role permissions by default
|
||||||
@role = Role.new(params[:role] || {:permissions => Role.non_member.permissions})
|
@role = Role.new(params[:role] || {:permissions => Role.non_member.permissions})
|
||||||
|
if params[:copy].present? && @copy_from = Role.find_by_id(params[:copy])
|
||||||
|
@role.copy_from(@copy_from)
|
||||||
|
end
|
||||||
@roles = Role.sorted.all
|
@roles = Role.sorted.all
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -46,7 +49,7 @@ class RolesController < ApplicationController
|
|||||||
if request.post? && @role.save
|
if request.post? && @role.save
|
||||||
# workflow copy
|
# workflow copy
|
||||||
if !params[:copy_workflow_from].blank? && (copy_from = Role.find_by_id(params[:copy_workflow_from]))
|
if !params[:copy_workflow_from].blank? && (copy_from = Role.find_by_id(params[:copy_workflow_from]))
|
||||||
@role.workflows.copy(copy_from)
|
@role.workflow_rules.copy(copy_from)
|
||||||
end
|
end
|
||||||
flash[:notice] = l(:notice_successful_create)
|
flash[:notice] = l(:notice_successful_create)
|
||||||
redirect_to :action => 'index'
|
redirect_to :action => 'index'
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -18,12 +18,13 @@
|
|||||||
class TimelogController < ApplicationController
|
class TimelogController < ApplicationController
|
||||||
menu_item :issues
|
menu_item :issues
|
||||||
|
|
||||||
before_filter :find_project, :only => [:create]
|
before_filter :find_project_for_new_time_entry, :only => [:create]
|
||||||
before_filter :find_time_entry, :only => [:show, :edit, :update]
|
before_filter :find_time_entry, :only => [:show, :edit, :update]
|
||||||
before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
|
before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
|
||||||
before_filter :authorize, :except => [:new, :index, :report]
|
before_filter :authorize, :except => [:new, :index, :report]
|
||||||
|
|
||||||
before_filter :find_optional_project, :only => [:new, :index, :report]
|
before_filter :find_optional_project, :only => [:index, :report]
|
||||||
|
before_filter :find_optional_project_for_new_time_entry, :only => [:new]
|
||||||
before_filter :authorize_global, :only => [:new, :index, :report]
|
before_filter :authorize_global, :only => [:new, :index, :report]
|
||||||
|
|
||||||
accept_rss_auth :index
|
accept_rss_auth :index
|
||||||
@@ -38,7 +39,7 @@ class TimelogController < ApplicationController
|
|||||||
|
|
||||||
def index
|
def index
|
||||||
sort_init 'spent_on', 'desc'
|
sort_init 'spent_on', 'desc'
|
||||||
sort_update 'spent_on' => 'spent_on',
|
sort_update 'spent_on' => ['spent_on', "#{TimeEntry.table_name}.created_on"],
|
||||||
'user' => 'user_id',
|
'user' => 'user_id',
|
||||||
'activity' => 'activity_id',
|
'activity' => 'activity_id',
|
||||||
'project' => "#{Project.table_name}.name",
|
'project' => "#{Project.table_name}.name",
|
||||||
@@ -133,9 +134,13 @@ class TimelogController < ApplicationController
|
|||||||
flash[:notice] = l(:notice_successful_create)
|
flash[:notice] = l(:notice_successful_create)
|
||||||
if params[:continue]
|
if params[:continue]
|
||||||
if params[:project_id]
|
if params[:project_id]
|
||||||
redirect_to :action => 'new', :project_id => @time_entry.project, :issue_id => @time_entry.issue, :back_url => params[:back_url]
|
redirect_to :action => 'new', :project_id => @time_entry.project, :issue_id => @time_entry.issue,
|
||||||
|
:time_entry => {:issue_id => @time_entry.issue_id, :activity_id => @time_entry.activity_id},
|
||||||
|
:back_url => params[:back_url]
|
||||||
else
|
else
|
||||||
redirect_to :action => 'new', :back_url => params[:back_url]
|
redirect_to :action => 'new',
|
||||||
|
:time_entry => {:project_id => @time_entry.project_id, :issue_id => @time_entry.issue_id, :activity_id => @time_entry.activity_id},
|
||||||
|
:back_url => params[:back_url]
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
|
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
|
||||||
@@ -166,7 +171,7 @@ class TimelogController < ApplicationController
|
|||||||
flash[:notice] = l(:notice_successful_update)
|
flash[:notice] = l(:notice_successful_update)
|
||||||
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
|
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
|
||||||
}
|
}
|
||||||
format.api { head :ok }
|
format.api { render_api_ok }
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
@@ -218,7 +223,7 @@ class TimelogController < ApplicationController
|
|||||||
}
|
}
|
||||||
format.api {
|
format.api {
|
||||||
if destroyed
|
if destroyed
|
||||||
head :ok
|
render_api_ok
|
||||||
else
|
else
|
||||||
render_validation_errors(@time_entries)
|
render_validation_errors(@time_entries)
|
||||||
end
|
end
|
||||||
@@ -258,7 +263,7 @@ private
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_project
|
def find_optional_project_for_new_time_entry
|
||||||
if (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
|
if (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
|
||||||
@project = Project.find(project_id)
|
@project = Project.find(project_id)
|
||||||
end
|
end
|
||||||
@@ -266,14 +271,17 @@ private
|
|||||||
@issue = Issue.find(issue_id)
|
@issue = Issue.find(issue_id)
|
||||||
@project ||= @issue.project
|
@project ||= @issue.project
|
||||||
end
|
end
|
||||||
if @project.nil?
|
|
||||||
render_404
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
render_404
|
render_404
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def find_project_for_new_time_entry
|
||||||
|
find_optional_project_for_new_time_entry
|
||||||
|
if @project.nil?
|
||||||
|
render_404
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def find_optional_project
|
def find_optional_project
|
||||||
if !params[:issue_id].blank?
|
if !params[:issue_id].blank?
|
||||||
@issue = Issue.find(params[:issue_id])
|
@issue = Issue.find(params[:issue_id])
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -29,7 +29,7 @@ class TrackersController < ApplicationController
|
|||||||
render :action => "index", :layout => false if request.xhr?
|
render :action => "index", :layout => false if request.xhr?
|
||||||
}
|
}
|
||||||
format.api {
|
format.api {
|
||||||
@trackers = Tracker.all
|
@trackers = Tracker.sorted.all
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -45,7 +45,7 @@ class TrackersController < ApplicationController
|
|||||||
if request.post? and @tracker.save
|
if request.post? and @tracker.save
|
||||||
# workflow copy
|
# workflow copy
|
||||||
if !params[:copy_workflow_from].blank? && (copy_from = Tracker.find_by_id(params[:copy_workflow_from]))
|
if !params[:copy_workflow_from].blank? && (copy_from = Tracker.find_by_id(params[:copy_workflow_from]))
|
||||||
@tracker.workflows.copy(copy_from)
|
@tracker.workflow_rules.copy(copy_from)
|
||||||
end
|
end
|
||||||
flash[:notice] = l(:notice_successful_create)
|
flash[:notice] = l(:notice_successful_create)
|
||||||
redirect_to :action => 'index'
|
redirect_to :action => 'index'
|
||||||
@@ -80,4 +80,22 @@ class TrackersController < ApplicationController
|
|||||||
end
|
end
|
||||||
redirect_to :action => 'index'
|
redirect_to :action => 'index'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def fields
|
||||||
|
if request.post? && params[:trackers]
|
||||||
|
params[:trackers].each do |tracker_id, tracker_params|
|
||||||
|
tracker = Tracker.find_by_id(tracker_id)
|
||||||
|
if tracker
|
||||||
|
tracker.core_fields = tracker_params[:core_fields]
|
||||||
|
tracker.custom_field_ids = tracker_params[:custom_field_ids]
|
||||||
|
tracker.save
|
||||||
|
end
|
||||||
|
end
|
||||||
|
flash[:notice] = l(:notice_successful_update)
|
||||||
|
redirect_to :action => 'fields'
|
||||||
|
return
|
||||||
|
end
|
||||||
|
@trackers = Tracker.sorted.all
|
||||||
|
@custom_fields = IssueCustomField.all.sort
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -99,11 +99,11 @@ class UsersController < ApplicationController
|
|||||||
@user.pref.save
|
@user.pref.save
|
||||||
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
|
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
|
||||||
|
|
||||||
Mailer.deliver_account_information(@user, params[:user][:password]) if params[:send_information]
|
Mailer.account_information(@user, params[:user][:password]).deliver if params[:send_information]
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html {
|
format.html {
|
||||||
flash[:notice] = l(:notice_successful_create)
|
flash[:notice] = l(:notice_user_successful_create, :id => view_context.link_to(@user.login, user_path(@user)))
|
||||||
redirect_to(params[:continue] ?
|
redirect_to(params[:continue] ?
|
||||||
{:controller => 'users', :action => 'new'} :
|
{:controller => 'users', :action => 'new'} :
|
||||||
{:controller => 'users', :action => 'edit', :id => @user}
|
{:controller => 'users', :action => 'edit', :id => @user}
|
||||||
@@ -146,17 +146,17 @@ class UsersController < ApplicationController
|
|||||||
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
|
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
|
||||||
|
|
||||||
if was_activated
|
if was_activated
|
||||||
Mailer.deliver_account_activated(@user)
|
Mailer.account_activated(@user).deliver
|
||||||
elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil?
|
elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil?
|
||||||
Mailer.deliver_account_information(@user, params[:user][:password])
|
Mailer.account_information(@user, params[:user][:password]).deliver
|
||||||
end
|
end
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html {
|
format.html {
|
||||||
flash[:notice] = l(:notice_successful_update)
|
flash[:notice] = l(:notice_successful_update)
|
||||||
redirect_to :back
|
redirect_to_referer_or edit_user_path(@user)
|
||||||
}
|
}
|
||||||
format.api { head :ok }
|
format.api { render_api_ok }
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@auth_sources = AuthSource.find(:all)
|
@auth_sources = AuthSource.find(:all)
|
||||||
@@ -169,15 +169,13 @@ class UsersController < ApplicationController
|
|||||||
format.api { render_validation_errors(@user) }
|
format.api { render_validation_errors(@user) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
rescue ::ActionController::RedirectBackError
|
|
||||||
redirect_to :controller => 'users', :action => 'edit', :id => @user
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@user.destroy
|
@user.destroy
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to(users_url) }
|
format.html { redirect_back_or_default(users_url) }
|
||||||
format.api { head :ok }
|
format.api { render_api_ok }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -185,21 +183,8 @@ class UsersController < ApplicationController
|
|||||||
@membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
|
@membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
|
||||||
@membership.save
|
@membership.save
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @membership.valid?
|
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
|
||||||
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
|
format.js
|
||||||
format.js {
|
|
||||||
render(:update) {|page|
|
|
||||||
page.replace_html "tab-content-memberships", :partial => 'users/memberships'
|
|
||||||
page.visual_effect(:highlight, "member-#{@membership.id}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
format.js {
|
|
||||||
render(:update) {|page|
|
|
||||||
page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', ')))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -210,7 +195,7 @@ class UsersController < ApplicationController
|
|||||||
end
|
end
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
|
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
|
||||||
format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} }
|
format.js
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -78,13 +78,7 @@ class VersionsController < ApplicationController
|
|||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html
|
format.html
|
||||||
format.js do
|
format.js
|
||||||
render :update do |page|
|
|
||||||
page.replace_html 'ajax-modal', :partial => 'versions/new_modal'
|
|
||||||
page << "showModal('ajax-modal', '600px');"
|
|
||||||
page << "Form.Element.focus('version_name');"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -103,14 +97,7 @@ class VersionsController < ApplicationController
|
|||||||
flash[:notice] = l(:notice_successful_create)
|
flash[:notice] = l(:notice_successful_create)
|
||||||
redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
|
redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
|
||||||
end
|
end
|
||||||
format.js do
|
format.js
|
||||||
render(:update) {|page|
|
|
||||||
page << 'hideModal();'
|
|
||||||
# IE doesn't support the replace_html rjs method for select box options
|
|
||||||
page.replace "issue_fixed_version_id",
|
|
||||||
content_tag('select', content_tag('option') + version_options_for_select(@project.shared_versions.open, @version), :id => 'issue_fixed_version_id', :name => 'issue[fixed_version_id]')
|
|
||||||
}
|
|
||||||
end
|
|
||||||
format.api do
|
format.api do
|
||||||
render :action => 'show', :status => :created, :location => version_url(@version)
|
render :action => 'show', :status => :created, :location => version_url(@version)
|
||||||
end
|
end
|
||||||
@@ -118,12 +105,7 @@ class VersionsController < ApplicationController
|
|||||||
else
|
else
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { render :action => 'new' }
|
format.html { render :action => 'new' }
|
||||||
format.js do
|
format.js { render :action => 'new' }
|
||||||
render :update do |page|
|
|
||||||
page.replace_html 'ajax-modal', :partial => 'versions/new_modal'
|
|
||||||
page << "Form.Element.focus('version_name');"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
format.api { render_validation_errors(@version) }
|
format.api { render_validation_errors(@version) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -144,7 +126,7 @@ class VersionsController < ApplicationController
|
|||||||
flash[:notice] = l(:notice_successful_update)
|
flash[:notice] = l(:notice_successful_update)
|
||||||
redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
|
redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
|
||||||
}
|
}
|
||||||
format.api { head :ok }
|
format.api { render_api_ok }
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
@@ -167,7 +149,7 @@ class VersionsController < ApplicationController
|
|||||||
@version.destroy
|
@version.destroy
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project }
|
format.html { redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project }
|
||||||
format.api { head :ok }
|
format.api { render_api_ok }
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
@@ -183,7 +165,7 @@ class VersionsController < ApplicationController
|
|||||||
def status_by
|
def status_by
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { render :action => 'show' }
|
format.html { render :action => 'show' }
|
||||||
format.js { render(:update) {|page| page.replace_html 'status_by', render_issue_status_by(@version, params[:status_by])} }
|
format.js
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -33,15 +33,6 @@ class WatchersController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
respond_to do |format|
|
|
||||||
format.js do
|
|
||||||
render :update do |page|
|
|
||||||
page.replace_html 'ajax-modal', :partial => 'watchers/new', :locals => {:watched => @watched}
|
|
||||||
page << "showModal('ajax-modal', '400px');"
|
|
||||||
page << "$('ajax-modal').addClassName('new-watcher');"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@@ -52,34 +43,15 @@ class WatchersController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to :back }
|
format.html { redirect_to_referer_or {render :text => 'Watcher added.', :layout => true}}
|
||||||
format.js do
|
format.js
|
||||||
render :update do |page|
|
|
||||||
page.replace_html 'ajax-modal', :partial => 'watchers/new', :locals => {:watched => @watched}
|
|
||||||
page.replace_html 'watchers', :partial => 'watchers/watchers', :locals => {:watched => @watched}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
rescue ::ActionController::RedirectBackError
|
|
||||||
render :text => 'Watcher added.', :layout => true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def append
|
def append
|
||||||
if params[:watcher].is_a?(Hash)
|
if params[:watcher].is_a?(Hash)
|
||||||
user_ids = params[:watcher][:user_ids] || [params[:watcher][:user_id]]
|
user_ids = params[:watcher][:user_ids] || [params[:watcher][:user_id]]
|
||||||
users = User.active.find_all_by_id(user_ids)
|
@users = User.active.find_all_by_id(user_ids)
|
||||||
respond_to do |format|
|
|
||||||
format.js do
|
|
||||||
render :update do |page|
|
|
||||||
users.each do |user|
|
|
||||||
page.select("#issue_watcher_user_ids_#{user.id}").each do |item|
|
|
||||||
page.remove item
|
|
||||||
end
|
|
||||||
end
|
|
||||||
page.insert_html :bottom, 'watchers_inputs', :text => watchers_checkboxes(nil, users, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -87,11 +59,7 @@ class WatchersController < ApplicationController
|
|||||||
@watched.set_watcher(User.find(params[:user_id]), false) if request.post?
|
@watched.set_watcher(User.find(params[:user_id]), false) if request.post?
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to :back }
|
format.html { redirect_to :back }
|
||||||
format.js do
|
format.js
|
||||||
render :update do |page|
|
|
||||||
page.replace_html 'watchers', :partial => 'watchers/watchers', :locals => {:watched => @watched}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -111,7 +79,7 @@ private
|
|||||||
@watched = klass.find(params[:object_id])
|
@watched = klass.find(params[:object_id])
|
||||||
@project = @watched.project
|
@project = @watched.project
|
||||||
elsif params[:project_id]
|
elsif params[:project_id]
|
||||||
@project = Project.visible.find(params[:project_id])
|
@project = Project.visible.find_by_param(params[:project_id])
|
||||||
end
|
end
|
||||||
rescue
|
rescue
|
||||||
render_404
|
render_404
|
||||||
@@ -120,17 +88,8 @@ private
|
|||||||
def set_watcher(user, watching)
|
def set_watcher(user, watching)
|
||||||
@watched.set_watcher(user, watching)
|
@watched.set_watcher(user, watching)
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to :back }
|
format.html { redirect_to_referer_or {render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true}}
|
||||||
format.js do
|
format.js { render :partial => 'set_watcher', :locals => {:user => user, :watched => @watched} }
|
||||||
render(:update) do |page|
|
|
||||||
c = watcher_css(@watched)
|
|
||||||
page.select(".#{c}").each do |item|
|
|
||||||
page.replace_html item, watcher_link(@watched, user)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
rescue ::ActionController::RedirectBackError
|
|
||||||
render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -109,7 +109,7 @@ class WikiController < ApplicationController
|
|||||||
|
|
||||||
# To prevent StaleObjectError exception when reverting to a previous version
|
# To prevent StaleObjectError exception when reverting to a previous version
|
||||||
@content.version = @page.content.version
|
@content.version = @page.content.version
|
||||||
|
|
||||||
@text = @content.text
|
@text = @content.text
|
||||||
if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
|
if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
|
||||||
@section = params[:section].to_i
|
@section = params[:section].to_i
|
||||||
@@ -129,15 +129,6 @@ class WikiController < ApplicationController
|
|||||||
# don't keep previous comment
|
# don't keep previous comment
|
||||||
@content.comments = nil
|
@content.comments = nil
|
||||||
|
|
||||||
if !@page.new_record? && params[:content].present? && @content.text == params[:content][:text]
|
|
||||||
attachments = Attachment.attach_files(@page, params[:attachments])
|
|
||||||
render_attachment_warning_if_needed(@page)
|
|
||||||
# don't save content if text wasn't changed
|
|
||||||
@page.save
|
|
||||||
redirect_to :action => 'show', :project_id => @project, :id => @page.title
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
@content.comments = params[:content][:comments]
|
@content.comments = params[:content][:comments]
|
||||||
@text = params[:content][:text]
|
@text = params[:content][:text]
|
||||||
if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
|
if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
|
||||||
@@ -149,8 +140,8 @@ class WikiController < ApplicationController
|
|||||||
@content.text = @text
|
@content.text = @text
|
||||||
end
|
end
|
||||||
@content.author = User.current
|
@content.author = User.current
|
||||||
@page.content = @content
|
|
||||||
if @page.save
|
if @page.save_with_content
|
||||||
attachments = Attachment.attach_files(@page, params[:attachments])
|
attachments = Attachment.attach_files(@page, params[:attachments])
|
||||||
render_attachment_warning_if_needed(@page)
|
render_attachment_warning_if_needed(@page)
|
||||||
call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
|
call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
|
||||||
@@ -163,6 +154,8 @@ class WikiController < ApplicationController
|
|||||||
# Optimistic locking exception
|
# Optimistic locking exception
|
||||||
flash.now[:error] = l(:notice_locking_conflict)
|
flash.now[:error] = l(:notice_locking_conflict)
|
||||||
render :action => 'edit'
|
render :action => 'edit'
|
||||||
|
rescue ActiveRecord::RecordNotSaved
|
||||||
|
render :action => 'edit'
|
||||||
end
|
end
|
||||||
|
|
||||||
# rename a page
|
# rename a page
|
||||||
@@ -237,7 +230,7 @@ class WikiController < ApplicationController
|
|||||||
|
|
||||||
# Export wiki to a single pdf or html file
|
# Export wiki to a single pdf or html file
|
||||||
def export
|
def export
|
||||||
@pages = @wiki.pages.all(:order => 'title', :include => [:content, :attachments], :limit => 75)
|
@pages = @wiki.pages.all(:order => 'title', :include => [:content, :attachments])
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html {
|
format.html {
|
||||||
export = render_to_string :action => 'export_multiple', :layout => false
|
export = render_to_string :action => 'export_multiple', :layout => false
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -24,7 +24,6 @@ class WikisController < ApplicationController
|
|||||||
@wiki = @project.wiki || Wiki.new(:project => @project)
|
@wiki = @project.wiki || Wiki.new(:project => @project)
|
||||||
@wiki.safe_attributes = params[:wiki]
|
@wiki.safe_attributes = params[:wiki]
|
||||||
@wiki.save if request.post?
|
@wiki.save if request.post?
|
||||||
render(:update) {|page| page.replace_html "tab-content-wiki", :partial => 'projects/settings/wiki'}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Delete a project's wiki
|
# Delete a project's wiki
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -18,30 +18,27 @@
|
|||||||
class WorkflowsController < ApplicationController
|
class WorkflowsController < ApplicationController
|
||||||
layout 'admin'
|
layout 'admin'
|
||||||
|
|
||||||
before_filter :require_admin
|
before_filter :require_admin, :find_roles, :find_trackers
|
||||||
before_filter :find_roles
|
|
||||||
before_filter :find_trackers
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@workflow_counts = Workflow.count_by_tracker_and_role
|
@workflow_counts = WorkflowTransition.count_by_tracker_and_role
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
@role = Role.find_by_id(params[:role_id])
|
@role = Role.find_by_id(params[:role_id]) if params[:role_id]
|
||||||
@tracker = Tracker.find_by_id(params[:tracker_id])
|
@tracker = Tracker.find_by_id(params[:tracker_id]) if params[:tracker_id]
|
||||||
|
|
||||||
if request.post?
|
if request.post?
|
||||||
Workflow.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id])
|
WorkflowTransition.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id])
|
||||||
(params[:issue_status] || []).each { |status_id, transitions|
|
(params[:issue_status] || []).each { |status_id, transitions|
|
||||||
transitions.each { |new_status_id, options|
|
transitions.each { |new_status_id, options|
|
||||||
author = options.is_a?(Array) && options.include?('author') && !options.include?('always')
|
author = options.is_a?(Array) && options.include?('author') && !options.include?('always')
|
||||||
assignee = options.is_a?(Array) && options.include?('assignee') && !options.include?('always')
|
assignee = options.is_a?(Array) && options.include?('assignee') && !options.include?('always')
|
||||||
@role.workflows.build(:tracker_id => @tracker.id, :old_status_id => status_id, :new_status_id => new_status_id, :author => author, :assignee => assignee)
|
WorkflowTransition.create(:role_id => @role.id, :tracker_id => @tracker.id, :old_status_id => status_id, :new_status_id => new_status_id, :author => author, :assignee => assignee)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if @role.save
|
if @role.save
|
||||||
flash[:notice] = l(:notice_successful_update)
|
redirect_to :action => 'edit', :role_id => @role, :tracker_id => @tracker, :used_statuses_only => params[:used_statuses_only]
|
||||||
redirect_to :action => 'edit', :role_id => @role, :tracker_id => @tracker
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -50,10 +47,10 @@ class WorkflowsController < ApplicationController
|
|||||||
if @tracker && @used_statuses_only && @tracker.issue_statuses.any?
|
if @tracker && @used_statuses_only && @tracker.issue_statuses.any?
|
||||||
@statuses = @tracker.issue_statuses
|
@statuses = @tracker.issue_statuses
|
||||||
end
|
end
|
||||||
@statuses ||= IssueStatus.find(:all, :order => 'position')
|
@statuses ||= IssueStatus.sorted.all
|
||||||
|
|
||||||
if @tracker && @role && @statuses.any?
|
if @tracker && @role && @statuses.any?
|
||||||
workflows = Workflow.all(:conditions => {:role_id => @role.id, :tracker_id => @tracker.id})
|
workflows = WorkflowTransition.where(:role_id => @role.id, :tracker_id => @tracker.id).all
|
||||||
@workflows = {}
|
@workflows = {}
|
||||||
@workflows['always'] = workflows.select {|w| !w.author && !w.assignee}
|
@workflows['always'] = workflows.select {|w| !w.author && !w.assignee}
|
||||||
@workflows['author'] = workflows.select {|w| w.author}
|
@workflows['author'] = workflows.select {|w| w.author}
|
||||||
@@ -61,6 +58,35 @@ class WorkflowsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def permissions
|
||||||
|
@role = Role.find_by_id(params[:role_id]) if params[:role_id]
|
||||||
|
@tracker = Tracker.find_by_id(params[:tracker_id]) if params[:tracker_id]
|
||||||
|
|
||||||
|
if request.post? && @role && @tracker
|
||||||
|
WorkflowPermission.replace_permissions(@tracker, @role, params[:permissions] || {})
|
||||||
|
redirect_to :action => 'permissions', :role_id => @role, :tracker_id => @tracker, :used_statuses_only => params[:used_statuses_only]
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
@used_statuses_only = (params[:used_statuses_only] == '0' ? false : true)
|
||||||
|
if @tracker && @used_statuses_only && @tracker.issue_statuses.any?
|
||||||
|
@statuses = @tracker.issue_statuses
|
||||||
|
end
|
||||||
|
@statuses ||= IssueStatus.sorted.all
|
||||||
|
|
||||||
|
if @role && @tracker
|
||||||
|
@fields = (Tracker::CORE_FIELDS_ALL - @tracker.disabled_core_fields).map {|field| [field, l("field_"+field.sub(/_id$/, ''))]}
|
||||||
|
@custom_fields = @tracker.custom_fields
|
||||||
|
|
||||||
|
@permissions = WorkflowPermission.where(:tracker_id => @tracker.id, :role_id => @role.id).all.inject({}) do |h, w|
|
||||||
|
h[w.old_status_id] ||= {}
|
||||||
|
h[w.old_status_id][w.field_name] = w.rule
|
||||||
|
h
|
||||||
|
end
|
||||||
|
@statuses.each {|status| @permissions[status.id] ||= {}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def copy
|
def copy
|
||||||
|
|
||||||
if params[:source_tracker_id].blank? || params[:source_tracker_id] == 'any'
|
if params[:source_tracker_id].blank? || params[:source_tracker_id] == 'any'
|
||||||
@@ -83,7 +109,7 @@ class WorkflowsController < ApplicationController
|
|||||||
elsif @target_trackers.nil? || @target_roles.nil?
|
elsif @target_trackers.nil? || @target_roles.nil?
|
||||||
flash.now[:error] = l(:error_workflow_copy_target)
|
flash.now[:error] = l(:error_workflow_copy_target)
|
||||||
else
|
else
|
||||||
Workflow.copy(@source_tracker, @source_role, @target_trackers, @target_roles)
|
WorkflowRule.copy(@source_tracker, @source_role, @target_trackers, @target_roles)
|
||||||
flash[:notice] = l(:notice_successful_update)
|
flash[:notice] = l(:notice_successful_update)
|
||||||
redirect_to :action => 'copy', :source_tracker_id => @source_tracker, :source_role_id => @source_role
|
redirect_to :action => 'copy', :source_tracker_id => @source_tracker, :source_role_id => @source_role
|
||||||
end
|
end
|
||||||
@@ -93,10 +119,10 @@ class WorkflowsController < ApplicationController
|
|||||||
private
|
private
|
||||||
|
|
||||||
def find_roles
|
def find_roles
|
||||||
@roles = Role.find(:all, :order => 'builtin, position')
|
@roles = Role.sorted.all
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_trackers
|
def find_trackers
|
||||||
@trackers = Tracker.find(:all, :order => 'position')
|
@trackers = Tracker.sorted.all
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -20,6 +20,8 @@
|
|||||||
module AdminHelper
|
module AdminHelper
|
||||||
def project_status_options_for_select(selected)
|
def project_status_options_for_select(selected)
|
||||||
options_for_select([[l(:label_all), ''],
|
options_for_select([[l(:label_all), ''],
|
||||||
[l(:status_active), '1']], selected.to_s)
|
[l(:project_status_active), '1'],
|
||||||
|
[l(:project_status_closed), '5'],
|
||||||
|
[l(:project_status_archived), '9']], selected.to_s)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+262
-119
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -43,12 +43,6 @@ module ApplicationHelper
|
|||||||
link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
|
link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
|
||||||
end
|
end
|
||||||
|
|
||||||
# Display a link to remote if user is authorized
|
|
||||||
def link_to_remote_if_authorized(name, options = {}, html_options = nil)
|
|
||||||
url = options[:url] || {}
|
|
||||||
link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
|
|
||||||
end
|
|
||||||
|
|
||||||
# Displays a link to user's account page if active
|
# Displays a link to user's account page if active
|
||||||
def link_to_user(user, options={})
|
def link_to_user(user, options={})
|
||||||
if user.is_a?(User)
|
if user.is_a?(User)
|
||||||
@@ -128,7 +122,7 @@ module ApplicationHelper
|
|||||||
h(truncate(message.subject, :length => 60)),
|
h(truncate(message.subject, :length => 60)),
|
||||||
{ :controller => 'messages', :action => 'show',
|
{ :controller => 'messages', :action => 'show',
|
||||||
:board_id => message.board_id,
|
:board_id => message.board_id,
|
||||||
:id => message.root,
|
:id => (message.parent_id || message.id),
|
||||||
:r => (message.parent_id && message.id),
|
:r => (message.parent_id && message.id),
|
||||||
:anchor => (message.parent_id ? "message-#{message.id}" : nil)
|
:anchor => (message.parent_id ? "message-#{message.id}" : nil)
|
||||||
}.merge(options),
|
}.merge(options),
|
||||||
@@ -145,17 +139,23 @@ module ApplicationHelper
|
|||||||
# link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
|
# link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
|
||||||
#
|
#
|
||||||
def link_to_project(project, options={}, html_options = nil)
|
def link_to_project(project, options={}, html_options = nil)
|
||||||
if project.active?
|
if project.archived?
|
||||||
|
h(project)
|
||||||
|
else
|
||||||
url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
|
url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
|
||||||
link_to(h(project), url, html_options)
|
link_to(h(project), url, html_options)
|
||||||
else
|
|
||||||
h(project)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def thumbnail_tag(attachment)
|
||||||
|
link_to image_tag(url_for(:controller => 'attachments', :action => 'thumbnail', :id => attachment)),
|
||||||
|
{:controller => 'attachments', :action => 'show', :id => attachment, :filename => attachment.filename},
|
||||||
|
:title => attachment.filename
|
||||||
|
end
|
||||||
|
|
||||||
def toggle_link(name, id, options={})
|
def toggle_link(name, id, options={})
|
||||||
onclick = "Element.toggle('#{id}'); "
|
onclick = "$('##{id}').toggle(); "
|
||||||
onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
|
onclick << (options[:focus] ? "$('##{options[:focus]}').focus(); " : "this.blur(); ")
|
||||||
onclick << "return false;"
|
onclick << "return false;"
|
||||||
link_to(name, "#", :onclick => onclick)
|
link_to(name, "#", :onclick => onclick)
|
||||||
end
|
end
|
||||||
@@ -168,17 +168,12 @@ module ApplicationHelper
|
|||||||
}))
|
}))
|
||||||
end
|
end
|
||||||
|
|
||||||
def prompt_to_remote(name, text, param, url, html_options = {})
|
|
||||||
html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
|
|
||||||
link_to name, {}, html_options
|
|
||||||
end
|
|
||||||
|
|
||||||
def format_activity_title(text)
|
def format_activity_title(text)
|
||||||
h(truncate_single_line(text, :length => 100))
|
h(truncate_single_line(text, :length => 100))
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_activity_day(date)
|
def format_activity_day(date)
|
||||||
date == Date.today ? l(:label_today).titleize : format_date(date)
|
date == User.current.today ? l(:label_today).titleize : format_date(date)
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_activity_description(text)
|
def format_activity_description(text)
|
||||||
@@ -200,6 +195,39 @@ module ApplicationHelper
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Renders a tree of projects as a nested set of unordered lists
|
||||||
|
# The given collection may be a subset of the whole project tree
|
||||||
|
# (eg. some intermediate nodes are private and can not be seen)
|
||||||
|
def render_project_nested_lists(projects)
|
||||||
|
s = ''
|
||||||
|
if projects.any?
|
||||||
|
ancestors = []
|
||||||
|
original_project = @project
|
||||||
|
projects.sort_by(&:lft).each do |project|
|
||||||
|
# set the project environment to please macros.
|
||||||
|
@project = project
|
||||||
|
if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
|
||||||
|
s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n"
|
||||||
|
else
|
||||||
|
ancestors.pop
|
||||||
|
s << "</li>"
|
||||||
|
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
|
||||||
|
ancestors.pop
|
||||||
|
s << "</ul></li>\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
classes = (ancestors.empty? ? 'root' : 'child')
|
||||||
|
s << "<li class='#{classes}'><div class='#{classes}'>"
|
||||||
|
s << h(block_given? ? yield(project) : project.name)
|
||||||
|
s << "</div>\n"
|
||||||
|
ancestors << project
|
||||||
|
end
|
||||||
|
s << ("</li></ul>\n" * ancestors.size)
|
||||||
|
@project = original_project
|
||||||
|
end
|
||||||
|
s.html_safe
|
||||||
|
end
|
||||||
|
|
||||||
def render_page_hierarchy(pages, node=nil, options={})
|
def render_page_hierarchy(pages, node=nil, options={})
|
||||||
content = ''
|
content = ''
|
||||||
if pages[node]
|
if pages[node]
|
||||||
@@ -220,7 +248,7 @@ module ApplicationHelper
|
|||||||
def render_flash_messages
|
def render_flash_messages
|
||||||
s = ''
|
s = ''
|
||||||
flash.each do |k,v|
|
flash.each do |k,v|
|
||||||
s << (content_tag('div', v.html_safe, :class => "flash #{k}"))
|
s << content_tag('div', v.html_safe, :class => "flash #{k}", :id => "flash_#{k}")
|
||||||
end
|
end
|
||||||
s.html_safe
|
s.html_safe
|
||||||
end
|
end
|
||||||
@@ -237,23 +265,24 @@ module ApplicationHelper
|
|||||||
# Renders the project quick-jump box
|
# Renders the project quick-jump box
|
||||||
def render_project_jump_box
|
def render_project_jump_box
|
||||||
return unless User.current.logged?
|
return unless User.current.logged?
|
||||||
projects = User.current.memberships.collect(&:project).compact.uniq
|
projects = User.current.memberships.collect(&:project).compact.select(&:active?).uniq
|
||||||
if projects.any?
|
if projects.any?
|
||||||
s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
|
options =
|
||||||
"<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
|
("<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
|
||||||
'<option value="" disabled="disabled">---</option>'
|
'<option value="" disabled="disabled">---</option>').html_safe
|
||||||
s << project_tree_options_for_select(projects, :selected => @project) do |p|
|
|
||||||
{ :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
|
options << project_tree_options_for_select(projects, :selected => @project) do |p|
|
||||||
|
{ :value => project_path(:id => p, :jump => current_menu_item) }
|
||||||
end
|
end
|
||||||
s << '</select>'
|
|
||||||
s.html_safe
|
select_tag('project_quick_jump_box', options, :onchange => 'if (this.value != \'\') { window.location = this.value; }')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_tree_options_for_select(projects, options = {})
|
def project_tree_options_for_select(projects, options = {})
|
||||||
s = ''
|
s = ''
|
||||||
project_tree(projects) do |project, level|
|
project_tree(projects) do |project, level|
|
||||||
name_prefix = (level > 0 ? (' ' * 2 * level + '» ').html_safe : '')
|
name_prefix = (level > 0 ? ' ' * 2 * level + '» ' : '').html_safe
|
||||||
tag_options = {:value => project.id}
|
tag_options = {:value => project.id}
|
||||||
if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project))
|
if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project))
|
||||||
tag_options[:selected] = 'selected'
|
tag_options[:selected] = 'selected'
|
||||||
@@ -273,30 +302,6 @@ module ApplicationHelper
|
|||||||
Project.project_tree(projects, &block)
|
Project.project_tree(projects, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_nested_ul(projects, &block)
|
|
||||||
s = ''
|
|
||||||
if projects.any?
|
|
||||||
ancestors = []
|
|
||||||
projects.sort_by(&:lft).each do |project|
|
|
||||||
if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
|
|
||||||
s << "<ul>\n"
|
|
||||||
else
|
|
||||||
ancestors.pop
|
|
||||||
s << "</li>"
|
|
||||||
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
|
|
||||||
ancestors.pop
|
|
||||||
s << "</ul></li>\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
s << "<li>"
|
|
||||||
s << yield(project).to_s
|
|
||||||
ancestors << project
|
|
||||||
end
|
|
||||||
s << ("</li></ul>\n" * ancestors.size)
|
|
||||||
end
|
|
||||||
s.html_safe
|
|
||||||
end
|
|
||||||
|
|
||||||
def principals_check_box_tags(name, principals)
|
def principals_check_box_tags(name, principals)
|
||||||
s = ''
|
s = ''
|
||||||
principals.sort.each do |principal|
|
principals.sort.each do |principal|
|
||||||
@@ -309,7 +314,7 @@ module ApplicationHelper
|
|||||||
def principals_options_for_select(collection, selected=nil)
|
def principals_options_for_select(collection, selected=nil)
|
||||||
s = ''
|
s = ''
|
||||||
if collection.include?(User.current)
|
if collection.include?(User.current)
|
||||||
s << content_tag('option', "<< #{l(:label_me)} >>".html_safe, :value => User.current.id)
|
s << content_tag('option', "<< #{l(:label_me)} >>", :value => User.current.id)
|
||||||
end
|
end
|
||||||
groups = ''
|
groups = ''
|
||||||
collection.sort.each do |element|
|
collection.sort.each do |element|
|
||||||
@@ -319,7 +324,7 @@ module ApplicationHelper
|
|||||||
unless groups.empty?
|
unless groups.empty?
|
||||||
s << %(<optgroup label="#{h(l(:label_group_plural))}">#{groups}</optgroup>)
|
s << %(<optgroup label="#{h(l(:label_group_plural))}">#{groups}</optgroup>)
|
||||||
end
|
end
|
||||||
s
|
s.html_safe
|
||||||
end
|
end
|
||||||
|
|
||||||
# Truncates and returns the string as a single line
|
# Truncates and returns the string as a single line
|
||||||
@@ -352,7 +357,7 @@ module ApplicationHelper
|
|||||||
def time_tag(time)
|
def time_tag(time)
|
||||||
text = distance_of_time_in_words(Time.now, time)
|
text = distance_of_time_in_words(Time.now, time)
|
||||||
if @project
|
if @project
|
||||||
link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => time.to_date}, :title => format_time(time))
|
link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => User.current.time_to_date(time)}, :title => format_time(time))
|
||||||
else
|
else
|
||||||
content_tag('acronym', text, :title => format_time(time))
|
content_tag('acronym', text, :title => format_time(time))
|
||||||
end
|
end
|
||||||
@@ -369,7 +374,8 @@ module ApplicationHelper
|
|||||||
end
|
end
|
||||||
|
|
||||||
def to_path_param(path)
|
def to_path_param(path)
|
||||||
path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
|
str = path.to_s.split(%r{[/\\]}).select{|p| !p.blank?}.join("/")
|
||||||
|
str.blank? ? nil : str
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_links_full(paginator, count=nil, options={})
|
def pagination_links_full(paginator, count=nil, options={})
|
||||||
@@ -398,7 +404,7 @@ module ApplicationHelper
|
|||||||
|
|
||||||
unless count.nil?
|
unless count.nil?
|
||||||
html << " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})"
|
html << " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})"
|
||||||
if per_page_links != false && links = per_page_links(paginator.items_per_page)
|
if per_page_links != false && links = per_page_links(paginator.items_per_page, count)
|
||||||
html << " | #{links}"
|
html << " | #{links}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -406,11 +412,23 @@ module ApplicationHelper
|
|||||||
html.html_safe
|
html.html_safe
|
||||||
end
|
end
|
||||||
|
|
||||||
def per_page_links(selected=nil)
|
def per_page_links(selected=nil, item_count=nil)
|
||||||
links = Setting.per_page_options_array.collect do |n|
|
values = Setting.per_page_options_array
|
||||||
|
if item_count && values.any?
|
||||||
|
if item_count > values.first
|
||||||
|
max = values.detect {|value| value >= item_count} || item_count
|
||||||
|
else
|
||||||
|
max = item_count
|
||||||
|
end
|
||||||
|
values = values.select {|value| value <= max || value == selected}
|
||||||
|
end
|
||||||
|
if values.empty? || (values.size == 1 && values.first == selected)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
links = values.collect do |n|
|
||||||
n == selected ? n : link_to_content_update(n, params.merge(:per_page => n))
|
n == selected ? n : link_to_content_update(n, params.merge(:per_page => n))
|
||||||
end
|
end
|
||||||
links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
|
l(:label_display_per_page, links.join(', '))
|
||||||
end
|
end
|
||||||
|
|
||||||
def reorder_links(name, url, method = :post)
|
def reorder_links(name, url, method = :post)
|
||||||
@@ -509,6 +527,8 @@ module ApplicationHelper
|
|||||||
project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
|
project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
|
||||||
only_path = options.delete(:only_path) == false ? false : true
|
only_path = options.delete(:only_path) == false ? false : true
|
||||||
|
|
||||||
|
text = text.dup
|
||||||
|
macros = catch_macros(text)
|
||||||
text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr)
|
text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr)
|
||||||
|
|
||||||
@parsed_headings = []
|
@parsed_headings = []
|
||||||
@@ -516,8 +536,8 @@ module ApplicationHelper
|
|||||||
@current_section = 0 if options[:edit_section_links]
|
@current_section = 0 if options[:edit_section_links]
|
||||||
|
|
||||||
parse_sections(text, project, obj, attr, only_path, options)
|
parse_sections(text, project, obj, attr, only_path, options)
|
||||||
text = parse_non_pre_blocks(text) do |text|
|
text = parse_non_pre_blocks(text, obj, macros) do |text|
|
||||||
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_macros].each do |method_name|
|
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links].each do |method_name|
|
||||||
send method_name, text, project, obj, attr, only_path, options
|
send method_name, text, project, obj, attr, only_path, options
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -530,7 +550,7 @@ module ApplicationHelper
|
|||||||
text.html_safe
|
text.html_safe
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse_non_pre_blocks(text)
|
def parse_non_pre_blocks(text, obj, macros)
|
||||||
s = StringScanner.new(text)
|
s = StringScanner.new(text)
|
||||||
tags = []
|
tags = []
|
||||||
parsed = ''
|
parsed = ''
|
||||||
@@ -539,6 +559,9 @@ module ApplicationHelper
|
|||||||
text, full_tag, closing, tag = s[1], s[2], s[3], s[4]
|
text, full_tag, closing, tag = s[1], s[2], s[3], s[4]
|
||||||
if tags.empty?
|
if tags.empty?
|
||||||
yield text
|
yield text
|
||||||
|
inject_macros(text, obj, macros) if macros.any?
|
||||||
|
else
|
||||||
|
inject_macros(text, obj, macros, false) if macros.any?
|
||||||
end
|
end
|
||||||
parsed << text
|
parsed << text
|
||||||
if tag
|
if tag
|
||||||
@@ -694,7 +717,7 @@ module ApplicationHelper
|
|||||||
oid = identifier.to_i
|
oid = identifier.to_i
|
||||||
case prefix
|
case prefix
|
||||||
when nil
|
when nil
|
||||||
if issue = Issue.visible.find_by_id(oid, :include => :status)
|
if oid.to_s == identifier && issue = Issue.visible.find_by_id(oid, :include => :status)
|
||||||
anchor = comment_id ? "note-#{comment_id}" : nil
|
anchor = comment_id ? "note-#{comment_id}" : nil
|
||||||
link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid, :anchor => anchor},
|
link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid, :anchor => anchor},
|
||||||
:class => issue.css_classes,
|
:class => issue.css_classes,
|
||||||
@@ -772,11 +795,10 @@ module ApplicationHelper
|
|||||||
if repository && User.current.allowed_to?(:browse_repository, project)
|
if repository && User.current.allowed_to?(:browse_repository, project)
|
||||||
name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
|
name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
|
||||||
path, rev, anchor = $1, $3, $5
|
path, rev, anchor = $1, $3, $5
|
||||||
link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => 'entry', :id => project, :repository_id => repository.identifier_param,
|
link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => (prefix == 'export' ? 'raw' : 'entry'), :id => project, :repository_id => repository.identifier_param,
|
||||||
:path => to_path_param(path),
|
:path => to_path_param(path),
|
||||||
:rev => rev,
|
:rev => rev,
|
||||||
:anchor => anchor,
|
:anchor => anchor},
|
||||||
:format => (prefix == 'export' ? 'raw' : nil)},
|
|
||||||
:class => (prefix == 'export' ? 'source download' : 'source')
|
:class => (prefix == 'export' ? 'source download' : 'source')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -799,7 +821,7 @@ module ApplicationHelper
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
HEADING_RE = /(<h(1|2|3|4)( [^>]+)?>(.+?)<\/h(1|2|3|4)>)/i unless const_defined?(:HEADING_RE)
|
HEADING_RE = /(<h(\d)( [^>]+)?>(.+?)<\/h(\d)>)/i unless const_defined?(:HEADING_RE)
|
||||||
|
|
||||||
def parse_sections(text, project, obj, attr, only_path, options)
|
def parse_sections(text, project, obj, attr, only_path, options)
|
||||||
return unless options[:edit_section_links]
|
return unless options[:edit_section_links]
|
||||||
@@ -838,31 +860,57 @@ module ApplicationHelper
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
MACROS_RE = /
|
MACROS_RE = /(
|
||||||
(!)? # escaping
|
(!)? # escaping
|
||||||
(
|
(
|
||||||
\{\{ # opening tag
|
\{\{ # opening tag
|
||||||
([\w]+) # macro name
|
([\w]+) # macro name
|
||||||
(\(([^\}]*)\))? # optional arguments
|
(\(([^\n\r]*?)\))? # optional arguments
|
||||||
|
([\n\r].*?[\n\r])? # optional block of text
|
||||||
\}\} # closing tag
|
\}\} # closing tag
|
||||||
)
|
)
|
||||||
/x unless const_defined?(:MACROS_RE)
|
)/mx unless const_defined?(:MACROS_RE)
|
||||||
|
|
||||||
# Macros substitution
|
MACRO_SUB_RE = /(
|
||||||
def parse_macros(text, project, obj, attr, only_path, options)
|
\{\{
|
||||||
|
macro\((\d+)\)
|
||||||
|
\}\}
|
||||||
|
)/x unless const_defined?(:MACRO_SUB_RE)
|
||||||
|
|
||||||
|
# Extracts macros from text
|
||||||
|
def catch_macros(text)
|
||||||
|
macros = {}
|
||||||
text.gsub!(MACROS_RE) do
|
text.gsub!(MACROS_RE) do
|
||||||
esc, all, macro = $1, $2, $3.downcase
|
all, macro = $1, $4.downcase
|
||||||
args = ($5 || '').split(',').each(&:strip)
|
if macro_exists?(macro) || all =~ MACRO_SUB_RE
|
||||||
if esc.nil?
|
index = macros.size
|
||||||
begin
|
macros[index] = all
|
||||||
exec_macro(macro, obj, args)
|
"{{macro(#{index})}}"
|
||||||
rescue => e
|
|
||||||
"<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
|
|
||||||
end || all
|
|
||||||
else
|
else
|
||||||
all
|
all
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
macros
|
||||||
|
end
|
||||||
|
|
||||||
|
# Executes and replaces macros in text
|
||||||
|
def inject_macros(text, obj, macros, execute=true)
|
||||||
|
text.gsub!(MACRO_SUB_RE) do
|
||||||
|
all, index = $1, $2.to_i
|
||||||
|
orig = macros.delete(index)
|
||||||
|
if execute && orig && orig =~ MACROS_RE
|
||||||
|
esc, all, macro, args, block = $2, $3, $4.downcase, $6.to_s, $7.try(:strip)
|
||||||
|
if esc.nil?
|
||||||
|
h(exec_macro(macro, obj, args, block) || all)
|
||||||
|
else
|
||||||
|
h(all)
|
||||||
|
end
|
||||||
|
elsif orig
|
||||||
|
h(orig)
|
||||||
|
else
|
||||||
|
h(all)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
|
TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
|
||||||
@@ -870,6 +918,8 @@ module ApplicationHelper
|
|||||||
# Renders the TOC with given headings
|
# Renders the TOC with given headings
|
||||||
def replace_toc(text, headings)
|
def replace_toc(text, headings)
|
||||||
text.gsub!(TOC_RE) do
|
text.gsub!(TOC_RE) do
|
||||||
|
# Keep only the 4 first levels
|
||||||
|
headings = headings.select{|level, anchor, item| level <= 4}
|
||||||
if headings.empty?
|
if headings.empty?
|
||||||
''
|
''
|
||||||
else
|
else
|
||||||
@@ -917,19 +967,12 @@ module ApplicationHelper
|
|||||||
content_tag("label", label_text)
|
content_tag("label", label_text)
|
||||||
end
|
end
|
||||||
|
|
||||||
def labelled_tabular_form_for(*args, &proc)
|
|
||||||
ActiveSupport::Deprecation.warn "ApplicationHelper#labelled_tabular_form_for is deprecated and will be removed in Redmine 1.5. Use #labelled_form_for instead."
|
|
||||||
args << {} unless args.last.is_a?(Hash)
|
|
||||||
options = args.last
|
|
||||||
options[:html] ||= {}
|
|
||||||
options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
|
|
||||||
options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
|
|
||||||
form_for(*args, &proc)
|
|
||||||
end
|
|
||||||
|
|
||||||
def labelled_form_for(*args, &proc)
|
def labelled_form_for(*args, &proc)
|
||||||
args << {} unless args.last.is_a?(Hash)
|
args << {} unless args.last.is_a?(Hash)
|
||||||
options = args.last
|
options = args.last
|
||||||
|
if args.first.is_a?(Symbol)
|
||||||
|
options.merge!(:as => args.shift)
|
||||||
|
end
|
||||||
options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
|
options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
|
||||||
form_for(*args, &proc)
|
form_for(*args, &proc)
|
||||||
end
|
end
|
||||||
@@ -942,10 +985,11 @@ module ApplicationHelper
|
|||||||
end
|
end
|
||||||
|
|
||||||
def labelled_remote_form_for(*args, &proc)
|
def labelled_remote_form_for(*args, &proc)
|
||||||
|
ActiveSupport::Deprecation.warn "ApplicationHelper#labelled_remote_form_for is deprecated and will be removed in Redmine 2.2."
|
||||||
args << {} unless args.last.is_a?(Hash)
|
args << {} unless args.last.is_a?(Hash)
|
||||||
options = args.last
|
options = args.last
|
||||||
options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
|
options.merge!({:builder => Redmine::Views::LabelledFormBuilder, :remote => true})
|
||||||
remote_form_for(*args, &proc)
|
form_for(*args, &proc)
|
||||||
end
|
end
|
||||||
|
|
||||||
def error_messages_for(*objects)
|
def error_messages_for(*objects)
|
||||||
@@ -962,10 +1006,44 @@ module ApplicationHelper
|
|||||||
html.html_safe
|
html.html_safe
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def delete_link(url, options={})
|
||||||
|
options = {
|
||||||
|
:method => :delete,
|
||||||
|
:data => {:confirm => l(:text_are_you_sure)},
|
||||||
|
:class => 'icon icon-del'
|
||||||
|
}.merge(options)
|
||||||
|
|
||||||
|
link_to l(:button_delete), url, options
|
||||||
|
end
|
||||||
|
|
||||||
|
def preview_link(url, form, target='preview', options={})
|
||||||
|
content_tag 'a', l(:label_preview), {
|
||||||
|
:href => "#",
|
||||||
|
:onclick => %|submitPreview("#{escape_javascript url_for(url)}", "#{escape_javascript form}", "#{escape_javascript target}"); return false;|,
|
||||||
|
:accesskey => accesskey(:preview)
|
||||||
|
}.merge(options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def link_to_function(name, function, html_options={})
|
||||||
|
content_tag(:a, name, {:href => '#', :onclick => "#{function}; return false;"}.merge(html_options))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Helper to render JSON in views
|
||||||
|
def raw_json(arg)
|
||||||
|
arg.to_json.to_s.gsub('/', '\/').html_safe
|
||||||
|
end
|
||||||
|
|
||||||
|
def back_url
|
||||||
|
url = params[:back_url]
|
||||||
|
if url.nil? && referer = request.env['HTTP_REFERER']
|
||||||
|
url = CGI.unescape(referer.to_s)
|
||||||
|
end
|
||||||
|
url
|
||||||
|
end
|
||||||
|
|
||||||
def back_url_hidden_field_tag
|
def back_url_hidden_field_tag
|
||||||
back_url = params[:back_url] || request.env['HTTP_REFERER']
|
url = back_url
|
||||||
back_url = CGI.unescape(back_url.to_s)
|
hidden_field_tag('back_url', url, :id => nil) unless url.blank?
|
||||||
hidden_field_tag('back_url', CGI.escape(back_url), :id => nil) unless back_url.blank?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_all_links(form_name)
|
def check_all_links(form_name)
|
||||||
@@ -1009,39 +1087,91 @@ module ApplicationHelper
|
|||||||
end
|
end
|
||||||
@context_menu_included = true
|
@context_menu_included = true
|
||||||
end
|
end
|
||||||
javascript_tag "new ContextMenu('#{ url_for(url) }')"
|
javascript_tag "contextMenuInit('#{ url_for(url) }')"
|
||||||
end
|
end
|
||||||
|
|
||||||
def calendar_for(field_id)
|
def calendar_for(field_id)
|
||||||
include_calendar_headers_tags
|
include_calendar_headers_tags
|
||||||
image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
|
javascript_tag("$(function() { $('##{field_id}').datepicker(datepickerOptions); });")
|
||||||
javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def include_calendar_headers_tags
|
def include_calendar_headers_tags
|
||||||
unless @calendar_headers_tags_included
|
unless @calendar_headers_tags_included
|
||||||
@calendar_headers_tags_included = true
|
@calendar_headers_tags_included = true
|
||||||
content_for :header_tags do
|
content_for :header_tags do
|
||||||
start_of_week = case Setting.start_of_week.to_i
|
start_of_week = Setting.start_of_week
|
||||||
when 1
|
start_of_week = l(:general_first_day_of_week, :default => '1') if start_of_week.blank?
|
||||||
'Calendar._FD = 1;' # Monday
|
# Redmine uses 1..7 (monday..sunday) in settings and locales
|
||||||
when 7
|
# JQuery uses 0..6 (sunday..saturday), 7 needs to be changed to 0
|
||||||
'Calendar._FD = 0;' # Sunday
|
start_of_week = start_of_week.to_i % 7
|
||||||
when 6
|
|
||||||
'Calendar._FD = 6;' # Saturday
|
|
||||||
else
|
|
||||||
'' # use language
|
|
||||||
end
|
|
||||||
|
|
||||||
javascript_include_tag('calendar/calendar') +
|
tags = javascript_tag(
|
||||||
javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
|
"var datepickerOptions={dateFormat: 'yy-mm-dd', firstDay: #{start_of_week}, " +
|
||||||
javascript_tag(start_of_week) +
|
"showOn: 'button', buttonImageOnly: true, buttonImage: '" +
|
||||||
javascript_include_tag('calendar/calendar-setup') +
|
path_to_image('/images/calendar.png') +
|
||||||
stylesheet_link_tag('calendar')
|
"', showButtonPanel: true};")
|
||||||
|
jquery_locale = l('jquery.locale', :default => current_language.to_s)
|
||||||
|
unless jquery_locale == 'en'
|
||||||
|
tags << javascript_include_tag("i18n/jquery.ui.datepicker-#{jquery_locale}.js")
|
||||||
|
end
|
||||||
|
tags
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Overrides Rails' stylesheet_link_tag with themes and plugins support.
|
||||||
|
# Examples:
|
||||||
|
# stylesheet_link_tag('styles') # => picks styles.css from the current theme or defaults
|
||||||
|
# stylesheet_link_tag('styles', :plugin => 'foo) # => picks styles.css from plugin's assets
|
||||||
|
#
|
||||||
|
def stylesheet_link_tag(*sources)
|
||||||
|
options = sources.last.is_a?(Hash) ? sources.pop : {}
|
||||||
|
plugin = options.delete(:plugin)
|
||||||
|
sources = sources.map do |source|
|
||||||
|
if plugin
|
||||||
|
"/plugin_assets/#{plugin}/stylesheets/#{source}"
|
||||||
|
elsif current_theme && current_theme.stylesheets.include?(source)
|
||||||
|
current_theme.stylesheet_path(source)
|
||||||
|
else
|
||||||
|
source
|
||||||
|
end
|
||||||
|
end
|
||||||
|
super sources, options
|
||||||
|
end
|
||||||
|
|
||||||
|
# Overrides Rails' image_tag with themes and plugins support.
|
||||||
|
# Examples:
|
||||||
|
# image_tag('image.png') # => picks image.png from the current theme or defaults
|
||||||
|
# image_tag('image.png', :plugin => 'foo) # => picks image.png from plugin's assets
|
||||||
|
#
|
||||||
|
def image_tag(source, options={})
|
||||||
|
if plugin = options.delete(:plugin)
|
||||||
|
source = "/plugin_assets/#{plugin}/images/#{source}"
|
||||||
|
elsif current_theme && current_theme.images.include?(source)
|
||||||
|
source = current_theme.image_path(source)
|
||||||
|
end
|
||||||
|
super source, options
|
||||||
|
end
|
||||||
|
|
||||||
|
# Overrides Rails' javascript_include_tag with plugins support
|
||||||
|
# Examples:
|
||||||
|
# javascript_include_tag('scripts') # => picks scripts.js from defaults
|
||||||
|
# javascript_include_tag('scripts', :plugin => 'foo) # => picks scripts.js from plugin's assets
|
||||||
|
#
|
||||||
|
def javascript_include_tag(*sources)
|
||||||
|
options = sources.last.is_a?(Hash) ? sources.pop : {}
|
||||||
|
if plugin = options.delete(:plugin)
|
||||||
|
sources = sources.map do |source|
|
||||||
|
if plugin
|
||||||
|
"/plugin_assets/#{plugin}/javascripts/#{source}"
|
||||||
|
else
|
||||||
|
source
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
super sources, options
|
||||||
|
end
|
||||||
|
|
||||||
def content_for(name, content = nil, &block)
|
def content_for(name, content = nil, &block)
|
||||||
@has_content ||= {}
|
@has_content ||= {}
|
||||||
@has_content[name] = true
|
@has_content[name] = true
|
||||||
@@ -1052,6 +1182,14 @@ module ApplicationHelper
|
|||||||
(@has_content && @has_content[name]) || false
|
(@has_content && @has_content[name]) || false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sidebar_content?
|
||||||
|
has_content?(:sidebar) || view_layouts_base_sidebar_hook_response.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def view_layouts_base_sidebar_hook_response
|
||||||
|
@view_layouts_base_sidebar_hook_response ||= call_hook(:view_layouts_base_sidebar)
|
||||||
|
end
|
||||||
|
|
||||||
def email_delivery_enabled?
|
def email_delivery_enabled?
|
||||||
!!ActionMailer::Base.perform_deliveries
|
!!ActionMailer::Base.perform_deliveries
|
||||||
end
|
end
|
||||||
@@ -1060,7 +1198,7 @@ module ApplicationHelper
|
|||||||
# +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
|
# +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
|
||||||
def avatar(user, options = { })
|
def avatar(user, options = { })
|
||||||
if Setting.gravatar_enabled?
|
if Setting.gravatar_enabled?
|
||||||
options.merge!({:ssl => (defined?(request) && request.ssl?), :default => Setting.gravatar_default})
|
options.merge!({:ssl => (request && request.ssl?), :default => Setting.gravatar_default})
|
||||||
email = nil
|
email = nil
|
||||||
if user.respond_to?(:mail)
|
if user.respond_to?(:mail)
|
||||||
email = user.mail
|
email = user.mail
|
||||||
@@ -1074,14 +1212,19 @@ module ApplicationHelper
|
|||||||
end
|
end
|
||||||
|
|
||||||
def sanitize_anchor_name(anchor)
|
def sanitize_anchor_name(anchor)
|
||||||
anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
|
if ''.respond_to?(:encoding) || RUBY_PLATFORM == 'java'
|
||||||
|
anchor.gsub(%r{[^\p{Word}\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
|
||||||
|
else
|
||||||
|
# TODO: remove when ruby1.8 is no longer supported
|
||||||
|
anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the javascript tags that are included in the html layout head
|
# Returns the javascript tags that are included in the html layout head
|
||||||
def javascript_heads
|
def javascript_heads
|
||||||
tags = javascript_include_tag(:defaults)
|
tags = javascript_include_tag('jquery-1.7.2-ui-1.8.21-ujs-2.0.3', 'application')
|
||||||
unless User.current.pref.warn_on_leaving_unsaved == '0'
|
unless User.current.pref.warn_on_leaving_unsaved == '0'
|
||||||
tags << "\n".html_safe + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
|
tags << "\n".html_safe + javascript_tag("$(window).load(function(){ warnLeavingUnsaved('#{escape_javascript l(:text_warn_on_leaving_unsaved)}'); });")
|
||||||
end
|
end
|
||||||
tags
|
tags
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -21,12 +21,14 @@ module AttachmentsHelper
|
|||||||
# Displays view/delete links to the attachments of the given object
|
# Displays view/delete links to the attachments of the given object
|
||||||
# Options:
|
# Options:
|
||||||
# :author -- author names are not displayed if set to false
|
# :author -- author names are not displayed if set to false
|
||||||
|
# :thumbails -- display thumbnails if enabled in settings
|
||||||
def link_to_attachments(container, options = {})
|
def link_to_attachments(container, options = {})
|
||||||
options.assert_valid_keys(:author)
|
options.assert_valid_keys(:author, :thumbnails)
|
||||||
|
|
||||||
if container.attachments.any?
|
if container.attachments.any?
|
||||||
options = {:deletable => container.attachments_deletable?, :author => true}.merge(options)
|
options = {:deletable => container.attachments_deletable?, :author => true}.merge(options)
|
||||||
render :partial => 'attachments/links', :locals => {:attachments => container.attachments, :options => options}
|
render :partial => 'attachments/links',
|
||||||
|
:locals => {:attachments => container.attachments, :options => options, :thumbnails => (options[:thumbnails] && Setting.thumbnails_enabled?)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -18,4 +18,24 @@
|
|||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
module BoardsHelper
|
module BoardsHelper
|
||||||
|
def board_breadcrumb(item)
|
||||||
|
board = item.is_a?(Message) ? item.board : item
|
||||||
|
links = [link_to(l(:label_board_plural), project_boards_path(item.project))]
|
||||||
|
boards = board.ancestors.reverse
|
||||||
|
if item.is_a?(Message)
|
||||||
|
boards << board
|
||||||
|
end
|
||||||
|
links += boards.map {|ancestor| link_to(h(ancestor.name), project_board_path(ancestor.project, ancestor))}
|
||||||
|
breadcrumb links
|
||||||
|
end
|
||||||
|
|
||||||
|
def boards_options_for_select(boards)
|
||||||
|
options = []
|
||||||
|
Board.board_tree(boards) do |board, level|
|
||||||
|
label = (level > 0 ? ' ' * 2 * level + '» ' : '').html_safe
|
||||||
|
label << board.name
|
||||||
|
options << [label, board.id]
|
||||||
|
end
|
||||||
|
options
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ module ContextMenusHelper
|
|||||||
end
|
end
|
||||||
if options.delete(:disabled)
|
if options.delete(:disabled)
|
||||||
options.delete(:method)
|
options.delete(:method)
|
||||||
options.delete(:confirm)
|
options.delete(:data)
|
||||||
options.delete(:onclick)
|
options[:onclick] = 'return false;'
|
||||||
options[:class] << ' disabled'
|
options[:class] << ' disabled'
|
||||||
url = '#'
|
url = '#'
|
||||||
end
|
end
|
||||||
@@ -36,7 +36,7 @@ module ContextMenusHelper
|
|||||||
|
|
||||||
def bulk_update_custom_field_context_menu_link(field, text, value)
|
def bulk_update_custom_field_context_menu_link(field, text, value)
|
||||||
context_menu_link h(text),
|
context_menu_link h(text),
|
||||||
{:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'custom_field_values' => {field.id => value}}, :back_url => @back},
|
{:controller => 'issues', :action => 'bulk_update', :ids => @issue_ids, :issue => {'custom_field_values' => {field.id => value}}, :back_url => @back},
|
||||||
:method => :post,
|
:method => :post,
|
||||||
:selected => (@issue && @issue.custom_field_value(field) == value)
|
:selected => (@issue && @issue.custom_field_value(field) == value)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -39,72 +39,79 @@ module CustomFieldsHelper
|
|||||||
field_name << "[]" if custom_field.multiple?
|
field_name << "[]" if custom_field.multiple?
|
||||||
field_id = "#{name}_custom_field_values_#{custom_field.id}"
|
field_id = "#{name}_custom_field_values_#{custom_field.id}"
|
||||||
|
|
||||||
|
tag_options = {:id => field_id, :class => "#{custom_field.field_format}_cf"}
|
||||||
|
|
||||||
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
|
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
|
||||||
case field_format.try(:edit_as)
|
case field_format.try(:edit_as)
|
||||||
when "date"
|
when "date"
|
||||||
text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
|
text_field_tag(field_name, custom_value.value, tag_options.merge(:size => 10)) +
|
||||||
calendar_for(field_id)
|
calendar_for(field_id)
|
||||||
when "text"
|
when "text"
|
||||||
text_area_tag(field_name, custom_value.value, :id => field_id, :rows => 3, :style => 'width:90%')
|
text_area_tag(field_name, custom_value.value, tag_options.merge(:rows => 3))
|
||||||
when "bool"
|
when "bool"
|
||||||
hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, :id => field_id)
|
hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, tag_options)
|
||||||
when "list"
|
when "list"
|
||||||
blank_option = ''
|
blank_option = ''.html_safe
|
||||||
unless custom_field.multiple?
|
unless custom_field.multiple?
|
||||||
if custom_field.is_required?
|
if custom_field.is_required?
|
||||||
unless custom_field.default_value.present?
|
unless custom_field.default_value.present?
|
||||||
blank_option = "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>"
|
blank_option = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---", :value => '')
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
blank_option = '<option></option>'
|
blank_option = content_tag('option')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
s = select_tag(field_name, blank_option.html_safe + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value),
|
s = select_tag(field_name, blank_option + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value),
|
||||||
:id => field_id, :multiple => custom_field.multiple?)
|
tag_options.merge(:multiple => custom_field.multiple?))
|
||||||
if custom_field.multiple?
|
if custom_field.multiple?
|
||||||
s << hidden_field_tag(field_name, '')
|
s << hidden_field_tag(field_name, '')
|
||||||
end
|
end
|
||||||
s
|
s
|
||||||
else
|
else
|
||||||
text_field_tag(field_name, custom_value.value, :id => field_id)
|
text_field_tag(field_name, custom_value.value, tag_options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return custom field label tag
|
# Return custom field label tag
|
||||||
def custom_field_label_tag(name, custom_value)
|
def custom_field_label_tag(name, custom_value, options={})
|
||||||
|
required = options[:required] || custom_value.custom_field.is_required?
|
||||||
|
|
||||||
content_tag "label", h(custom_value.custom_field.name) +
|
content_tag "label", h(custom_value.custom_field.name) +
|
||||||
(custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>".html_safe : ""),
|
(required ? " <span class=\"required\">*</span>".html_safe : ""),
|
||||||
:for => "#{name}_custom_field_values_#{custom_value.custom_field.id}"
|
:for => "#{name}_custom_field_values_#{custom_value.custom_field.id}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return custom field tag with its label tag
|
# Return custom field tag with its label tag
|
||||||
def custom_field_tag_with_label(name, custom_value)
|
def custom_field_tag_with_label(name, custom_value, options={})
|
||||||
custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
|
custom_field_label_tag(name, custom_value, options) + custom_field_tag(name, custom_value)
|
||||||
end
|
end
|
||||||
|
|
||||||
def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)
|
def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)
|
||||||
field_name = "#{name}[custom_field_values][#{custom_field.id}]"
|
field_name = "#{name}[custom_field_values][#{custom_field.id}]"
|
||||||
field_name << "[]" if custom_field.multiple?
|
field_name << "[]" if custom_field.multiple?
|
||||||
field_id = "#{name}_custom_field_values_#{custom_field.id}"
|
field_id = "#{name}_custom_field_values_#{custom_field.id}"
|
||||||
|
|
||||||
|
tag_options = {:id => field_id, :class => "#{custom_field.field_format}_cf"}
|
||||||
|
|
||||||
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
|
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
|
||||||
case field_format.try(:edit_as)
|
case field_format.try(:edit_as)
|
||||||
when "date"
|
when "date"
|
||||||
text_field_tag(field_name, '', :id => field_id, :size => 10) +
|
text_field_tag(field_name, '', tag_options.merge(:size => 10)) +
|
||||||
calendar_for(field_id)
|
calendar_for(field_id)
|
||||||
when "text"
|
when "text"
|
||||||
text_area_tag(field_name, '', :id => field_id, :rows => 3, :style => 'width:90%')
|
text_area_tag(field_name, '', tag_options.merge(:rows => 3))
|
||||||
when "bool"
|
when "bool"
|
||||||
select_tag(field_name, options_for_select([[l(:label_no_change_option), ''],
|
select_tag(field_name, options_for_select([[l(:label_no_change_option), ''],
|
||||||
[l(:general_text_yes), '1'],
|
[l(:general_text_yes), '1'],
|
||||||
[l(:general_text_no), '0']]), :id => field_id)
|
[l(:general_text_no), '0']]), tag_options)
|
||||||
when "list"
|
when "list"
|
||||||
options = []
|
options = []
|
||||||
options << [l(:label_no_change_option), ''] unless custom_field.multiple?
|
options << [l(:label_no_change_option), ''] unless custom_field.multiple?
|
||||||
|
options << [l(:label_none), '__none__'] unless custom_field.is_required?
|
||||||
options += custom_field.possible_values_options(projects)
|
options += custom_field.possible_values_options(projects)
|
||||||
select_tag(field_name, options_for_select(options),
|
select_tag(field_name, options_for_select(options), tag_options.merge(:multiple => custom_field.multiple?))
|
||||||
:id => field_id, :multiple => custom_field.multiple?)
|
|
||||||
else
|
else
|
||||||
text_field_tag(field_name, '', :id => field_id)
|
text_field_tag(field_name, '', tag_options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -92,6 +92,48 @@ module IssuesHelper
|
|||||||
s.html_safe
|
s.html_safe
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class IssueFieldsRows
|
||||||
|
include ActionView::Helpers::TagHelper
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@left = []
|
||||||
|
@right = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def left(*args)
|
||||||
|
args.any? ? @left << cells(*args) : @left
|
||||||
|
end
|
||||||
|
|
||||||
|
def right(*args)
|
||||||
|
args.any? ? @right << cells(*args) : @right
|
||||||
|
end
|
||||||
|
|
||||||
|
def size
|
||||||
|
@left.size > @right.size ? @left.size : @right.size
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_html
|
||||||
|
html = ''.html_safe
|
||||||
|
blank = content_tag('th', '') + content_tag('td', '')
|
||||||
|
size.times do |i|
|
||||||
|
left = @left[i] || blank
|
||||||
|
right = @right[i] || blank
|
||||||
|
html << content_tag('tr', left + right)
|
||||||
|
end
|
||||||
|
html
|
||||||
|
end
|
||||||
|
|
||||||
|
def cells(label, text, options={})
|
||||||
|
content_tag('th', "#{label}:", options) + content_tag('td', text, options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def issue_fields_rows
|
||||||
|
r = IssueFieldsRows.new
|
||||||
|
yield r
|
||||||
|
r.to_html
|
||||||
|
end
|
||||||
|
|
||||||
def render_custom_fields_rows(issue)
|
def render_custom_fields_rows(issue)
|
||||||
return if issue.custom_field_values.empty?
|
return if issue.custom_field_values.empty?
|
||||||
ordered_values = []
|
ordered_values = []
|
||||||
@@ -248,7 +290,7 @@ module IssuesHelper
|
|||||||
unless no_html
|
unless no_html
|
||||||
label = content_tag('strong', label)
|
label = content_tag('strong', label)
|
||||||
old_value = content_tag("i", h(old_value)) if detail.old_value
|
old_value = content_tag("i", h(old_value)) if detail.old_value
|
||||||
old_value = content_tag("strike", old_value) if detail.old_value and detail.value.blank?
|
old_value = content_tag("del", old_value) if detail.old_value and detail.value.blank?
|
||||||
if detail.property == 'attachment' && !value.blank? && atta = Attachment.find_by_id(detail.prop_key)
|
if detail.property == 'attachment' && !value.blank? && atta = Attachment.find_by_id(detail.prop_key)
|
||||||
# Link to the attachment if it has not been removed
|
# Link to the attachment if it has not been removed
|
||||||
value = link_to_attachment(atta, :download => true, :only_path => options[:only_path])
|
value = link_to_attachment(atta, :download => true, :only_path => options[:only_path])
|
||||||
@@ -332,7 +374,7 @@ module IssuesHelper
|
|||||||
cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id}
|
cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id}
|
||||||
show_value(cv)
|
show_value(cv)
|
||||||
else
|
else
|
||||||
value = issue.send(column.name)
|
value = column.value(issue)
|
||||||
if value.is_a?(Date)
|
if value.is_a?(Date)
|
||||||
format_date(value)
|
format_date(value)
|
||||||
elsif value.is_a?(Time)
|
elsif value.is_a?(Time)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -23,11 +23,13 @@ module JournalsHelper
|
|||||||
editable = User.current.logged? && (User.current.allowed_to?(:edit_issue_notes, issue.project) || (journal.user == User.current && User.current.allowed_to?(:edit_own_issue_notes, issue.project)))
|
editable = User.current.logged? && (User.current.allowed_to?(:edit_issue_notes, issue.project) || (journal.user == User.current && User.current.allowed_to?(:edit_own_issue_notes, issue.project)))
|
||||||
links = []
|
links = []
|
||||||
if !journal.notes.blank?
|
if !journal.notes.blank?
|
||||||
links << link_to_remote(image_tag('comment.png'),
|
links << link_to(image_tag('comment.png'),
|
||||||
{ :url => {:controller => 'journals', :action => 'new', :id => issue, :journal_id => journal} },
|
{:controller => 'journals', :action => 'new', :id => issue, :journal_id => journal},
|
||||||
:title => l(:button_quote)) if options[:reply_links]
|
:remote => true,
|
||||||
|
:method => 'post',
|
||||||
|
:title => l(:button_quote)) if options[:reply_links]
|
||||||
links << link_to_in_place_notes_editor(image_tag('edit.png'), "journal-#{journal.id}-notes",
|
links << link_to_in_place_notes_editor(image_tag('edit.png'), "journal-#{journal.id}-notes",
|
||||||
{ :controller => 'journals', :action => 'edit', :id => journal },
|
{ :controller => 'journals', :action => 'edit', :id => journal, :format => 'js' },
|
||||||
:title => l(:button_edit)) if editable
|
:title => l(:button_edit)) if editable
|
||||||
end
|
end
|
||||||
content << content_tag('div', links.join(' ').html_safe, :class => 'contextual') unless links.empty?
|
content << content_tag('div', links.join(' ').html_safe, :class => 'contextual') unless links.empty?
|
||||||
@@ -38,7 +40,7 @@ module JournalsHelper
|
|||||||
end
|
end
|
||||||
|
|
||||||
def link_to_in_place_notes_editor(text, field_id, url, options={})
|
def link_to_in_place_notes_editor(text, field_id, url, options={})
|
||||||
onclick = "new Ajax.Request('#{url_for(url)}', {asynchronous:true, evalScripts:true, method:'get'}); return false;"
|
onclick = "$.ajax({url: '#{url_for(url)}', type: 'get'}); return false;"
|
||||||
link_to text, '#', options.merge(:onclick => onclick)
|
link_to text, '#', options.merge(:onclick => onclick)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -51,38 +51,15 @@ module ProjectsHelper
|
|||||||
content_tag('select', options.html_safe, :name => 'project[parent_id]', :id => 'project_parent_id')
|
content_tag('select', options.html_safe, :name => 'project[parent_id]', :id => 'project_parent_id')
|
||||||
end
|
end
|
||||||
|
|
||||||
# Renders a tree of projects as a nested set of unordered lists
|
# Renders the projects index
|
||||||
# The given collection may be a subset of the whole project tree
|
|
||||||
# (eg. some intermediate nodes are private and can not be seen)
|
|
||||||
def render_project_hierarchy(projects)
|
def render_project_hierarchy(projects)
|
||||||
s = ''
|
render_project_nested_lists(projects) do |project|
|
||||||
if projects.any?
|
s = link_to_project(project, {}, :class => "#{project.css_classes} #{User.current.member_of?(project) ? 'my-project' : nil}")
|
||||||
ancestors = []
|
if project.description.present?
|
||||||
original_project = @project
|
s << content_tag('div', textilizable(project.short_description, :project => project), :class => 'wiki description')
|
||||||
projects.each do |project|
|
|
||||||
# set the project environment to please macros.
|
|
||||||
@project = project
|
|
||||||
if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
|
|
||||||
s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n"
|
|
||||||
else
|
|
||||||
ancestors.pop
|
|
||||||
s << "</li>"
|
|
||||||
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
|
|
||||||
ancestors.pop
|
|
||||||
s << "</ul></li>\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
classes = (ancestors.empty? ? 'root' : 'child')
|
|
||||||
s << "<li class='#{classes}'><div class='#{classes}'>" +
|
|
||||||
link_to_project(project, {}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}")
|
|
||||||
s << "<div class='wiki description'>#{textilizable(project.short_description, :project => project)}</div>" unless project.description.blank?
|
|
||||||
s << "</div>\n"
|
|
||||||
ancestors << project
|
|
||||||
end
|
end
|
||||||
s << ("</li></ul>\n" * ancestors.size)
|
s
|
||||||
@project = original_project
|
|
||||||
end
|
end
|
||||||
s.html_safe
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a set of options for a select field, grouped by project.
|
# Returns a set of options for a select field, grouped by project.
|
||||||
@@ -91,10 +68,6 @@ module ProjectsHelper
|
|||||||
versions.each do |version|
|
versions.each do |version|
|
||||||
grouped[version.project.name] << [version.name, version.id]
|
grouped[version.project.name] << [version.name, version.id]
|
||||||
end
|
end
|
||||||
# Add in the selected
|
|
||||||
if selected && !versions.include?(selected)
|
|
||||||
grouped[selected.project.name] << [selected.name, selected.id]
|
|
||||||
end
|
|
||||||
|
|
||||||
if grouped.keys.size > 1
|
if grouped.keys.size > 1
|
||||||
grouped_options_for_select(grouped, selected && selected.id)
|
grouped_options_for_select(grouped, selected && selected.id)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -18,9 +18,12 @@
|
|||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
module QueriesHelper
|
module QueriesHelper
|
||||||
|
def filters_options_for_select(query)
|
||||||
def operators_for_select(filter_type)
|
options = [[]]
|
||||||
Query.operators_by_filter_type[filter_type].collect {|o| [l(Query.operators[o]), o]}
|
options += query.available_filters.sort {|a,b| a[1][:order] <=> b[1][:order]}.map do |field, field_options|
|
||||||
|
[field_options[:name], field]
|
||||||
|
end
|
||||||
|
options_for_select(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def column_header(column)
|
def column_header(column)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -46,17 +46,17 @@ module RepositoriesHelper
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render_changeset_changes
|
def render_changeset_changes
|
||||||
changes = @changeset.changes.find(:all, :limit => 1000, :order => 'path').collect do |change|
|
changes = @changeset.filechanges.find(:all, :limit => 1000, :order => 'path').collect do |change|
|
||||||
case change.action
|
case change.action
|
||||||
when 'A'
|
when 'A'
|
||||||
# Detects moved/copied files
|
# Detects moved/copied files
|
||||||
if !change.from_path.blank?
|
if !change.from_path.blank?
|
||||||
change.action =
|
change.action =
|
||||||
@changeset.changes.detect {|c| c.action == 'D' && c.path == change.from_path} ? 'R' : 'C'
|
@changeset.filechanges.detect {|c| c.action == 'D' && c.path == change.from_path} ? 'R' : 'C'
|
||||||
end
|
end
|
||||||
change
|
change
|
||||||
when 'D'
|
when 'D'
|
||||||
@changeset.changes.detect {|c| c.from_path == change.path} ? nil : change
|
@changeset.filechanges.detect {|c| c.from_path == change.path} ? nil : change
|
||||||
else
|
else
|
||||||
change
|
change
|
||||||
end
|
end
|
||||||
@@ -141,12 +141,7 @@ module RepositoriesHelper
|
|||||||
select_tag('repository_scm',
|
select_tag('repository_scm',
|
||||||
options_for_select(scm_options, repository.class.name.demodulize),
|
options_for_select(scm_options, repository.class.name.demodulize),
|
||||||
:disabled => (repository && !repository.new_record?),
|
:disabled => (repository && !repository.new_record?),
|
||||||
:onchange => remote_function(
|
:data => {:remote => true, :method => 'get'})
|
||||||
:url => new_project_repository_path(@project),
|
|
||||||
:method => :get,
|
|
||||||
:update => 'content',
|
|
||||||
:with => "Form.serialize(this.form)")
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_leading_slash(path)
|
def with_leading_slash(path)
|
||||||
@@ -159,7 +154,7 @@ module RepositoriesHelper
|
|||||||
|
|
||||||
def subversion_field_tags(form, repository)
|
def subversion_field_tags(form, repository)
|
||||||
content_tag('p', form.text_field(:url, :size => 60, :required => true,
|
content_tag('p', form.text_field(:url, :size => 60, :required => true,
|
||||||
:disabled => (repository && !repository.root_url.blank?)) +
|
:disabled => !repository.safe_attribute?('url')) +
|
||||||
'<br />'.html_safe +
|
'<br />'.html_safe +
|
||||||
'(file:///, http://, https://, svn://, svn+[tunnelscheme]://)') +
|
'(file:///, http://, https://, svn://, svn+[tunnelscheme]://)') +
|
||||||
content_tag('p', form.text_field(:login, :size => 30)) +
|
content_tag('p', form.text_field(:login, :size => 30)) +
|
||||||
@@ -174,7 +169,7 @@ module RepositoriesHelper
|
|||||||
content_tag('p', form.text_field(
|
content_tag('p', form.text_field(
|
||||||
:url, :label => l(:field_path_to_repository),
|
:url, :label => l(:field_path_to_repository),
|
||||||
:size => 60, :required => true,
|
:size => 60, :required => true,
|
||||||
:disabled => (repository && !repository.new_record?))) +
|
:disabled => !repository.safe_attribute?('url'))) +
|
||||||
content_tag('p', form.select(
|
content_tag('p', form.select(
|
||||||
:log_encoding, [nil] + Setting::ENCODINGS,
|
:log_encoding, [nil] + Setting::ENCODINGS,
|
||||||
:label => l(:field_commit_logs_encoding), :required => true))
|
:label => l(:field_commit_logs_encoding), :required => true))
|
||||||
@@ -184,7 +179,7 @@ module RepositoriesHelper
|
|||||||
content_tag('p', form.text_field(
|
content_tag('p', form.text_field(
|
||||||
:url, :label => l(:field_path_to_repository),
|
:url, :label => l(:field_path_to_repository),
|
||||||
:size => 60, :required => true,
|
:size => 60, :required => true,
|
||||||
:disabled => (repository && !repository.root_url.blank?)
|
:disabled => !repository.safe_attribute?('url')
|
||||||
) +
|
) +
|
||||||
'<br />'.html_safe + l(:text_mercurial_repository_note)) +
|
'<br />'.html_safe + l(:text_mercurial_repository_note)) +
|
||||||
content_tag('p', form.select(
|
content_tag('p', form.select(
|
||||||
@@ -198,7 +193,7 @@ module RepositoriesHelper
|
|||||||
content_tag('p', form.text_field(
|
content_tag('p', form.text_field(
|
||||||
:url, :label => l(:field_path_to_repository),
|
:url, :label => l(:field_path_to_repository),
|
||||||
:size => 60, :required => true,
|
:size => 60, :required => true,
|
||||||
:disabled => (repository && !repository.root_url.blank?)
|
:disabled => !repository.safe_attribute?('url')
|
||||||
) +
|
) +
|
||||||
'<br />'.html_safe +
|
'<br />'.html_safe +
|
||||||
l(:text_git_repository_note)) +
|
l(:text_git_repository_note)) +
|
||||||
@@ -218,12 +213,12 @@ module RepositoriesHelper
|
|||||||
:root_url,
|
:root_url,
|
||||||
:label => l(:field_cvsroot),
|
:label => l(:field_cvsroot),
|
||||||
:size => 60, :required => true,
|
:size => 60, :required => true,
|
||||||
:disabled => !repository.new_record?)) +
|
:disabled => !repository.safe_attribute?('root_url'))) +
|
||||||
content_tag('p', form.text_field(
|
content_tag('p', form.text_field(
|
||||||
:url,
|
:url,
|
||||||
:label => l(:field_cvs_module),
|
:label => l(:field_cvs_module),
|
||||||
:size => 30, :required => true,
|
:size => 30, :required => true,
|
||||||
:disabled => !repository.new_record?)) +
|
:disabled => !repository.safe_attribute?('url'))) +
|
||||||
content_tag('p', form.select(
|
content_tag('p', form.select(
|
||||||
:log_encoding, [nil] + Setting::ENCODINGS,
|
:log_encoding, [nil] + Setting::ENCODINGS,
|
||||||
:label => l(:field_commit_logs_encoding), :required => true)) +
|
:label => l(:field_commit_logs_encoding), :required => true)) +
|
||||||
@@ -238,7 +233,7 @@ module RepositoriesHelper
|
|||||||
content_tag('p', form.text_field(
|
content_tag('p', form.text_field(
|
||||||
:url, :label => l(:field_path_to_repository),
|
:url, :label => l(:field_path_to_repository),
|
||||||
:size => 60, :required => true,
|
:size => 60, :required => true,
|
||||||
:disabled => (repository && !repository.new_record?))) +
|
:disabled => !repository.safe_attribute?('url'))) +
|
||||||
content_tag('p', form.select(
|
content_tag('p', form.select(
|
||||||
:log_encoding, [nil] + Setting::ENCODINGS,
|
:log_encoding, [nil] + Setting::ENCODINGS,
|
||||||
:label => l(:field_commit_logs_encoding), :required => true))
|
:label => l(:field_commit_logs_encoding), :required => true))
|
||||||
@@ -248,7 +243,7 @@ module RepositoriesHelper
|
|||||||
content_tag('p', form.text_field(
|
content_tag('p', form.text_field(
|
||||||
:url, :label => l(:field_root_directory),
|
:url, :label => l(:field_root_directory),
|
||||||
:size => 60, :required => true,
|
:size => 60, :required => true,
|
||||||
:disabled => (repository && !repository.root_url.blank?))) +
|
:disabled => !repository.safe_attribute?('url'))) +
|
||||||
content_tag('p', form.select(
|
content_tag('p', form.select(
|
||||||
:path_encoding, [nil] + Setting::ENCODINGS,
|
:path_encoding, [nil] + Setting::ENCODINGS,
|
||||||
:label => l(:field_scm_path_encoding)
|
:label => l(:field_scm_path_encoding)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -44,7 +44,7 @@ module SettingsHelper
|
|||||||
setting_values = Setting.send(setting)
|
setting_values = Setting.send(setting)
|
||||||
setting_values = [] unless setting_values.is_a?(Array)
|
setting_values = [] unless setting_values.is_a?(Array)
|
||||||
|
|
||||||
setting_label(setting, options).html_safe +
|
content_tag("label", l(options[:label] || "setting_#{setting}")) +
|
||||||
hidden_field_tag("settings[#{setting}][]", '').html_safe +
|
hidden_field_tag("settings[#{setting}][]", '').html_safe +
|
||||||
choices.collect do |choice|
|
choices.collect do |choice|
|
||||||
text, value = (choice.is_a?(Array) ? choice : [choice, choice])
|
text, value = (choice.is_a?(Array) ? choice : [choice, choice])
|
||||||
@@ -53,7 +53,8 @@ module SettingsHelper
|
|||||||
check_box_tag(
|
check_box_tag(
|
||||||
"settings[#{setting}][]",
|
"settings[#{setting}][]",
|
||||||
value,
|
value,
|
||||||
Setting.send(setting).include?(value)
|
Setting.send(setting).include?(value),
|
||||||
|
:id => nil
|
||||||
) + text.to_s,
|
) + text.to_s,
|
||||||
:class => 'block'
|
:class => 'block'
|
||||||
)
|
)
|
||||||
@@ -72,13 +73,13 @@ module SettingsHelper
|
|||||||
|
|
||||||
def setting_check_box(setting, options={})
|
def setting_check_box(setting, options={})
|
||||||
setting_label(setting, options).html_safe +
|
setting_label(setting, options).html_safe +
|
||||||
hidden_field_tag("settings[#{setting}]", 0).html_safe +
|
hidden_field_tag("settings[#{setting}]", 0, :id => nil).html_safe +
|
||||||
check_box_tag("settings[#{setting}]", 1, Setting.send("#{setting}?"), options).html_safe
|
check_box_tag("settings[#{setting}]", 1, Setting.send("#{setting}?"), options).html_safe
|
||||||
end
|
end
|
||||||
|
|
||||||
def setting_label(setting, options={})
|
def setting_label(setting, options={})
|
||||||
label = options.delete(:label)
|
label = options.delete(:label)
|
||||||
label != false ? content_tag("label", l(label || "setting_#{setting}")).html_safe : ''
|
label != false ? label_tag("settings_#{setting}", l(label || "setting_#{setting}")).html_safe : ''
|
||||||
end
|
end
|
||||||
|
|
||||||
# Renders a notification field for a Redmine::Notifiable option
|
# Renders a notification field for a Redmine::Notifiable option
|
||||||
@@ -86,7 +87,7 @@ module SettingsHelper
|
|||||||
return content_tag(:label,
|
return content_tag(:label,
|
||||||
check_box_tag('settings[notified_events][]',
|
check_box_tag('settings[notified_events][]',
|
||||||
notifiable.name,
|
notifiable.name,
|
||||||
Setting.notified_events.include?(notifiable.name)).html_safe +
|
Setting.notified_events.include?(notifiable.name), :id => nil).html_safe +
|
||||||
l_or_humanize(notifiable.name, :prefix => 'label_').html_safe,
|
l_or_humanize(notifiable.name, :prefix => 'label_').html_safe,
|
||||||
:class => notifiable.parent.present? ? "parent" : '').html_safe
|
:class => notifiable.parent.present? ? "parent" : '').html_safe
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -19,10 +19,18 @@
|
|||||||
|
|
||||||
module VersionsHelper
|
module VersionsHelper
|
||||||
|
|
||||||
STATUS_BY_CRITERIAS = %w(category tracker status priority author assigned_to)
|
def version_anchor(version)
|
||||||
|
if @project == version.project
|
||||||
|
anchor version.name
|
||||||
|
else
|
||||||
|
anchor "#{version.project.try(:identifier)}-#{version.name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
STATUS_BY_CRITERIAS = %w(tracker status priority author assigned_to category)
|
||||||
|
|
||||||
def render_issue_status_by(version, criteria)
|
def render_issue_status_by(version, criteria)
|
||||||
criteria = 'category' unless STATUS_BY_CRITERIAS.include?(criteria)
|
criteria = 'tracker' unless STATUS_BY_CRITERIAS.include?(criteria)
|
||||||
|
|
||||||
h = Hash.new {|k,v| k[v] = [0, 0]}
|
h = Hash.new {|k,v| k[v] = [0, 0]}
|
||||||
begin
|
begin
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -30,10 +30,8 @@ module WatchersHelper
|
|||||||
:action => (watched ? 'unwatch' : 'watch'),
|
:action => (watched ? 'unwatch' : 'watch'),
|
||||||
:object_type => object.class.to_s.underscore,
|
:object_type => object.class.to_s.underscore,
|
||||||
:object_id => object.id}
|
:object_id => object.id}
|
||||||
link_to_remote((watched ? l(:button_unwatch) : l(:button_watch)),
|
link_to((watched ? l(:button_unwatch) : l(:button_watch)), url,
|
||||||
{:url => url},
|
:remote => true, :method => 'post', :class => (watched ? 'icon icon-fav' : 'icon icon-fav-off'))
|
||||||
:href => url_for(url),
|
|
||||||
:class => (watched ? 'icon icon-fav' : 'icon icon-fav-off'))
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -45,30 +43,33 @@ module WatchersHelper
|
|||||||
# Returns a comma separated list of users watching the given object
|
# Returns a comma separated list of users watching the given object
|
||||||
def watchers_list(object)
|
def watchers_list(object)
|
||||||
remove_allowed = User.current.allowed_to?("delete_#{object.class.name.underscore}_watchers".to_sym, object.project)
|
remove_allowed = User.current.allowed_to?("delete_#{object.class.name.underscore}_watchers".to_sym, object.project)
|
||||||
|
content = ''.html_safe
|
||||||
lis = object.watcher_users.collect do |user|
|
lis = object.watcher_users.collect do |user|
|
||||||
s = avatar(user, :size => "16").to_s + link_to_user(user, :class => 'user').to_s
|
s = ''.html_safe
|
||||||
|
s << avatar(user, :size => "16").to_s
|
||||||
|
s << link_to_user(user, :class => 'user')
|
||||||
if remove_allowed
|
if remove_allowed
|
||||||
url = {:controller => 'watchers',
|
url = {:controller => 'watchers',
|
||||||
:action => 'destroy',
|
:action => 'destroy',
|
||||||
:object_type => object.class.to_s.underscore,
|
:object_type => object.class.to_s.underscore,
|
||||||
:object_id => object.id,
|
:object_id => object.id,
|
||||||
:user_id => user}
|
:user_id => user}
|
||||||
s += ' ' + link_to_remote(image_tag('delete.png'),
|
s << ' '
|
||||||
{:url => url},
|
s << link_to(image_tag('delete.png'), url,
|
||||||
:href => url_for(url),
|
:remote => true, :method => 'post', :style => "vertical-align: middle", :class => "delete")
|
||||||
:style => "vertical-align: middle",
|
|
||||||
:class => "delete")
|
|
||||||
end
|
end
|
||||||
content_tag :li, s.html_safe
|
content << content_tag('li', s)
|
||||||
end
|
end
|
||||||
(lis.empty? ? "" : "<ul>#{ lis.join("\n") }</ul>").html_safe
|
content.present? ? content_tag('ul', content) : content
|
||||||
end
|
end
|
||||||
|
|
||||||
def watchers_checkboxes(object, users, checked=nil)
|
def watchers_checkboxes(object, users, checked=nil)
|
||||||
users.map do |user|
|
users.map do |user|
|
||||||
c = checked.nil? ? object.watched_by?(user) : checked
|
c = checked.nil? ? object.watched_by?(user) : checked
|
||||||
tag = check_box_tag 'issue[watcher_user_ids][]', user.id, c, :id => nil
|
tag = check_box_tag 'issue[watcher_user_ids][]', user.id, c, :id => nil
|
||||||
content_tag 'label', "#{tag} #{h(user)}", :id => "issue_watcher_user_ids_#{user.id}", :class => "floating"
|
content_tag 'label', "#{tag} #{h(user)}".html_safe,
|
||||||
end.join
|
:id => "issue_watcher_user_ids_#{user.id}",
|
||||||
|
:class => "floating"
|
||||||
|
end.join.html_safe
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -21,14 +21,14 @@ module WikiHelper
|
|||||||
|
|
||||||
def wiki_page_options_for_select(pages, selected = nil, parent = nil, level = 0)
|
def wiki_page_options_for_select(pages, selected = nil, parent = nil, level = 0)
|
||||||
pages = pages.group_by(&:parent) unless pages.is_a?(Hash)
|
pages = pages.group_by(&:parent) unless pages.is_a?(Hash)
|
||||||
s = ''
|
s = ''.html_safe
|
||||||
if pages.has_key?(parent)
|
if pages.has_key?(parent)
|
||||||
pages[parent].each do |page|
|
pages[parent].each do |page|
|
||||||
attrs = "value='#{page.id}'"
|
attrs = "value='#{page.id}'"
|
||||||
attrs << " selected='selected'" if selected == page
|
attrs << " selected='selected'" if selected == page
|
||||||
indent = (level > 0) ? (' ' * level * 2 + '» ') : nil
|
indent = (level > 0) ? (' ' * level * 2 + '» ') : ''
|
||||||
|
|
||||||
s << "<option #{attrs}>#{indent}#{h page.pretty_title}</option>\n" +
|
s << content_tag('option', (indent + h(page.pretty_title)).html_safe, :value => page.id.to_s, :selected => selected == page) +
|
||||||
wiki_page_options_for_select(pages, selected, page, level + 1)
|
wiki_page_options_for_select(pages, selected, page, level + 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
#
|
#
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -18,4 +18,15 @@
|
|||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
module WorkflowsHelper
|
module WorkflowsHelper
|
||||||
|
def field_required?(field)
|
||||||
|
field.is_a?(CustomField) ? field.is_required? : %w(project_id tracker_id subject priority_id is_private).include?(field)
|
||||||
|
end
|
||||||
|
|
||||||
|
def field_permission_tag(permissions, status, field)
|
||||||
|
name = field.is_a?(CustomField) ? field.id.to_s : field
|
||||||
|
options = [["", ""], [l(:label_readonly), "readonly"]]
|
||||||
|
options << [l(:label_required), "required"] unless field_required?(field)
|
||||||
|
|
||||||
|
select_tag("permissions[#{name}][#{status.id}]", options_for_select(options, permissions[status.id][name]))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+63
-20
@@ -24,6 +24,7 @@ class Attachment < ActiveRecord::Base
|
|||||||
validates_presence_of :filename, :author
|
validates_presence_of :filename, :author
|
||||||
validates_length_of :filename, :maximum => 255
|
validates_length_of :filename, :maximum => 255
|
||||||
validates_length_of :disk_filename, :maximum => 255
|
validates_length_of :disk_filename, :maximum => 255
|
||||||
|
validates_length_of :description, :maximum => 255
|
||||||
validate :validate_max_file_size
|
validate :validate_max_file_size
|
||||||
|
|
||||||
acts_as_event :title => :filename,
|
acts_as_event :title => :filename,
|
||||||
@@ -44,20 +45,14 @@ class Attachment < ActiveRecord::Base
|
|||||||
"LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id"}
|
"LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id"}
|
||||||
|
|
||||||
cattr_accessor :storage_path
|
cattr_accessor :storage_path
|
||||||
@@storage_path = Redmine::Configuration['attachments_storage_path'] || "#{Rails.root}/files"
|
@@storage_path = Redmine::Configuration['attachments_storage_path'] || File.join(Rails.root, "files")
|
||||||
|
|
||||||
|
cattr_accessor :thumbnails_storage_path
|
||||||
|
@@thumbnails_storage_path = File.join(Rails.root, "tmp", "thumbnails")
|
||||||
|
|
||||||
before_save :files_to_final_location
|
before_save :files_to_final_location
|
||||||
after_destroy :delete_from_disk
|
after_destroy :delete_from_disk
|
||||||
|
|
||||||
def container_with_blank_type_check
|
|
||||||
if container_type.blank?
|
|
||||||
nil
|
|
||||||
else
|
|
||||||
container_without_blank_type_check
|
|
||||||
end
|
|
||||||
end
|
|
||||||
alias_method_chain :container, :blank_type_check unless method_defined?(:container_without_blank_type_check)
|
|
||||||
|
|
||||||
# Returns an unsaved copy of the attachment
|
# Returns an unsaved copy of the attachment
|
||||||
def copy(attributes=nil)
|
def copy(attributes=nil)
|
||||||
copy = self.class.new
|
copy = self.class.new
|
||||||
@@ -110,10 +105,15 @@ class Attachment < ActiveRecord::Base
|
|||||||
logger.info("Saving attachment '#{self.diskfile}' (#{@temp_file.size} bytes)")
|
logger.info("Saving attachment '#{self.diskfile}' (#{@temp_file.size} bytes)")
|
||||||
md5 = Digest::MD5.new
|
md5 = Digest::MD5.new
|
||||||
File.open(diskfile, "wb") do |f|
|
File.open(diskfile, "wb") do |f|
|
||||||
buffer = ""
|
if @temp_file.respond_to?(:read)
|
||||||
while (buffer = @temp_file.read(8192))
|
buffer = ""
|
||||||
f.write(buffer)
|
while (buffer = @temp_file.read(8192))
|
||||||
md5.update(buffer)
|
f.write(buffer)
|
||||||
|
md5.update(buffer)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
f.write(@temp_file)
|
||||||
|
md5.update(@temp_file)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.digest = md5.hexdigest
|
self.digest = md5.hexdigest
|
||||||
@@ -127,14 +127,22 @@ class Attachment < ActiveRecord::Base
|
|||||||
|
|
||||||
# Deletes the file from the file system if it's not referenced by other attachments
|
# Deletes the file from the file system if it's not referenced by other attachments
|
||||||
def delete_from_disk
|
def delete_from_disk
|
||||||
if Attachment.first(:conditions => ["disk_filename = ? AND id <> ?", disk_filename, id]).nil?
|
if Attachment.where("disk_filename = ? AND id <> ?", disk_filename, id).empty?
|
||||||
delete_from_disk!
|
delete_from_disk!
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns file's location on disk
|
# Returns file's location on disk
|
||||||
def diskfile
|
def diskfile
|
||||||
"#{@@storage_path}/#{self.disk_filename}"
|
File.join(self.class.storage_path, disk_filename.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
def title
|
||||||
|
title = filename.to_s
|
||||||
|
if description.present?
|
||||||
|
title << " (#{description})"
|
||||||
|
end
|
||||||
|
title
|
||||||
end
|
end
|
||||||
|
|
||||||
def increment_download
|
def increment_download
|
||||||
@@ -154,7 +162,43 @@ class Attachment < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def image?
|
def image?
|
||||||
self.filename =~ /\.(bmp|gif|jpg|jpe|jpeg|png)$/i
|
!!(self.filename =~ /\.(bmp|gif|jpg|jpe|jpeg|png)$/i)
|
||||||
|
end
|
||||||
|
|
||||||
|
def thumbnailable?
|
||||||
|
image?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the full path the attachment thumbnail, or nil
|
||||||
|
# if the thumbnail cannot be generated.
|
||||||
|
def thumbnail(options={})
|
||||||
|
if thumbnailable? && readable?
|
||||||
|
size = options[:size].to_i
|
||||||
|
if size > 0
|
||||||
|
# Limit the number of thumbnails per image
|
||||||
|
size = (size / 50) * 50
|
||||||
|
# Maximum thumbnail size
|
||||||
|
size = 800 if size > 800
|
||||||
|
else
|
||||||
|
size = Setting.thumbnails_size.to_i
|
||||||
|
end
|
||||||
|
size = 100 unless size > 0
|
||||||
|
target = File.join(self.class.thumbnails_storage_path, "#{id}_#{digest}_#{size}.thumb")
|
||||||
|
|
||||||
|
begin
|
||||||
|
Redmine::Thumbnail.generate(self.diskfile, target, size)
|
||||||
|
rescue => e
|
||||||
|
logger.error "An error occured while generating thumbnail for #{disk_filename} to #{target}\nException was: #{e.message}" if logger
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Deletes all thumbnails
|
||||||
|
def self.clear_thumbnails
|
||||||
|
Dir.glob(File.join(thumbnails_storage_path, "*.thumb")).each do |file|
|
||||||
|
File.delete file
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_text?
|
def is_text?
|
||||||
@@ -179,7 +223,7 @@ class Attachment < ActiveRecord::Base
|
|||||||
def self.find_by_token(token)
|
def self.find_by_token(token)
|
||||||
if token.to_s =~ /^(\d+)\.([0-9a-f]+)$/
|
if token.to_s =~ /^(\d+)\.([0-9a-f]+)$/
|
||||||
attachment_id, attachment_digest = $1, $2
|
attachment_id, attachment_digest = $1, $2
|
||||||
attachment = Attachment.first(:conditions => {:id => attachment_id, :digest => attachment_digest})
|
attachment = Attachment.where(:id => attachment_id, :digest => attachment_digest).first
|
||||||
if attachment && attachment.container.nil?
|
if attachment && attachment.container.nil?
|
||||||
attachment
|
attachment
|
||||||
end
|
end
|
||||||
@@ -204,8 +248,7 @@ class Attachment < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.prune(age=1.day)
|
def self.prune(age=1.day)
|
||||||
attachments = Attachment.all(:conditions => ["created_on < ? AND (container_type IS NULL OR container_type = '')", Time.now - age])
|
Attachment.where("created_on < ? AND (container_type IS NULL OR container_type = '')", Time.now - age).destroy_all
|
||||||
attachments.each(&:destroy)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
# Generic exception for when the AuthSource can not be reached
|
# Generic exception for when the AuthSource can not be reached
|
||||||
# (eg. can not connect to the LDAP)
|
# (eg. can not connect to the LDAP)
|
||||||
class AuthSourceException < Exception; end
|
class AuthSourceException < Exception; end
|
||||||
|
class AuthSourceTimeoutException < AuthSourceException; end
|
||||||
|
|
||||||
class AuthSource < ActiveRecord::Base
|
class AuthSource < ActiveRecord::Base
|
||||||
include Redmine::SubclassFactory
|
include Redmine::SubclassFactory
|
||||||
@@ -58,7 +59,7 @@ class AuthSource < ActiveRecord::Base
|
|||||||
|
|
||||||
# Try to authenticate a user not yet registered against available sources
|
# Try to authenticate a user not yet registered against available sources
|
||||||
def self.authenticate(login, password)
|
def self.authenticate(login, password)
|
||||||
AuthSource.find(:all, :conditions => ["onthefly_register=?", true]).each do |source|
|
AuthSource.where(:onthefly_register => true).all.each do |source|
|
||||||
begin
|
begin
|
||||||
logger.debug "Authenticating '#{login}' against '#{source.name}'" if logger && logger.debug?
|
logger.debug "Authenticating '#{login}' against '#{source.name}'" if logger && logger.debug?
|
||||||
attrs = source.authenticate(login, password)
|
attrs = source.authenticate(login, password)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
require 'iconv'
|
require 'iconv'
|
||||||
require 'net/ldap'
|
require 'net/ldap'
|
||||||
require 'net/ldap/dn'
|
require 'net/ldap/dn'
|
||||||
|
require 'timeout'
|
||||||
|
|
||||||
class AuthSourceLdap < AuthSource
|
class AuthSourceLdap < AuthSource
|
||||||
validates_presence_of :host, :port, :attr_login
|
validates_presence_of :host, :port, :attr_login
|
||||||
@@ -25,18 +26,11 @@ class AuthSourceLdap < AuthSource
|
|||||||
validates_length_of :account, :account_password, :base_dn, :filter, :maximum => 255, :allow_blank => true
|
validates_length_of :account, :account_password, :base_dn, :filter, :maximum => 255, :allow_blank => true
|
||||||
validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true
|
validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true
|
||||||
validates_numericality_of :port, :only_integer => true
|
validates_numericality_of :port, :only_integer => true
|
||||||
|
validates_numericality_of :timeout, :only_integer => true, :allow_blank => true
|
||||||
validate :validate_filter
|
validate :validate_filter
|
||||||
|
|
||||||
before_validation :strip_ldap_attributes
|
before_validation :strip_ldap_attributes
|
||||||
|
|
||||||
def self.human_attribute_name(attribute_key_name, *args)
|
|
||||||
attr_name = attribute_key_name.to_s
|
|
||||||
if attr_name == "filter"
|
|
||||||
attr_name = "ldap_filter"
|
|
||||||
end
|
|
||||||
super(attr_name, *args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(attributes=nil, *args)
|
def initialize(attributes=nil, *args)
|
||||||
super
|
super
|
||||||
self.port = 389 if self.port == 0
|
self.port = 389 if self.port == 0
|
||||||
@@ -44,22 +38,26 @@ class AuthSourceLdap < AuthSource
|
|||||||
|
|
||||||
def authenticate(login, password)
|
def authenticate(login, password)
|
||||||
return nil if login.blank? || password.blank?
|
return nil if login.blank? || password.blank?
|
||||||
attrs = get_user_dn(login, password)
|
|
||||||
|
|
||||||
if attrs && attrs[:dn] && authenticate_dn(attrs[:dn], password)
|
with_timeout do
|
||||||
logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
|
attrs = get_user_dn(login, password)
|
||||||
return attrs.except(:dn)
|
if attrs && attrs[:dn] && authenticate_dn(attrs[:dn], password)
|
||||||
|
logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
|
||||||
|
return attrs.except(:dn)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
rescue Net::LDAP::LdapError => e
|
rescue Net::LDAP::LdapError => e
|
||||||
raise AuthSourceException.new(e.message)
|
raise AuthSourceException.new(e.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
# test the connection to the LDAP
|
# test the connection to the LDAP
|
||||||
def test_connection
|
def test_connection
|
||||||
ldap_con = initialize_ldap_con(self.account, self.account_password)
|
with_timeout do
|
||||||
ldap_con.open { }
|
ldap_con = initialize_ldap_con(self.account, self.account_password)
|
||||||
rescue Net::LDAP::LdapError => e
|
ldap_con.open { }
|
||||||
raise "LdapError: " + e.message
|
end
|
||||||
|
rescue Net::LDAP::LdapError => e
|
||||||
|
raise AuthSourceException.new(e.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
def auth_method_name
|
def auth_method_name
|
||||||
@@ -68,6 +66,16 @@ class AuthSourceLdap < AuthSource
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def with_timeout(&block)
|
||||||
|
timeout = self.timeout
|
||||||
|
timeout = 20 unless timeout && timeout > 0
|
||||||
|
Timeout.timeout(timeout) do
|
||||||
|
return yield
|
||||||
|
end
|
||||||
|
rescue Timeout::Error => e
|
||||||
|
raise AuthSourceTimeoutException.new(e.message)
|
||||||
|
end
|
||||||
|
|
||||||
def ldap_filter
|
def ldap_filter
|
||||||
if filter.present?
|
if filter.present?
|
||||||
Net::LDAP::Filter.construct(filter)
|
Net::LDAP::Filter.construct(filter)
|
||||||
|
|||||||
+37
-4
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -21,26 +21,37 @@ class Board < ActiveRecord::Base
|
|||||||
has_many :topics, :class_name => 'Message', :conditions => "#{Message.table_name}.parent_id IS NULL", :order => "#{Message.table_name}.created_on DESC"
|
has_many :topics, :class_name => 'Message', :conditions => "#{Message.table_name}.parent_id IS NULL", :order => "#{Message.table_name}.created_on DESC"
|
||||||
has_many :messages, :dependent => :destroy, :order => "#{Message.table_name}.created_on DESC"
|
has_many :messages, :dependent => :destroy, :order => "#{Message.table_name}.created_on DESC"
|
||||||
belongs_to :last_message, :class_name => 'Message', :foreign_key => :last_message_id
|
belongs_to :last_message, :class_name => 'Message', :foreign_key => :last_message_id
|
||||||
acts_as_list :scope => :project_id
|
acts_as_tree :dependent => :nullify
|
||||||
|
acts_as_list :scope => '(project_id = #{project_id} AND parent_id #{parent_id ? "= #{parent_id}" : "IS NULL"})'
|
||||||
acts_as_watchable
|
acts_as_watchable
|
||||||
|
|
||||||
validates_presence_of :name, :description
|
validates_presence_of :name, :description
|
||||||
validates_length_of :name, :maximum => 30
|
validates_length_of :name, :maximum => 30
|
||||||
validates_length_of :description, :maximum => 255
|
validates_length_of :description, :maximum => 255
|
||||||
|
validate :validate_board
|
||||||
|
|
||||||
named_scope :visible, lambda {|*args| { :include => :project,
|
scope :visible, lambda {|*args| { :include => :project,
|
||||||
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_messages, *args) } }
|
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_messages, *args) } }
|
||||||
|
|
||||||
safe_attributes 'name', 'description', 'move_to'
|
safe_attributes 'name', 'description', 'parent_id', 'move_to'
|
||||||
|
|
||||||
def visible?(user=User.current)
|
def visible?(user=User.current)
|
||||||
!user.nil? && user.allowed_to?(:view_messages, project)
|
!user.nil? && user.allowed_to?(:view_messages, project)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reload(*args)
|
||||||
|
@valid_parents = nil
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
name
|
name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def valid_parents
|
||||||
|
@valid_parents ||= project.boards - self_and_descendants
|
||||||
|
end
|
||||||
|
|
||||||
def reset_counters!
|
def reset_counters!
|
||||||
self.class.reset_counters!(id)
|
self.class.reset_counters!(id)
|
||||||
end
|
end
|
||||||
@@ -53,4 +64,26 @@ class Board < ActiveRecord::Base
|
|||||||
" last_message_id = (SELECT MAX(id) FROM #{Message.table_name} WHERE board_id=#{board_id})",
|
" last_message_id = (SELECT MAX(id) FROM #{Message.table_name} WHERE board_id=#{board_id})",
|
||||||
["id = ?", board_id])
|
["id = ?", board_id])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.board_tree(boards, parent_id=nil, level=0)
|
||||||
|
tree = []
|
||||||
|
boards.select {|board| board.parent_id == parent_id}.sort_by(&:position).each do |board|
|
||||||
|
tree << [board, level]
|
||||||
|
tree += board_tree(boards, board.id, level+1)
|
||||||
|
end
|
||||||
|
if block_given?
|
||||||
|
tree.each do |board, level|
|
||||||
|
yield board, level
|
||||||
|
end
|
||||||
|
end
|
||||||
|
tree
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def validate_board
|
||||||
|
if parent_id && parent_id_changed?
|
||||||
|
errors.add(:parent_id, :invalid) unless valid_parents.include?(parent)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
+7
-12
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -20,7 +20,7 @@ require 'iconv'
|
|||||||
class Changeset < ActiveRecord::Base
|
class Changeset < ActiveRecord::Base
|
||||||
belongs_to :repository
|
belongs_to :repository
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
has_many :changes, :dependent => :delete_all
|
has_many :filechanges, :class_name => 'Change', :dependent => :delete_all
|
||||||
has_and_belongs_to_many :issues
|
has_and_belongs_to_many :issues
|
||||||
has_and_belongs_to_many :parents,
|
has_and_belongs_to_many :parents,
|
||||||
:class_name => "Changeset",
|
:class_name => "Changeset",
|
||||||
@@ -49,7 +49,8 @@ class Changeset < ActiveRecord::Base
|
|||||||
validates_uniqueness_of :revision, :scope => :repository_id
|
validates_uniqueness_of :revision, :scope => :repository_id
|
||||||
validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
|
validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
|
||||||
|
|
||||||
named_scope :visible, lambda {|*args| { :include => {:repository => :project},
|
scope :visible,
|
||||||
|
lambda {|*args| { :include => {:repository => :project},
|
||||||
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args) } }
|
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args) } }
|
||||||
|
|
||||||
after_create :scan_for_issues
|
after_create :scan_for_issues
|
||||||
@@ -161,7 +162,7 @@ class Changeset < ActiveRecord::Base
|
|||||||
tag = "#{repository.identifier}|#{tag}"
|
tag = "#{repository.identifier}|#{tag}"
|
||||||
end
|
end
|
||||||
if ref_project && project && ref_project != project
|
if ref_project && project && ref_project != project
|
||||||
tag = "#{project.identifier}:#{tag}"
|
tag = "#{project.identifier}:#{tag}"
|
||||||
end
|
end
|
||||||
tag
|
tag
|
||||||
end
|
end
|
||||||
@@ -175,18 +176,12 @@ class Changeset < ActiveRecord::Base
|
|||||||
|
|
||||||
# Returns the previous changeset
|
# Returns the previous changeset
|
||||||
def previous
|
def previous
|
||||||
@previous ||= Changeset.find(:first,
|
@previous ||= Changeset.where(["id < ? AND repository_id = ?", id, repository_id]).order('id DESC').first
|
||||||
:conditions => ['id < ? AND repository_id = ?',
|
|
||||||
self.id, self.repository_id],
|
|
||||||
:order => 'id DESC')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the next changeset
|
# Returns the next changeset
|
||||||
def next
|
def next
|
||||||
@next ||= Changeset.find(:first,
|
@next ||= Changeset.where(["id > ? AND repository_id = ?", id, repository_id]).order('id ASC').first
|
||||||
:conditions => ['id > ? AND repository_id = ?',
|
|
||||||
self.id, self.repository_id],
|
|
||||||
:order => 'id ASC')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates a new Change from it's common parameters
|
# Creates a new Change from it's common parameters
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
class CommentObserver < ActiveRecord::Observer
|
class CommentObserver < ActiveRecord::Observer
|
||||||
def after_create(comment)
|
def after_create(comment)
|
||||||
if comment.commented.is_a?(News) && Setting.notified_events.include?('news_comment_added')
|
if comment.commented.is_a?(News) && Setting.notified_events.include?('news_comment_added')
|
||||||
Mailer.deliver_news_comment_added(comment)
|
Mailer.news_comment_added(comment).deliver
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+88
-21
@@ -30,11 +30,6 @@ class CustomField < ActiveRecord::Base
|
|||||||
validate :validate_custom_field
|
validate :validate_custom_field
|
||||||
before_validation :set_searchable
|
before_validation :set_searchable
|
||||||
|
|
||||||
def initialize(attributes=nil, *args)
|
|
||||||
super
|
|
||||||
self.possible_values ||= []
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_searchable
|
def set_searchable
|
||||||
# make sure these fields are not searchable
|
# make sure these fields are not searchable
|
||||||
self.searchable = false if %w(int float date bool).include?(field_format)
|
self.searchable = false if %w(int float date bool).include?(field_format)
|
||||||
@@ -80,7 +75,7 @@ class CustomField < ActiveRecord::Base
|
|||||||
when 'bool'
|
when 'bool'
|
||||||
[[l(:general_text_Yes), '1'], [l(:general_text_No), '0']]
|
[[l(:general_text_Yes), '1'], [l(:general_text_No), '0']]
|
||||||
else
|
else
|
||||||
read_possible_values_utf8_encoded || []
|
possible_values || []
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -91,14 +86,20 @@ class CustomField < ActiveRecord::Base
|
|||||||
when 'bool'
|
when 'bool'
|
||||||
['1', '0']
|
['1', '0']
|
||||||
else
|
else
|
||||||
read_possible_values_utf8_encoded
|
values = super()
|
||||||
|
if values.is_a?(Array)
|
||||||
|
values.each do |value|
|
||||||
|
value.force_encoding('UTF-8') if value.respond_to?(:force_encoding)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
values || []
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Makes possible_values accept a multiline string
|
# Makes possible_values accept a multiline string
|
||||||
def possible_values=(arg)
|
def possible_values=(arg)
|
||||||
if arg.is_a?(Array)
|
if arg.is_a?(Array)
|
||||||
write_attribute(:possible_values, arg.compact.collect(&:strip).select {|v| !v.blank?})
|
super(arg.compact.collect(&:strip).select {|v| !v.blank?})
|
||||||
else
|
else
|
||||||
self.possible_values = arg.to_s.split(/[\n\r]+/)
|
self.possible_values = arg.to_s.split(/[\n\r]+/)
|
||||||
end
|
end
|
||||||
@@ -125,16 +126,32 @@ class CustomField < ActiveRecord::Base
|
|||||||
casted
|
casted
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def value_from_keyword(keyword, customized)
|
||||||
|
possible_values_options = possible_values_options(customized)
|
||||||
|
if possible_values_options.present?
|
||||||
|
keyword = keyword.to_s.downcase
|
||||||
|
if v = possible_values_options.detect {|text, id| text.downcase == keyword}
|
||||||
|
if v.is_a?(Array)
|
||||||
|
v.last
|
||||||
|
else
|
||||||
|
v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
keyword
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Returns a ORDER BY clause that can used to sort customized
|
# Returns a ORDER BY clause that can used to sort customized
|
||||||
# objects by their value of the custom field.
|
# objects by their value of the custom field.
|
||||||
# Returns false, if the custom field can not be used for sorting.
|
# Returns nil if the custom field can not be used for sorting.
|
||||||
def order_statement
|
def order_statement
|
||||||
return nil if multiple?
|
return nil if multiple?
|
||||||
case field_format
|
case field_format
|
||||||
when 'string', 'text', 'list', 'date', 'bool'
|
when 'string', 'text', 'list', 'date', 'bool'
|
||||||
# COALESCE is here to make sure that blank and NULL values are sorted equally
|
# COALESCE is here to make sure that blank and NULL values are sorted equally
|
||||||
"COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
|
"COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
|
||||||
" WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
|
" WHERE cv_sort.customized_type='#{self.class.customized_class.base_class.name}'" +
|
||||||
" AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
|
" AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
|
||||||
" AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
|
" AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
|
||||||
when 'int', 'float'
|
when 'int', 'float'
|
||||||
@@ -142,18 +159,74 @@ class CustomField < ActiveRecord::Base
|
|||||||
# Postgresql will raise an error if a value can not be casted!
|
# Postgresql will raise an error if a value can not be casted!
|
||||||
# CustomValue validations should ensure that it doesn't occur
|
# CustomValue validations should ensure that it doesn't occur
|
||||||
"(SELECT CAST(cv_sort.value AS decimal(60,3)) FROM #{CustomValue.table_name} cv_sort" +
|
"(SELECT CAST(cv_sort.value AS decimal(60,3)) FROM #{CustomValue.table_name} cv_sort" +
|
||||||
" WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
|
" WHERE cv_sort.customized_type='#{self.class.customized_class.base_class.name}'" +
|
||||||
" AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
|
" AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
|
||||||
" AND cv_sort.custom_field_id=#{id} AND cv_sort.value <> '' AND cv_sort.value IS NOT NULL LIMIT 1)"
|
" AND cv_sort.custom_field_id=#{id} AND cv_sort.value <> '' AND cv_sort.value IS NOT NULL LIMIT 1)"
|
||||||
|
when 'user', 'version'
|
||||||
|
value_class.fields_for_order_statement(value_join_alias)
|
||||||
else
|
else
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns a GROUP BY clause that can used to group by custom value
|
||||||
|
# Returns nil if the custom field can not be used for grouping.
|
||||||
|
def group_statement
|
||||||
|
return nil if multiple?
|
||||||
|
case field_format
|
||||||
|
when 'list', 'date', 'bool', 'int'
|
||||||
|
order_statement
|
||||||
|
when 'user', 'version'
|
||||||
|
"COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
|
||||||
|
" WHERE cv_sort.customized_type='#{self.class.customized_class.base_class.name}'" +
|
||||||
|
" AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
|
||||||
|
" AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def join_for_order_statement
|
||||||
|
case field_format
|
||||||
|
when 'user', 'version'
|
||||||
|
"LEFT OUTER JOIN #{CustomValue.table_name} #{join_alias}" +
|
||||||
|
" ON #{join_alias}.customized_type = '#{self.class.customized_class.base_class.name}'" +
|
||||||
|
" AND #{join_alias}.customized_id = #{self.class.customized_class.table_name}.id" +
|
||||||
|
" AND #{join_alias}.custom_field_id = #{id}" +
|
||||||
|
" AND #{join_alias}.value <> ''" +
|
||||||
|
" AND #{join_alias}.id = (SELECT max(#{join_alias}_2.id) FROM #{CustomValue.table_name} #{join_alias}_2" +
|
||||||
|
" WHERE #{join_alias}_2.customized_type = #{join_alias}.customized_type" +
|
||||||
|
" AND #{join_alias}_2.customized_id = #{join_alias}.customized_id" +
|
||||||
|
" AND #{join_alias}_2.custom_field_id = #{join_alias}.custom_field_id)" +
|
||||||
|
" LEFT OUTER JOIN #{value_class.table_name} #{value_join_alias}" +
|
||||||
|
" ON CAST(#{join_alias}.value as decimal(60,0)) = #{value_join_alias}.id"
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def join_alias
|
||||||
|
"cf_#{id}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def value_join_alias
|
||||||
|
join_alias + "_" + field_format
|
||||||
|
end
|
||||||
|
|
||||||
def <=>(field)
|
def <=>(field)
|
||||||
position <=> field.position
|
position <=> field.position
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns the class that values represent
|
||||||
|
def value_class
|
||||||
|
case field_format
|
||||||
|
when 'user', 'version'
|
||||||
|
field_format.classify.constantize
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def self.customized_class
|
def self.customized_class
|
||||||
self.name =~ /^(.+)CustomField$/
|
self.name =~ /^(.+)CustomField$/
|
||||||
begin; $1.constantize; rescue nil; end
|
begin; $1.constantize; rescue nil; end
|
||||||
@@ -194,6 +267,10 @@ class CustomField < ActiveRecord::Base
|
|||||||
validate_field_value(value).empty?
|
validate_field_value(value).empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def format_in?(*args)
|
||||||
|
args.include?(field_format)
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
# Returns the error message for the given value regarding its format
|
# Returns the error message for the given value regarding its format
|
||||||
@@ -218,14 +295,4 @@ class CustomField < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
errs
|
errs
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_possible_values_utf8_encoded
|
|
||||||
values = read_attribute(:possible_values)
|
|
||||||
if values.is_a?(Array)
|
|
||||||
values.each do |value|
|
|
||||||
value.force_encoding('UTF-8') if value.respond_to?(:force_encoding)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
values
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# RedMine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -30,7 +30,7 @@ class Document < ActiveRecord::Base
|
|||||||
validates_presence_of :project, :title, :category
|
validates_presence_of :project, :title, :category
|
||||||
validates_length_of :title, :maximum => 60
|
validates_length_of :title, :maximum => 60
|
||||||
|
|
||||||
named_scope :visible, lambda {|*args| { :include => :project,
|
scope :visible, lambda {|*args| { :include => :project,
|
||||||
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_documents, *args) } }
|
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_documents, *args) } }
|
||||||
|
|
||||||
safe_attributes 'category_id', 'title', 'description'
|
safe_attributes 'category_id', 'title', 'description'
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -31,4 +31,10 @@ class DocumentCategory < Enumeration
|
|||||||
def transfer_relations(to)
|
def transfer_relations(to)
|
||||||
documents.update_all("category_id = #{to.id}")
|
documents.update_all("category_id = #{to.id}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.default
|
||||||
|
d = super
|
||||||
|
d = first if d.nil?
|
||||||
|
d
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@@ -17,6 +17,6 @@
|
|||||||
|
|
||||||
class DocumentObserver < ActiveRecord::Observer
|
class DocumentObserver < ActiveRecord::Observer
|
||||||
def after_create(document)
|
def after_create(document)
|
||||||
Mailer.deliver_document_added(document) if Setting.notified_events.include?('document_added')
|
Mailer.document_added(document).deliver if Setting.notified_events.include?('document_added')
|
||||||
end
|
end
|
||||||
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