Compare commits
921
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1dd4d870e0 | ||
|
|
31b959a7dc | ||
|
|
2a132b4e8f | ||
|
|
ff1a1e6f59 | ||
|
|
a4a75607fa | ||
|
|
6641bbded2 | ||
|
|
78975b9e68 | ||
|
|
2f122f12b6 | ||
|
|
5e19a889c2 | ||
|
|
87aec2fc01 | ||
|
|
5327df4c7a | ||
|
|
a220ee67b7 | ||
|
|
124ca5d8cf | ||
|
|
9dac8e55ff | ||
|
|
4539fd4c5e | ||
|
|
923ab97309 | ||
|
|
c56644f0e5 | ||
|
|
da119ef3ad | ||
|
|
6e114b84be | ||
|
|
dff7b59036 | ||
|
|
7f43f894b0 | ||
|
|
a27f106a4a | ||
|
|
95a360dd36 | ||
|
|
09224f587f | ||
|
|
ff1b9e7ed1 | ||
|
|
6d260c58c7 | ||
|
|
ba69bd4c82 | ||
|
|
2a45a2133b | ||
|
|
57fbc453a6 | ||
|
|
f17b3638d7 | ||
|
|
67517e0b28 | ||
|
|
0038ce03fa | ||
|
|
d2b2cb186e | ||
|
|
ca8b64bd88 | ||
|
|
732b8c9d8a | ||
|
|
ba6418fb93 | ||
|
|
ab1cd36c77 | ||
|
|
9f2c5b5f50 | ||
|
|
b6ea1de4c0 | ||
|
|
21f214659e | ||
|
|
82720d341c | ||
|
|
a330ff7320 | ||
|
|
375eac2be0 | ||
|
|
4cf82bd41c | ||
|
|
9f202cfcdc | ||
|
|
393321b223 | ||
|
|
6835cd5319 | ||
|
|
31bc88d9d3 | ||
|
|
f515106bcc | ||
|
|
ec1420c19b | ||
|
|
f44af10cdd | ||
|
|
e25305980f | ||
|
|
0c7bdac091 | ||
|
|
2f8f8c78f7 | ||
|
|
90b7ce7ec3 | ||
|
|
6ecea9d702 | ||
|
|
a54b8780a8 | ||
|
|
40d269c504 | ||
|
|
1d52be415f | ||
|
|
3b9a989abd | ||
|
|
a728616bca | ||
|
|
65b8382020 | ||
|
|
85f4efab46 | ||
|
|
af7fb1c6ee | ||
|
|
a52d42f6b2 | ||
|
|
1f8edbdd1f | ||
|
|
b61eb00b99 | ||
|
|
449093c3f0 | ||
|
|
0fc7c96924 | ||
|
|
b73450e992 | ||
|
|
8e00387152 | ||
|
|
b6a5eb0b60 | ||
|
|
d7b7824aad | ||
|
|
3e6c50b7dc | ||
|
|
9291dc29b6 | ||
|
|
f5927944fa | ||
|
|
16885f4ebf | ||
|
|
d3a233b222 | ||
|
|
7e10cff395 | ||
|
|
762f305914 | ||
|
|
e208ee3fde | ||
|
|
571be37c1d | ||
|
|
433983e7c4 | ||
|
|
d7aae0e667 | ||
|
|
e87a6de46b | ||
|
|
13a779019b | ||
|
|
17432deaa7 | ||
|
|
7365f77dd5 | ||
|
|
e8dcc6df37 | ||
|
|
a8765eb41b | ||
|
|
c700608f0f | ||
|
|
86a0032cc1 | ||
|
|
049c1385d6 | ||
|
|
a168d0227d | ||
|
|
40dd3e5875 | ||
|
|
dc9a0896e5 | ||
|
|
e064dc4a46 | ||
|
|
843a689094 | ||
|
|
f09c06f8c4 | ||
|
|
c365d646a2 | ||
|
|
cbbb3dd9d4 | ||
|
|
c5a35a64a2 | ||
|
|
a03715f74a | ||
|
|
51b8ce7400 | ||
|
|
2981b0af00 | ||
|
|
194c5635b5 | ||
|
|
73d62800e8 | ||
|
|
c5834e2f89 | ||
|
|
ab28476e7e | ||
|
|
538e6fa4b1 | ||
|
|
c3aa601f3c | ||
|
|
613f05ac02 | ||
|
|
8f49778501 | ||
|
|
f0b88811d6 | ||
|
|
c1d744e47a | ||
|
|
12875539bb | ||
|
|
a8dd698425 | ||
|
|
f8b6864553 | ||
|
|
01f213e65e | ||
|
|
22ead81c48 | ||
|
|
a7afcbf4a0 | ||
|
|
a8ab01a5eb | ||
|
|
731264d9dc | ||
|
|
046c4db2e8 | ||
|
|
ac21649633 | ||
|
|
efd182f145 | ||
|
|
b6c6e09a5a | ||
|
|
83a8f388ce | ||
|
|
8650492989 | ||
|
|
cc03430ac6 | ||
|
|
9c464c15ad | ||
|
|
82ed44ee31 | ||
|
|
d7c008f2d9 | ||
|
|
ea68715f59 | ||
|
|
601ed94d77 | ||
|
|
bee36814e5 | ||
|
|
a75524af9f | ||
|
|
5eeb473fe8 | ||
|
|
dfb750caf4 | ||
|
|
cdd449fec9 | ||
|
|
7c2cb259a2 | ||
|
|
e7a73413d5 | ||
|
|
4c51d4702f | ||
|
|
191a1afe23 | ||
|
|
8c8070ff16 | ||
|
|
ee0e45cad6 | ||
|
|
d58030a475 | ||
|
|
d1e1d11e4a | ||
|
|
f09f1a4282 | ||
|
|
25f364991e | ||
|
|
730b803ad6 | ||
|
|
dbb8acca9a | ||
|
|
aee229e700 | ||
|
|
7c6716784e | ||
|
|
9a3aeb0004 | ||
|
|
6f6ec58a09 | ||
|
|
88860e2e38 | ||
|
|
5bbb768fe8 | ||
|
|
396bc3b0f6 | ||
|
|
164654f784 | ||
|
|
9514103031 | ||
|
|
4d4458a183 | ||
|
|
5a2eb67cb7 | ||
|
|
9bdc365bf0 | ||
|
|
066bdc5f3f | ||
|
|
396892b0e1 | ||
|
|
dd5b325864 | ||
|
|
fe20cc220f | ||
|
|
02112dea8a | ||
|
|
f369eabd06 | ||
|
|
4ab2c57903 | ||
|
|
9015f82413 | ||
|
|
44b8dab9c4 | ||
|
|
9814a8cd0e | ||
|
|
ecce179a2f | ||
|
|
cf0aded1a4 | ||
|
|
a49687fe41 | ||
|
|
3bfc47d105 | ||
|
|
01750fa98e | ||
|
|
b948384ec9 | ||
|
|
1c34e71cdc | ||
|
|
2337d4057f | ||
|
|
b39dedd2d2 | ||
|
|
05d59df68b | ||
|
|
9225f2b908 | ||
|
|
e4e18dea5e | ||
|
|
b0d8148ca6 | ||
|
|
52253d8c10 | ||
|
|
94a34f41b8 | ||
|
|
d1636314f1 | ||
|
|
1ae7cddf09 | ||
|
|
5ef2db59f7 | ||
|
|
1a78882c51 | ||
|
|
6471524627 | ||
|
|
bc9e1f9558 | ||
|
|
a408fdff52 | ||
|
|
cc7eeca2c4 | ||
|
|
35ff82710a | ||
|
|
ba5f6410ed | ||
|
|
763ce37f8c | ||
|
|
f449f0456b | ||
|
|
06cb06fce6 | ||
|
|
647f052486 | ||
|
|
013e316e7d | ||
|
|
33345ed1f0 | ||
|
|
0b5146655d | ||
|
|
d674b33a20 | ||
|
|
c524782395 | ||
|
|
c144f736dc | ||
|
|
f4a8638fb4 | ||
|
|
69424be245 | ||
|
|
c3d364558e | ||
|
|
aa6741ee4b | ||
|
|
412dca053a | ||
|
|
b41828a7f1 | ||
|
|
bb8c32a77e | ||
|
|
1a5763d468 | ||
|
|
8402152a59 | ||
|
|
d89f02ff53 | ||
|
|
94cded35d2 | ||
|
|
ba4520da51 | ||
|
|
92c4ee50fa | ||
|
|
4249d6cc5b | ||
|
|
e65ff2105e | ||
|
|
e5c4907aba | ||
|
|
a49032a6bd | ||
|
|
f7f83ae1c7 | ||
|
|
6b31e3d3b9 | ||
|
|
0ed67f1355 | ||
|
|
abe8f8f93c | ||
|
|
6041a9c009 | ||
|
|
da95a810d4 | ||
|
|
f1fc78c71b | ||
|
|
d41458dfdc | ||
|
|
406e9a3df2 | ||
|
|
b4f9b9095d | ||
|
|
e18b61f180 | ||
|
|
af6e20d627 | ||
|
|
8100dfa797 | ||
|
|
4514d7ce32 | ||
|
|
0612c3dff1 | ||
|
|
ee9e559765 | ||
|
|
a89b116940 | ||
|
|
942c972942 | ||
|
|
20914a8812 | ||
|
|
b85b2ed416 | ||
|
|
3a1c1226bc | ||
|
|
6d12c2a2c8 | ||
|
|
23d7c06829 | ||
|
|
290264262c | ||
|
|
a95ed0f8e6 | ||
|
|
a2383db454 | ||
|
|
1d1f7445cb | ||
|
|
b469045de8 | ||
|
|
a1c493358f | ||
|
|
4cece88a42 | ||
|
|
8124faa6d4 | ||
|
|
fb992db5ad | ||
|
|
e385bf34d5 | ||
|
|
4f75499439 | ||
|
|
34140e77fc | ||
|
|
04b992e1f1 | ||
|
|
11cf483e2c | ||
|
|
6a835c745f | ||
|
|
af353fb73f | ||
|
|
d159e12b3f | ||
|
|
28477e9709 | ||
|
|
9a3f4080c0 | ||
|
|
cdd52a0b37 | ||
|
|
e226a32413 | ||
|
|
1f83680e12 | ||
|
|
d5f4da3934 | ||
|
|
dc91be3c0d | ||
|
|
7e96f37809 | ||
|
|
0ac27aab50 | ||
|
|
3350e2fa12 | ||
|
|
e4e5106f97 | ||
|
|
ed7f979635 | ||
|
|
46bfe6befb | ||
|
|
22db835036 | ||
|
|
f6f704ddd8 | ||
|
|
9222bf80ef | ||
|
|
b017cb1711 | ||
|
|
9db7d3b85d | ||
|
|
18cf3d5f9c | ||
|
|
8e286b5d15 | ||
|
|
ec2b52bf7b | ||
|
|
ac705547e8 | ||
|
|
a9ad21b395 | ||
|
|
ec123b4512 | ||
|
|
f27544c72c | ||
|
|
9ed65e34ad | ||
|
|
6cd02043af | ||
|
|
0edc2c1217 | ||
|
|
2fa0e91ca7 | ||
|
|
b37892e033 | ||
|
|
2b6d9db83a | ||
|
|
7d54bd5e86 | ||
|
|
499e66ffe9 | ||
|
|
395f3585ca | ||
|
|
9f9cd68997 | ||
|
|
ab38134ba9 | ||
|
|
a52e6604c1 | ||
|
|
b65573472e | ||
|
|
160055a874 | ||
|
|
2d46f4fd33 | ||
|
|
0eb2ecd2f2 | ||
|
|
8568094266 | ||
|
|
5e2bdcd2ba | ||
|
|
2095df7865 | ||
|
|
618d3d9cc0 | ||
|
|
874e393948 | ||
|
|
d056f5581a | ||
|
|
a8f52b30b8 | ||
|
|
1dc5f0d412 | ||
|
|
b1ff1d4365 | ||
|
|
7e82ece74c | ||
|
|
a5cf7659eb | ||
|
|
43b6006a86 | ||
|
|
7eb58db949 | ||
|
|
12c8207836 | ||
|
|
b16c17457e | ||
|
|
2d22c56efe | ||
|
|
8a74c9f9e0 | ||
|
|
c09e76f2d1 | ||
|
|
4fa33c0bc2 | ||
|
|
b0318ca7b6 | ||
|
|
8e0230488f | ||
|
|
b20707d0ed | ||
|
|
cc7c7f7dc7 | ||
|
|
8b483136d0 | ||
|
|
2888034ea6 | ||
|
|
520103cffe | ||
|
|
12d5b57624 | ||
|
|
523532b153 | ||
|
|
fc3c362515 | ||
|
|
0c213c9e66 | ||
|
|
494e2be680 | ||
|
|
d9184d2363 | ||
|
|
d9b67a85f2 | ||
|
|
e59a4b4246 | ||
|
|
666d36736f | ||
|
|
33b035b360 | ||
|
|
41bc68946d | ||
|
|
f515543e33 | ||
|
|
6d64242382 | ||
|
|
d3916e6e5e | ||
|
|
f2833a2781 | ||
|
|
9e8d991099 | ||
|
|
1e3cb57567 | ||
|
|
bc67ffe20d | ||
|
|
d0ee85e77a | ||
|
|
b390a4b117 | ||
|
|
bb05ce89c9 | ||
|
|
16b75b1deb | ||
|
|
4dbd796fd5 | ||
|
|
affa6ba656 | ||
|
|
8074e67b84 | ||
|
|
d43d31d144 | ||
|
|
966a088a0f | ||
|
|
ebf4c1a9c7 | ||
|
|
dd13abd701 | ||
|
|
aa4dc45aa7 | ||
|
|
e1d8854d10 | ||
|
|
243726f096 | ||
|
|
f49610f058 | ||
|
|
a166d1b4cf | ||
|
|
8557b2a161 | ||
|
|
d77f75da49 | ||
|
|
0f1b0bfbbe | ||
|
|
91c92e3a11 | ||
|
|
1f5248371c | ||
|
|
dbf614a2dc | ||
|
|
c957b4b45f | ||
|
|
feee156c0a | ||
|
|
0d62ec0bea | ||
|
|
550fc09ee4 | ||
|
|
5e3b3e6987 | ||
|
|
77269e5cd6 | ||
|
|
8c9a074688 | ||
|
|
5247200bf0 | ||
|
|
96ebc32d8a | ||
|
|
19c12add73 | ||
|
|
ecf30daaf3 | ||
|
|
47e7201eb6 | ||
|
|
174accdb23 | ||
|
|
62468d21ef | ||
|
|
9571befbfd | ||
|
|
36189e4203 | ||
|
|
428f2586c7 | ||
|
|
5ea57f195b | ||
|
|
11179cf27d | ||
|
|
2ff6131483 | ||
|
|
7b874d08d4 | ||
|
|
3858911007 | ||
|
|
a25a01495f | ||
|
|
781d55cc37 | ||
|
|
5d5d8bc201 | ||
|
|
0cdd19e3aa | ||
|
|
0f21c02a5f | ||
|
|
ecad0c367f | ||
|
|
eeca191103 | ||
|
|
3f27e5b775 | ||
|
|
991351999e | ||
|
|
c76bca3f68 | ||
|
|
0529de2c1c | ||
|
|
b141031ccd | ||
|
|
6971632165 | ||
|
|
b9eb0f9a69 | ||
|
|
0924b5154e | ||
|
|
14597f8f16 | ||
|
|
fc69d6b1d4 | ||
|
|
321cfb8467 | ||
|
|
da9dbd8ad5 | ||
|
|
4626039e5c | ||
|
|
41f598c9ed | ||
|
|
f080536b3f | ||
|
|
af57fa84e4 | ||
|
|
46a262acfa | ||
|
|
a276ac366e | ||
|
|
82d1d5e701 | ||
|
|
305c13d720 | ||
|
|
0de02262d0 | ||
|
|
d581054214 | ||
|
|
e8acb225e4 | ||
|
|
6e36154b1b | ||
|
|
8f47e62dd2 | ||
|
|
a2c469d46a | ||
|
|
6f59955340 | ||
|
|
70e5b1d4aa | ||
|
|
f262347149 | ||
|
|
7168779684 | ||
|
|
d21715d8f1 | ||
|
|
1fe5e39254 | ||
|
|
023362797b | ||
|
|
772a682655 | ||
|
|
4252b66b6e | ||
|
|
91acb9d0c5 | ||
|
|
171b5085ab | ||
|
|
c38a23d9b1 | ||
|
|
92ecdd52ad | ||
|
|
8b093f9001 | ||
|
|
5797e70358 | ||
|
|
a75719e151 | ||
|
|
a5c9f41375 | ||
|
|
00d2154c0e | ||
|
|
0fc027e5e9 | ||
|
|
5f7abec980 | ||
|
|
6c4a82571c | ||
|
|
7ef01304ee | ||
|
|
8d66f62cad | ||
|
|
eaeae77be4 | ||
|
|
1477bd6eae | ||
|
|
fe8c1b927c | ||
|
|
a554158686 | ||
|
|
f6cb8f226a | ||
|
|
5224a7b304 | ||
|
|
b5bea16abb | ||
|
|
3441a66040 | ||
|
|
e69c47854a | ||
|
|
2c8d1b5db7 | ||
|
|
2446677172 | ||
|
|
abe9a85c4f | ||
|
|
a57bf56165 | ||
|
|
9a1c2b6a11 | ||
|
|
4b47e5de83 | ||
|
|
22c9b61246 | ||
|
|
e9db6cda61 | ||
|
|
44c49abbec | ||
|
|
8b6a3d0e3f | ||
|
|
d72f83ee95 | ||
|
|
ba35bc801f | ||
|
|
10a017503e | ||
|
|
bb1977fa93 | ||
|
|
cf4593917f | ||
|
|
ae2990d8d6 | ||
|
|
05e58b30cc | ||
|
|
2bc44dec9b | ||
|
|
48b74b0f6b | ||
|
|
516e5fd303 | ||
|
|
0241cacbf9 | ||
|
|
78da72f3cb | ||
|
|
660af9abfd | ||
|
|
b4f2b470dc | ||
|
|
f99dae331d | ||
|
|
655fe5f731 | ||
|
|
72968f2e6e | ||
|
|
0f2e3df164 | ||
|
|
818af41ea6 | ||
|
|
6153e88f62 | ||
|
|
6636fe3625 | ||
|
|
96d2a4cea8 | ||
|
|
2edfd1863b | ||
|
|
766c421b53 | ||
|
|
f909fe657f | ||
|
|
885140c92a | ||
|
|
6bf22795f3 | ||
|
|
3864264787 | ||
|
|
a3aaf36477 | ||
|
|
4f6babcdcc | ||
|
|
56763e1aca | ||
|
|
a787521977 | ||
|
|
ca1831271b | ||
|
|
d6a294b89f | ||
|
|
6b1fe301a6 | ||
|
|
8d003a7c35 | ||
|
|
db9e3a5401 | ||
|
|
01d4134a23 | ||
|
|
44164c0f43 | ||
|
|
68eec08de6 | ||
|
|
0e3cbe8c98 | ||
|
|
4ab4ac0f11 | ||
|
|
8cebc00f7a | ||
|
|
01f1cbbd75 | ||
|
|
6a304b3e07 | ||
|
|
5b674abcf1 | ||
|
|
e756053f8b | ||
|
|
69d0a3ad57 | ||
|
|
a57713523b | ||
|
|
9fc713dc5f | ||
|
|
31bb97aaec | ||
|
|
1d8dbbfe2c | ||
|
|
fc72d3b12a | ||
|
|
bdbbc5be47 | ||
|
|
f1cdb3a472 | ||
|
|
fdb4cd71eb | ||
|
|
90cd79a21b | ||
|
|
90c359779e | ||
|
|
0cd4688e64 | ||
|
|
9c42350caa | ||
|
|
1ac49415d1 | ||
|
|
bed46ba2eb | ||
|
|
02cdc869be | ||
|
|
4264d054ca | ||
|
|
b6f83f3275 | ||
|
|
1fc846bf5c | ||
|
|
2d2edabb18 | ||
|
|
dcfa43ab28 | ||
|
|
b0d7fff56a | ||
|
|
be4d56b7e1 | ||
|
|
fefd4fc7c7 | ||
|
|
855acccd6d | ||
|
|
80d6c0226a | ||
|
|
15dab4dfe5 | ||
|
|
dbb08a762a | ||
|
|
af144585a5 | ||
|
|
6a25ccfe96 | ||
|
|
dd84782427 | ||
|
|
012d953d65 | ||
|
|
5819cc75d3 | ||
|
|
5c3f0138f5 | ||
|
|
575676deb8 | ||
|
|
4b9eb44790 | ||
|
|
dc538f969e | ||
|
|
e32f6d3086 | ||
|
|
4bce4ff988 | ||
|
|
fe071a5435 | ||
|
|
6b58f3a080 | ||
|
|
1d61e33a51 | ||
|
|
79dc49b905 | ||
|
|
b81f98236c | ||
|
|
b7adf2814d | ||
|
|
5980f8e90e | ||
|
|
cfed9c2058 | ||
|
|
16440998de | ||
|
|
1264dbad73 | ||
|
|
3fb8a42856 | ||
|
|
4a4658e29d | ||
|
|
7ff934a5cb | ||
|
|
6d4147fa34 | ||
|
|
b2c71ceae9 | ||
|
|
4185cc37fe | ||
|
|
79a0a3684c | ||
|
|
dcd6c5fd5a | ||
|
|
cc24b9b599 | ||
|
|
1569306ea0 | ||
|
|
a1f0c9fd6e | ||
|
|
da22cd854b | ||
|
|
94105e937a | ||
|
|
f47a22068f | ||
|
|
177c933af4 | ||
|
|
bbc5a2a048 | ||
|
|
48ffe8fb7e | ||
|
|
e3d4a7a373 | ||
|
|
4ddf92aadb | ||
|
|
e226825bde | ||
|
|
d8a816a335 | ||
|
|
b9e7e24344 | ||
|
|
23ce8904a9 | ||
|
|
d1271799f4 | ||
|
|
e5726d2840 | ||
|
|
fcd5abfafc | ||
|
|
67c62fe85c | ||
|
|
f9271b747c | ||
|
|
f694f57638 | ||
|
|
522108bdd3 | ||
|
|
780d757d6b | ||
|
|
df8b4e302f | ||
|
|
1bb66bbf7d | ||
|
|
a1299fea97 | ||
|
|
221e49a5d1 | ||
|
|
d123b2d5d4 | ||
|
|
4f2fc5c99e | ||
|
|
e429dc3ff1 | ||
|
|
9e7a051d25 | ||
|
|
75f38173c5 | ||
|
|
4cd6ffcaec | ||
|
|
12807457d4 | ||
|
|
aefb27fc5c | ||
|
|
cd32bcf961 | ||
|
|
16f275af52 | ||
|
|
419f1bd698 | ||
|
|
e00416e1b8 | ||
|
|
daaa4284cb | ||
|
|
33e70cd3f3 | ||
|
|
44e56c92d0 | ||
|
|
6404536d19 | ||
|
|
e7b7e9cefe | ||
|
|
174b6f365b | ||
|
|
08a76aef97 | ||
|
|
bd61c354f0 | ||
|
|
13144aad3b | ||
|
|
b163e59563 | ||
|
|
09fa2be5bd | ||
|
|
36c931b596 | ||
|
|
6040838396 | ||
|
|
1a81abebaa | ||
|
|
a191c7a382 | ||
|
|
907df81436 | ||
|
|
8f01b0c739 | ||
|
|
8c974e6c1a | ||
|
|
e73ed8ae44 | ||
|
|
f59b078faa | ||
|
|
6319b6f531 | ||
|
|
9671fbf506 | ||
|
|
670d2bc382 | ||
|
|
6f6338bb64 | ||
|
|
1528b32cc7 | ||
|
|
e1b155b455 | ||
|
|
72ae466541 | ||
|
|
5cc14bb729 | ||
|
|
79a5d61efb | ||
|
|
c664648eb6 | ||
|
|
df6d957721 | ||
|
|
ee9ef2f0e4 | ||
|
|
383ccdbc82 | ||
|
|
225d039f89 | ||
|
|
7f74d5a3d9 | ||
|
|
862068eeb5 | ||
|
|
56607a1baf | ||
|
|
750a41f19a | ||
|
|
6651b02ba2 | ||
|
|
3054c04261 | ||
|
|
04132023e6 | ||
|
|
60bd406eac | ||
|
|
7dc7662adf | ||
|
|
047138984a | ||
|
|
1a7bfee6b6 | ||
|
|
ee4c1c0706 | ||
|
|
eea5cf8d0b | ||
|
|
67076cdffb | ||
|
|
80785304f1 | ||
|
|
c934b3d9c0 | ||
|
|
794c1999bc | ||
|
|
aaed279c50 | ||
|
|
bd6a430d3a | ||
|
|
746afe0f60 | ||
|
|
1b807bd6f3 | ||
|
|
6e26040e19 | ||
|
|
6fde77ac3f | ||
|
|
296e8f09e5 | ||
|
|
3b43cabc68 | ||
|
|
cc34d56a0e | ||
|
|
6080e800d7 | ||
|
|
f2587aa385 | ||
|
|
cf8c4c482d | ||
|
|
8986a18dac | ||
|
|
9829cd4027 | ||
|
|
2e2da46d84 | ||
|
|
37e91733ca | ||
|
|
c29719f49d | ||
|
|
a5a2028da7 | ||
|
|
b8ad85b2e2 | ||
|
|
50b45678e3 | ||
|
|
bcce9ac76a | ||
|
|
137e8e7290 | ||
|
|
a22195beec | ||
|
|
99af190e29 | ||
|
|
01736318d8 | ||
|
|
f0108a5069 | ||
|
|
f4f53ec21e | ||
|
|
8e76c699dd | ||
|
|
256d5e1bac | ||
|
|
5217c0ac64 | ||
|
|
a87bbc2de5 | ||
|
|
d3f0abf3ef | ||
|
|
9b73d4e9a5 | ||
|
|
4f632174ce | ||
|
|
263ff962a1 | ||
|
|
b9414a77ab | ||
|
|
f180617a3f | ||
|
|
5919580b5d | ||
|
|
25213605bb | ||
|
|
c7309d7dc8 | ||
|
|
7e2c60e495 | ||
|
|
a98320783f | ||
|
|
c196e11fde | ||
|
|
414f0fbb26 | ||
|
|
8286fb68a4 | ||
|
|
64615ea6fa | ||
|
|
aa64e86a43 | ||
|
|
2cd3920532 | ||
|
|
23bd56a854 | ||
|
|
307d38c13d | ||
|
|
f1490e7b92 | ||
|
|
8148209fc8 | ||
|
|
f3597616ea | ||
|
|
34d7a3bf52 | ||
|
|
1a1d51f427 | ||
|
|
0d9c78c60b | ||
|
|
2644dc9305 | ||
|
|
c00e09f40b | ||
|
|
3526cadc96 | ||
|
|
c514cb64bc | ||
|
|
225c998a97 | ||
|
|
90f07c28f9 | ||
|
|
ad012cfd60 | ||
|
|
5bbdecbec2 | ||
|
|
9065a8038a | ||
|
|
8c2bcc92b5 | ||
|
|
bc86ff1e04 | ||
|
|
4248cbcb16 | ||
|
|
2d076358d3 | ||
|
|
2d026b5ca5 | ||
|
|
b552882011 | ||
|
|
de273973b4 | ||
|
|
3aec2828b4 | ||
|
|
533d7110c7 | ||
|
|
32196b56db | ||
|
|
5db79ce218 | ||
|
|
9e97c11401 | ||
|
|
af09691964 | ||
|
|
fe41798b19 | ||
|
|
09d469927f | ||
|
|
e01e0adac8 | ||
|
|
fb63689573 | ||
|
|
74177e4d04 | ||
|
|
ac06a4d4ce | ||
|
|
fd40cb71ac | ||
|
|
df019f515f | ||
|
|
5a106e1613 | ||
|
|
6d899008ca | ||
|
|
ee1f56ad27 | ||
|
|
61e25184fc | ||
|
|
4e9a187c85 | ||
|
|
60ae5f0eb2 | ||
|
|
28f7811e86 | ||
|
|
876b21437f | ||
|
|
1ca1a8c249 | ||
|
|
cdd1131699 | ||
|
|
6ce64828e8 | ||
|
|
1fc15061ed | ||
|
|
0ee725b945 | ||
|
|
2f17e94fef | ||
|
|
1c9da902d3 | ||
|
|
7e11806bf9 | ||
|
|
f2efd3fb49 | ||
|
|
54444ddf3b | ||
|
|
a64a8172be | ||
|
|
3b4731d6b7 | ||
|
|
2cb5aecdcb | ||
|
|
277bdd2b1c | ||
|
|
bd9da51e83 | ||
|
|
e5ce053a62 | ||
|
|
ec56079635 | ||
|
|
78a349fecd | ||
|
|
8c7846a1fa | ||
|
|
2af7e85f81 | ||
|
|
441834edbf | ||
|
|
28f9963f87 | ||
|
|
e964496759 | ||
|
|
ca14a0d4b3 | ||
|
|
989cb0bca2 | ||
|
|
25b0d872c0 | ||
|
|
4016fdd430 | ||
|
|
55f0d5a959 | ||
|
|
6395d7cd6a | ||
|
|
7812e3757d | ||
|
|
93540b4fc6 | ||
|
|
c063832d0d | ||
|
|
97ce3e6b72 | ||
|
|
bd39312b19 | ||
|
|
22faf5c285 | ||
|
|
c82143fd2e | ||
|
|
9b026ebbcd | ||
|
|
4e8d3ce900 | ||
|
|
df3ad5445c | ||
|
|
9113ca54f7 | ||
|
|
f3c26671af | ||
|
|
e4b1c0d5b9 | ||
|
|
3364263247 | ||
|
|
61c48d37e4 | ||
|
|
5b83b0c7b6 | ||
|
|
7e43b13f21 | ||
|
|
57791e384b | ||
|
|
63b20c4ba2 | ||
|
|
e11eea7548 | ||
|
|
f7df4c1fa0 | ||
|
|
4457997276 | ||
|
|
16628ab104 | ||
|
|
56b765c6a5 | ||
|
|
3bf1224c9b | ||
|
|
edbd661104 | ||
|
|
8ef222ab44 | ||
|
|
654c8d26b7 | ||
|
|
31ea7a5ab5 | ||
|
|
342441ecf8 | ||
|
|
df37fd8b92 | ||
|
|
32d732caf0 | ||
|
|
3e2173db7a | ||
|
|
17ee62f4de | ||
|
|
f363257e51 | ||
|
|
865bf096f5 | ||
|
|
c4802df024 | ||
|
|
130194a740 | ||
|
|
21f63c15fc | ||
|
|
bb9e8c0348 | ||
|
|
16cd231ce3 | ||
|
|
20497e49e3 | ||
|
|
b97bf2b25f | ||
|
|
4f7a5d6ef5 | ||
|
|
f321abc63a | ||
|
|
4b0ef6086a | ||
|
|
b57a8163fe | ||
|
|
86a6f5c67b | ||
|
|
e40b42c213 | ||
|
|
2f6bc1c4ff | ||
|
|
958f0b4958 | ||
|
|
0c720bb902 | ||
|
|
34b3f2ac0c | ||
|
|
ef4de28ed4 | ||
|
|
c98d580b29 | ||
|
|
a2460f9368 | ||
|
|
c0eac04437 | ||
|
|
7f9be26541 | ||
|
|
c8ea3da5bc | ||
|
|
a70287870b | ||
|
|
e4ec5313db | ||
|
|
a07d918fbf | ||
|
|
8da98367db | ||
|
|
af5489bf57 | ||
|
|
54103124e8 | ||
|
|
459c040d8a | ||
|
|
f248f81583 | ||
|
|
a6d8dcc5b6 | ||
|
|
b4d6667f88 | ||
|
|
77b80e27c6 | ||
|
|
16d04e998f | ||
|
|
d52d7582ca | ||
|
|
e817a3b3d0 | ||
|
|
2d6f8e4ea6 | ||
|
|
fbe412db08 | ||
|
|
24f01e72c2 | ||
|
|
6970acfaa4 | ||
|
|
0a49031b01 | ||
|
|
29e147ee86 | ||
|
|
c21ec86365 | ||
|
|
825c45f872 | ||
|
|
3ad4352ffe | ||
|
|
a45dc96885 | ||
|
|
b041ec6e65 | ||
|
|
257d731e04 | ||
|
|
720a946d39 | ||
|
|
5ad3662640 | ||
|
|
667e38ff0c | ||
|
|
76196bf6f4 | ||
|
|
5435c2dd04 | ||
|
|
a70ea38572 | ||
|
|
5e66e8e299 | ||
|
|
44af47f69e | ||
|
|
54224637b6 | ||
|
|
d9896192f3 | ||
|
|
85ad849189 | ||
|
|
2c5f598a23 | ||
|
|
ac30c82165 | ||
|
|
6994c029b8 | ||
|
|
977406de76 | ||
|
|
7d018221ef | ||
|
|
662d06d24a | ||
|
|
9897cee779 | ||
|
|
1f83ba9d50 | ||
|
|
e40a19cf89 | ||
|
|
6bc471ad9a | ||
|
|
b1453e1d7e | ||
|
|
88cab67f6d | ||
|
|
e1ecf797f5 | ||
|
|
a56dbeb6a9 | ||
|
|
76360958a2 | ||
|
|
aa623c5171 | ||
|
|
b93cbeee26 | ||
|
|
f3c2d3ca09 | ||
|
|
24edb2bce8 | ||
|
|
05232526a7 | ||
|
|
8c4cb64246 | ||
|
|
ccbaea6999 | ||
|
|
75206f1f2c | ||
|
|
18a0422367 | ||
|
|
2f533f6dae | ||
|
|
4df323eddb | ||
|
|
9435a22905 | ||
|
|
15bbfeda3a | ||
|
|
617e6f6454 | ||
|
|
f65076defd | ||
|
|
2d5f2d88fa | ||
|
|
eb5ca2cba6 | ||
|
|
54cd55ebef | ||
|
|
5f4ab6eaaf | ||
|
|
1d8e6b6295 | ||
|
|
aa791b2571 | ||
|
|
92d68de332 | ||
|
|
51d2372976 |
No files matched your search
+27
-12
@@ -1,18 +1,33 @@
|
||||
config.make
|
||||
tags
|
||||
*._*
|
||||
*/obj/
|
||||
*._*
|
||||
*/*/obj/
|
||||
*/*/*/obj/
|
||||
*/*/*.SOGo/
|
||||
*.css
|
||||
*.pyc
|
||||
*.sax
|
||||
*.wox/
|
||||
*.swp
|
||||
*.wox/
|
||||
*/*/*.SOGo/
|
||||
*/*/*/obj/
|
||||
*/*/obj/
|
||||
*/obj/
|
||||
.scss-lint-config.yml_
|
||||
Documentation/*.docbook
|
||||
Documentation/*.pdf
|
||||
SoObjects/SOGo/SOGo.framework/
|
||||
SoObjects/SOGo/derived_src/
|
||||
Tests/*/config.py
|
||||
*.pyc
|
||||
._*
|
||||
Documentation/*.docbook
|
||||
Documentation/*.pdf
|
||||
UI/WebServerResources/.bowerrc
|
||||
UI/WebServerResources/.sass-cache/
|
||||
UI/WebServerResources/bower_components/
|
||||
UI/WebServerResources/css/
|
||||
UI/WebServerResources/css/styles.css
|
||||
UI/WebServerResources/css/styles.css.map
|
||||
UI/WebServerResources/js/Common.js*
|
||||
UI/WebServerResources/js/Contacts.js*
|
||||
UI/WebServerResources/js/Mailer.js*
|
||||
UI/WebServerResources/js/Preferences.js*
|
||||
UI/WebServerResources/js/Scheduler.js*
|
||||
UI/WebServerResources/js/vendor/
|
||||
UI/WebServerResources/node_modules/
|
||||
UI/WebServerResources/scss/.sass-cache/
|
||||
config.make
|
||||
doc
|
||||
tags
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "UI/WebServerResources/angular-material"]
|
||||
path = UI/WebServerResources/angular-material
|
||||
url = https://github.com/angular/material
|
||||
+22
@@ -25,8 +25,10 @@ trans.pl = UI/MailerUI/Polish.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/MailerUI/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = UI/MailerUI/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/MailerUI/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = UI/MailerUI/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = UI/MailerUI/Swedish.lproj/Localizable.strings
|
||||
trans.uk = UI/MailerUI/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/MailerUI/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-preferencesui]
|
||||
source_file = UI/PreferencesUI/English.lproj/Localizable.strings
|
||||
@@ -52,8 +54,10 @@ trans.pl = UI/PreferencesUI/Polish.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = UI/PreferencesUI/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/PreferencesUI/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = UI/PreferencesUI/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = UI/PreferencesUI/Swedish.lproj/Localizable.strings
|
||||
trans.uk = UI/PreferencesUI/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-scheduler]
|
||||
source_file = UI/Scheduler/English.lproj/Localizable.strings
|
||||
@@ -79,8 +83,10 @@ trans.pl = UI/Scheduler/Polish.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = UI/Scheduler/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/Scheduler/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = UI/Scheduler/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = UI/Scheduler/Swedish.lproj/Localizable.strings
|
||||
trans.uk = UI/Scheduler/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/Scheduler/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-contacts]
|
||||
source_file = UI/Contacts/English.lproj/Localizable.strings
|
||||
@@ -106,8 +112,10 @@ trans.pl = UI/Contacts/Polish.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/Contacts/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = UI/Contacts/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/Contacts/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = UI/Contacts/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = UI/Contacts/Swedish.lproj/Localizable.strings
|
||||
trans.uk = UI/Contacts/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/Contacts/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-mainui]
|
||||
source_file = UI/MainUI/English.lproj/Localizable.strings
|
||||
@@ -133,8 +141,10 @@ trans.pl = UI/MainUI/Polish.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/MainUI/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = UI/MainUI/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/MainUI/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = UI/MainUI/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = UI/MainUI/Swedish.lproj/Localizable.strings
|
||||
trans.uk = UI/MainUI/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/MainUI/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-common]
|
||||
source_file = UI/Common/English.lproj/Localizable.strings
|
||||
@@ -160,8 +170,10 @@ trans.pl = UI/Common/Polish.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/Common/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = UI/Common/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/Common/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = UI/Common/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = UI/Common/Swedish.lproj/Localizable.strings
|
||||
trans.uk = UI/Common/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/Common/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-administrationui]
|
||||
source_file = UI/AdministrationUI/English.lproj/Localizable.strings
|
||||
@@ -187,8 +199,10 @@ trans.pl = UI/AdministrationUI/Polish.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/AdministrationUI/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = UI/AdministrationUI/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/AdministrationUI/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = UI/AdministrationUI/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = UI/AdministrationUI/Swedish.lproj/Localizable.strings
|
||||
trans.uk = UI/AdministrationUI/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/AdministrationUI/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.soobjects-appointments]
|
||||
source_file = SoObjects/Appointments/English.lproj/Localizable.strings
|
||||
@@ -214,8 +228,10 @@ trans.pl = SoObjects/Appointments/Polish.lproj/Localizable.strings
|
||||
trans.pt_BR = SoObjects/Appointments/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = SoObjects/Appointments/Russian.lproj/Localizable.strings
|
||||
trans.sk = SoObjects/Appointments/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = SoObjects/Appointments/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = SoObjects/Appointments/Swedish.lproj/Localizable.strings
|
||||
trans.uk = SoObjects/Appointments/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = SoObjects/Appointments/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.soobjects-contacts]
|
||||
source_file = SoObjects/Contacts/English.lproj/Localizable.strings
|
||||
@@ -241,8 +257,10 @@ trans.pl = SoObjects/Contacts/Polish.lproj/Localizable.strings
|
||||
trans.pt_BR = SoObjects/Contacts/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = SoObjects/Contacts/Russian.lproj/Localizable.strings
|
||||
trans.sk = SoObjects/Contacts/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = SoObjects/Contacts/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = SoObjects/Contacts/Swedish.lproj/Localizable.strings
|
||||
trans.uk = SoObjects/Contacts/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = SoObjects/Contacts/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.soobjects-mailer]
|
||||
source_file = SoObjects/Mailer/English.lproj/Localizable.strings
|
||||
@@ -268,8 +286,10 @@ trans.pl = SoObjects/Mailer/Polish.lproj/Localizable.strings
|
||||
trans.pt_BR = SoObjects/Mailer/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = SoObjects/Mailer/Russian.lproj/Localizable.strings
|
||||
trans.sk = SoObjects/Mailer/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = SoObjects/Mailer/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = SoObjects/Mailer/Swedish.lproj/Localizable.strings
|
||||
trans.uk = SoObjects/Mailer/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = SoObjects/Mailer/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-mailpartviewers]
|
||||
source_file = UI/MailPartViewers/English.lproj/Localizable.strings
|
||||
@@ -295,5 +315,7 @@ trans.pl = UI/MailPartViewers/Polish.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/MailPartViewers/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = UI/MailPartViewers/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/MailPartViewers/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = UI/MailPartViewers/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = UI/MailPartViewers/Swedish.lproj/Localizable.strings
|
||||
trans.uk = UI/MailPartViewers/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/MailPartViewers/ChineseTaiwan.lproj/Localizable.strings
|
||||
@@ -66,7 +66,33 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
return [[self stringByEncodingBase64] stringByReplacingString: @"\n" withString: @""];
|
||||
NSString *tmp, *s;
|
||||
unichar *buf, *start, c;
|
||||
int len, i, j;
|
||||
|
||||
tmp = [self stringByEncodingBase64] ;
|
||||
|
||||
len = [tmp length];
|
||||
|
||||
start = buf = (unichar *)malloc(len*sizeof(unichar));
|
||||
[tmp getCharacters: buf range: NSMakeRange(0, len)];
|
||||
|
||||
for (i = 0, j = 0; i < len; i++)
|
||||
{
|
||||
c = *buf;
|
||||
|
||||
if (!(c == 0xA))
|
||||
{
|
||||
*(start+j) = c;
|
||||
j++;
|
||||
}
|
||||
|
||||
buf++;
|
||||
}
|
||||
|
||||
s = [[NSString alloc] initWithCharactersNoCopy: start length: j freeWhenDone: YES];
|
||||
|
||||
return AUTORELEASE(s);
|
||||
}
|
||||
|
||||
- (NSData *) wbxml2xml
|
||||
|
||||
@@ -46,6 +46,99 @@ static NSArray *easCommandParameters = nil;
|
||||
|
||||
@implementation NSString (ActiveSync)
|
||||
|
||||
//
|
||||
// This is a copy from NSString+XMLEscaping.m from SOPE.
|
||||
// The difference here is that we use wchar_t instead of unichar.
|
||||
// This is needed to get the rigth numeric character reference.
|
||||
// e.g. SMILING FACE WITH OPEN MOUTH
|
||||
// ok: wchar_t -> 😃 wrong: unichar -> � �
|
||||
//
|
||||
// We avoir naming it like the one in SOPE since if the ActiveSync
|
||||
// bundle is loaded, it'll overwrite the one provided by SOPE.
|
||||
//
|
||||
- (NSString *) _stringByEscapingXMLStringUsingCharacters {
|
||||
register unsigned i, len, j;
|
||||
register wchar_t *buf;
|
||||
const wchar_t *chars;
|
||||
unsigned escapeCount;
|
||||
|
||||
if ([self length] == 0) return @"";
|
||||
|
||||
NSData *data = [self dataUsingEncoding:NSUTF32StringEncoding];
|
||||
chars = [data bytes];
|
||||
len = [data length]/4;
|
||||
|
||||
/* check for characters to escape ... */
|
||||
for (i = 0, escapeCount = 0; i < len; i++) {
|
||||
switch (chars[i]) {
|
||||
case '&': case '"': case '<': case '>': case '\r':
|
||||
escapeCount++;
|
||||
break;
|
||||
default:
|
||||
if (chars[i] > 127)
|
||||
escapeCount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (escapeCount == 0 ) {
|
||||
/* nothing to escape ... */
|
||||
return [[self copy] autorelease];
|
||||
}
|
||||
|
||||
buf = calloc((len + 5) + (escapeCount * 16), sizeof(wchar_t));
|
||||
for (i = 0, j = 0; i < len; i++) {
|
||||
switch (chars[i]) {
|
||||
/* escape special chars */
|
||||
case '\r':
|
||||
buf[j] = '&'; j++; buf[j] = '#'; j++; buf[j] = '1'; j++;
|
||||
buf[j] = '3'; j++; buf[j] = ';'; j++;
|
||||
break;
|
||||
case '&':
|
||||
buf[j] = '&'; j++; buf[j] = 'a'; j++; buf[j] = 'm'; j++;
|
||||
buf[j] = 'p'; j++; buf[j] = ';'; j++;
|
||||
break;
|
||||
case '"':
|
||||
buf[j] = '&'; j++; buf[j] = 'q'; j++; buf[j] = 'u'; j++;
|
||||
buf[j] = 'o'; j++; buf[j] = 't'; j++; buf[j] = ';'; j++;
|
||||
break;
|
||||
case '<':
|
||||
buf[j] = '&'; j++; buf[j] = 'l'; j++; buf[j] = 't'; j++;
|
||||
buf[j] = ';'; j++;
|
||||
break;
|
||||
case '>':
|
||||
buf[j] = '&'; j++; buf[j] = 'g'; j++; buf[j] = 't'; j++;
|
||||
buf[j] = ';'; j++;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* escape big chars */
|
||||
if (chars[i] > 127) {
|
||||
unsigned char nbuf[32];
|
||||
unsigned int k;
|
||||
|
||||
sprintf((char *)nbuf, "&#%i;", (int)chars[i]);
|
||||
for (k = 0; nbuf[k] != '\0'; k++) {
|
||||
buf[j] = nbuf[k];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
else if (chars[i] == 0x9 || chars[i] == 0xA || chars[i] == 0xD || chars[i] >= 0x20) { // ignore any unsupported control character
|
||||
/* nothing to escape */
|
||||
buf[j] = chars[i];
|
||||
j++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self = [[NSString alloc] initWithBytesNoCopy: buf
|
||||
length: (j*sizeof(wchar_t))
|
||||
encoding: NSUTF32StringEncoding
|
||||
freeWhenDone: YES];
|
||||
|
||||
return [self autorelease];
|
||||
}
|
||||
|
||||
- (NSString *) sanitizedServerIdWithType: (SOGoMicrosoftActiveSyncFolderType) folderType
|
||||
{
|
||||
if (folderType == ActiveSyncEventFolder)
|
||||
@@ -65,11 +158,7 @@ static NSArray *easCommandParameters = nil;
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
NSString *s;
|
||||
|
||||
s = [self safeString];
|
||||
|
||||
return [s stringByEscapingHTMLString];
|
||||
return [self _stringByEscapingXMLStringUsingCharacters];
|
||||
}
|
||||
|
||||
- (int) activeSyncFolderType
|
||||
|
||||
@@ -283,7 +283,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
serverId = [NSString stringWithFormat: @"%@.ics", [theCollection globallyUniqueObjectId]];
|
||||
sogoObject = [[SOGoTaskObject alloc] initWithName: serverId
|
||||
inContainer: theCollection];
|
||||
o = [sogoObject component: YES secure: NO];
|
||||
o = [sogoObject component: YES secure: NO];
|
||||
}
|
||||
break;
|
||||
case ActiveSyncMailFolder:
|
||||
@@ -311,10 +311,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
||||
dateCache = [folderMetadata objectForKey: @"DateCache"];
|
||||
|
||||
[syncCache setObject: [folderMetadata objectForKey: @"SyncKey"] forKey: serverId];
|
||||
|
||||
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
|
||||
[dateCache setObject: [NSCalendarDate date] forKey: serverId];
|
||||
|
||||
|
||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
}
|
||||
}
|
||||
@@ -365,6 +365,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
NSString *serverId;
|
||||
NSArray *changes;
|
||||
id aChange, o, sogoObject;
|
||||
NSMutableDictionary *folderMetadata, *syncCache;
|
||||
|
||||
int i;
|
||||
|
||||
@@ -372,6 +373,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if ([changes count])
|
||||
{
|
||||
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
||||
|
||||
for (i = 0; i < [changes count]; i++)
|
||||
{
|
||||
aChange = [changes objectAtIndex: i];
|
||||
@@ -398,6 +402,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
o = [sogoObject vCard];
|
||||
[o takeActiveSyncValues: allChanges inContext: context];
|
||||
[sogoObject saveComponent: o];
|
||||
|
||||
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
|
||||
|
||||
}
|
||||
break;
|
||||
case ActiveSyncEventFolder:
|
||||
@@ -406,15 +413,30 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
o = [sogoObject component: NO secure: NO];
|
||||
[o takeActiveSyncValues: allChanges inContext: context];
|
||||
[sogoObject saveComponent: o];
|
||||
|
||||
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
|
||||
|
||||
}
|
||||
break;
|
||||
case ActiveSyncMailFolder:
|
||||
default:
|
||||
{
|
||||
NSDictionary *result;
|
||||
NSString *modseq;
|
||||
|
||||
[sogoObject takeActiveSyncValues: allChanges inContext: context];
|
||||
|
||||
result = [sogoObject fetchParts: [NSArray arrayWithObject: @"MODSEQ"]];
|
||||
modseq = [[[result objectForKey: @"RawResponse"] objectForKey: @"fetch"] objectForKey: @"modseq"];
|
||||
|
||||
if (modseq)
|
||||
[syncCache setObject: modseq forKey: serverId];
|
||||
}
|
||||
}
|
||||
|
||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
|
||||
|
||||
[theBuffer appendString: @"<Change>"];
|
||||
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", serverId];
|
||||
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
|
||||
@@ -453,16 +475,27 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
||||
inBuffer: (NSMutableString *) theBuffer
|
||||
{
|
||||
|
||||
id aDelete, sogoObject, value;
|
||||
NSArray *deletions;
|
||||
NSString *serverId;
|
||||
|
||||
id aDelete, sogoObject;
|
||||
BOOL deletesAsMoves, useTrash;
|
||||
int i;
|
||||
|
||||
deletions = (id)[theDocumentElement getElementsByTagName: @"Delete"];
|
||||
|
||||
if ([deletions count])
|
||||
{
|
||||
// From the documention, if DeletesAsMoves is missing, we must assume it's a YES.
|
||||
// See https://msdn.microsoft.com/en-us/library/gg675480(v=exchg.80).aspx for all details.
|
||||
value = [theDocumentElement getElementsByTagName: @"DeletesAsMoves"];
|
||||
deletesAsMoves = YES;
|
||||
useTrash = YES;
|
||||
|
||||
if ([value count] && [[[value lastObject] textValue] length])
|
||||
deletesAsMoves = [[[value lastObject] textValue] boolValue];
|
||||
|
||||
for (i = 0; i < [deletions count]; i++)
|
||||
{
|
||||
aDelete = [deletions objectAtIndex: i];
|
||||
@@ -474,7 +507,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
acquire: NO];
|
||||
|
||||
if (![sogoObject isKindOfClass: [NSException class]])
|
||||
[sogoObject delete];
|
||||
{
|
||||
// FIXME: handle errors here
|
||||
if (deletesAsMoves && theFolderType == ActiveSyncMailFolder)
|
||||
[(SOGoMailFolder *)[sogoObject container] deleteUIDs: [NSArray arrayWithObjects: serverId, nil] useTrashFolder: &useTrash inContext: context];
|
||||
else
|
||||
[sogoObject delete];
|
||||
}
|
||||
|
||||
[theBuffer appendString: @"<Delete>"];
|
||||
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", serverId];
|
||||
@@ -893,6 +932,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
[syncCache removeObjectForKey: [aCacheObject uid]];
|
||||
[dateCache removeObjectForKey: [aCacheObject uid]];
|
||||
|
||||
return_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -902,17 +943,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
inContext: context
|
||||
acquire: 0];
|
||||
|
||||
[s appendString: @"<Change xmlns=\"AirSync:\">"];
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", [aCacheObject uid]];
|
||||
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
|
||||
[s appendString: [mailObject activeSyncRepresentationInContext: context]];
|
||||
[s appendString: @"</ApplicationData>"];
|
||||
[s appendString: @"</Change>"];
|
||||
|
||||
if (![[aCacheObject sequence] isEqual: [syncCache objectForKey: [aCacheObject uid]]])
|
||||
{
|
||||
[s appendString: @"<Change xmlns=\"AirSync:\">"];
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", [aCacheObject uid]];
|
||||
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
|
||||
[s appendString: [mailObject activeSyncRepresentationInContext: context]];
|
||||
[s appendString: @"</ApplicationData>"];
|
||||
[s appendString: @"</Change>"];
|
||||
|
||||
return_count++;
|
||||
}
|
||||
|
||||
[syncCache setObject: [aCacheObject sequence] forKey: [aCacheObject uid]];
|
||||
}
|
||||
|
||||
return_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1187,22 +1231,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
|
||||
|
||||
// We generate the commands, if any, for the response. We might also have
|
||||
// generated some in processSyncCommand:inResponse: as we could have
|
||||
// received a Fetch command
|
||||
if (getChanges && !first_sync)
|
||||
{
|
||||
[self processSyncGetChanges: theDocumentElement
|
||||
inCollection: collection
|
||||
withWindowSize: windowSize
|
||||
withMaxSyncResponseSize: theMaxSyncResponseSize
|
||||
withSyncKey: syncKey
|
||||
withFolderType: folderType
|
||||
withFilterType: [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]]
|
||||
inBuffer: changeBuffer
|
||||
lastServerKey: &lastServerKey];
|
||||
}
|
||||
|
||||
//
|
||||
// We process the commands from the request
|
||||
//
|
||||
@@ -1220,12 +1248,29 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
inBuffer: s
|
||||
processed: &processed];
|
||||
|
||||
// Windows phons don't empty Responses tags - such as: <Responses></Responses>.
|
||||
// We onnly generate this tag when the command has generated a response.
|
||||
// Windows phones don't like empty Responses tags - such as: <Responses></Responses>.
|
||||
// We only generate this tag when there is a response
|
||||
if (processed && [s length])
|
||||
[commandsBuffer appendFormat: @"<Responses>%@</Responses>", s];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// We generate the commands, if any, for the response. We might also have
|
||||
// generated some in processSyncCommand:inResponse: as we could have
|
||||
// received a Fetch command
|
||||
if (getChanges && !first_sync)
|
||||
{
|
||||
[self processSyncGetChanges: theDocumentElement
|
||||
inCollection: collection
|
||||
withWindowSize: windowSize
|
||||
withMaxSyncResponseSize: theMaxSyncResponseSize
|
||||
withSyncKey: syncKey
|
||||
withFolderType: folderType
|
||||
withFilterType: [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]]
|
||||
inBuffer: changeBuffer
|
||||
lastServerKey: &lastServerKey];
|
||||
}
|
||||
|
||||
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]];
|
||||
|
||||
// If we got any changes or if we have applied any commands
|
||||
@@ -1250,7 +1295,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{
|
||||
// Make sure that client is updated with the right syncKey. - This keeps vtodo's and vevent's syncKey in sync.
|
||||
syncKeyInCache = [folderMetadata objectForKey: @"SyncKey"];
|
||||
if (syncKeyInCache && !([davCollectionTag isEqualToString:syncKeyInCache]) && ![davCollectionTag isEqualToString: @"-1"])
|
||||
if (syncKeyInCache && !([davCollectionTag isEqualToString:syncKeyInCache]) && !first_sync)
|
||||
{
|
||||
davCollectionTag = syncKeyInCache;
|
||||
*changeDetected = YES;
|
||||
|
||||
@@ -453,7 +453,7 @@ static BOOL debugOn = NO;
|
||||
[s appendString: @"<FolderCreate xmlns=\"FolderHierarchy:\">"];
|
||||
[s appendFormat: @"<Status>%d</Status>", 1];
|
||||
[s appendFormat: @"<SyncKey>%@</SyncKey>", syncKey];
|
||||
[s appendFormat: @"<ServerId>%@</ServerId>", nameInContainer];
|
||||
[s appendFormat: @"<ServerId>%@</ServerId>", [nameInContainer stringByEscapingURL]];
|
||||
[s appendString: @"</FolderCreate>"];
|
||||
|
||||
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
||||
@@ -699,7 +699,7 @@ static BOOL debugOn = NO;
|
||||
SOGoMailAccount *accountFolder;
|
||||
NSMutableString *s, *commands;
|
||||
SOGoUserFolder *userFolder;
|
||||
NSMutableArray *folders;
|
||||
NSMutableArray *folders, *processedFolders;
|
||||
SoSecurityManager *sm;
|
||||
SOGoCacheGCSObject *o;
|
||||
id currentFolder;
|
||||
@@ -719,6 +719,8 @@ static BOOL debugOn = NO;
|
||||
command_count = 0;
|
||||
commands = [NSMutableString string];
|
||||
|
||||
processedFolders = [NSMutableArray array];
|
||||
|
||||
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||
|
||||
@@ -727,7 +729,7 @@ static BOOL debugOn = NO;
|
||||
first_sync = YES;
|
||||
syncKey = @"1";
|
||||
}
|
||||
else if (![syncKey isEqualToString: [metadata objectForKey: @"FolderSyncKey"]])
|
||||
else if (![metadata objectForKey: @"FolderSyncKey"])
|
||||
{
|
||||
// Synchronization key mismatch or invalid synchronization key
|
||||
//NSLog(@"FolderSync syncKey mismatch %@ <> %@", syncKey, metadata);
|
||||
@@ -862,7 +864,13 @@ static BOOL debugOn = NO;
|
||||
|
||||
serverId = [NSString stringWithFormat: @"mail/%@", [[imapGUIDs objectForKey: nameInCache] substringFromIndex: 6]];
|
||||
name = [folderMetadata objectForKey: @"displayName"];
|
||||
|
||||
|
||||
// avoid duplicate folders if folder is returned by different imap namespaces
|
||||
if ([processedFolders indexOfObject: serverId] == NSNotFound)
|
||||
[processedFolders addObject: serverId];
|
||||
else
|
||||
continue;
|
||||
|
||||
if ([name hasPrefix: @"/"])
|
||||
name = [name substringFromIndex: 1];
|
||||
|
||||
@@ -874,7 +882,10 @@ static BOOL debugOn = NO;
|
||||
|
||||
if ([folderMetadata objectForKey: @"parent"])
|
||||
{
|
||||
parentId = [NSString stringWithFormat: @"mail/%@", [[imapGUIDs objectForKey: [NSString stringWithFormat: @"folder%@", [[folderMetadata objectForKey: @"parent"] substringFromIndex: 1]]] substringFromIndex: 6]];
|
||||
// make sure that parent of main-folders is always 0
|
||||
if (type == 12)
|
||||
parentId = [NSString stringWithFormat: @"mail/%@", [[imapGUIDs objectForKey: [NSString stringWithFormat: @"folder%@", [[folderMetadata objectForKey: @"parent"] substringFromIndex: 1]]] substringFromIndex: 6]];
|
||||
|
||||
name = [[name pathComponents] lastObject];
|
||||
}
|
||||
|
||||
@@ -1084,6 +1095,8 @@ static BOOL debugOn = NO;
|
||||
SOGoMailAccounts *accountsFolder;
|
||||
SOGoUserFolder *userFolder;
|
||||
SOGoMailObject *mailObject;
|
||||
NSArray *partKeys;
|
||||
int p;
|
||||
|
||||
NSRange r1, r2;
|
||||
|
||||
@@ -1103,7 +1116,14 @@ static BOOL debugOn = NO;
|
||||
acquire: NO];
|
||||
|
||||
mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO];
|
||||
currentBodyPart = [mailObject lookupImap4BodyPartKey: pathToPart inContext: context];
|
||||
|
||||
partKeys = [pathToPart componentsSeparatedByString: @"."];
|
||||
|
||||
currentBodyPart = [mailObject lookupImap4BodyPartKey: [partKeys objectAtIndex:0] inContext: context];
|
||||
for (p = 1; p < [partKeys count]; p++)
|
||||
{
|
||||
currentBodyPart = [currentBodyPart lookupImap4BodyPartKey: [partKeys objectAtIndex:p] inContext: context];
|
||||
}
|
||||
|
||||
[theResponse setHeader: [NSString stringWithFormat: @"%@/%@", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]]
|
||||
forKey: @"Content-Type"];
|
||||
@@ -1158,11 +1178,11 @@ static BOOL debugOn = NO;
|
||||
{
|
||||
collectionId = [[(id)[[allCollections objectAtIndex: j] getElementsByTagName: @"CollectionId"] lastObject] textValue];
|
||||
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
|
||||
|
||||
|
||||
if (folderType == ActiveSyncMailFolder)
|
||||
nameInCache = [NSString stringWithFormat: @"folder%@", realCollectionId];
|
||||
nameInCache = [NSString stringWithFormat: @"folder%@", realCollectionId];
|
||||
else
|
||||
nameInCache = collectionId;
|
||||
nameInCache = collectionId;
|
||||
|
||||
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
||||
|
||||
@@ -1234,10 +1254,11 @@ static BOOL debugOn = NO;
|
||||
- (void) processItemOperations: (id <DOMElement>) theDocumentElement
|
||||
inResponse: (WOResponse *) theResponse
|
||||
{
|
||||
NSString *fileReference, *realCollectionId;
|
||||
NSString *fileReference, *realCollectionId, *serverId, *bodyPreferenceType, *collectionId;
|
||||
NSMutableString *s;
|
||||
NSArray *fetchRequests;
|
||||
id aFetch;
|
||||
NSData *d;
|
||||
int i;
|
||||
|
||||
SOGoMicrosoftActiveSyncFolderType folderType;
|
||||
@@ -1247,8 +1268,6 @@ static BOOL debugOn = NO;
|
||||
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||
[s appendString: @"<ItemOperations xmlns=\"ItemOperations:\">"];
|
||||
[s appendString: @"<Status>1</Status>"];
|
||||
[s appendString: @"<Response>"];
|
||||
|
||||
fetchRequests = (id)[theDocumentElement getElementsByTagName: @"Fetch"];
|
||||
|
||||
@@ -1256,17 +1275,25 @@ static BOOL debugOn = NO;
|
||||
{
|
||||
NSMutableData *bytes, *parts;
|
||||
NSMutableArray *partLength;
|
||||
NSData *d;
|
||||
|
||||
bytes = [NSMutableData data];
|
||||
parts = [NSMutableData data];
|
||||
partLength = [NSMutableArray array];
|
||||
|
||||
[s appendString: @"<Status>1</Status>"];
|
||||
[s appendString: @"<Response>"];
|
||||
|
||||
for (i = 0; i < [fetchRequests count]; i++)
|
||||
{
|
||||
aFetch = [fetchRequests objectAtIndex: i];
|
||||
fileReference = [[[(id)[aFetch getElementsByTagName: @"FileReference"] lastObject] textValue] stringByUnescapingURL];
|
||||
realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType];
|
||||
collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
|
||||
|
||||
// its either a itemOperation to fetch an attachment or an email
|
||||
if ([fileReference length])
|
||||
realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType];
|
||||
else
|
||||
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
|
||||
|
||||
if (folderType == ActiveSyncMailFolder)
|
||||
{
|
||||
@@ -1276,43 +1303,80 @@ static BOOL debugOn = NO;
|
||||
SOGoUserFolder *userFolder;
|
||||
SOGoMailObject *mailObject;
|
||||
|
||||
NSRange r1, r2;
|
||||
|
||||
r1 = [realCollectionId rangeOfString: @"/"];
|
||||
r2 = [realCollectionId rangeOfString: @"/" options: 0 range: NSMakeRange(NSMaxRange(r1)+1, [realCollectionId length]-NSMaxRange(r1)-1)];
|
||||
|
||||
folderName = [realCollectionId substringToIndex: r1.location];
|
||||
messageName = [realCollectionId substringWithRange: NSMakeRange(NSMaxRange(r1), r2.location-r1.location-1)];
|
||||
pathToPart = [realCollectionId substringFromIndex: r2.location+1];
|
||||
|
||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||
currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
||||
|
||||
currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", folderName]
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
|
||||
mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO];
|
||||
currentBodyPart = [mailObject lookupImap4BodyPartKey: pathToPart inContext: context];
|
||||
|
||||
[s appendString: @"<Fetch>"];
|
||||
[s appendString: @"<Status>1</Status>"];
|
||||
[s appendFormat: @"<FileReference xmlns=\"AirSyncBase:\">%@</FileReference>", [fileReference stringByEscapingURL]];
|
||||
[s appendString: @"<Properties>"];
|
||||
|
||||
[s appendFormat: @"<ContentType xmlns=\"AirSyncBase:\">%@/%@</ContentType>", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]];
|
||||
|
||||
if ([[theResponse headerForKey: @"Content-Type"] isEqualToString:@"application/vnd.ms-sync.multipart"])
|
||||
if ([fileReference length])
|
||||
{
|
||||
[s appendFormat: @"<Part>%d</Part>", i+1];
|
||||
[partLength addObject: [NSNumber numberWithInteger: [[currentBodyPart fetchBLOB] length]]];
|
||||
[parts appendData:[currentBodyPart fetchBLOB]];
|
||||
// fetch attachment
|
||||
NSRange r1, r2;
|
||||
NSArray *partKeys;
|
||||
int p;
|
||||
|
||||
r1 = [realCollectionId rangeOfString: @"/"];
|
||||
r2 = [realCollectionId rangeOfString: @"/" options: 0 range: NSMakeRange(NSMaxRange(r1)+1, [realCollectionId length]-NSMaxRange(r1)-1)];
|
||||
|
||||
folderName = [realCollectionId substringToIndex: r1.location];
|
||||
messageName = [realCollectionId substringWithRange: NSMakeRange(NSMaxRange(r1), r2.location-r1.location-1)];
|
||||
pathToPart = [realCollectionId substringFromIndex: r2.location+1];
|
||||
|
||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||
currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
||||
|
||||
currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", folderName]
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
|
||||
mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO];
|
||||
|
||||
partKeys = [pathToPart componentsSeparatedByString: @"."];
|
||||
|
||||
currentBodyPart = [mailObject lookupImap4BodyPartKey: [partKeys objectAtIndex:0] inContext: context];
|
||||
for (p = 1; p < [partKeys count]; p++)
|
||||
{
|
||||
currentBodyPart = [currentBodyPart lookupImap4BodyPartKey: [partKeys objectAtIndex:p] inContext: context];
|
||||
}
|
||||
|
||||
[s appendString: @"<Fetch>"];
|
||||
[s appendString: @"<Status>1</Status>"];
|
||||
[s appendFormat: @"<FileReference xmlns=\"AirSyncBase:\">%@</FileReference>", [fileReference stringByEscapingURL]];
|
||||
[s appendString: @"<Properties>"];
|
||||
|
||||
[s appendFormat: @"<ContentType xmlns=\"AirSyncBase:\">%@/%@</ContentType>", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]];
|
||||
|
||||
if ([[theResponse headerForKey: @"Content-Type"] isEqualToString:@"application/vnd.ms-sync.multipart"])
|
||||
{
|
||||
NSData *d;
|
||||
d = [currentBodyPart fetchBLOB];
|
||||
|
||||
[s appendFormat: @"<Part>%d</Part>", i+1];
|
||||
[partLength addObject: [NSNumber numberWithInteger: [d length]]];
|
||||
[parts appendData: d];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *a;
|
||||
a = [[currentBodyPart fetchBLOB] activeSyncRepresentationInContext: context];
|
||||
|
||||
[s appendFormat: @"<Range>0-%d</Range>", [a length]-1];
|
||||
[s appendFormat: @"<Data>%@</Data>", a];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendFormat: @"<Range>0-%d</Range>", [[[currentBodyPart fetchBLOB] activeSyncRepresentationInContext: context] length]-1];
|
||||
[s appendFormat: @"<Data>%@</Data>", [[currentBodyPart fetchBLOB] activeSyncRepresentationInContext: context]];
|
||||
// fetch mail
|
||||
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
||||
serverId = [[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue];
|
||||
bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue];
|
||||
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
|
||||
|
||||
currentCollection = [self collectionFromId: realCollectionId type: folderType];
|
||||
|
||||
mailObject = [currentCollection lookupName: serverId inContext: context acquire: NO];
|
||||
[s appendString: @"<Fetch>"];
|
||||
[s appendString: @"<Status>1</Status>"];
|
||||
[s appendFormat: @"<CollectionId xmlns=\"AirSyncBase:\">%@</CollectionId>", collectionId];
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSyncBase:\">%@</ServerId>", serverId];
|
||||
[s appendString: @"<Properties>"];
|
||||
[s appendString: [mailObject activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
[s appendString: @"</Properties>"];
|
||||
@@ -1366,6 +1430,62 @@ static BOOL debugOn = NO;
|
||||
{
|
||||
[theResponse setContent: d];
|
||||
}
|
||||
}
|
||||
else if ([theDocumentElement getElementsByTagName: @"EmptyFolderContents"])
|
||||
{
|
||||
NGImap4Connection *connection;
|
||||
NSEnumerator *subfolders;
|
||||
NSException *error;
|
||||
NSURL *currentURL;
|
||||
id co;
|
||||
|
||||
collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
|
||||
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
|
||||
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
||||
|
||||
if (folderType == ActiveSyncMailFolder)
|
||||
{
|
||||
co = [self collectionFromId: realCollectionId type: folderType];
|
||||
error = [co addFlagsToAllMessages: @"deleted"];
|
||||
|
||||
if (!error)
|
||||
error = [(SOGoMailFolder *)co expunge];
|
||||
|
||||
if (!error)
|
||||
{
|
||||
[co flushMailCaches];
|
||||
|
||||
if ([theDocumentElement getElementsByTagName: @"DeleteSubFolders"])
|
||||
{
|
||||
// Delete sub-folders
|
||||
connection = [co imap4Connection];
|
||||
subfolders = [[co allFolderURLs] objectEnumerator];
|
||||
|
||||
while ((currentURL = [subfolders nextObject]))
|
||||
{
|
||||
[[connection client] unsubscribe: [currentURL path]];
|
||||
[connection deleteMailboxAtURL: currentURL];
|
||||
}
|
||||
}
|
||||
|
||||
[s appendString: @"<Status>1</Status>"];
|
||||
[s appendString: @"</ItemOperations>"];
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
[s appendString: @"<Status>3</Status>"];
|
||||
[s appendString: @"</ItemOperations>"];
|
||||
}
|
||||
|
||||
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
||||
[theResponse setContent: d];
|
||||
}
|
||||
else
|
||||
{
|
||||
[theResponse setStatus: 500];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2456,7 +2576,7 @@ static BOOL debugOn = NO;
|
||||
NGMimeFileData *fdata;
|
||||
NSException *error;
|
||||
NSArray *attachmentKeys;
|
||||
NSMutableArray *attachments;
|
||||
NSMutableArray *attachments, *references;
|
||||
|
||||
id body, bodyFromSmartForward, htmlPart, textPart;
|
||||
NSString *fullName, *email, *charset, *s;
|
||||
@@ -2495,7 +2615,25 @@ static BOOL debugOn = NO;
|
||||
[map setObject: email forKey: @"from"];
|
||||
|
||||
if ([mailObject messageId])
|
||||
[map setObject: [mailObject messageId] forKey: @"in-reply-to"];
|
||||
{
|
||||
[map setObject: [mailObject messageId] forKey: @"in-reply-to"];
|
||||
|
||||
references = [[[[[mailObject mailHeaders] objectForKey: @"references"] componentsSeparatedByString: @" "] mutableCopy] autorelease];
|
||||
if ([references count] > 0)
|
||||
{
|
||||
// If there are more than ten identifiers listed, we eliminate the second one.
|
||||
if ([references count] >= 10)
|
||||
[references removeObjectAtIndex: 1];
|
||||
|
||||
[references addObject: [mailObject messageId]];
|
||||
|
||||
[map setObject: [references componentsJoinedByString:@" "] forKey: @"references"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[map setObject: [mailObject messageId] forKey: @"references"];
|
||||
}
|
||||
}
|
||||
|
||||
messageToSend = [[[NGMimeMessage alloc] initWithHeader: map] autorelease];
|
||||
body = [[[NGMimeMultipartBody alloc] initWithPart: messageToSend] autorelease];
|
||||
@@ -2753,6 +2891,7 @@ static BOOL debugOn = NO;
|
||||
NSAutoreleasePool *pool;
|
||||
id builder, dom;
|
||||
SEL aSelector;
|
||||
id activeUser;
|
||||
|
||||
NSString *cmdName, *deviceId;
|
||||
NSData *d;
|
||||
@@ -2761,6 +2900,14 @@ static BOOL debugOn = NO;
|
||||
|
||||
ASSIGN(context, theContext);
|
||||
|
||||
activeUser = [context activeUser];
|
||||
if (![activeUser canAccessModule: @"ActiveSync"])
|
||||
{
|
||||
[(WOResponse *)theResponse setStatus: 403];
|
||||
[self logWithFormat: @"EAS - Forbidden access for user %@", [activeUser loginInDomain]];
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Get the device ID, device type and "stash" them
|
||||
deviceId = [[theRequest uri] deviceId];
|
||||
[context setObject: deviceId forKey: @"DeviceId"];
|
||||
|
||||
@@ -77,6 +77,41 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#import <Appointments/SOGoAptMailNotification.h>
|
||||
|
||||
unsigned char strToChar(char a, char b) {
|
||||
char encoder[3] = {'\0','\0','\0'};
|
||||
encoder[0] = a;
|
||||
encoder[1] = b;
|
||||
return (char) strtol(encoder,NULL,16);
|
||||
}
|
||||
|
||||
@interface NSString (NSStringExtensions)
|
||||
- (NSData *) decodeFromHexidecimal;
|
||||
@end
|
||||
|
||||
@implementation NSString (NSStringExtensions)
|
||||
|
||||
- (NSData *) decodeFromHexidecimal;
|
||||
{
|
||||
const char * bytes = [self cStringUsingEncoding: NSUTF8StringEncoding];
|
||||
NSUInteger length = strlen(bytes);
|
||||
unsigned char * r = (unsigned char *) malloc(length / 2 + 1);
|
||||
unsigned char * index = r;
|
||||
|
||||
while ((*bytes) && (*(bytes +1))) {
|
||||
*index = strToChar(*bytes, *(bytes +1));
|
||||
index++;
|
||||
bytes+=2;
|
||||
}
|
||||
*index = '\0';
|
||||
|
||||
NSData * result = [NSData dataWithBytes: r length: length / 2];
|
||||
free(r);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
typedef struct {
|
||||
uint32_t dwLowDateTime;
|
||||
uint32_t dwHighDateTime;
|
||||
@@ -91,7 +126,7 @@ struct GlobalObjectId {
|
||||
FILETIME CreationTime;
|
||||
uint8_t X[8];
|
||||
uint32_t Size;
|
||||
uint8_t* Data;
|
||||
uint8_t Data[0];
|
||||
};
|
||||
|
||||
@implementation SOGoMailObject (ActiveSync)
|
||||
@@ -118,39 +153,40 @@ struct GlobalObjectId {
|
||||
//
|
||||
// The GlobalObjId is documented here: http://msdn.microsoft.com/en-us/library/ee160198(v=EXCHG.80).aspx
|
||||
//
|
||||
|
||||
- (NSData *) _computeGlobalObjectIdFromEvent: (iCalEvent *) event
|
||||
{
|
||||
NSData *binPrefix, *globalObjectId, *uidAsASCII;
|
||||
NSString *prefix, *uid;
|
||||
|
||||
struct GlobalObjectId newGlobalId;
|
||||
struct GlobalObjectId *newGlobalId;
|
||||
const char *bytes;
|
||||
|
||||
|
||||
uid = [event uid];
|
||||
uidAsASCII = [uid decodeFromHexidecimal];
|
||||
newGlobalId = (struct GlobalObjectId*)calloc(sizeof(uint8_t), sizeof(struct GlobalObjectId) + 0x0c + [uidAsASCII length]);
|
||||
|
||||
prefix = @"040000008200e00074c5b7101a82e008";
|
||||
|
||||
// dataPrefix is "vCal-Uid %x01 %x00 %x00 %x00"
|
||||
uint8_t dataPrefix[] = { 0x76, 0x43, 0x61, 0x6c, 0x2d, 0x55, 0x69, 0x64, 0x01, 0x00, 0x00, 0x00 };
|
||||
uid = [event uid];
|
||||
|
||||
binPrefix = [prefix convertHexStringToBytes];
|
||||
[binPrefix getBytes: &newGlobalId.ByteArrayID];
|
||||
[self _setInstanceDate: &newGlobalId
|
||||
[binPrefix getBytes: &newGlobalId->ByteArrayID];
|
||||
[self _setInstanceDate: newGlobalId
|
||||
fromDate: [event recurrenceId]];
|
||||
uidAsASCII = [uid dataUsingEncoding: NSASCIIStringEncoding];
|
||||
bytes = [uidAsASCII bytes];
|
||||
|
||||
// 0x0c is the size of our dataPrefix
|
||||
newGlobalId.Size = 0x0c + [uidAsASCII length];
|
||||
newGlobalId.Data = malloc(newGlobalId.Size * sizeof(uint8_t));
|
||||
memcpy(newGlobalId.Data, dataPrefix, 0x0c);
|
||||
memcpy(newGlobalId.Data + 0x0c, bytes, newGlobalId.Size - 0x0c);
|
||||
newGlobalId->Size = 0x0c + [uidAsASCII length];
|
||||
memcpy(newGlobalId->Data, dataPrefix, 0x0c);
|
||||
memcpy(newGlobalId->Data + 0x0c, bytes, newGlobalId->Size - 0x0c);
|
||||
|
||||
globalObjectId = [[NSData alloc] initWithBytes: &newGlobalId length: 40 + newGlobalId.Size*sizeof(uint8_t)];
|
||||
free(newGlobalId.Data);
|
||||
|
||||
globalObjectId = [[NSData alloc] initWithBytes: newGlobalId length: 40 + newGlobalId->Size*sizeof(uint8_t)];
|
||||
free(newGlobalId);
|
||||
return [globalObjectId autorelease];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// For debugging purposes...
|
||||
//
|
||||
@@ -381,11 +417,6 @@ struct GlobalObjectId {
|
||||
|
||||
if (s)
|
||||
{
|
||||
// We sanitize the content immediately, in case we have non-UNICODE safe
|
||||
// characters that would be re-encoded later in HTML entities and thus,
|
||||
// ignore afterwards.
|
||||
s = [s safeString];
|
||||
|
||||
body = [s dataUsingEncoding: NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
@@ -519,6 +550,28 @@ struct GlobalObjectId {
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *) _getNormalizedSubject
|
||||
{
|
||||
NSString *subject;
|
||||
NSUInteger colIdx;
|
||||
NSString *stringValue;
|
||||
|
||||
subject = [[self subject] decodedHeader];
|
||||
|
||||
colIdx = [subject rangeOfString: @":" options:NSBackwardsSearch].location;
|
||||
if (colIdx != NSNotFound && colIdx + 1 < [subject length])
|
||||
stringValue = [[subject substringFromIndex: colIdx + 1] stringByTrimmingLeadSpaces];
|
||||
else
|
||||
stringValue = subject;
|
||||
|
||||
if (!stringValue)
|
||||
stringValue = @"";
|
||||
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
@@ -632,7 +685,7 @@ struct GlobalObjectId {
|
||||
if (value)
|
||||
{
|
||||
[s appendFormat: @"<Subject xmlns=\"Email:\">%@</Subject>", [value activeSyncRepresentationInContext: context]];
|
||||
[s appendFormat: @"<ThreadTopic xmlns=\"Email:\">%@</ThreadTopic>", [value activeSyncRepresentationInContext: context]];
|
||||
[s appendFormat: @"<ThreadTopic xmlns=\"Email:\">%@</ThreadTopic>", [[self _getNormalizedSubject] activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
// DateReceived
|
||||
@@ -866,10 +919,41 @@ struct GlobalObjectId {
|
||||
|
||||
if (d)
|
||||
{
|
||||
NSMutableData *sanitizedData;
|
||||
NSString *content;
|
||||
int len, truncated;
|
||||
|
||||
content = [[NSString alloc] initWithData: d encoding: NSUTF8StringEncoding];
|
||||
|
||||
// Outlook fails to decode quoted-printable (see #3082) if lines are not termined by CRLF.
|
||||
const char *bytes;
|
||||
char *mbytes;
|
||||
int mlen;
|
||||
|
||||
len = [d length];
|
||||
mlen = 0;
|
||||
|
||||
sanitizedData = [NSMutableData dataWithLength: len*2];
|
||||
|
||||
bytes = [d bytes];
|
||||
mbytes = [sanitizedData mutableBytes];
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
if (*bytes == '\n' && *(bytes-1) != '\r' && mlen > 0)
|
||||
{
|
||||
*mbytes = '\r';
|
||||
mbytes++;
|
||||
mlen++;
|
||||
}
|
||||
|
||||
*mbytes = *bytes;
|
||||
mbytes++; bytes++;
|
||||
len--;
|
||||
mlen++;
|
||||
}
|
||||
|
||||
[sanitizedData setLength: mlen];
|
||||
|
||||
content = [[NSString alloc] initWithData: sanitizedData encoding: NSUTF8StringEncoding];
|
||||
|
||||
// FIXME: This is a hack. We should normally avoid doing this as we might get
|
||||
// broken encodings. We should rather tell that the data was truncated and expect
|
||||
@@ -879,15 +963,15 @@ struct GlobalObjectId {
|
||||
// for an "interesting" discussion around this.
|
||||
//
|
||||
if (!content)
|
||||
content = [[NSString alloc] initWithData: d encoding: NSISOLatin1StringEncoding];
|
||||
content = [[NSString alloc] initWithData: sanitizedData encoding: NSISOLatin1StringEncoding];
|
||||
|
||||
AUTORELEASE(content);
|
||||
|
||||
content = [content activeSyncRepresentationInContext: context];
|
||||
truncated = 0;
|
||||
|
||||
|
||||
len = [content length];
|
||||
|
||||
|
||||
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||
{
|
||||
[s appendFormat: @"<Body xmlns=\"Email:\">%@</Body>", content];
|
||||
@@ -987,7 +1071,13 @@ struct GlobalObjectId {
|
||||
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.0"] ||
|
||||
[[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.1"])
|
||||
{
|
||||
if ([self inReplyTo])
|
||||
NSString *reference;
|
||||
|
||||
reference = [[[[self mailHeaders] objectForKey: @"references"] componentsSeparatedByString: @" "] objectAtIndex: 0];
|
||||
|
||||
if ([reference length] > 0)
|
||||
[s appendFormat: @"<ConversationId xmlns=\"Email2:\">%@</ConversationId>", [[reference dataUsingEncoding: NSUTF8StringEncoding] activeSyncRepresentationInContext: context]];
|
||||
else if ([self inReplyTo])
|
||||
[s appendFormat: @"<ConversationId xmlns=\"Email2:\">%@</ConversationId>", [[[self inReplyTo] dataUsingEncoding: NSUTF8StringEncoding] activeSyncRepresentationInContext: context]];
|
||||
else if ([self messageId])
|
||||
[s appendFormat: @"<ConversationId xmlns=\"Email2:\">%@</ConversationId>", [[[self messageId] dataUsingEncoding: NSUTF8StringEncoding] activeSyncRepresentationInContext: context]];
|
||||
|
||||
@@ -506,14 +506,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[self setOrganizer: person];
|
||||
}
|
||||
|
||||
//
|
||||
// iOS is plain stupid here. It seends event invitations with no Organizer.
|
||||
// We check this corner-case and if MeetingStatus == 1 (see http://msdn.microsoft.com/en-us/library/ee219342(v=exchg.80).aspx or details)
|
||||
// and there's no organizer, we fake one.
|
||||
//
|
||||
if ((o = [theValues objectForKey: @"MeetingStatus"]))
|
||||
{
|
||||
if ([o intValue] == 1 && ![theValues objectForKey: @"Organizer_Email"])
|
||||
//
|
||||
// iOS is plain stupid here. It seends event invitations with no Organizer.
|
||||
// We check this corner-case and if MeetingStatus == 1 (see http://msdn.microsoft.com/en-us/library/ee219342(v=exchg.80).aspx or details)
|
||||
// and there's no organizer, we fake one.
|
||||
//
|
||||
if ([o intValue] == 1 && ![theValues objectForKey: @"Organizer_Email"] && ![[[self organizer] rfc822Email] length])
|
||||
{
|
||||
iCalPerson *person;
|
||||
|
||||
@@ -523,6 +523,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[person setPartStat: @"ACCEPTED"];
|
||||
[self setOrganizer: person];
|
||||
}
|
||||
|
||||
//
|
||||
// When MeetingResponse fails Outlook still sends a new calendar entry with MeetingStatus=3.
|
||||
// Use the organizer from the request if the event has no organizer.
|
||||
//
|
||||
if ([o intValue] == 3 && [theValues objectForKey: @"Organizer_Email"] && ![[[self organizer] rfc822Email] length])
|
||||
{
|
||||
iCalPerson *person;
|
||||
person = [iCalPerson elementWithTag: @"organizer"];
|
||||
[person setEmail: [theValues objectForKey: @"Organizer_Email"]];
|
||||
[person setCn: [theValues objectForKey: @"Organizer_Name"]];
|
||||
[person setPartStat: @"ACCEPTED"];
|
||||
[self setOrganizer: person];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
-5655
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,157 @@
|
||||
https://wiki.inverse.ca/focus/SOGo/CodingStyle
|
||||
https://wiki.inverse.ca/focus/SOGo/D%C3%A9veloppement
|
||||
https://wiki.inverse.ca/focus/PacketFence/Devel/BestPractices
|
||||
|
||||
UI and UX
|
||||
---------
|
||||
|
||||
* http://www.google.com/design/spec/material-design/
|
||||
* http://goodui.org/
|
||||
* Try Repeating Your Primary Action instead of showing it just once.
|
||||
* Try Distinct Clickable/Selected Styles instead of blurring them.
|
||||
* Try Undos instead of prompting for confirmation.
|
||||
* Try More Contrast instead of similarity.
|
||||
* Try Exposing Options instead of hiding them.
|
||||
* Try Showing State instead of being state agnostic.
|
||||
* Try Direct Manipulation instead of contextless menus.
|
||||
* Try Exposing Fields instead of creating extra pages.
|
||||
* Try Transitions instead of showing changes instantly.
|
||||
* Try Fewer Borders instead of wasting attention.
|
||||
* Try Designing For Zero Data instead of just data heavy cases.
|
||||
* Try Consistency instead of making people relearn.
|
||||
* Try Conventions instead of reinventing the wheel.
|
||||
* Try Visual Hierarchy instead of dullness.
|
||||
* Try Grouping Related Items instead of disordering.
|
||||
* Try Inline Validation instead of delaying errors.
|
||||
* Try Forgiving Inputs instead of being strict with data.
|
||||
* Try Bigger Click Areas instead of tiny ones.
|
||||
* Try Faster Load Times instead of making people wait.
|
||||
* Try Keyboard Shortcuts instead of buttons only.
|
||||
* Try Upfront Progress instead of starting with a blank.
|
||||
* Try Softer Prompts instead of modal windows.
|
||||
* Try Multifunctional Controls instead of more parts.
|
||||
* Try Icon Labels instead of opening for interpretation.
|
||||
* http://designmodo.com/create-style-guides/
|
||||
* http://uxmyths.com/
|
||||
|
||||
Frameworks & Tools
|
||||
------------------
|
||||
|
||||
GNUstep
|
||||
~~~~~~~
|
||||
|
||||
SOPE
|
||||
~~~~
|
||||
|
||||
AngularJS
|
||||
~~~~~~~~~
|
||||
|
||||
* https://angularjs.org/
|
||||
* http://angular-ui.github.io/ui-router/
|
||||
* https://www.airpair.com/angularjs/posts/angularjs-tutorial
|
||||
|
||||
Angular Material
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
* https://material.angularjs.org/
|
||||
|
||||
Lo-Dash
|
||||
~~~~~~~
|
||||
|
||||
* http://lodash.com/
|
||||
* http://underscorejs.org/
|
||||
|
||||
Code Style
|
||||
----------
|
||||
|
||||
Objective-C
|
||||
~~~~~~~~~~~
|
||||
|
||||
* To document the Web APIs, we follow the http://apidocjs.com/[APIDOC] standards.
|
||||
|
||||
apidoc -f ".*\\.m" -i UI -o Documentation/JSON-API
|
||||
|
||||
HTML
|
||||
~~~~
|
||||
|
||||
* Localize your strings:
|
||||
|
||||
<var:string label:value="I'm localized!"/>
|
||||
|
||||
* Localize your attributes:
|
||||
|
||||
<input type="text" label:placeholder="I'm localized!"/>
|
||||
|
||||
* Reuse existing localized strings as much as possible. Otherwise don't forget to update the English Localizable.strings file of the appropriate module.
|
||||
|
||||
JavaScript
|
||||
~~~~~~~~~~
|
||||
|
||||
* http://eslint.org/
|
||||
* https://github.com/toddmotto/angularjs-styleguide
|
||||
* https://github.com/angular/material/blob/master/docs/guides/CODING.md
|
||||
|
||||
* We conform to https://github.com/airbnb/javascript[Airbnb coding standards]. Use https://github.com/jscs-dev/node-jscs[jscs] with the 'airbnb' preset to validate your code style:
|
||||
|
||||
jscs -p airbnb *.js
|
||||
|
||||
* For documentation, we follow the http://usejsdoc.org/[JSDoc3] standards:
|
||||
|
||||
jsdoc UI/WebServerResources/js/{Appointments,Common,Contacts,Mailer}/*.js -d Documentation/JavaScript-API
|
||||
|
||||
Models (facades)
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
////
|
||||
http://trochette.github.io/Angular-Design-Patterns-Best-Practices/
|
||||
////
|
||||
* Move business logic into models and share them.
|
||||
* Keep it simple, separate server interaction and error handling from the model. That way model only handle data processing and code is easier to maintain.
|
||||
|
||||
SASS/CSS
|
||||
~~~~~~~~
|
||||
|
||||
* http://componentcss.com/
|
||||
* https://github.com/styleguide/css
|
||||
* https://smacss.com/
|
||||
|
||||
* For documentation, we follow http://sassdoc.com/[SassDoc] annotations.
|
||||
* We need the SASS files of Angular Material to build our CSS. The git repository of Angular Material is included as a submodule of SOGo:
|
||||
|
||||
git submodule init
|
||||
git submodule update
|
||||
|
||||
* FlexBox compatibility http://caniuse.com/#feat=flexbox
|
||||
|
||||
Fonts
|
||||
^^^^^
|
||||
|
||||
* We build a custom icons font using the online app https://icomoon.io/app/#/select[icomoon].
|
||||
* http://www.carrois.com/fira-4-0/
|
||||
* Material Design Iconic Font http://zavoloklom.github.io/material-design-iconic-font/icons.html
|
||||
|
||||
Version Control
|
||||
---------------
|
||||
|
||||
* https://devcharm.com/articles/46/improve-your-git-workflow/
|
||||
* Each commit should cover only one thing;
|
||||
* Begin the commit message with a single short (less than 50 characters) line summarizing the change, followed by a blank line and then a more thorough description;
|
||||
* When fixing a bug, commit to the devel branch as well as the maintenance branch of the latest release version (named maintenance/x.y). When a ticket is associated to the bug, add to the description a line saying *Fixes #1234*.
|
||||
* `git pull` may introduce http://stackoverflow.com/questions/15316601/in-what-cases-could-git-pull-be-harmful[inconsistencies and problems]. Replace it with the following alias:
|
||||
|
||||
git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
* https://github.com/angular/protractor
|
||||
* http://karma-runner.github.io/
|
||||
|
||||
JSON API
|
||||
~~~~~~~~
|
||||
|
||||
One practical way to test the JSON API is to use `curl`. To do so, you need to enable `SOGoTrustProxyAuthentication` and configure HTTP authentifcation in Apache. You can pipe the result to [jq](http://stedolan.github.io/jq/) to nicely format and filter the output:
|
||||
|
||||
curl -u username:password http://localhost/SOGo/so/username/Calendar/calendarslist | jq '.'
|
||||
|
||||
curl -u username:password -H 'Content-Type: application/json' -d '{}' http://localhost/SOGo/so/francis/Calendar/personal/71B6-54904400-1-7C308500.ics/save
|
||||
@@ -1035,8 +1035,8 @@ repository
|
||||
|
||||
|ModulesConstraints (optional)
|
||||
|Limits the access of any module through a constraint based on an LDAP
|
||||
attribute; must be a dictionary with keys `Mail`, and/or `Calendar`, for
|
||||
example:
|
||||
attribute; must be a dictionary with keys `Mail`, and/or `Calendar`,
|
||||
and/or `ActiveSync` for example:
|
||||
|
||||
----
|
||||
ModulesConstraints = {
|
||||
@@ -1920,10 +1920,11 @@ events. This parameter is an array of arbitrary strings.
|
||||
|
||||
Defaults to a list that depends on the language.
|
||||
|
||||
|U |SOGoCalendarDefaultCategoryColor
|
||||
|Parameter used to define the default colour of categories.
|
||||
|U |SOGoCalendarCategoriesColors
|
||||
|Parameter used to define the colour of categories. This parameter
|
||||
is a dictionary of category name/color.
|
||||
|
||||
Defaults to `#F0F0F0` when unset.
|
||||
Defaults to `#F0F0F0` for all categories when unset.
|
||||
|
||||
|U |SOGoCalendarEventsDefaultClassification
|
||||
|Parameter used to defined the default classification for new events.
|
||||
@@ -2712,6 +2713,11 @@ current version of SOGo from the previous release.
|
||||
|
||||
[cols="100a"]
|
||||
|=======================================================================
|
||||
h|2.3.1
|
||||
|The SOGoCalendarDefaultCategoryColor default has been removed. If you
|
||||
want to customize the color of calendar categories, use the
|
||||
SOGoCalendarCategories and SOGoCalendarCategoriesColors defaults.
|
||||
|
||||
h|2.3.0
|
||||
|Run the shell script `sql-update-2.2.17_to_2.3.0.sh` or
|
||||
`sql-update-2.2.17_to_2.3.0-mysql.sh` (if you use MySQL).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!-- TODO have the build system take care of this -->
|
||||
<releaseinfo>Version 2.3.0 - June 2015</releaseinfo>
|
||||
<subtitle>for version 2.3.0</subtitle>
|
||||
<date>2015-06-01</date>
|
||||
<releaseinfo>Version 2.3.1 - July 2015</releaseinfo>
|
||||
<subtitle>for version 2.3.1</subtitle>
|
||||
<date>2015-07-23</date>
|
||||
|
||||
<legalnotice>
|
||||
<para>Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".</para>
|
||||
|
||||
@@ -13,6 +13,6 @@
|
||||
|
||||
// TODO have the build system take care of this
|
||||
|
||||
:release_version: 2.3.0
|
||||
:release_version: 2.3.1
|
||||
|
||||
// vim: set syntax=asciidoc tabstop=2 shiftwidth=2 expandtab:
|
||||
@@ -1,3 +1,44 @@
|
||||
2.3.x (2015-MM-DD)
|
||||
------------------
|
||||
|
||||
Bug fixes
|
||||
- fixed display of whitelisted attendees in Preferences window on Firefox (#3285)
|
||||
- Non-latin subfolder names are displayed correctly on Outlook (Zentyal)
|
||||
- Fixed several sync issues on environments with multiple users (Zentyal)
|
||||
- Folders from other users will no longer appear on your Outlook (Zentyal)
|
||||
- Use right auth in multidomain environments in contacts and calendar from Outlook (Zentyal)
|
||||
|
||||
2.3.1 (2015-07-23)
|
||||
------------------
|
||||
|
||||
Enhancements
|
||||
- improved EAS speed, especially when fetching big attachments
|
||||
- now always enforce the organizer's default identity in appointments
|
||||
- improved the handling of default calendar categories/colors (#3200)
|
||||
- added support for DeletesAsMoves over EAS
|
||||
- added create-folder subcommand to sogo-tool to create contact and calendar folders
|
||||
- group mail addresses can be used as recipient in Outlook
|
||||
- added 'ActiveSync' module constraints
|
||||
- added Slovenian translation - thanks to Jens Riecken
|
||||
- added Chinese (Taiwan) translation
|
||||
|
||||
Bug fixes
|
||||
- EAS's GetItemEstimate/ItemOperations now support fetching mails and empty folders
|
||||
- fixed some rare cornercases in multidomain configurations
|
||||
- properly escape folder after creation using EAS (#3237)
|
||||
- fixed potential organizer highjacking when using EAS (#3131)
|
||||
- properly support big characters in EAS and fix encoding QP EAS error for Outlook (#3082)
|
||||
- properly encode id of DOM elements in Address Book module (#3239, #3245)
|
||||
- fixed multi-domain support for sogo-tool backup/restore (#2600)
|
||||
- fixed data ordering in events list of Calendar module (#3261)
|
||||
- fixed data ordering in tasks list of Calendar module (#3267)
|
||||
- Android EAS Lollipop fixes (#3268 and #3269)
|
||||
- improved EAS email flagging handling (#3140)
|
||||
- fixed computation of GlobalObjectId (#3235)
|
||||
- fixed EAS conversation ID issues on BB10 (#3152)
|
||||
- fixed CR/LF printing in event's description (#3228)
|
||||
- optimized Calendar module in multidomain configurations
|
||||
|
||||
2.3.0 (2015-06-01)
|
||||
-------------------
|
||||
|
||||
@@ -8,10 +49,10 @@ Enhancements
|
||||
- improved multipart handling using EAS
|
||||
- added systemd startup script (PR#76)
|
||||
- added Basque translation - thanks to Gorka Gonzalez
|
||||
- updated Brazilian (Portuguese), Dutch, Norwegian (Bokmal), Polish, Russian, and Spanish (Spain) translations Enhancements
|
||||
- sharing request among different Outlook versions (Zentyal)
|
||||
- improve sync speed from Outlook by non-reprocessing already downloaded unread mails (Zentyal)
|
||||
- give support to calendar sharing invitations
|
||||
- updated Brazilian (Portuguese), Dutch, Norwegian (Bokmal), Polish, Russian, and Spanish (Spain) translations
|
||||
- calendar sharing request support among different Outlook versions (Zentyal)
|
||||
- improved sync speed from Outlook by non-reprocessing already downloaded unread mails (Zentyal)
|
||||
- added support for sharing calendar invitations
|
||||
- missing contact fields are now saved and available when sharing it (Office, Profession, Manager's name, Assistant's name, Spouse/Partner, Anniversary) (Zentyal)
|
||||
- appointment color and importance work now between Outlooks (Zentyal)
|
||||
- synchronize events, contacts and tasks in reverse chronological order (Zentyal)
|
||||
@@ -20,7 +61,7 @@ Enhancements
|
||||
- now able to print event/task's description (new components only) in the list view (#2881)
|
||||
- now possible to log EAS commands using the SOGoEASDebugEnabled system defaults
|
||||
- many improvements to EAS SmartReply/SmartForward commands
|
||||
- event invitation response mails are now sent
|
||||
- event invitation response mails from Outlook are now sent
|
||||
- mail subfolders created in WebMail are created when Outlook synchronises
|
||||
- mail root folder created in WebMail (same level INBOX) are created on Outlook logon
|
||||
|
||||
|
||||
@@ -33,8 +33,9 @@
|
||||
}
|
||||
|
||||
- (id) authenticatorInContext: (id) context;
|
||||
|
||||
- (MAPIStoreUserContext *) userContext;
|
||||
- (void) setUserContext: (MAPIStoreUserContext *) newContext;
|
||||
- (void) cleanup;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -76,6 +76,11 @@ MAPIApplication *MAPIApp = nil;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (MAPIStoreUserContext *) userContext
|
||||
{
|
||||
return userContext;
|
||||
}
|
||||
|
||||
- (void) setUserContext: (MAPIStoreUserContext *) newContext
|
||||
{
|
||||
/* user contexts must not be retained here ad their holder (mapistore)
|
||||
@@ -88,4 +93,9 @@ MAPIApplication *MAPIApp = nil;
|
||||
return [userContext authenticator];
|
||||
}
|
||||
|
||||
- (void) cleanup
|
||||
{
|
||||
[userContext deactivate];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -63,19 +63,15 @@
|
||||
SOGoAppointmentObject *newEntry;
|
||||
NSString *name;
|
||||
|
||||
//[self logWithFormat: @"METHOD '%s' (%d)", __FUNCTION__, __LINE__];
|
||||
[[self userContext] activate];
|
||||
|
||||
name = [NSString stringWithFormat: @"%@.ics",
|
||||
[SOGoObject globallyUniqueObjectId]];
|
||||
newEntry = [SOGoAppointmentObject objectWithName: name
|
||||
inContainer: sogoObject];
|
||||
[newEntry setIsNew: YES];
|
||||
/* the WOContext is required here for resolving notification pages */
|
||||
[newEntry setContext: [[self userContext] woContext]];
|
||||
newMessage = [MAPIStoreCalendarMessage mapiStoreObjectWithSOGoObject: newEntry
|
||||
inContainer: self];
|
||||
|
||||
|
||||
return newMessage;
|
||||
}
|
||||
|
||||
|
||||
@@ -494,6 +494,7 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK;
|
||||
folder = [sogoObject container];
|
||||
|
||||
/* reinstantiate the old sogo object and attach it to self */
|
||||
[[self userContext] activate];
|
||||
woContext = [[self userContext] woContext];
|
||||
if (isNew)
|
||||
{
|
||||
@@ -577,7 +578,7 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) save: (TALLOC_CTX *) memCtx
|
||||
- (void) save: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
// iCalCalendar *vCalendar;
|
||||
// NSCalendarDate *now;
|
||||
@@ -634,7 +635,7 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK;
|
||||
withActiveUser: activeUser
|
||||
inMemCtx: memCtx];
|
||||
[self _updateAttachedEvents];
|
||||
[[self userContext] activateWithUser: activeUser];
|
||||
[[self userContext] activate];
|
||||
[sogoObject updateContentWithCalendar: calendar
|
||||
fromRequest: nil];
|
||||
[self updateVersions];
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#import <Contacts/SOGoContactFolders.h>
|
||||
|
||||
#import "MAPIApplication.h"
|
||||
#import "MAPIStoreUserContext.h"
|
||||
#import "MAPIStoreContactsContext.h"
|
||||
#import "MAPIStoreContactsMessage.h"
|
||||
#import "MAPIStoreContactsMessageTable.h"
|
||||
@@ -58,10 +59,12 @@
|
||||
SOGoContactGCSEntry *newEntry;
|
||||
NSString *name;
|
||||
|
||||
[[self userContext] activate];
|
||||
|
||||
name = [NSString stringWithFormat: @"%@.vcf",
|
||||
[SOGoObject globallyUniqueObjectId]];
|
||||
newEntry = [SOGoContactGCSEntry objectWithName: name
|
||||
inContainer: sogoObject];
|
||||
inContainer: sogoObject];
|
||||
[newEntry setIsNew: YES];
|
||||
newMessage = [MAPIStoreContactsMessage mapiStoreObjectWithSOGoObject: newEntry
|
||||
inContainer: self];
|
||||
@@ -100,7 +103,7 @@
|
||||
rights |= RightsReadItems;
|
||||
if (rights != 0)
|
||||
rights |= RoleNone; /* actually "folder visible" */
|
||||
|
||||
|
||||
return rights;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#import <SOGo/SOGoUser.h>
|
||||
|
||||
#import "MAPIStoreAttachment.h"
|
||||
// #import "MAPIStoreAttachmentTable.h"
|
||||
#import "MAPIStoreFallbackContext.h"
|
||||
#import "MAPIStoreFolder.h"
|
||||
#import "MAPIStoreFolderTable.h"
|
||||
@@ -89,32 +88,31 @@ static NSMutableDictionary *contextClassMapping;
|
||||
currentClass = [classes objectAtIndex: count];
|
||||
moduleName = [currentClass MAPIModuleName];
|
||||
if (moduleName)
|
||||
{
|
||||
[contextClassMapping setObject: currentClass
|
||||
{
|
||||
[contextClassMapping setObject: currentClass
|
||||
forKey: moduleName];
|
||||
NSLog (@" registered class '%@' as handler of '%@' contexts",
|
||||
NSStringFromClass (currentClass), moduleName);
|
||||
}
|
||||
NSLog (@" registered class '%@' as handler of '%@' contexts",
|
||||
NSStringFromClass (currentClass), moduleName);
|
||||
}
|
||||
}
|
||||
|
||||
MAPIStoreFallbackContextK = [MAPIStoreFallbackContext class];
|
||||
}
|
||||
|
||||
+ (struct mapistore_contexts_list *) listAllContextsForUser: (NSString *) userName
|
||||
withIndexing: (struct indexing_context *) indexing
|
||||
withIndexing: (struct indexing_context *) indexing
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
struct mapistore_contexts_list *list, *current;
|
||||
NSArray *classes;
|
||||
Class currentClass;
|
||||
NSUInteger count, max;
|
||||
MAPIStoreUserContext *userContext;
|
||||
|
||||
list = NULL;
|
||||
|
||||
userContext = [MAPIStoreUserContext userContextWithUsername: userName
|
||||
andTDBIndexing: indexing];
|
||||
[userContext activateWithUser: [userContext sogoUser]];
|
||||
// User context is activated on initialization
|
||||
[MAPIStoreUserContext userContextWithUsername: userName
|
||||
andTDBIndexing: indexing];
|
||||
|
||||
classes = GSObjCAllSubclassesOfClass (self);
|
||||
max = [classes count];
|
||||
@@ -122,7 +120,7 @@ static NSMutableDictionary *contextClassMapping;
|
||||
{
|
||||
currentClass = [classes objectAtIndex: count];
|
||||
current = [currentClass listContextsForUser: userName
|
||||
withIndexing: indexing
|
||||
withIndexing: indexing
|
||||
inMemCtx: memCtx];
|
||||
if (current)
|
||||
DLIST_CONCATENATE(list, current, void);
|
||||
@@ -132,7 +130,7 @@ static NSMutableDictionary *contextClassMapping;
|
||||
}
|
||||
|
||||
+ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName
|
||||
withIndexing: (struct indexing_context *) indexing
|
||||
withIndexing: (struct indexing_context *) indexing
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return NULL;
|
||||
@@ -225,8 +223,6 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
NSURL *baseURL;
|
||||
int rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
|
||||
//NSLog (@"METHOD '%s' (%d) -- uri: '%s'", __FUNCTION__, __LINE__, newUri);
|
||||
|
||||
context = nil;
|
||||
|
||||
baseURL = CompleteURLFromMapistoreURI (newUri);
|
||||
@@ -298,13 +294,6 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
ASSIGN (userContext,
|
||||
[MAPIStoreUserContext userContextWithUsername: username
|
||||
andTDBIndexing: indexing]);
|
||||
|
||||
#if 0
|
||||
mapistore_mgmt_backend_register_user (newConnInfo,
|
||||
"SOGo",
|
||||
[username UTF8String]);
|
||||
#endif
|
||||
|
||||
connInfo = newConnInfo;
|
||||
username = [NSString stringWithUTF8String: newConnInfo->username];
|
||||
ASSIGN (activeUser, [SOGoUser userWithLogin: username]);
|
||||
@@ -322,12 +311,6 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
#if 0
|
||||
mapistore_mgmt_backend_unregister_user ([self connectionInfo], "SOGo",
|
||||
[[userContext username]
|
||||
UTF8String]);
|
||||
#endif
|
||||
|
||||
[contextUrl release];
|
||||
[userContext release];
|
||||
[containersBag release];
|
||||
@@ -355,27 +338,16 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
return activeUser;
|
||||
}
|
||||
|
||||
// - (void) logRestriction: (struct mapi_SRestriction *) res
|
||||
// withState: (MAPIRestrictionState) state
|
||||
// {
|
||||
// NSString *resStr;
|
||||
|
||||
// resStr = MAPIStringForRestriction (res);
|
||||
|
||||
// [self logWithFormat: @"%@ --> %@", resStr, MAPIStringForRestrictionState (state)];
|
||||
// }
|
||||
|
||||
- (int) getPath: (char **) path
|
||||
ofFMID: (uint64_t) fmid
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
int rc;
|
||||
NSString *objectURL, *url;
|
||||
// TDB_DATA key, dbuf;
|
||||
|
||||
url = [contextUrl absoluteString];
|
||||
// FIXME transform percent escapes but not for user part of the url
|
||||
//stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
|
||||
//[xxxx stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
|
||||
objectURL = [[userContext mapping] urlFromID: fmid];
|
||||
if (objectURL)
|
||||
{
|
||||
@@ -396,14 +368,6 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"%s: you should *never* get here", __PRETTY_FUNCTION__];
|
||||
// /* attempt to populate our mapping dict with data from indexing.tdb */
|
||||
// key.dptr = (unsigned char *) talloc_asprintf (memCtx, "0x%.16llx",
|
||||
// (long long unsigned int )fmid);
|
||||
// key.dsize = strlen ((const char *) key.dptr);
|
||||
|
||||
// dbuf = tdb_fetch (memCtx->indexing_list->index_ctx->tdb, key);
|
||||
// talloc_free (key.dptr);
|
||||
// uri = talloc_strndup (memCtx, (const char *)dbuf.dptr, dbuf.dsize);
|
||||
*path = NULL;
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
@@ -426,7 +390,7 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
NSArray *pathComponents;
|
||||
NSUInteger count, max;
|
||||
|
||||
[userContext activateWithUser: activeUser];
|
||||
[userContext activate];
|
||||
woContext = [userContext woContext];
|
||||
|
||||
[self ensureContextFolder];
|
||||
@@ -488,7 +452,7 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
/* utils */
|
||||
|
||||
- (NSString *) extractChildNameFromURL: (NSString *) objectURL
|
||||
andFolderURLAt: (NSString **) folderURL;
|
||||
andFolderURLAt: (NSString **) folderURL;
|
||||
{
|
||||
NSString *childKey;
|
||||
NSRange lastSlash;
|
||||
@@ -497,15 +461,15 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
if ([objectURL hasSuffix: @"/"])
|
||||
objectURL = [objectURL substringToIndex: [objectURL length] - 2];
|
||||
lastSlash = [objectURL rangeOfString: @"/"
|
||||
options: NSBackwardsSearch];
|
||||
options: NSBackwardsSearch];
|
||||
if (lastSlash.location != NSNotFound)
|
||||
{
|
||||
slashPtr = NSMaxRange (lastSlash);
|
||||
childKey = [objectURL substringFromIndex: slashPtr];
|
||||
if ([childKey length] == 0)
|
||||
childKey = nil;
|
||||
childKey = nil;
|
||||
if (folderURL)
|
||||
*folderURL = [objectURL substringToIndex: slashPtr];
|
||||
*folderURL = [objectURL substringToIndex: slashPtr];
|
||||
}
|
||||
else
|
||||
childKey = nil;
|
||||
@@ -516,11 +480,10 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
- (uint64_t) idForObjectWithKey: (NSString *) key
|
||||
inFolderURL: (NSString *) folderURL
|
||||
{
|
||||
NSString *childURL, *owner;
|
||||
NSString *childURL;
|
||||
MAPIStoreMapping *mapping;
|
||||
uint64_t mappingId;
|
||||
uint32_t contextId;
|
||||
void *rootObject;
|
||||
enum mapistore_error ret;
|
||||
|
||||
if (key)
|
||||
childURL = [NSString stringWithFormat: @"%@%@", folderURL,
|
||||
@@ -531,17 +494,13 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
mappingId = [mapping idFromURL: childURL];
|
||||
if (mappingId == NSNotFound)
|
||||
{
|
||||
//[self warnWithFormat: @"no id exist yet for '%@', requesting one...",
|
||||
// childURL];
|
||||
mapistore_indexing_get_new_folderID (connInfo->mstore_ctx, &mappingId);
|
||||
[mapping registerURL: childURL withID: mappingId];
|
||||
contextId = 0;
|
||||
|
||||
mapistore_search_context_by_uri (connInfo->mstore_ctx, [folderURL UTF8String],
|
||||
&contextId, &rootObject);
|
||||
owner = [userContext username];
|
||||
mapistore_indexing_record_add_mid (connInfo->mstore_ctx, contextId,
|
||||
[owner UTF8String], mappingId);
|
||||
[self logWithFormat: @"No id exist yet for '%@', requesting one", childURL];
|
||||
ret = mapistore_indexing_get_new_folderID (connInfo->mstore_ctx, &mappingId);
|
||||
if (ret == MAPISTORE_SUCCESS)
|
||||
[mapping registerURL: childURL withID: mappingId];
|
||||
else
|
||||
[self errorWithFormat: @"Error trying to get new folder id (%d): %s",
|
||||
ret, mapistore_errstr (ret)];
|
||||
}
|
||||
|
||||
return mappingId;
|
||||
@@ -595,15 +554,14 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
|
||||
memCtx = talloc_zero(NULL, TALLOC_CTX);
|
||||
newFMIDs = [NSMutableArray arrayWithCapacity: max];
|
||||
|
||||
|
||||
if (mapistore_indexing_get_new_folderIDs (connInfo->mstore_ctx,
|
||||
memCtx, max, &numbers)
|
||||
!= MAPISTORE_SUCCESS || numbers->cValues != max)
|
||||
abort ();
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
newNumber
|
||||
= [NSString stringWithUnsignedLongLong: numbers->lpui8[count]];
|
||||
newNumber = [NSString stringWithUnsignedLongLong: numbers->lpui8[count]];
|
||||
[newFMIDs addObject: newNumber];
|
||||
}
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
else
|
||||
[dbFolder changePathTo: [NSString stringWithFormat: @"/fallback/%@", pathComponent]
|
||||
intoNewContainer: nil];
|
||||
|
||||
|
||||
mapping = [self mapping];
|
||||
|
||||
if (targetFolder)
|
||||
@@ -193,6 +193,8 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
SOGoMAPIDBMessage *fsObject;
|
||||
NSString *newKey;
|
||||
|
||||
[[self userContext] activate];
|
||||
|
||||
newKey = [NSString stringWithFormat: @"%@.plist",
|
||||
[SOGoObject globallyUniqueObjectId]];
|
||||
fsObject = [SOGoMAPIDBMessage objectWithName: newKey
|
||||
@@ -214,10 +216,13 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
ownerUser = [[self userContext] sogoUser];
|
||||
if ([[context activeUser] isEqual: ownerUser]
|
||||
|| [self subscriberCanReadMessages])
|
||||
keys = [(SOGoCacheGCSFolder *) sogoObject childKeysOfType: MAPIMessageCacheObject
|
||||
includeDeleted: NO
|
||||
matchingQualifier: qualifier
|
||||
andSortOrderings: sortOrderings];
|
||||
{
|
||||
[[self userContext] activate];
|
||||
keys = [(SOGoCacheGCSFolder *) sogoObject childKeysOfType: MAPIMessageCacheObject
|
||||
includeDeleted: NO
|
||||
matchingQualifier: qualifier
|
||||
andSortOrderings: sortOrderings];
|
||||
}
|
||||
else
|
||||
keys = [NSArray array];
|
||||
|
||||
@@ -227,6 +232,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
- (NSArray *) folderKeysMatchingQualifier: (EOQualifier *) qualifier
|
||||
andSortOrderings: (NSArray *) sortOrderings
|
||||
{
|
||||
[[self userContext] activate];
|
||||
return [dbFolder childKeysOfType: MAPIFolderCacheObject
|
||||
includeDeleted: NO
|
||||
matchingQualifier: qualifier
|
||||
@@ -262,7 +268,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
if ([date laterDate: fileDate] == fileDate)
|
||||
{
|
||||
//[self logWithFormat: @"current date: %@", date];
|
||||
|
||||
|
||||
date = fileDate;
|
||||
}
|
||||
}
|
||||
@@ -326,7 +332,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
rights |= RightsFolderContact;
|
||||
if (rights != 0)
|
||||
rights |= RoleNone; /* actually "folder visible" */
|
||||
|
||||
|
||||
return rights;
|
||||
}
|
||||
|
||||
@@ -361,7 +367,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
subscribed to read an open contained messages in order to enable them to
|
||||
find the "LocalFreebusy" message */
|
||||
[sogoObject reloadIfNeeded];
|
||||
|
||||
|
||||
displayName = [[sogoObject properties]
|
||||
objectForKey: MAPIPropertyKey (PidTagDisplayName)];
|
||||
|
||||
|
||||
@@ -120,6 +120,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
folderName = [folderURL host];
|
||||
|
||||
userContext = [self userContext];
|
||||
[userContext activate];
|
||||
[userContext ensureFolderTableExists];
|
||||
|
||||
ASSIGN (dbFolder,
|
||||
@@ -231,30 +232,19 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
|
||||
- (id) lookupFolder: (NSString *) folderKey
|
||||
{
|
||||
MAPIStoreFolder *childFolder;
|
||||
MAPIStoreFolder *childFolder = nil;
|
||||
SOGoFolder *sogoFolder;
|
||||
WOContext *woContext;
|
||||
|
||||
childFolder = nil;
|
||||
if ([[self folderKeys] containsObject: folderKey])
|
||||
{
|
||||
woContext = [[self userContext] woContext];
|
||||
/* We activate the user for the context using the root folder
|
||||
context as there are times where the active user is not
|
||||
matching with the one stored in the application context
|
||||
and SOGo object is storing cached data with the wrong user */
|
||||
[[self userContext] activateWithUser: [woContext activeUser]];
|
||||
sogoFolder = [sogoObject lookupName: folderKey inContext: woContext
|
||||
[[self userContext] activate];
|
||||
sogoFolder = [sogoObject lookupName: folderKey
|
||||
inContext: nil
|
||||
acquire: NO];
|
||||
if (sogoFolder && ![sogoFolder isKindOfClass: NSExceptionK])
|
||||
{
|
||||
[sogoFolder setContext: woContext];
|
||||
childFolder = [isa mapiStoreObjectWithSOGoObject: sogoFolder
|
||||
inContainer: self];
|
||||
}
|
||||
childFolder = [isa mapiStoreObjectWithSOGoObject: sogoFolder
|
||||
inContainer: self];
|
||||
}
|
||||
else
|
||||
childFolder = nil;
|
||||
|
||||
return childFolder;
|
||||
}
|
||||
@@ -285,6 +275,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
|
||||
if (messageKey)
|
||||
{
|
||||
[[self userContext] activate];
|
||||
msgObject = [sogoObject lookupName: messageKey
|
||||
inContext: nil
|
||||
acquire: NO];
|
||||
@@ -314,6 +305,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
|
||||
if (messageKey)
|
||||
{
|
||||
[[self userContext] activate];
|
||||
if ([[self faiMessageKeys] containsObject: messageKey])
|
||||
{
|
||||
msgObject = [dbFolder lookupName: messageKey
|
||||
@@ -546,6 +538,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
|
||||
context = [self context];
|
||||
ownerUser = [[self userContext] sogoUser];
|
||||
[[self userContext] activate];
|
||||
|
||||
if ([[context activeUser] isEqual: ownerUser]
|
||||
|| (!isAssociated && [self subscriberCanCreateMessages]))
|
||||
@@ -758,9 +751,9 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
}
|
||||
else
|
||||
rc = MAPISTORE_ERR_DENIED;
|
||||
|
||||
|
||||
//talloc_free (memCtx);
|
||||
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -931,7 +924,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
NSArray *newIDs;
|
||||
uint64_t idNbr;
|
||||
bool softDeleted;
|
||||
|
||||
|
||||
baseURL = [self url];
|
||||
|
||||
mapping = [self mapping];
|
||||
@@ -1060,9 +1053,10 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
{
|
||||
static enum MAPITAGS bannedProps[] = { PR_MID, PR_FID, PR_PARENT_FID,
|
||||
PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY,
|
||||
PR_CHANGE_KEY, 0x00000000 };
|
||||
PR_CHANGE_KEY, PidTagChangeNumber, 0x00000000 };
|
||||
enum MAPITAGS *currentProp;
|
||||
NSMutableDictionary *propsCopy;
|
||||
uint64_t cn;
|
||||
|
||||
/* TODO: this should no longer be required once mapistore v2 API is in
|
||||
place, when we can then do this from -dealloc below */
|
||||
@@ -1080,6 +1074,12 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
}
|
||||
|
||||
[properties addEntriesFromDictionary: propsCopy];
|
||||
|
||||
/* Update change number after setting the properties */
|
||||
cn = [[self context] getNewChangeNumber];
|
||||
[properties setObject: [NSNumber numberWithUnsignedLongLong: cn]
|
||||
forKey: MAPIPropertyKey (PidTagChangeNumber)];
|
||||
|
||||
[dbFolder save];
|
||||
}
|
||||
|
||||
@@ -1105,6 +1105,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
- (NSArray *) faiMessageKeysMatchingQualifier: (EOQualifier *) qualifier
|
||||
andSortOrderings: (NSArray *) sortOrderings
|
||||
{
|
||||
[[self userContext] activate];
|
||||
return [dbFolder childKeysOfType: MAPIFAICacheObject
|
||||
includeDeleted: NO
|
||||
matchingQualifier: qualifier
|
||||
@@ -1213,7 +1214,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
|
||||
/*
|
||||
Possible values are:
|
||||
|
||||
|
||||
0x00000001 Modify
|
||||
0x00000002 Read
|
||||
0x00000004 Delete
|
||||
@@ -1244,7 +1245,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
access |= 0x10;
|
||||
if (userIsOwner)
|
||||
access |= 0x20;
|
||||
|
||||
|
||||
*data = MAPILongValue (memCtx, access);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
@@ -1273,7 +1274,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
rights |= RightsCreateSubfolders;
|
||||
if (userIsOwner)
|
||||
rights |= RightsFolderOwner | RightsFolderContact;
|
||||
|
||||
|
||||
*data = MAPILongValue (memCtx, rights);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
@@ -1309,7 +1310,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
*data = MAPIBoolValue (memCtx, [self supportsSubFolders] && [[self folderKeys] count] > 0);
|
||||
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1317,7 +1318,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
*data = MAPILongValue (memCtx, [[self folderKeys] count]);
|
||||
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1419,7 +1420,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
[dbObject setIsNew: YES];
|
||||
newMessage = [MAPIStoreFAIMessageK mapiStoreObjectWithSOGoObject: dbObject
|
||||
inContainer: self];
|
||||
|
||||
|
||||
return newMessage;
|
||||
}
|
||||
|
||||
@@ -1428,6 +1429,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
MAPIStoreMessage *newMessage;
|
||||
WOContext *woContext;
|
||||
|
||||
[[self userContext] activate];
|
||||
|
||||
if (isAssociated)
|
||||
newMessage = [self _createAssociatedMessage];
|
||||
else
|
||||
@@ -1627,7 +1630,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
|
||||
permissionUser = nil;
|
||||
permissionRoles = nil;
|
||||
|
||||
|
||||
if (currentPermission->PermissionDataFlags == ROW_ADD)
|
||||
isAdd = YES;
|
||||
else if (currentPermission->PermissionDataFlags == ROW_MODIFY)
|
||||
@@ -1778,7 +1781,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
return nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSArray *) getDeletedKeysFromChangeNumber: (uint64_t) changeNum
|
||||
|
||||
@@ -121,7 +121,6 @@
|
||||
|
||||
userContext = [MAPIStoreUserContext userContextWithUsername: userName
|
||||
andTDBIndexing: NULL];
|
||||
[MAPIApp setUserContext: userContext];
|
||||
moduleName = [self MAPIModuleName];
|
||||
parentFolder = [[userContext rootFolders] objectForKey: moduleName];
|
||||
nameInContainer = nil;
|
||||
@@ -134,7 +133,6 @@
|
||||
moduleName, nameInContainer];
|
||||
else
|
||||
mapistoreURI = nil;
|
||||
[MAPIApp setUserContext: nil];
|
||||
|
||||
return mapistoreURI;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
NSArray *parts;
|
||||
NSString *lastFolder;
|
||||
NSUInteger max;
|
||||
|
||||
|
||||
parts = [folderName componentsSeparatedByString: @"/"];
|
||||
max = [parts count];
|
||||
if (max > 1)
|
||||
@@ -198,7 +198,6 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
|
||||
userContext = [MAPIStoreUserContext userContextWithUsername: userName
|
||||
andTDBIndexing: NULL];
|
||||
[MAPIApp setUserContext: userContext];
|
||||
accountFolder = [[userContext rootFolders] objectForKey: @"mail"];
|
||||
folderName = [NSString stringWithFormat: @"folder%@",
|
||||
[newFolderName asCSSIdentifier]];
|
||||
@@ -213,7 +212,6 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
[[folderName stringByEncodingImap4FolderName] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
|
||||
else
|
||||
mapistoreURI = nil;
|
||||
[MAPIApp setUserContext: nil];
|
||||
|
||||
return mapistoreURI;
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
[[self mapping] updateID: fid withURL: [self url]];
|
||||
[dbFolder setNameInContainer: newNameInContainer];
|
||||
[self cleanupCaches];
|
||||
|
||||
|
||||
propsCopy = [newProperties mutableCopy];
|
||||
[propsCopy removeObjectForKey: key];
|
||||
[propsCopy autorelease];
|
||||
@@ -186,10 +186,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
nameInContainer = [NSString stringWithFormat: @"folder%@",
|
||||
[[folderName stringByEncodingImap4FolderName] asCSSIdentifier]];
|
||||
|
||||
/* it may be the operation is interleaved with operations
|
||||
from other users having cached information in the thread
|
||||
with the other user, so it'd better activate the user again here... */
|
||||
[[self userContext] activateWithUser: [[[self userContext] woContext] activeUser]];
|
||||
[[self userContext] activate];
|
||||
|
||||
newFolder = [SOGoMailFolderK objectWithName: nameInContainer
|
||||
inContainer: sogoObject];
|
||||
@@ -245,7 +242,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
sortOrdering: nil]
|
||||
count];
|
||||
*data = MAPILongValue (memCtx, longValue);
|
||||
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -253,7 +250,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
*data = [@"IPF.Note" asUnicodeInMemCtx: memCtx];
|
||||
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -265,7 +262,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
if (!nonDeletedQualifier)
|
||||
{
|
||||
deletedQualifier
|
||||
= [[EOKeyValueQualifier alloc]
|
||||
= [[EOKeyValueQualifier alloc]
|
||||
initWithKey: @"FLAGS"
|
||||
operatorSelector: EOQualifierOperatorContains
|
||||
value: [NSArray arrayWithObject: @"Deleted"]];
|
||||
@@ -378,7 +375,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
[self warnWithFormat: @"qualifier is only used for folders with name"];
|
||||
if (sortOrderings)
|
||||
[self errorWithFormat: @"sort orderings are not used for folders"];
|
||||
|
||||
|
||||
/* FIXME: Flush any cache before retrieving the hierarchy, this
|
||||
slows things down but it is safer */
|
||||
if (!qualifier)
|
||||
@@ -472,7 +469,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
}
|
||||
else
|
||||
supportsSubFolders = YES;
|
||||
|
||||
|
||||
return supportsSubFolders;
|
||||
}
|
||||
|
||||
@@ -641,7 +638,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
fetchResults
|
||||
= [fetchResults sortedArrayUsingFunction: _compareFetchResultsByMODSEQ
|
||||
context: NULL];
|
||||
|
||||
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
result = [fetchResults objectAtIndex: count];
|
||||
@@ -951,7 +948,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
}
|
||||
}
|
||||
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
|
||||
|
||||
|
||||
[versionsMessage save];
|
||||
}
|
||||
|
||||
@@ -1178,7 +1175,7 @@ _parseIMAPRange (const unichar *uniString, NSArray **UIDsP)
|
||||
count++;
|
||||
}
|
||||
*UIDsP = UIDs;
|
||||
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -1456,7 +1453,7 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
[uids addObject: [self messageUIDFromMessageKey: childKey]];
|
||||
}
|
||||
|
||||
result = [client copyUids: uids
|
||||
result = [client copyUids: uids
|
||||
toFolder: newFolderIMAPName];
|
||||
if ([[result objectForKey: @"result"] boolValue])
|
||||
{
|
||||
@@ -1495,9 +1492,10 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
{
|
||||
SOGoCacheObject *childObject;
|
||||
|
||||
childObject = [SOGoCacheObject objectWithName: [SOGoCacheObject
|
||||
globallyUniqueObjectId]
|
||||
inContainer: sogoObject];
|
||||
[[[self context] userContext] activate];
|
||||
|
||||
childObject = [SOGoCacheObject objectWithName: [SOGoCacheObject globallyUniqueObjectId]
|
||||
inContainer: sogoObject];
|
||||
return [MAPIStoreMailVolatileMessage
|
||||
mapiStoreObjectWithSOGoObject: childObject
|
||||
inContainer: self];
|
||||
@@ -1573,7 +1571,7 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
rights |= RoleNone; /* actually "folder visible" */
|
||||
|
||||
// [self logWithFormat: @"rights for roles (%@) = %.8x", roles, rights];
|
||||
|
||||
|
||||
return rights;
|
||||
}
|
||||
|
||||
@@ -1624,7 +1622,7 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
client = [[(SOGoMailFolder *) sogoObject imap4Connection] client];
|
||||
[client select: [sogoObject absoluteImap4Name]];
|
||||
response = [client fetchUids: [keyAssoc allKeys]
|
||||
|
||||
@@ -1614,6 +1614,8 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
max = [keyParts count];
|
||||
if (max > 0)
|
||||
{
|
||||
[[self userContext] activate];
|
||||
|
||||
currentPart = [sogoObject lookupName: [keyParts objectAtIndex: 0]
|
||||
inContext: nil
|
||||
acquire: NO];
|
||||
|
||||
@@ -771,6 +771,12 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers,
|
||||
subjectData = [mailProperties objectForKey: MAPIPropertyKey (PR_NORMALIZED_SUBJECT_UNICODE)];
|
||||
if (subjectData)
|
||||
[subject appendString: subjectData];
|
||||
if ([subject length] == 0)
|
||||
{
|
||||
subjectData = [mailProperties objectForKey: MAPIPropertyKey (PR_SUBJECT_UNICODE)];
|
||||
if (subjectData)
|
||||
[subject appendString: subjectData];
|
||||
}
|
||||
[headers setObject: [subject asQPSubjectString: @"utf-8"] forKey: @"subject"];
|
||||
|
||||
messageId = [mailProperties objectForKey: MAPIPropertyKey (PR_INTERNET_MESSAGE_ID_UNICODE)];
|
||||
@@ -1077,6 +1083,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
|
||||
dd = [activeUser domainDefaults];
|
||||
from = [[activeUser allEmails] objectAtIndex: 0];
|
||||
|
||||
[[self userContext] activate];
|
||||
woContext = [[self userContext] woContext];
|
||||
authenticator = [sogoObject authenticatorInContext: woContext];
|
||||
error = [[SOGoMailer mailerWithDomainDefaults: dd]
|
||||
|
||||
@@ -178,56 +178,101 @@ rtf2html (NSData *compressedRTF)
|
||||
andColumns: (struct SPropTagArray *) columns
|
||||
{
|
||||
NSMutableDictionary *recipientProperties;
|
||||
enum MAPITAGS prop_tag;
|
||||
char *displayName = NULL;
|
||||
char *email = NULL;
|
||||
enum MAPI_OBJTYPE object_type = 0;
|
||||
char *smtpAddress = NULL;
|
||||
SOGoUser *recipientUser;
|
||||
NSUInteger count;
|
||||
NSString *recipientEmail = nil;
|
||||
NSString *recipientFullName = nil;
|
||||
id value;
|
||||
|
||||
recipientProperties = [NSMutableDictionary dictionaryWithCapacity: columns->cValues + 2];
|
||||
|
||||
for (count = 0; count < columns->cValues; count++)
|
||||
{
|
||||
prop_tag = columns->aulPropTag[count];
|
||||
switch(prop_tag) {
|
||||
case PidTagDisplayName:
|
||||
displayName = recipient->data[count];
|
||||
break;
|
||||
case PidTagEmailAddress:
|
||||
email = recipient->data[count];
|
||||
break;
|
||||
case PidTagObjectType:
|
||||
if (recipient->data[count])
|
||||
object_type = *((uint8_t*) recipient->data[count]);
|
||||
break;
|
||||
case PidTagSmtpAddress:
|
||||
smtpAddress = recipient->data[count];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (recipient->data[count])
|
||||
{
|
||||
value = NSObjectFromValuePointer (prop_tag,
|
||||
recipient->data[count]);
|
||||
if (value)
|
||||
[recipientProperties setObject: value
|
||||
forKey: MAPIPropertyKey (prop_tag)];
|
||||
}
|
||||
}
|
||||
|
||||
if (recipient->username)
|
||||
{
|
||||
value = [NSString stringWithUTF8String: recipient->username];
|
||||
[recipientProperties setObject: value forKey: @"x500dn"];
|
||||
}
|
||||
|
||||
if (object_type == MAPI_MAILUSER && recipient->username)
|
||||
{
|
||||
/* values from user object have priority over data sent by the client */
|
||||
recipientUser = [SOGoUser userWithLogin: [value lowercaseString]];
|
||||
if (recipientUser)
|
||||
{
|
||||
value = [recipientUser cn];
|
||||
if ([value length] > 0)
|
||||
[recipientProperties setObject: value forKey: @"fullName"];
|
||||
recipientFullName = value;
|
||||
|
||||
value = [[recipientUser allEmails] objectAtIndex: 0];
|
||||
if ([value length] > 0)
|
||||
[recipientProperties setObject: value forKey: @"email"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (recipient->data[0])
|
||||
{
|
||||
value = [NSString stringWithUTF8String: recipient->data[0]];
|
||||
if ([value length] > 0)
|
||||
[recipientProperties setObject: value forKey: @"fullName"];
|
||||
}
|
||||
if (recipient->data[1])
|
||||
{
|
||||
value = [NSString stringWithUTF8String: recipient->data[1]];
|
||||
if ([value length] > 0)
|
||||
[recipientProperties setObject: value forKey: @"email"];
|
||||
}
|
||||
recipientEmail = value;
|
||||
}
|
||||
}
|
||||
|
||||
for (count = 0; count < columns->cValues; count++)
|
||||
/* If we do not have values from the user object we try to get them from the parameters */
|
||||
if (!recipientFullName && displayName)
|
||||
{
|
||||
if (recipient->data[count])
|
||||
{
|
||||
value = NSObjectFromValuePointer (columns->aulPropTag[count],
|
||||
recipient->data[count]);
|
||||
if (value)
|
||||
[recipientProperties setObject: value
|
||||
forKey: MAPIPropertyKey (columns->aulPropTag[count])];
|
||||
}
|
||||
value = [NSString stringWithUTF8String: displayName];
|
||||
if ([value length] > 0)
|
||||
recipientFullName = value;
|
||||
}
|
||||
|
||||
if (!recipientEmail && email)
|
||||
{
|
||||
value = [NSString stringWithUTF8String: email];
|
||||
if ([value length] > 0)
|
||||
recipientEmail = value;
|
||||
}
|
||||
|
||||
if (!recipientEmail && smtpAddress)
|
||||
{
|
||||
value = [NSString stringWithUTF8String: smtpAddress];
|
||||
if ([value length] > 0)
|
||||
recipientEmail = value;
|
||||
}
|
||||
|
||||
/* Now we can set the properties if we have them */
|
||||
if (recipientFullName)
|
||||
[recipientProperties setObject: recipientFullName forKey: @"fullName"];
|
||||
|
||||
if (recipientEmail)
|
||||
[recipientProperties setObject: recipientEmail forKey: @"email"];
|
||||
|
||||
return recipientProperties;
|
||||
}
|
||||
|
||||
|
||||
+34
-116
@@ -61,20 +61,25 @@ static BOOL initialization_done = NO;
|
||||
if (!initialization_done) { \
|
||||
OC_DEBUG(5, "[SOGo] You should call sogo_backend_init() first. Current thread: %p, pid: %d\n", \
|
||||
GSCurrentThread(), getpid()); \
|
||||
}
|
||||
} \
|
||||
OC_DEBUG(5, "[SOGo] --->");
|
||||
|
||||
#define NS_CURRENT_THREAD_TRY_UNREGISTER() \
|
||||
if (__nsrct_thread_registered) { \
|
||||
GSUnregisterCurrentThread(); \
|
||||
}
|
||||
} \
|
||||
OC_DEBUG(6, "[SOGo] <---");
|
||||
|
||||
#define TRYCATCH_START @try {
|
||||
#define TRYCATCH_END(pool) \
|
||||
} @catch (NSException * e) { \
|
||||
enum mapistore_error ret = sogo_backend_handle_objc_exception(e, __PRETTY_FUNCTION__, __LINE__); \
|
||||
mapiapp_cleanup(); \
|
||||
[pool release]; \
|
||||
NS_CURRENT_THREAD_TRY_UNREGISTER(); \
|
||||
return ret; \
|
||||
}
|
||||
} \
|
||||
mapiapp_cleanup();
|
||||
|
||||
|
||||
static enum mapistore_error
|
||||
@@ -196,12 +201,28 @@ sogo_backend_init (void)
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
\details Cleanup operation to execute after an action has been performed
|
||||
so there won't be any conflicts with future calls.
|
||||
In practice this will deactivate the current user context set on MAPIApp
|
||||
(which is the current WOApplication), this means two things: (1) set nil
|
||||
as current user context on MAPIApp and (2) remove woContext from current
|
||||
thread dictionary (this is used on WOContext.m).
|
||||
*/
|
||||
static void mapiapp_cleanup(void)
|
||||
{
|
||||
Class MAPIApplicationK;
|
||||
MAPIApplicationK = NSClassFromString (@"MAPIApplication");
|
||||
if (MAPIApplicationK)
|
||||
[[MAPIApplicationK application] cleanup];
|
||||
}
|
||||
|
||||
/**
|
||||
\details Create a connection context to the sogo backend
|
||||
|
||||
\param mem_ctx pointer to the memory context
|
||||
\param uri pointer to the sogo path
|
||||
\param private_data pointer to the private backend context
|
||||
\param private_data pointer to the private backend context
|
||||
*/
|
||||
|
||||
static enum mapistore_error
|
||||
@@ -214,8 +235,6 @@ sogo_backend_create_context(TALLOC_CTX *mem_ctx,
|
||||
MAPIStoreContext *context;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
NS_CURRENT_THREAD_REGISTER();
|
||||
pool = [NSAutoreleasePool new];
|
||||
|
||||
@@ -251,8 +270,6 @@ sogo_backend_create_root_folder (const char *username,
|
||||
NSString *mapistoreUri;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
NS_CURRENT_THREAD_REGISTER();
|
||||
pool = [NSAutoreleasePool new];
|
||||
|
||||
@@ -288,8 +305,6 @@ sogo_backend_list_contexts(const char *username, struct indexing_context *indexi
|
||||
NSString *userName;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
NS_CURRENT_THREAD_REGISTER();
|
||||
pool = [NSAutoreleasePool new];
|
||||
|
||||
@@ -336,8 +351,6 @@ sogo_context_get_path(void *backend_object, TALLOC_CTX *mem_ctx,
|
||||
MAPIStoreContext *context;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (backend_object)
|
||||
{
|
||||
wrapper = backend_object;
|
||||
@@ -370,8 +383,6 @@ sogo_context_get_root_folder(void *backend_object, TALLOC_CTX *mem_ctx,
|
||||
MAPIStoreFolder *folder;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (backend_object)
|
||||
{
|
||||
wrapper = backend_object;
|
||||
@@ -413,8 +424,6 @@ sogo_folder_open_folder(void *folder_object, TALLOC_CTX *mem_ctx, uint64_t fid,
|
||||
MAPIStoreFolder *folder, *childFolder;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
wrapper = folder_object;
|
||||
@@ -442,7 +451,7 @@ sogo_folder_open_folder(void *folder_object, TALLOC_CTX *mem_ctx, uint64_t fid,
|
||||
|
||||
/**
|
||||
\details Create a folder in the sogo backend
|
||||
|
||||
|
||||
\param private_data pointer to the current sogo context
|
||||
|
||||
\return MAPISTORE_SUCCESS on success, otherwise MAPISTORE_ERROR
|
||||
@@ -457,8 +466,6 @@ sogo_folder_create_folder(void *folder_object, TALLOC_CTX *mem_ctx,
|
||||
MAPIStoreFolder *folder, *childFolder;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
wrapper = folder_object;
|
||||
@@ -500,8 +507,6 @@ sogo_folder_delete(void *folder_object)
|
||||
MAPIStoreFolder *folder;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
wrapper = folder_object;
|
||||
@@ -532,8 +537,6 @@ sogo_folder_get_child_count(void *folder_object, enum mapistore_table_type table
|
||||
MAPIStoreFolder *folder;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
wrapper = folder_object;
|
||||
@@ -568,8 +571,6 @@ sogo_folder_open_message(void *folder_object,
|
||||
MAPIStoreMessage *message;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
wrapper = folder_object;
|
||||
@@ -610,8 +611,6 @@ sogo_folder_create_message(void *folder_object,
|
||||
MAPIStoreMessage *message;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
wrapper = folder_object;
|
||||
@@ -622,7 +621,7 @@ sogo_folder_create_message(void *folder_object,
|
||||
TRYCATCH_START
|
||||
rc = [folder createMessage: &message
|
||||
withMID: mid
|
||||
isAssociated: associated];
|
||||
isAssociated: associated];
|
||||
if (rc == MAPISTORE_SUCCESS)
|
||||
*message_object = [message tallocWrapper: mem_ctx];
|
||||
TRYCATCH_END(pool)
|
||||
@@ -646,8 +645,6 @@ sogo_folder_delete_message(void *folder_object, uint64_t mid, uint8_t flags)
|
||||
MAPIStoreFolder *folder;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
wrapper = folder_object;
|
||||
@@ -684,8 +681,6 @@ sogo_folder_move_copy_messages(void *folder_object,
|
||||
struct MAPIStoreTallocWrapper *wrapper;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
wrapper = folder_object;
|
||||
@@ -728,8 +723,6 @@ sogo_folder_move_folder(void *folder_object, void *target_folder_object,
|
||||
struct MAPIStoreTallocWrapper *wrapper;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
wrapper = folder_object;
|
||||
@@ -778,8 +771,6 @@ sogo_folder_copy_folder(void *folder_object, void *target_folder_object, TALLOC_
|
||||
struct MAPIStoreTallocWrapper *wrapper;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
wrapper = folder_object;
|
||||
@@ -822,8 +813,6 @@ sogo_folder_get_deleted_fmids(void *folder_object, TALLOC_CTX *mem_ctx,
|
||||
MAPIStoreFolder *folder;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
wrapper = folder_object;
|
||||
@@ -861,8 +850,6 @@ sogo_folder_open_table(void *folder_object, TALLOC_CTX *mem_ctx,
|
||||
MAPIStoreTable *table;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
wrapper = folder_object;
|
||||
@@ -900,8 +887,6 @@ sogo_folder_modify_permissions(void *folder_object, uint8_t flags,
|
||||
MAPIStoreFolder *folder;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
wrapper = folder_object;
|
||||
@@ -934,8 +919,6 @@ sogo_folder_preload_message_bodies(void *folder_object, enum mapistore_table_typ
|
||||
MAPIStoreFolder *folder;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
wrapper = folder_object;
|
||||
@@ -969,8 +952,6 @@ sogo_message_get_message_data(void *message_object,
|
||||
MAPIStoreMessage *message;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (message_object)
|
||||
{
|
||||
wrapper = message_object;
|
||||
@@ -1004,8 +985,6 @@ sogo_message_create_attachment (void *message_object, TALLOC_CTX *mem_ctx, void
|
||||
MAPIStoreAttachment *attachment;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (message_object)
|
||||
{
|
||||
wrapper = message_object;
|
||||
@@ -1017,7 +996,6 @@ sogo_message_create_attachment (void *message_object, TALLOC_CTX *mem_ctx, void
|
||||
rc = [message createAttachment: &attachment inAID: aidp];
|
||||
if (rc == MAPISTORE_SUCCESS)
|
||||
*attachment_object = [attachment tallocWrapper: mem_ctx];
|
||||
// [context tearDownRequest];
|
||||
TRYCATCH_END(pool)
|
||||
|
||||
[pool release];
|
||||
@@ -1041,8 +1019,6 @@ sogo_message_open_attachment (void *message_object, TALLOC_CTX *mem_ctx,
|
||||
MAPIStoreAttachment *attachment;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (message_object)
|
||||
{
|
||||
wrapper = message_object;
|
||||
@@ -1054,7 +1030,6 @@ sogo_message_open_attachment (void *message_object, TALLOC_CTX *mem_ctx,
|
||||
rc = [message getAttachment: &attachment withAID: aid];
|
||||
if (rc == MAPISTORE_SUCCESS)
|
||||
*attachment_object = [attachment tallocWrapper: mem_ctx];
|
||||
// [context tearDownRequest];
|
||||
TRYCATCH_END(pool)
|
||||
|
||||
[pool release];
|
||||
@@ -1077,8 +1052,6 @@ sogo_message_get_attachment_table (void *message_object, TALLOC_CTX *mem_ctx, vo
|
||||
MAPIStoreAttachmentTable *table;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (message_object)
|
||||
{
|
||||
wrapper = message_object;
|
||||
@@ -1091,7 +1064,6 @@ sogo_message_get_attachment_table (void *message_object, TALLOC_CTX *mem_ctx, vo
|
||||
andRowCount: row_count];
|
||||
if (rc == MAPISTORE_SUCCESS)
|
||||
*table_object = [table tallocWrapper: mem_ctx];
|
||||
// [context tearDownRequest];
|
||||
TRYCATCH_END(pool)
|
||||
|
||||
[pool release];
|
||||
@@ -1116,9 +1088,6 @@ sogo_message_modify_recipients (void *message_object,
|
||||
MAPIStoreMessage *message;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (message_object)
|
||||
{
|
||||
wrapper = message_object;
|
||||
@@ -1130,7 +1099,6 @@ sogo_message_modify_recipients (void *message_object,
|
||||
rc = [message modifyRecipientsWithRecipients: recipients
|
||||
andCount: count
|
||||
andColumns: columns];
|
||||
// [context tearDownRequest];
|
||||
TRYCATCH_END(pool)
|
||||
|
||||
[pool release];
|
||||
@@ -1152,9 +1120,6 @@ sogo_message_set_read_flag (void *message_object, uint8_t flag)
|
||||
MAPIStoreMessage *message;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (message_object)
|
||||
{
|
||||
wrapper = message_object;
|
||||
@@ -1164,7 +1129,6 @@ sogo_message_set_read_flag (void *message_object, uint8_t flag)
|
||||
|
||||
TRYCATCH_START
|
||||
rc = [message setReadFlag: flag];
|
||||
// [context tearDownRequest];
|
||||
TRYCATCH_END(pool)
|
||||
|
||||
[pool release];
|
||||
@@ -1186,9 +1150,6 @@ sogo_message_save (void *message_object, TALLOC_CTX *mem_ctx)
|
||||
MAPIStoreMessage *message;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (message_object)
|
||||
{
|
||||
wrapper = message_object;
|
||||
@@ -1198,7 +1159,6 @@ sogo_message_save (void *message_object, TALLOC_CTX *mem_ctx)
|
||||
|
||||
TRYCATCH_START
|
||||
rc = [message saveMessage: mem_ctx];
|
||||
// [context tearDownRequest];
|
||||
TRYCATCH_END(pool)
|
||||
|
||||
[pool release];
|
||||
@@ -1220,9 +1180,6 @@ sogo_message_submit (void *message_object, enum SubmitFlags flags)
|
||||
MAPIStoreMailVolatileMessage *message;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (message_object)
|
||||
{
|
||||
wrapper = message_object;
|
||||
@@ -1232,7 +1189,6 @@ sogo_message_submit (void *message_object, enum SubmitFlags flags)
|
||||
|
||||
TRYCATCH_START
|
||||
rc = [message submitWithFlags: flags];
|
||||
// [context tearDownRequest];
|
||||
TRYCATCH_END(pool)
|
||||
|
||||
[pool release];
|
||||
@@ -1259,9 +1215,6 @@ sogo_message_attachment_open_embedded_message (void *attachment_object,
|
||||
MAPIStoreEmbeddedMessage *message;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (attachment_object)
|
||||
{
|
||||
wrapper = attachment_object;
|
||||
@@ -1301,9 +1254,6 @@ sogo_message_attachment_create_embedded_message (void *attachment_object,
|
||||
MAPIStoreEmbeddedMessage *message;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (attachment_object)
|
||||
{
|
||||
wrapper = attachment_object;
|
||||
@@ -1338,9 +1288,6 @@ static enum mapistore_error sogo_table_get_available_properties(void *table_obje
|
||||
MAPIStoreTable *table;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (table_object)
|
||||
{
|
||||
wrapper = table_object;
|
||||
@@ -1371,9 +1318,6 @@ sogo_table_set_columns (void *table_object, uint16_t count, enum MAPITAGS *prope
|
||||
MAPIStoreTable *table;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (table_object)
|
||||
{
|
||||
wrapper = table_object;
|
||||
@@ -1405,9 +1349,6 @@ sogo_table_set_restrictions (void *table_object, struct mapi_SRestriction *restr
|
||||
MAPIStoreTable *table;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (table_object)
|
||||
{
|
||||
wrapper = table_object;
|
||||
@@ -1441,9 +1382,6 @@ sogo_table_set_sort_order (void *table_object, struct SSortOrderSet *sort_order,
|
||||
MAPIStoreTable *table;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (table_object)
|
||||
{
|
||||
wrapper = table_object;
|
||||
@@ -1479,9 +1417,6 @@ sogo_table_get_row (void *table_object, TALLOC_CTX *mem_ctx,
|
||||
MAPIStoreTable *table;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (table_object)
|
||||
{
|
||||
wrapper = table_object;
|
||||
@@ -1515,9 +1450,6 @@ sogo_table_get_row_count (void *table_object,
|
||||
MAPIStoreTable *table;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (table_object)
|
||||
{
|
||||
wrapper = table_object;
|
||||
@@ -1549,9 +1481,6 @@ sogo_table_handle_destructor (void *table_object, uint32_t handle_id)
|
||||
MAPIStoreTable *table;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (table_object)
|
||||
{
|
||||
wrapper = table_object;
|
||||
@@ -1584,9 +1513,6 @@ static enum mapistore_error sogo_properties_get_available_properties(void *objec
|
||||
MAPIStoreObject *propObject;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (object)
|
||||
{
|
||||
wrapper = object;
|
||||
@@ -1620,9 +1546,6 @@ sogo_properties_get_properties (void *object,
|
||||
MAPIStoreObject *propObject;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (object)
|
||||
{
|
||||
wrapper = object;
|
||||
@@ -1655,9 +1578,6 @@ sogo_properties_set_properties (void *object, struct SRow *aRow)
|
||||
MAPIStoreObject *propObject;
|
||||
int rc;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (object)
|
||||
{
|
||||
wrapper = object;
|
||||
@@ -1681,18 +1601,16 @@ sogo_properties_set_properties (void *object, struct SRow *aRow)
|
||||
}
|
||||
|
||||
static enum mapistore_error
|
||||
sogo_manager_generate_uri (TALLOC_CTX *mem_ctx,
|
||||
const char *user,
|
||||
const char *folder,
|
||||
const char *message,
|
||||
sogo_manager_generate_uri (TALLOC_CTX *mem_ctx,
|
||||
const char *user,
|
||||
const char *folder,
|
||||
const char *message,
|
||||
const char *rootURI,
|
||||
char **uri)
|
||||
{
|
||||
NSAutoreleasePool *pool;
|
||||
NSString *partialURLString, *username, *directory;
|
||||
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (uri == NULL) return MAPISTORE_ERR_INVALID_PARAMETER;
|
||||
|
||||
/* This fixes a crash occurring during the instantiation of the
|
||||
@@ -1738,9 +1656,9 @@ sogo_manager_generate_uri (TALLOC_CTX *mem_ctx,
|
||||
*/
|
||||
int mapistore_init_backend(void)
|
||||
{
|
||||
struct mapistore_backend backend;
|
||||
int ret;
|
||||
static BOOL registered = NO;
|
||||
struct mapistore_backend backend;
|
||||
int ret;
|
||||
static BOOL registered = NO;
|
||||
|
||||
if (registered)
|
||||
ret = MAPISTORE_SUCCESS;
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#import <Appointments/SOGoTaskObject.h>
|
||||
|
||||
#import "MAPIApplication.h"
|
||||
#import "MAPIStoreUserContext.h"
|
||||
#import "MAPIStoreTasksContext.h"
|
||||
#import "MAPIStoreTasksMessage.h"
|
||||
#import "MAPIStoreTasksMessageTable.h"
|
||||
@@ -61,6 +62,7 @@
|
||||
SOGoTaskObject *newEntry;
|
||||
NSString *name;
|
||||
|
||||
[[[self context] userContext] activate];
|
||||
name = [NSString stringWithFormat: @"%@.ics",
|
||||
[SOGoObject globallyUniqueObjectId]];
|
||||
newEntry = [SOGoTaskObject objectWithName: name
|
||||
@@ -69,7 +71,7 @@
|
||||
newMessage = [MAPIStoreTasksMessage mapiStoreObjectWithSOGoObject: newEntry
|
||||
inContainer: self];
|
||||
|
||||
|
||||
|
||||
return newMessage;
|
||||
}
|
||||
|
||||
@@ -116,7 +118,7 @@
|
||||
rights |= RightsReadItems;
|
||||
if (rights != 0)
|
||||
rights |= RoleNone; /* actually "folder visible" */
|
||||
|
||||
|
||||
return rights;
|
||||
}
|
||||
|
||||
|
||||
@@ -86,6 +86,8 @@
|
||||
|
||||
/* SOGo hacky magic */
|
||||
- (void) activateWithUser: (SOGoUser *) activeUser;
|
||||
- (void) activate;
|
||||
- (void) deactivate;
|
||||
- (MAPIStoreAuthenticator *) authenticator;
|
||||
- (WOContext *) woContext;
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserFolder.h>
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <Mailer/SOGoMailAccount.h>
|
||||
#import <Mailer/SOGoMailAccounts.h>
|
||||
|
||||
@@ -158,6 +159,8 @@ static NSMapTable *contextsTable = nil;
|
||||
if ([userPassword length] == 0)
|
||||
userPassword = username;
|
||||
[authenticator setPassword: userPassword];
|
||||
// Activate the profile on initialization
|
||||
[self activate];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -214,7 +217,7 @@ static NSMapTable *contextsTable = nil;
|
||||
if (!userFolder)
|
||||
{
|
||||
userFolder = [SOGoUserFolder objectWithName: username
|
||||
inContainer: MAPIApp];
|
||||
inContainer: nil];
|
||||
[userFolder retain];
|
||||
}
|
||||
|
||||
@@ -232,7 +235,8 @@ static NSMapTable *contextsTable = nil;
|
||||
if (!rootFolders)
|
||||
{
|
||||
rootFolders = [NSMutableDictionary new];
|
||||
[self userFolder];
|
||||
[self activate];
|
||||
[self userFolder]; // force lazy initialization
|
||||
[woContext setClientObject: userFolder];
|
||||
|
||||
/* Calendar */
|
||||
@@ -257,10 +261,10 @@ static NSMapTable *contextsTable = nil;
|
||||
acquire: NO];
|
||||
[containersBag addObject: accountsFolder];
|
||||
[woContext setClientObject: accountsFolder];
|
||||
|
||||
currentFolder = [accountsFolder lookupName: @"0"
|
||||
inContext: woContext
|
||||
acquire: NO];
|
||||
|
||||
[rootFolders setObject: currentFolder
|
||||
forKey: @"mail"];
|
||||
connection = [currentFolder imap4Connection];
|
||||
@@ -268,8 +272,7 @@ static NSMapTable *contextsTable = nil;
|
||||
|
||||
/* ensure the folder cache is filled */
|
||||
[currentFolder toManyRelationshipKeysWithNamespaces: YES];
|
||||
hierarchy = [connection
|
||||
cachedHierarchyResultsForURL: [currentFolder imap4URL]];
|
||||
hierarchy = [connection cachedHierarchyResultsForURL: [currentFolder imap4URL]];
|
||||
flags = [[hierarchy objectForKey: @"list"] objectForKey: @"/INBOX"];
|
||||
inboxHasNoInferiors = [flags containsObject: @"noinferiors"];
|
||||
}
|
||||
@@ -338,7 +341,7 @@ static NSMapTable *contextsTable = nil;
|
||||
|
||||
cm = [GCSChannelManager defaultChannelManager];
|
||||
channel = [cm acquireOpenChannelForURL: folderTableURL];
|
||||
|
||||
|
||||
/* FIXME: make use of [EOChannelAdaptor describeTableNames] instead */
|
||||
tableName = [[folderTableURL path] lastPathComponent];
|
||||
if ([channel evaluateExpressionX:
|
||||
@@ -355,7 +358,7 @@ static NSMapTable *contextsTable = nil;
|
||||
[channel cancelFetch];
|
||||
|
||||
|
||||
[cm releaseChannel: channel];
|
||||
[cm releaseChannel: channel];
|
||||
}
|
||||
|
||||
/* SOGo context objects */
|
||||
@@ -369,6 +372,11 @@ static NSMapTable *contextsTable = nil;
|
||||
return authenticator;
|
||||
}
|
||||
|
||||
- (void) activate
|
||||
{
|
||||
[self activateWithUser: [self sogoUser]];
|
||||
}
|
||||
|
||||
- (void) activateWithUser: (SOGoUser *) activeUser;
|
||||
{
|
||||
NSMutableDictionary *info;
|
||||
@@ -379,4 +387,24 @@ static NSMapTable *contextsTable = nil;
|
||||
[info setObject: woContext forKey: @"WOContext"];
|
||||
}
|
||||
|
||||
- (void) deactivate
|
||||
{
|
||||
NSMutableDictionary *info;
|
||||
|
||||
if (self == [MAPIApp userContext])
|
||||
[MAPIApp setUserContext: nil];
|
||||
else
|
||||
[self errorWithFormat: @"Error: Tried to deactivate an user context "
|
||||
@"not enabled (%@ vs %@)",
|
||||
[self username], [[MAPIApp userContext] username]];
|
||||
|
||||
info = [[NSThread currentThread] threadDictionary];
|
||||
if (woContext == [info objectForKey: @"WOContext"])
|
||||
[info removeObjectForKey: @"WOContext"];
|
||||
else
|
||||
[self errorWithFormat: @"Error: Tried to deactivate a WOContext "
|
||||
@"not enabled (%@ vs %@)",
|
||||
woContext, [info objectForKey: @"WOContext"]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,27 @@
|
||||
[](http://www.sogo.nu/)
|
||||
|
||||
Share your calendars, address books and mails in your community with a completely free and open source solution.
|
||||
|
||||
Let your Mozilla Thunderbird/Lightning, Microsoft Outlook, Apple iCal/iPhone, Android and BlackBerry users collaborate using a modern platform.
|
||||
|
||||
## What is SOGo?
|
||||
|
||||
SOGo is [fully supported](http://www.sogo.nu/support/commercial_support.html) and [trusted](http://www.sogo.nu/tour/testimonials.html) groupware server with a focus on scalability and open standards. SOGo is released under the GNU GPL/LGPL v2 and above.
|
||||
|
||||
SOGo provides a rich AJAX-based Web interface and supports multiple native clients through the use of standard protocols such as CalDAV, CardDAV and GroupDAV.
|
||||
|
||||
SOGo is the missing component of your infrastructure; it sits in the middle of your servers to offer your users an uniform and complete interface to access their information. It has been deployed in production environments where thousands of users are involved.
|
||||
|
||||
[](http://www.sogo.nu/)
|
||||
|
||||
[Learn more...](http://www.sogo.nu/about/overview.html)
|
||||
|
||||
## Microsoft Outlook Compatible
|
||||
|
||||
SOGo offers native Microsoft Outlook compatibility using the [OpenChange](http://www.openchange.org/) backend developed by [Inverse](http://www.inverse.ca/). This means Microsoft Outlook 2003, 2007, 2010 and 2013 can talk directly to SOGo – just like if it was a Microsoft Exchange server. **No plugins are required in Outlook to make this work**.
|
||||
|
||||
The following screencast shows Outlook 2010 connected to SOGo as if it was an Exchange server, along the Web interface of SOGo.
|
||||
|
||||
[](http://www.youtube.com/watch?v=1hcBSB4Kxww)
|
||||
|
||||
[](https://github.com/inverse-inc/sogo/) [](https://www.ohloh.net/p/sogo) [](http://twitter.com/scalableogo) [](http://sourceforge.net/projects/sogo-zeg) [](https://freecode.com/projects/sogo-inverse) [](http://www.linkedin.com/groups/SOGo-Groupware-4164805)
|
||||
@@ -1,8 +1,6 @@
|
||||
/* iCalByDayMask.h - this file is part of SOPE
|
||||
*
|
||||
* Copyright (C) 2010 Wolfgang Sourdeau
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2010-2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -55,6 +53,8 @@ typedef iCalWeekOccurrence iCalWeekOccurrences[7];
|
||||
- (id) initWithDays: (iCalWeekOccurrences) theDays;
|
||||
+ (id) byDayMaskWithRuleString: (NSString *) byDayRule;
|
||||
- (id) initWithRuleString: (NSString *) byDayRule;
|
||||
+ (id) byDayMaskWithDaysAndOccurrences: (NSArray *) values;
|
||||
- (id) initWithDaysAndOccurrences: (NSArray *) values;
|
||||
|
||||
- (BOOL) occursOnDay: (iCalWeekDay) weekDay;
|
||||
- (BOOL) occursOnDay: (iCalWeekDay) weekDay
|
||||
@@ -71,6 +71,7 @@ typedef iCalWeekOccurrence iCalWeekOccurrences[7];
|
||||
|
||||
- (NSString *) asRuleString;
|
||||
- (NSString *) asRuleStringWithIntegers;
|
||||
- (NSArray *) asRuleArray;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
+159
-20
@@ -1,8 +1,6 @@
|
||||
/* iCalByDayMask.m - this file is part of SOPE
|
||||
*
|
||||
* Copyright (C) 2010 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2010-2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -21,7 +19,9 @@
|
||||
*/
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import "iCalByDayMask.h"
|
||||
|
||||
@@ -39,10 +39,10 @@
|
||||
+ (id) byDayMaskWithDays: (iCalWeekOccurrences) theDays
|
||||
{
|
||||
id o;
|
||||
|
||||
|
||||
o = [[self alloc] initWithDays: theDays];
|
||||
AUTORELEASE(o);
|
||||
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
{
|
||||
id o;
|
||||
iCalWeekOccurrences d;
|
||||
|
||||
|
||||
d[iCalWeekDaySunday] = 0;
|
||||
d[iCalWeekDayMonday] = iCalWeekOccurrenceAll;
|
||||
d[iCalWeekDayTuesday] = iCalWeekOccurrenceAll;
|
||||
@@ -72,7 +72,7 @@
|
||||
d[iCalWeekDaySaturday] = 0;
|
||||
o = [[self alloc] initWithDays: d];
|
||||
AUTORELEASE(o);
|
||||
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
if (self)
|
||||
{
|
||||
memset(days, 0, 7 * sizeof(iCalWeekOccurrence));
|
||||
|
||||
|
||||
if ([byDayRule length] > 0)
|
||||
{
|
||||
values = [byDayRule componentsSeparatedByString: @","];
|
||||
@@ -120,7 +120,7 @@
|
||||
|
||||
[value getCharacters: chars
|
||||
range: NSMakeRange(valueLength - 2, 2)];
|
||||
|
||||
|
||||
switch (chars[0])
|
||||
{
|
||||
case 'M': day = iCalWeekDayMonday;
|
||||
@@ -142,7 +142,7 @@
|
||||
day = iCalWeekDaySunday;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (day != iCalWeekDayUnknown)
|
||||
{
|
||||
c = [value characterAtIndex: 0];
|
||||
@@ -157,15 +157,15 @@
|
||||
digitStart = 1;
|
||||
c = [value characterAtIndex: 1];
|
||||
}
|
||||
|
||||
|
||||
i = digitStart;
|
||||
|
||||
|
||||
while (i < valueLength && isdigit(c))
|
||||
{
|
||||
i++;
|
||||
c = [value characterAtIndex: i];
|
||||
}
|
||||
|
||||
|
||||
if (i != digitStart)
|
||||
order = [[value substringWithRange: NSMakeRange(digitStart, (i - digitStart))] intValue];
|
||||
|
||||
@@ -186,7 +186,123 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (id) byDayMaskWithDaysAndOccurrences: (NSArray *) values
|
||||
{
|
||||
id o;
|
||||
|
||||
o = [[self alloc] initWithDaysAndOccurrences: values];
|
||||
AUTORELEASE(o);
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
- (id) initWithDaysAndOccurrences: (NSArray *) values
|
||||
{
|
||||
unsigned int count, max;
|
||||
NSString *value;
|
||||
unichar c, chars[2];
|
||||
unsigned int valueLength, i, digitStart, order;
|
||||
iCalWeekDay day;
|
||||
id mask;
|
||||
BOOL reverse;
|
||||
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
memset(days, 0, 7 * sizeof(iCalWeekOccurrence));
|
||||
|
||||
max = [values count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
mask = [values objectAtIndex: count];
|
||||
if (![mask isKindOfClass: [NSDictionary class]])
|
||||
continue;
|
||||
value = [[mask objectForKey: @"day"] uppercaseString];
|
||||
valueLength = [value length];
|
||||
if (valueLength > 1)
|
||||
{
|
||||
day = iCalWeekDayUnknown;
|
||||
reverse = NO;
|
||||
digitStart = 0;
|
||||
order = 0;
|
||||
|
||||
[value getCharacters: chars
|
||||
range: NSMakeRange(0, valueLength)];
|
||||
|
||||
switch (chars[0])
|
||||
{
|
||||
case 'M': day = iCalWeekDayMonday;
|
||||
break;
|
||||
case 'W': day = iCalWeekDayWednesday;
|
||||
break;
|
||||
case 'F': day = iCalWeekDayFriday;
|
||||
break;
|
||||
case 'T':
|
||||
if (chars[1] == 'U')
|
||||
day = iCalWeekDayTuesday;
|
||||
else if (chars[1] == 'H')
|
||||
day = iCalWeekDayThursday;
|
||||
break;
|
||||
case 'S':
|
||||
if (chars[1] == 'A')
|
||||
day = iCalWeekDaySaturday;
|
||||
else if (chars[1] == 'U')
|
||||
day = iCalWeekDaySunday;
|
||||
break;
|
||||
}
|
||||
|
||||
if (day != iCalWeekDayUnknown)
|
||||
{
|
||||
value = [mask objectForKey: @"occurrence"];
|
||||
valueLength = [value length];
|
||||
if (valueLength > 0)
|
||||
{
|
||||
c = [value characterAtIndex: 0];
|
||||
if (c == '-')
|
||||
{
|
||||
digitStart = 1;
|
||||
reverse = YES;
|
||||
}
|
||||
else if (c == '+')
|
||||
{
|
||||
digitStart = 1;
|
||||
}
|
||||
|
||||
i = digitStart;
|
||||
while (i < valueLength)
|
||||
{
|
||||
c = [value characterAtIndex: i];
|
||||
i++;
|
||||
if (!isdigit(c))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i != digitStart)
|
||||
order = [[value substringWithRange: NSMakeRange(digitStart, (i - digitStart))] intValue];
|
||||
}
|
||||
|
||||
if (order > 0 && order < 6)
|
||||
{
|
||||
order = pow (2, order - 1);
|
||||
if (reverse)
|
||||
order = order << 5;
|
||||
days[day] |= order;
|
||||
//NSLog(@"*** iCalByDayMask [%i] %@ : day = %i, order = %i, result = %i", count, byDayRule, day, order, days[day]);
|
||||
}
|
||||
else
|
||||
{
|
||||
days[day] = iCalWeekOccurrenceAll;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -214,7 +330,7 @@
|
||||
withWeekNumber: (int) week
|
||||
{
|
||||
unsigned int absWeek, order;
|
||||
|
||||
|
||||
absWeek = abs (week);
|
||||
order = 0;
|
||||
if (absWeek > 0 && absWeek < 6)
|
||||
@@ -223,7 +339,7 @@
|
||||
if (week < 0)
|
||||
order = order << 5;
|
||||
}
|
||||
|
||||
|
||||
return ((days[weekDay] & order) > 0);
|
||||
}
|
||||
|
||||
@@ -249,7 +365,7 @@
|
||||
if (days[i])
|
||||
day = i;
|
||||
}
|
||||
|
||||
|
||||
return day;
|
||||
}
|
||||
|
||||
@@ -260,10 +376,10 @@
|
||||
|
||||
occurrence = 0;
|
||||
day = [self firstDay];
|
||||
|
||||
|
||||
if (day > -1 && days[day] != iCalWeekOccurrenceAll)
|
||||
occurrence = [self _iCalWeekOccurrenceIntValue: days[day]];
|
||||
|
||||
|
||||
return occurrence;
|
||||
}
|
||||
|
||||
@@ -323,7 +439,7 @@
|
||||
[rules addObject: rule];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return [rules componentsJoinedByString: @","];
|
||||
}
|
||||
|
||||
@@ -344,4 +460,27 @@
|
||||
return s;
|
||||
}
|
||||
|
||||
- (NSArray *) asRuleArray
|
||||
{
|
||||
NSMutableArray *rules;
|
||||
NSMutableDictionary *rule;
|
||||
int i;
|
||||
|
||||
rules = [NSMutableArray array];
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
if (days[i])
|
||||
{
|
||||
rule = [NSMutableDictionary dictionary];
|
||||
if (days[i] != iCalWeekOccurrenceAll)
|
||||
[rule setObject: [NSNumber numberWithInt: [self _iCalWeekOccurrenceIntValue: days[i]]]
|
||||
forKey: @"occurrence"];
|
||||
[rule setObject: iCalWeekDayString[i]
|
||||
forKey: @"day"];
|
||||
[rules addObject: rule];
|
||||
}
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
|
||||
@end /* iCalByDayMask */
|
||||
@@ -94,8 +94,8 @@ typedef enum
|
||||
- (void) increaseSequence;
|
||||
|
||||
/* url can either be set as NSString or NSURL */
|
||||
- (void) setAttach: (id) _value;
|
||||
- (NSURL *) attach;
|
||||
- (void) setAttach: (NSArray *) _value;
|
||||
- (NSArray *) attach;
|
||||
- (void) setUrl: (id) _value;
|
||||
- (NSURL *) url;
|
||||
|
||||
|
||||
@@ -400,32 +400,49 @@
|
||||
return [self childrenWithTag: @"valarm"];
|
||||
}
|
||||
|
||||
- (void) setAttach: (id) _value
|
||||
- (void) setAttach: (NSArray *) _value
|
||||
{
|
||||
NSString *aString;
|
||||
id anObject;
|
||||
int count, max;
|
||||
|
||||
if ([_value isKindOfClass: [NSString class]])
|
||||
aString = _value;
|
||||
else if ([_value isKindOfClass: [NSURL class]])
|
||||
aString = [_value absoluteString];
|
||||
else
|
||||
aString = @"";
|
||||
|
||||
[[self uniqueChildWithTag: @"attach"] setSingleValue: aString forKey: @""];
|
||||
max = [_value count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
anObject = [_value objectAtIndex: count];
|
||||
if ([anObject isKindOfClass: [NSURL class]])
|
||||
{
|
||||
aString = [anObject absoluteString];
|
||||
}
|
||||
else
|
||||
aString = anObject;
|
||||
[self addChild: [CardElement simpleElementWithTag: @"attach" value: aString]];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSURL *) attach
|
||||
- (NSArray *) attach
|
||||
{
|
||||
NSArray *elements;
|
||||
NSMutableArray *attachUrls;
|
||||
NSString *stringAttach;
|
||||
NSURL *url;
|
||||
int count, max;
|
||||
|
||||
stringAttach = [[self uniqueChildWithTag: @"attach"] flattenedValuesForKey: @""];
|
||||
url = [NSURL URLWithString: stringAttach];
|
||||
elements = [self childrenWithTag: @"attach"];
|
||||
max = [elements count];
|
||||
attachUrls = [NSMutableArray arrayWithCapacity: max];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
stringAttach = [[elements objectAtIndex: count] flattenedValuesForKey: @""];
|
||||
url = [NSURL URLWithString: stringAttach];
|
||||
|
||||
if (!url && [stringAttach length] > 0)
|
||||
url = [NSURL URLWithString: [NSString stringWithFormat: @"http://%@", stringAttach]];
|
||||
if (!url && [stringAttach length] > 0)
|
||||
url = [NSURL URLWithString: [NSString stringWithFormat: @"http://%@", stringAttach]];
|
||||
|
||||
[attachUrls addObject: [url absoluteString]];
|
||||
}
|
||||
|
||||
return url;
|
||||
return attachUrls;
|
||||
}
|
||||
|
||||
- (void) setUrl: (id) _value
|
||||
|
||||
@@ -44,6 +44,8 @@ typedef enum {
|
||||
|
||||
@interface iCalPerson : CardElement
|
||||
|
||||
+ (NSString *) descriptionForParticipationStatus: (iCalPersonPartStat) _status;
|
||||
|
||||
/* accessors */
|
||||
|
||||
- (void)setCn:(NSString *)_s;
|
||||
|
||||
+45
-34
@@ -25,6 +25,48 @@
|
||||
|
||||
@implementation iCalPerson
|
||||
|
||||
+ (NSString *) descriptionForParticipationStatus: (iCalPersonPartStat) _status
|
||||
{
|
||||
NSString *stat;
|
||||
|
||||
switch (_status) {
|
||||
case iCalPersonPartStatUndefined:
|
||||
stat = @"";
|
||||
break;
|
||||
case iCalPersonPartStatAccepted:
|
||||
stat = @"ACCEPTED";
|
||||
break;
|
||||
case iCalPersonPartStatDeclined:
|
||||
stat = @"DECLINED";
|
||||
break;
|
||||
case iCalPersonPartStatTentative:
|
||||
stat = @"TENTATIVE";
|
||||
break;
|
||||
case iCalPersonPartStatDelegated:
|
||||
stat = @"DELEGATED";
|
||||
break;
|
||||
case iCalPersonPartStatCompleted:
|
||||
stat = @"COMPLETED";
|
||||
break;
|
||||
case iCalPersonPartStatInProcess:
|
||||
stat = @"IN-PROCESS";
|
||||
break;
|
||||
case iCalPersonPartStatExperimental:
|
||||
case iCalPersonPartStatOther:
|
||||
// [NSException raise:NSInternalInconsistencyException
|
||||
// format:@"Attempt to set meaningless "
|
||||
// @"participationStatus (%d)!", _status];
|
||||
stat = nil; /* keep compiler happy */
|
||||
break;
|
||||
default:
|
||||
stat = @"NEEDS-ACTION";
|
||||
break;
|
||||
}
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
|
||||
/* accessors */
|
||||
|
||||
- (void) setCn: (NSString *) _s
|
||||
@@ -132,44 +174,13 @@
|
||||
{
|
||||
NSString *stat;
|
||||
|
||||
switch (_status) {
|
||||
case iCalPersonPartStatUndefined:
|
||||
stat = @"";
|
||||
break;
|
||||
case iCalPersonPartStatAccepted:
|
||||
stat = @"ACCEPTED";
|
||||
break;
|
||||
case iCalPersonPartStatDeclined:
|
||||
stat = @"DECLINED";
|
||||
break;
|
||||
case iCalPersonPartStatTentative:
|
||||
stat = @"TENTATIVE";
|
||||
break;
|
||||
case iCalPersonPartStatDelegated:
|
||||
stat = @"DELEGATED";
|
||||
break;
|
||||
case iCalPersonPartStatCompleted:
|
||||
stat = @"COMPLETED";
|
||||
break;
|
||||
case iCalPersonPartStatInProcess:
|
||||
stat = @"IN-PROCESS";
|
||||
break;
|
||||
case iCalPersonPartStatExperimental:
|
||||
case iCalPersonPartStatOther:
|
||||
// [NSException raise:NSInternalInconsistencyException
|
||||
// format:@"Attempt to set meaningless "
|
||||
// @"participationStatus (%d)!", _status];
|
||||
stat = nil; /* keep compiler happy */
|
||||
break;
|
||||
default:
|
||||
stat = @"NEEDS-ACTION";
|
||||
break;
|
||||
}
|
||||
stat = [iCalPerson descriptionForParticipationStatus: _status];
|
||||
|
||||
if (stat)
|
||||
[self setPartStat:stat];
|
||||
}
|
||||
|
||||
- (iCalPersonPartStat)participationStatus {
|
||||
- (iCalPersonPartStat) participationStatus {
|
||||
NSString *stat;
|
||||
|
||||
stat = [[self partStat] uppercaseString];
|
||||
|
||||
@@ -75,6 +75,7 @@ extern NSString *iCalWeekDayString[];
|
||||
- (void) setFrequency: (iCalRecurrenceFrequency) _frequency;
|
||||
- (iCalRecurrenceFrequency) frequency;
|
||||
- (iCalRecurrenceFrequency) valueForFrequency: (NSString *) value;
|
||||
- (NSString *) frequencyForValue: (iCalRecurrenceFrequency) freq;
|
||||
|
||||
- (void) setRepeatInterval: (int) _repeatInterval;
|
||||
- (int) repeatInterval;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* iCalTimeZone.m - this file is part of SOPE
|
||||
*
|
||||
* Copyright (C) 2006-2014 Inverse inc.
|
||||
* Copyright (C) 2006-2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -147,7 +147,8 @@ static NSArray *knownTimeZones;
|
||||
if (ext.location != NSNotFound)
|
||||
{
|
||||
zone = [zone substringToIndex: ext.location];
|
||||
[timeZoneNames addObject: zone];
|
||||
if (![timeZoneNames containsObject: zone])
|
||||
[timeZoneNames addObject: zone];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
- (NSString *) percentComplete;
|
||||
|
||||
- (void) setDue: (NSCalendarDate *) _date;
|
||||
- (void) setAllDayDue: (NSCalendarDate *) _date;
|
||||
- (NSCalendarDate *) due;
|
||||
|
||||
- (void) setCompleted: (NSCalendarDate *) _date;
|
||||
|
||||
@@ -65,6 +65,12 @@
|
||||
setDateTime: newDueDate];
|
||||
}
|
||||
|
||||
- (void) setAllDayDue: (NSCalendarDate *) newDueDate
|
||||
{
|
||||
[(iCalDateTime *) [self uniqueChildWithTag: @"due"]
|
||||
setDate: newDueDate];
|
||||
}
|
||||
|
||||
- (NSCalendarDate *) due
|
||||
{
|
||||
return [(iCalDateTime *) [self uniqueChildWithTag: @"due"]
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
|
||||
- (NSCalendarDate *) nextAlarmDate;
|
||||
|
||||
- (NSDictionary *) asDictionary;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __NGCards_iCalTrigger_H__ */
|
||||
@@ -20,7 +20,9 @@
|
||||
*/
|
||||
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import "iCalEvent.h"
|
||||
#import "iCalTrigger.h"
|
||||
@@ -52,6 +54,78 @@
|
||||
return [self value: 0 ofAttribute: @"related"];
|
||||
}
|
||||
|
||||
- (NSDictionary *) asDictionary
|
||||
{
|
||||
NSDictionary *data;
|
||||
NSString *duration, *relation, *reference, *quantity, *unit;
|
||||
NSUInteger i;
|
||||
unichar c;
|
||||
|
||||
data = nil;
|
||||
if (![[self valueType] length] ||
|
||||
[[self valueType] caseInsensitiveCompare: @"DURATION"] == NSOrderedSame)
|
||||
{
|
||||
relation = [[self relationType] uppercaseString];
|
||||
duration = [self flattenedValuesForKey: @""];
|
||||
i = 0;
|
||||
c = [duration characterAtIndex: i];
|
||||
if (c == '-')
|
||||
{
|
||||
reference = @"BEFORE";
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
reference = @"AFTER";
|
||||
}
|
||||
c = [duration characterAtIndex: i];
|
||||
if (c == 'P')
|
||||
{
|
||||
quantity = @"";
|
||||
unit = @"";
|
||||
// Parse duration -- ignore first character (P)
|
||||
for (i++; i < [duration length]; i++)
|
||||
{
|
||||
c = [duration characterAtIndex: i];
|
||||
if (c == 't' || c == 'T')
|
||||
// time -- ignore character
|
||||
continue;
|
||||
else if (isdigit (c))
|
||||
quantity = [quantity stringByAppendingFormat: @"%c", c];
|
||||
else
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'D': /* day */
|
||||
unit = @"DAYS";
|
||||
break;
|
||||
case 'H': /* hour */
|
||||
unit = @"HOURS";
|
||||
break;
|
||||
case 'M': /* min */
|
||||
unit = @"MINUTES";
|
||||
break;
|
||||
case 'W': /* week */
|
||||
unit = @"WEEKS";
|
||||
break;
|
||||
default:
|
||||
NSLog(@"Cannot process duration unit: '%c'", c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
data = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
relation, @"relation",
|
||||
reference, @"reference",
|
||||
[NSNumber numberWithInt: [quantity intValue]], @"quantity",
|
||||
unit, @"unit",
|
||||
nil];
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
- (NSCalendarDate *) nextAlarmDate
|
||||
{
|
||||
NSCalendarDate *relationDate, *nextAlarmDate;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# SOGo needs directory in /var/run
|
||||
d /var/run/sogo 0755 sogo sogo
|
||||
+1
@@ -49,6 +49,7 @@ function adjustSchema() {
|
||||
|
||||
echo "This script will ask for the sql password twice" >&2
|
||||
echo "Converting c_partstates from VARCHAR(255) to mediumtext in calendar quick tables" >&2
|
||||
echo "Adding c_description column as mediumtext in calendar quick tables" >&2
|
||||
tables=`mysql -p -s -u $username -h $hostname $database -e "select SUBSTRING_INDEX(c_quick_location, '/', -1) from $indextable where c_path3 = 'Calendar';"`
|
||||
|
||||
for table in $tables;
|
||||
@@ -45,7 +45,8 @@ function adjustSchema() {
|
||||
IFS="$oldIFS"
|
||||
}
|
||||
|
||||
echo "Converting c_cycleinfo from VARCHAR(255) to TEXT in calendar quick tables" >&2
|
||||
echo "Converting c_partstates from VARCHAR(255) to mediumtext in calendar quick tables" >&2
|
||||
echo "Adding c_description column as mediumtext in calendar quick tables" >&2
|
||||
tables=`psql -t -U $username -h $hostname $database -c "select split_part(c_quick_location, '/', 5) from $indextable where c_path3 = 'Calendar';"`
|
||||
|
||||
for table in $tables;
|
||||
Executable
+82
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
|
||||
DEBUG=False
|
||||
|
||||
dir_mappings = {"../UI/Templates":"../UI/Common",
|
||||
"../UI/Templates/AdministrationUI":"../UI/AdministrationUI",
|
||||
"../UI/Templates/Appointments":"../SoObjects/Appointments",
|
||||
"../UI/Templates/ContactsUI":"../UI/Contacts",
|
||||
"../UI/Templates/MailerUI":"../UI/MailerUI",
|
||||
"../UI/Templates/MailPartViewers":"../UI/MailPartViewers",
|
||||
"../UI/Templates/MainUI":"../UI/MainUI",
|
||||
"../UI/Templates/PreferencesUI":"../UI/PreferencesUI",
|
||||
"../UI/Templates/SchedulerUI":"../UI/Scheduler"
|
||||
}
|
||||
|
||||
def get_translations(path):
|
||||
try:
|
||||
transpath = dir_mappings.get(path, path)
|
||||
transname = transpath + '/English.lproj/Localizable.strings'
|
||||
transall = open(transname).read()
|
||||
except:
|
||||
transall = ""
|
||||
return transall
|
||||
|
||||
|
||||
def find_missing_translations(rootdir='.', extention='', recomp=None, greylist=()):
|
||||
for path, dirs, files in os.walk(rootdir):
|
||||
filelist = [f for f in files if f[(-1 * len(extention)):] == extention]
|
||||
if filelist:
|
||||
for filename in filelist:
|
||||
if filename in greylist:
|
||||
print "%s is greylisted -- SKIPPING" % filename
|
||||
continue
|
||||
pathname = path + '/' + filename
|
||||
lines = open(pathname).read().split("\n")
|
||||
values = [r.groups()[0] for r in [recomp.search(l) for l in lines] if r]
|
||||
if values:
|
||||
#- Get the current english translations for the path
|
||||
transgood = get_translations(path)
|
||||
if not transgood:
|
||||
print "No translation file found for %s, skipping %s" % (path, pathname)
|
||||
continue
|
||||
notfound = list()
|
||||
if DEBUG:print pathname
|
||||
for value in values:
|
||||
#- Try to find the value from the source file in the translation file
|
||||
escaped_value = re.escape(value)
|
||||
found = re.search('(%s|"%s")\s*=\s*"(.*?)";' % (escaped_value, escaped_value), transgood)
|
||||
if found:
|
||||
if DEBUG: print "\t", '[%s] FOUND -- "%s"' % found.groups()
|
||||
else:
|
||||
notfound.append("-->\t[%s] ==== Not Found ====" % value)
|
||||
if notfound:
|
||||
if not DEBUG:print pathname
|
||||
print "\n".join(notfound)
|
||||
|
||||
|
||||
def main():
|
||||
#- Only one option...
|
||||
if len(sys.argv) > 1:
|
||||
if sys.argv[1] == '-g':
|
||||
global DEBUG
|
||||
DEBUG = True
|
||||
else:
|
||||
print 'Usage:', sys.argv[0], '[-g]\n\t\t-g: debug will show matching also'
|
||||
sys.exit(1)
|
||||
|
||||
greylist = ('UIxFilterEditor.wox')
|
||||
|
||||
#- Get only the label:value from all lines
|
||||
recomp = re.compile(' label:[^=]*="(.*?)"')
|
||||
find_missing_translations('../UI', 'wox', recomp, greylist)
|
||||
|
||||
#- [self labelForKey: @"Issuer"]
|
||||
recomp = re.compile('\[self labelForKey: @"(.*?)"\]')
|
||||
find_missing_translations('../UI', 'm', recomp, ())
|
||||
|
||||
main()
|
||||
@@ -11,16 +11,16 @@ vtodo_class2 = "(مهمة سرية)";
|
||||
"The event \"%{Summary}\" was created" = "أُنشِئ الحدث \"%{Summary}\"";
|
||||
"The event \"%{Summary}\" was deleted" = "حُذِفَ الحدث \"%{Summary}\"";
|
||||
"The event \"%{Summary}\" was updated" = "حُدِّث الحدث \"%{Summary}\"";
|
||||
"The following attendees(s) were notified:" = "تم ابلاغ المدعو (المدعويين) الأتي أسماءهم:";
|
||||
"The following attendees(s) were added:" = "تم أضافة المدعو (المدعويين) الأتي أسماءهم:";
|
||||
"The following attendees(s) were removed:" = "تم مسح المدعو (المدعويين) الأتي أسماءهم:";
|
||||
"The following attendees(s) were notified" = "تم ابلاغ المدعو (المدعويين) الأتي أسماءهم";
|
||||
"The following attendees(s) were added" = "تم أضافة المدعو (المدعويين) الأتي أسماءهم";
|
||||
"The following attendees(s) were removed" = "تم مسح المدعو (المدعويين) الأتي أسماءهم";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "التقويم:";
|
||||
"startDate_label" = "البداية:";
|
||||
"endDate_label" = "النهاية:";
|
||||
"calendar_label" = "التقويم";
|
||||
"startDate_label" = "البداية";
|
||||
"endDate_label" = "النهاية";
|
||||
"due_label" = "تاريخ الاستحقاق:";
|
||||
"location_label" = "المكان:";
|
||||
"location_label" = "المكان";
|
||||
"summary_label" = "الملخص:";
|
||||
"comment_label" = "التعليق:";
|
||||
|
||||
|
||||
@@ -12,16 +12,16 @@ vtodo_class2 = "(Isilpeko egitekoa)";
|
||||
"The event \"%{Summary}\" was created" = "\"%{Summary}\" ekitaldia sortu da";
|
||||
"The event \"%{Summary}\" was deleted" = "\"%{Summary}\" ekitaldia ezabatu da";
|
||||
"The event \"%{Summary}\" was updated" = "\"%{Summary}\" ekitaldia eguneratu da";
|
||||
"The following attendees(s) were notified:" = "Ondorengo partaidea(k) jakinarazi d(ir)a:";
|
||||
"The following attendees(s) were added:" = "Ondorengo partaidea(k) gehitu d(ir)a:";
|
||||
"The following attendees(s) were removed:" = "Ondorengo partaidea(k) ezabatu d(ir)a:";
|
||||
"The following attendees(s) were notified" = "Ondorengo partaidea(k) jakinarazi d(ir)a";
|
||||
"The following attendees(s) were added" = "Ondorengo partaidea(k) gehitu d(ir)a";
|
||||
"The following attendees(s) were removed" = "Ondorengo partaidea(k) ezabatu d(ir)a";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Egutegia";
|
||||
"startDate_label" = "Hasi:";
|
||||
"endDate_label" = "Amaitu:";
|
||||
"startDate_label" = "Hasi";
|
||||
"endDate_label" = "Amaitu";
|
||||
"due_label" = "Epemuga:";
|
||||
"location_label" = "Kokapena:";
|
||||
"location_label" = "Kokapena";
|
||||
"summary_label" = "Laburpena:";
|
||||
"comment_label" = "Iruzkina:";
|
||||
|
||||
|
||||
@@ -12,16 +12,16 @@ vtodo_class2 = "(Tarefa Confidencial)";
|
||||
"The event \"%{Summary}\" was created" = "O evento \"%{Summary}\" foi criado";
|
||||
"The event \"%{Summary}\" was deleted" = "O evento \"%{Summary}\" foi removido";
|
||||
"The event \"%{Summary}\" was updated" = "O evento \"%{Summary}\" foi atualizado";
|
||||
"The following attendees(s) were notified:" = "Estes participantes foram notificados:";
|
||||
"The following attendees(s) were added:" = "Estes participantes foram adicionados:";
|
||||
"The following attendees(s) were removed:" = "Estes participantes foram removidos:";
|
||||
"The following attendees(s) were notified" = "Estes participantes foram notificados";
|
||||
"The following attendees(s) were added" = "Estes participantes foram adicionados";
|
||||
"The following attendees(s) were removed" = "Estes participantes foram removidos";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Calendário:";
|
||||
"startDate_label" = "Início:";
|
||||
"endDate_label" = "Fim:";
|
||||
"calendar_label" = "Calendário";
|
||||
"startDate_label" = "Início";
|
||||
"endDate_label" = "Fim";
|
||||
"due_label" = "Data de Vencimento:";
|
||||
"location_label" = "Local:";
|
||||
"location_label" = "Local";
|
||||
"summary_label" = "Resumo:";
|
||||
"comment_label" = "Comentário:";
|
||||
|
||||
|
||||
@@ -12,16 +12,16 @@ vtodo_class2 = "(Tasca confidencial)";
|
||||
"The event \"%{Summary}\" was created" = "Es va crear l'esdeveniment \"%{Summary}\" ";
|
||||
"The event \"%{Summary}\" was deleted" = "Es va esborrar l'esdeveniment \"%{Summary}\" ";
|
||||
"The event \"%{Summary}\" was updated" = "Es va modificar l'esdeveniment \"%{Summary}\" ";
|
||||
"The following attendees(s) were notified:" = "I va notificar als següents participants: ";
|
||||
"The following attendees(s) were added:" = "Es van agregar els següents participants: ";
|
||||
"The following attendees(s) were removed:" = "Heu suprimit els assistents següents:";
|
||||
"The following attendees(s) were notified" = "I va notificar als següents participants ";
|
||||
"The following attendees(s) were added" = "Es van agregar els següents participants ";
|
||||
"The following attendees(s) were removed" = "Heu suprimit els assistents següents";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Calendari:";
|
||||
"startDate_label" = "Inici:";
|
||||
"endDate_label" = "Final:";
|
||||
"calendar_label" = "Calendari";
|
||||
"startDate_label" = "Inici";
|
||||
"endDate_label" = "Final";
|
||||
"due_label" = "Data límit:";
|
||||
"location_label" = "Lloc:";
|
||||
"location_label" = "Lloc";
|
||||
"summary_label" = "Resum:";
|
||||
"comment_label" = "Comentari:";
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
"Inviting the following persons is prohibited:" = "禁止邀請以下人士:";
|
||||
"Personal Calendar" = "個人行事曆";
|
||||
vevent_class0 = "(公開事件)";
|
||||
vevent_class1 = "(私人事件)";
|
||||
vevent_class2 = "(機密事件)";
|
||||
|
||||
vtodo_class0 = "(公開任務)";
|
||||
vtodo_class1 = "(私人任務)";
|
||||
vtodo_class2 = "(機密任務)";
|
||||
|
||||
/* Receipts */
|
||||
"The event \"%{Summary}\" was created" = "事件 \"%{Summary}\" 已建立";
|
||||
"The event \"%{Summary}\" was deleted" = "事件\"%{Summary}\" 已刪除";
|
||||
"The event \"%{Summary}\" was updated" = "事件\"%{Summary}\" 已異動";
|
||||
"The following attendees(s) were notified" = "已通知下列出席者";
|
||||
"The following attendees(s) were added" = "已增加下列出席者";
|
||||
"The following attendees(s) were removed" = "已移除下列出席者";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "行事曆";
|
||||
"startDate_label" = "開始";
|
||||
"endDate_label" = "結束";
|
||||
"due_label" = "截止日期:";
|
||||
"location_label" = "地點";
|
||||
"summary_label" = "事件主題:";
|
||||
"comment_label" = "備註:";
|
||||
|
||||
/* Invitation */
|
||||
"Event Invitation: \"%{Summary}\"" = "邀請事件: \"%{Summary}\"";
|
||||
"(sent by %{SentBy}) " = "(來自 %{SentBy})";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText} 邀請您參加 %{Summary}。\n\n起始日期: %{StartDate}\n結束日期: %{EndDate}\n備註說明: %{Description}";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} 邀請您參加 %{Summary}。\n\n起始時間: %{StartDate} %{StartTime}\n結束時間: %{EndDate} %{EndTime}\n備註說明: %{Description}";
|
||||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "取消事件 : \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText} 己取消事件: %{Summary}。\n\n起始日期: %{StartDate}\n結束日期: %{EndDate}\n備註說明: %{Description}";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText} 己取消了事件: %{Summary}。\n\n起始時間: %{StartDate} %{StartTime}\n結束時間: %{EndDate} %{EndTime}\n備註說明: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
|
||||
= " 安排於%{OldStartDate} 的事件 \"%{Summary}\" 已變更";
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
= "安排於 %{OldStartDate} %{OldStartTime} 的事件 \"%{Summary}\" 已變更";
|
||||
"The following parameters have changed in the \"%{Summary}\" meeting:"
|
||||
= "已變更會議 \"%{Summary}\" 下列參數:";
|
||||
"Please accept or decline those changes."
|
||||
= "請接受或拒絶這些變更。";
|
||||
|
||||
/* Reply */
|
||||
"Accepted invitation: \"%{Summary}\"" = "接受邀請: \"%{Summary}\"";
|
||||
"Declined invitation: \"%{Summary}\"" = "拒絶邀請: \"%{Summary}\"";
|
||||
"Delegated invitation: \"%{Summary}\"" = "\"委派出席: \"%{Summary}\"";
|
||||
"Not yet decided on invitation: \"%{Summary}\"" = "未處理的邀請: \"%{Summary}\"";
|
||||
"%{Attendee} %{SentByText}has accepted your event invitation."
|
||||
= "\"%{Attendee} %{SentByText}已接受了您的邀請。";
|
||||
"%{Attendee} %{SentByText}has declined your event invitation."
|
||||
= "\"%{Attendee} %{SentByText}已拒絶了您的邀請。";
|
||||
"%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}."
|
||||
= "\"%{Attendee} %{SentByText}已將您的邀請委任給%{Delegate}。";
|
||||
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
|
||||
= "%{Attendee} %{SentByText}還沒有決定是否接受您的邀請。";
|
||||
|
||||
/* Resources */
|
||||
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "無法讀取資源: \"%{Cn} %{SystemEmail}\"";
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "\"%{Cn} %{SystemEmail}\" 的衝突事件己達上限 ( %{NumberOfSimultaneousBookings} )。衝突事件 \"%{EventTitle}\" 的起始日期為 %{StartDate}。";
|
||||
@@ -12,16 +12,16 @@ vtodo_class2 = "(Skrytý úkol)";
|
||||
"The event \"%{Summary}\" was created" = "Událost \"%{Summary}\" byla vytvořena";
|
||||
"The event \"%{Summary}\" was deleted" = "Událost \"%{Summary}\" byla smazána";
|
||||
"The event \"%{Summary}\" was updated" = "Událost \"%{Summary}\" byla upravena";
|
||||
"The following attendees(s) were notified:" = "Následující účastníci byli upozorněni:";
|
||||
"The following attendees(s) were added:" = "Následující účastníci byli přidáni:";
|
||||
"The following attendees(s) were removed:" = "Následující účastníci byli odebráni:";
|
||||
"The following attendees(s) were notified" = "Následující účastníci byli upozorněni";
|
||||
"The following attendees(s) were added" = "Následující účastníci byli přidáni";
|
||||
"The following attendees(s) were removed" = "Následující účastníci byli odebráni";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Kalendář:";
|
||||
"startDate_label" = "Začátek:";
|
||||
"endDate_label" = "Konec:";
|
||||
"calendar_label" = "Kalendář";
|
||||
"startDate_label" = "Začátek";
|
||||
"endDate_label" = "Konec";
|
||||
"due_label" = "Termín:";
|
||||
"location_label" = "Místo:";
|
||||
"location_label" = "Místo";
|
||||
"summary_label" = "Název:";
|
||||
"comment_label" = "Komentář:";
|
||||
|
||||
|
||||
@@ -11,16 +11,16 @@ vtodo_class2 = "(Hemmelig opgave)";
|
||||
"The event \"%{Summary}\" was created" = "Begivenheden \"%{Summary}\" blev oprettet";
|
||||
"The event \"%{Summary}\" was deleted" = "Begivenheden \"%{Summary}\" blev slettet";
|
||||
"The event \"%{Summary}\" was updated" = "Begivenheden \"%{Summary}\" blev opdateret";
|
||||
"The following attendees(s) were notified:" = "Følgende deltager(e) blev underrettet:";
|
||||
"The following attendees(s) were added:" = "Følgende deltager(e) blev tilføjet:";
|
||||
"The following attendees(s) were removed:" = "Følgende deltager(e) blev fjernet:";
|
||||
"The following attendees(s) were notified" = "Følgende deltager(e) blev underrettet";
|
||||
"The following attendees(s) were added" = "Følgende deltager(e) blev tilføjet";
|
||||
"The following attendees(s) were removed" = "Følgende deltager(e) blev fjernet";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Kalender:";
|
||||
"startDate_label" = "Start:";
|
||||
"endDate_label" = "Slut:";
|
||||
"calendar_label" = "Kalender";
|
||||
"startDate_label" = "Start";
|
||||
"endDate_label" = "Slut";
|
||||
"due_label" = "Forfaldsdato:";
|
||||
"location_label" = "Sted:";
|
||||
"location_label" = "Sted";
|
||||
"summary_label" = "Resumé:";
|
||||
"comment_label" = "Kommentér:";
|
||||
|
||||
|
||||
@@ -12,16 +12,16 @@ vtodo_class2 = "(Vertrouwelijke taak)";
|
||||
"The event \"%{Summary}\" was created" = "De gebeurtenis \"%{Summary}\" is aangemaakt";
|
||||
"The event \"%{Summary}\" was deleted" = "De gebeurtenis \"%{Summary}\" is verwijderd";
|
||||
"The event \"%{Summary}\" was updated" = "De gebeurtenis \"%{Summary}\" is gewijzigd";
|
||||
"The following attendees(s) were notified:" = "De volgende deelnemers zijn in kennis gesteld:";
|
||||
"The following attendees(s) were added:" = "De volgende deelnemers zijn toegevoegd:";
|
||||
"The following attendees(s) were removed:" = "De volgende deelnemers zijn verwijderd:";
|
||||
"The following attendees(s) were notified" = "De volgende deelnemers zijn in kennis gesteld";
|
||||
"The following attendees(s) were added" = "De volgende deelnemers zijn toegevoegd";
|
||||
"The following attendees(s) were removed" = "De volgende deelnemers zijn verwijderd";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Agenda:";
|
||||
"startDate_label" = "Start:";
|
||||
"endDate_label" = "Einde:";
|
||||
"calendar_label" = "Agenda";
|
||||
"startDate_label" = "Start";
|
||||
"endDate_label" = "Einde";
|
||||
"due_label" = "Vervaldag:";
|
||||
"location_label" = "Locatie:";
|
||||
"location_label" = "Locatie";
|
||||
"summary_label" = "Samenvatting:";
|
||||
"comment_label" = "Commentaar:";
|
||||
|
||||
|
||||
@@ -12,16 +12,16 @@ vtodo_class2 = "(Confidential task)";
|
||||
"The event \"%{Summary}\" was created" = "The event \"%{Summary}\" was created";
|
||||
"The event \"%{Summary}\" was deleted" = "The event \"%{Summary}\" was deleted";
|
||||
"The event \"%{Summary}\" was updated" = "The event \"%{Summary}\" was updated";
|
||||
"The following attendees(s) were notified:" = "The following attendee(s) were notified:";
|
||||
"The following attendees(s) were added:" = "The following attendee(s) were added:";
|
||||
"The following attendees(s) were removed:" = "The following attendee(s) were removed:";
|
||||
"The following attendees(s) were notified" = "The following attendee(s) were notified";
|
||||
"The following attendees(s) were added" = "The following attendee(s) were added";
|
||||
"The following attendees(s) were removed" = "The following attendee(s) were removed";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Calendar:";
|
||||
"startDate_label" = "Start:";
|
||||
"endDate_label" = "End:";
|
||||
"calendar_label" = "Calendar";
|
||||
"startDate_label" = "Start";
|
||||
"endDate_label" = "End";
|
||||
"due_label" = "Due Date:";
|
||||
"location_label" = "Location:";
|
||||
"location_label" = "Location";
|
||||
"summary_label" = "Summary:";
|
||||
"comment_label" = "Comment:";
|
||||
|
||||
|
||||
@@ -12,16 +12,16 @@ vtodo_class2 = "(Luottamuksellinen tehtävä)";
|
||||
"The event \"%{Summary}\" was created" = "Tapahtuma \"%{Summary}\" luotiin";
|
||||
"The event \"%{Summary}\" was deleted" = "Tapahtuma \"%{Summary}\" poistettiin";
|
||||
"The event \"%{Summary}\" was updated" = "Tapahtuma \"%{Summary}\" päivitettiin";
|
||||
"The following attendees(s) were notified:" = "Seuraava(t) osallistuja(t) on informoitu:";
|
||||
"The following attendees(s) were added:" = "Seuraava(t) osalllistuja(t) on lisätty:";
|
||||
"The following attendees(s) were removed:" = "Seuraava(t) osalllistuja(t) poistettiin:";
|
||||
"The following attendees(s) were notified" = "Seuraava(t) osallistuja(t) on informoitu";
|
||||
"The following attendees(s) were added" = "Seuraava(t) osalllistuja(t) on lisätty";
|
||||
"The following attendees(s) were removed" = "Seuraava(t) osalllistuja(t) poistettiin";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Kalenteri:";
|
||||
"startDate_label" = "Alkaa:";
|
||||
"endDate_label" = "Päättyy:";
|
||||
"calendar_label" = "Kalenteri";
|
||||
"startDate_label" = "Alkaa";
|
||||
"endDate_label" = "Päättyy";
|
||||
"due_label" = "Määräpäivä:";
|
||||
"location_label" = "Sijainti:";
|
||||
"location_label" = "Sijainti";
|
||||
"summary_label" = "Yhteenveto:";
|
||||
"comment_label" = "Kommentti:";
|
||||
|
||||
|
||||
@@ -12,16 +12,16 @@ vtodo_class2 = "(Tâche confidentielle)";
|
||||
"The event \"%{Summary}\" was created" = "L'événement «%{Summary}» a été créé";
|
||||
"The event \"%{Summary}\" was deleted" = "L'événement «%{Summary}» a été effacé";
|
||||
"The event \"%{Summary}\" was updated" = "L'événement «%{Summary}» a été modifié";
|
||||
"The following attendees(s) were notified:" = "Les invités suivants ont été avisés :";
|
||||
"The following attendees(s) were added:" = "Les invités suivants ont été ajoutés :";
|
||||
"The following attendees(s) were removed:" = "Les invités suivants ont été supprimés :";
|
||||
"The following attendees(s) were notified" = "Les invités suivants ont été avisés ";
|
||||
"The following attendees(s) were added" = "Les invités suivants ont été ajoutés ";
|
||||
"The following attendees(s) were removed" = "Les invités suivants ont été supprimés ";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Agenda :";
|
||||
"startDate_label" = "Début :";
|
||||
"endDate_label" = "Fin :";
|
||||
"calendar_label" = "Agenda ";
|
||||
"startDate_label" = "Début ";
|
||||
"endDate_label" = "Fin ";
|
||||
"due_label" = "Fin prévue :";
|
||||
"location_label" = "Lieu :";
|
||||
"location_label" = "Lieu ";
|
||||
"summary_label" = "Titre :";
|
||||
"comment_label" = "Description :";
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ Appointments_RESOURCE_FILES += \
|
||||
MSExchangeFreeBusySOAPResponseMap.plist \
|
||||
MSExchangeFreeBusySOAPRequest.wo
|
||||
|
||||
Appointments_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
|
||||
Appointments_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
|
||||
|
||||
Appointments_LOCALIZED_RESOURCE_FILES = Localizable.strings
|
||||
|
||||
|
||||
@@ -12,16 +12,16 @@ vtodo_class2 = "(Vertrauliche Aufgabe)";
|
||||
"The event \"%{Summary}\" was created" = "Der Termin \"%{Summary}\" wurde angelegt";
|
||||
"The event \"%{Summary}\" was deleted" = "Der Termin \"%{Summary}\" wurde gelöscht";
|
||||
"The event \"%{Summary}\" was updated" = "Der Termin \"%{Summary}\" wurde geändert";
|
||||
"The following attendees(s) were notified:" = "Die folgenden Teilnehmer wurden benachrichtigt:";
|
||||
"The following attendees(s) were added:" = "Die folgenden Teilnehmer wurden hinzugefügt:";
|
||||
"The following attendees(s) were removed:" = "Die folgenden Teilnehmer wurden entfernt:";
|
||||
"The following attendees(s) were notified" = "Die folgenden Teilnehmer wurden benachrichtigt";
|
||||
"The following attendees(s) were added" = "Die folgenden Teilnehmer wurden hinzugefügt";
|
||||
"The following attendees(s) were removed" = "Die folgenden Teilnehmer wurden entfernt";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Kalender:";
|
||||
"startDate_label" = "Beginn:";
|
||||
"endDate_label" = "Ende:";
|
||||
"calendar_label" = "Kalender";
|
||||
"startDate_label" = "Beginn";
|
||||
"endDate_label" = "Ende";
|
||||
"due_label" = "Fälligkeit:";
|
||||
"location_label" = "Ort:";
|
||||
"location_label" = "Ort";
|
||||
"summary_label" = "Zusammenfassung:";
|
||||
"comment_label" = "Kommentar:";
|
||||
|
||||
|
||||
@@ -12,16 +12,16 @@ vtodo_class2 = "(Bizalmas feladat)";
|
||||
"The event \"%{Summary}\" was created" = "A \"%{Summary}\" esemény létre lett hozva";
|
||||
"The event \"%{Summary}\" was deleted" = "A \"%{Summary}\" esemény törölve lett";
|
||||
"The event \"%{Summary}\" was updated" = "A \"%{Summary}\" esemény módosítva lett";
|
||||
"The following attendees(s) were notified:" = "Az alábbi résztvevők lettek értesítve:";
|
||||
"The following attendees(s) were added:" = "Az alábbi résztvevők lettek hozzáadva:";
|
||||
"The following attendees(s) were removed:" = "Az alábbi résztvevők lettek eltávolítva:";
|
||||
"The following attendees(s) were notified" = "Az alábbi résztvevők lettek értesítve";
|
||||
"The following attendees(s) were added" = "Az alábbi résztvevők lettek hozzáadva";
|
||||
"The following attendees(s) were removed" = "Az alábbi résztvevők lettek eltávolítva";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Naptár:";
|
||||
"startDate_label" = "Kezdete:";
|
||||
"endDate_label" = "Vége:";
|
||||
"calendar_label" = "Naptár";
|
||||
"startDate_label" = "Kezdete";
|
||||
"endDate_label" = "Vége";
|
||||
"due_label" = "Lejárat napja:";
|
||||
"location_label" = "Hely:";
|
||||
"location_label" = "Hely";
|
||||
"summary_label" = "Összegzés:";
|
||||
"comment_label" = "Megjegyzés:";
|
||||
|
||||
|
||||
@@ -25,10 +25,10 @@ vtodo_class2 = "(Verkefni er trúnaðarmál)";
|
||||
"... from the following event:" = "... af eftirfarandi viðburði:";
|
||||
|
||||
/* IMIP messages */
|
||||
"startDate_label" = "Byrjun:";
|
||||
"endDate_label" = "Endir:";
|
||||
"startDate_label" = "Byrjun";
|
||||
"endDate_label" = "Endir";
|
||||
"due_label" = "Lokadagur:";
|
||||
"location_label" = "Staðsetning:";
|
||||
"location_label" = "Staðsetning";
|
||||
"summary_label" = "Samantekt:";
|
||||
"comment_label" = "Athugasemd:";
|
||||
|
||||
|
||||
@@ -25,10 +25,10 @@ vtodo_class2 = "(Attività confidenziale)";
|
||||
"... from the following event:" = "... dal seguente evento:";
|
||||
|
||||
/* IMIP messages */
|
||||
"startDate_label" = "Inizio:";
|
||||
"endDate_label" = "Fine:";
|
||||
"startDate_label" = "Inizio";
|
||||
"endDate_label" = "Fine";
|
||||
"due_label" = "Scadenza:";
|
||||
"location_label" = "Luogo:";
|
||||
"location_label" = "Luogo";
|
||||
"summary_label" = "Summario:";
|
||||
"comment_label" = "Commento:";
|
||||
|
||||
|
||||
@@ -12,16 +12,16 @@ vtodo_class2 = "(Konfidensiell oppgave)";
|
||||
"The event \"%{Summary}\" was created" = "Hendelsen \"%{Summary}\" ble opprettet";
|
||||
"The event \"%{Summary}\" was deleted" = "Hendelsen \"%{Summary}\" ble slettet";
|
||||
"The event \"%{Summary}\" was updated" = "Hendelsen \"%{Summary}\" ble oppdatert";
|
||||
"The following attendees(s) were notified:" = "Følgende deltaker(e) ble varslet:";
|
||||
"The following attendees(s) were added:" = "Følgende deltaker(e) ble lagt til:";
|
||||
"The following attendees(s) were removed:" = "Følgende deltaker(e) ble fjernet:";
|
||||
"The following attendees(s) were notified" = "Følgende deltaker(e) ble varslet";
|
||||
"The following attendees(s) were added" = "Følgende deltaker(e) ble lagt til";
|
||||
"The following attendees(s) were removed" = "Følgende deltaker(e) ble fjernet";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Kalender:";
|
||||
"startDate_label" = "Start:";
|
||||
"endDate_label" = "Slutt:";
|
||||
"calendar_label" = "Kalender";
|
||||
"startDate_label" = "Start";
|
||||
"endDate_label" = "Slutt";
|
||||
"due_label" = "Forfallsdato:";
|
||||
"location_label" = "Sted:";
|
||||
"location_label" = "Sted";
|
||||
"summary_label" = "Sammendrag:";
|
||||
"comment_label" = "Kommentar:";
|
||||
|
||||
|
||||
@@ -25,10 +25,10 @@ vtodo_class2 = "(Konfidensiell oppgave)";
|
||||
"... from the following event:" = "... fra følgende hendelse:";
|
||||
|
||||
/* IMIP messages */
|
||||
"startDate_label" = "Start:";
|
||||
"endDate_label" = "End:";
|
||||
"startDate_label" = "Start";
|
||||
"endDate_label" = "End";
|
||||
"due_label" = "Due Date:";
|
||||
"location_label" = "Location:";
|
||||
"location_label" = "Location";
|
||||
"summary_label" = "Summary:";
|
||||
"comment_label" = "Comment:";
|
||||
|
||||
|
||||
@@ -12,16 +12,16 @@ vtodo_class2 = "(Zadanie poufne)";
|
||||
"The event \"%{Summary}\" was created" = "Utworzono wydarzenie \"%{Summary}\"";
|
||||
"The event \"%{Summary}\" was deleted" = "Skasowano wydarzenie \"%{Summary}\"";
|
||||
"The event \"%{Summary}\" was updated" = "Zaktualizowano wydarzenie \"%{Summary}\"";
|
||||
"The following attendees(s) were notified:" = "Następujący uczestnicy zostali powiadomieni:";
|
||||
"The following attendees(s) were added:" = "Następujący uczestnicy zostali dodani:";
|
||||
"The following attendees(s) were removed:" = "Następujący uczestnicy zostali usunięci:";
|
||||
"The following attendees(s) were notified" = "Następujący uczestnicy zostali powiadomieni";
|
||||
"The following attendees(s) were added" = "Następujący uczestnicy zostali dodani";
|
||||
"The following attendees(s) were removed" = "Następujący uczestnicy zostali usunięci";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Kalendarz:";
|
||||
"startDate_label" = "Początek:";
|
||||
"endDate_label" = "Koniec:";
|
||||
"calendar_label" = "Kalendarz";
|
||||
"startDate_label" = "Początek";
|
||||
"endDate_label" = "Koniec";
|
||||
"due_label" = "Termin:";
|
||||
"location_label" = "Miejsce:";
|
||||
"location_label" = "Miejsce";
|
||||
"summary_label" = "Podsumowanie:";
|
||||
"comment_label" = "Komentarz:";
|
||||
|
||||
|
||||
@@ -12,16 +12,16 @@ vtodo_class2 = "(Конфиденциальная задача)";
|
||||
"The event \"%{Summary}\" was created" = "Было создано мероприятие \"%{Summary}\"";
|
||||
"The event \"%{Summary}\" was deleted" = "Мероприятие \"%{Summary}\" было удалено";
|
||||
"The event \"%{Summary}\" was updated" = "Мероприятие \"%{Summary}\" было обновлено";
|
||||
"The following attendees(s) were notified:" = "Следующие приглашенные были оповещены:";
|
||||
"The following attendees(s) were added:" = "Следующие люди были добавлены в список приглашенных:";
|
||||
"The following attendees(s) were removed:" = "Следующие люди были исключены из списка приглашенных:";
|
||||
"The following attendees(s) were notified" = "Следующие приглашенные были оповещены";
|
||||
"The following attendees(s) were added" = "Следующие люди были добавлены в список приглашенных";
|
||||
"The following attendees(s) were removed" = "Следующие люди были исключены из списка приглашенных";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Календарь:";
|
||||
"startDate_label" = "Начало:";
|
||||
"endDate_label" = "Конец:";
|
||||
"calendar_label" = "Календарь";
|
||||
"startDate_label" = "Начало";
|
||||
"endDate_label" = "Конец";
|
||||
"due_label" = "Должно быть готово к:";
|
||||
"location_label" = "Местонахождение:";
|
||||
"location_label" = "Местонахождение";
|
||||
"summary_label" = "Краткое содержание:";
|
||||
"comment_label" = "Комментарий:";
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ typedef enum {
|
||||
NSMutableDictionary *uidToFilename;
|
||||
NSMutableDictionary *aclMatrix;
|
||||
NSMutableArray *stripFields;
|
||||
NSString *baseCalDAVURL, *basePublicCalDAVURL;
|
||||
int davCalendarStartTimeLimit;
|
||||
int davTimeLimitSeconds;
|
||||
int davTimeHalfLimitSeconds;
|
||||
@@ -73,6 +74,7 @@ typedef enum {
|
||||
}
|
||||
|
||||
- (BOOL) isActive;
|
||||
- (BOOL) isWebCalendar;
|
||||
|
||||
- (NSString *) calendarColor;
|
||||
- (void) setCalendarColor: (NSString *) newColor;
|
||||
@@ -129,6 +131,13 @@ typedef enum {
|
||||
- (NSString *) baseURLForAptWithUID: (NSString *) _uid
|
||||
inContext: (id) _ctx;
|
||||
|
||||
- (NSString *) calDavURL;
|
||||
- (NSString *) webDavICSURL;
|
||||
- (NSString *) webDavXMLURL;
|
||||
- (NSString *) publicCalDavURL;
|
||||
- (NSString *) publicWebDavICSURL;
|
||||
- (NSString *) publicWebDavXMLURL;
|
||||
|
||||
/* folder management */
|
||||
|
||||
- (id) lookupHomeFolderForUID: (NSString *) _uid
|
||||
@@ -157,12 +166,6 @@ typedef enum {
|
||||
- (BOOL) showCalendarTasks;
|
||||
- (void) setShowCalendarTasks: (BOOL) new;
|
||||
|
||||
- (NSString *) syncTag;
|
||||
- (void) setSyncTag: (NSString *) newSyncTag;
|
||||
|
||||
- (BOOL) synchronizeCalendar;
|
||||
- (void) setSynchronizeCalendar: (BOOL) new;
|
||||
|
||||
- (BOOL) includeInFreeBusy;
|
||||
- (void) setIncludeInFreeBusy: (BOOL) newInclude;
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@
|
||||
#import "SOGoAppointmentFolders.h"
|
||||
#import "SOGoFreeBusyObject.h"
|
||||
#import "SOGoTaskObject.h"
|
||||
#import "SOGoWebAppointmentFolder.h";
|
||||
|
||||
#import "SOGoAppointmentFolder.h"
|
||||
|
||||
@@ -332,13 +333,15 @@ static Class iCalEventK = nil;
|
||||
memset (userCanAccessObjectsClassifiedAs, NO,
|
||||
iCalAccessClassCount * sizeof (BOOL));
|
||||
|
||||
davCalendarStartTimeLimit
|
||||
= [[user domainDefaults] davCalendarStartTimeLimit];
|
||||
davCalendarStartTimeLimit = [[user domainDefaults] davCalendarStartTimeLimit];
|
||||
davTimeLimitSeconds = davCalendarStartTimeLimit * 86400;
|
||||
/* 86400 / 2 = 43200. We hardcode that value in order to avoid
|
||||
integer and float confusion. */
|
||||
davTimeHalfLimitSeconds = davCalendarStartTimeLimit * 43200;
|
||||
componentSet = nil;
|
||||
|
||||
baseCalDAVURL = nil;
|
||||
basePublicCalDAVURL = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -350,6 +353,8 @@ static Class iCalEventK = nil;
|
||||
[stripFields release];
|
||||
[uidToFilename release];
|
||||
[componentSet release];
|
||||
[baseCalDAVURL release];
|
||||
[basePublicCalDAVURL release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@@ -432,61 +437,6 @@ static Class iCalEventK = nil;
|
||||
inCategory: @"FolderShowTasks"];
|
||||
}
|
||||
|
||||
- (NSString *) syncTag
|
||||
{
|
||||
NSString *syncTag;
|
||||
|
||||
syncTag = [self folderPropertyValueInCategory: @"FolderSyncTags"];
|
||||
if (!syncTag)
|
||||
syncTag = @"";
|
||||
|
||||
return syncTag;
|
||||
}
|
||||
|
||||
- (void) setSyncTag: (NSString *) newSyncTag
|
||||
{
|
||||
// Check for duplicated tags
|
||||
SOGoUserSettings *settings;
|
||||
NSDictionary *syncTags;
|
||||
NSArray *values;
|
||||
|
||||
if ([newSyncTag length])
|
||||
{
|
||||
settings = [[context activeUser] userSettings];
|
||||
syncTags = [[settings objectForKey: @"Calendar"]
|
||||
objectForKey: @"FolderSyncTags"];
|
||||
values = [syncTags allValues];
|
||||
if (![values containsObject: newSyncTag])
|
||||
[self setFolderPropertyValue: newSyncTag
|
||||
inCategory: @"FolderSyncTags"];
|
||||
}
|
||||
else
|
||||
[self setFolderPropertyValue: nil
|
||||
inCategory: @"FolderSyncTags"];
|
||||
}
|
||||
|
||||
- (BOOL) synchronizeCalendar
|
||||
{
|
||||
NSNumber *synchronize;
|
||||
|
||||
synchronize = [self folderPropertyValueInCategory: @"FolderSynchronize"];
|
||||
|
||||
return [synchronize boolValue];
|
||||
}
|
||||
|
||||
- (void) setSynchronizeCalendar: (BOOL) new
|
||||
{
|
||||
NSNumber *synchronize;
|
||||
|
||||
if (new)
|
||||
synchronize = [NSNumber numberWithBool: YES];
|
||||
else
|
||||
synchronize = nil;
|
||||
|
||||
[self setFolderPropertyValue: synchronize
|
||||
inCategory: @"FolderSynchronize"];
|
||||
}
|
||||
|
||||
//
|
||||
// If the user is the owner of the calendar, by default we include the freebusy information.
|
||||
//
|
||||
@@ -2869,6 +2819,70 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
return [url stringByAppendingString:_uid];
|
||||
}
|
||||
|
||||
- (NSString *) _baseCalDAVURL
|
||||
{
|
||||
NSString *davURL;
|
||||
|
||||
if (!baseCalDAVURL)
|
||||
{
|
||||
davURL = [[self realDavURL] absoluteString];
|
||||
if ([davURL hasSuffix: @"/"])
|
||||
baseCalDAVURL = [davURL substringToIndex: [davURL length] - 1];
|
||||
else
|
||||
baseCalDAVURL = davURL;
|
||||
[baseCalDAVURL retain];
|
||||
}
|
||||
|
||||
return baseCalDAVURL;
|
||||
}
|
||||
|
||||
- (NSString *) _basePublicCalDAVURL
|
||||
{
|
||||
NSString *davURL;
|
||||
|
||||
if (!basePublicCalDAVURL)
|
||||
{
|
||||
davURL = [[self publicDavURL] absoluteString];
|
||||
if ([davURL hasSuffix: @"/"])
|
||||
basePublicCalDAVURL = [davURL substringToIndex: [davURL length] - 1];
|
||||
else
|
||||
basePublicCalDAVURL = davURL;
|
||||
[basePublicCalDAVURL retain];
|
||||
}
|
||||
|
||||
return basePublicCalDAVURL;
|
||||
}
|
||||
|
||||
- (NSString *) calDavURL
|
||||
{
|
||||
return [NSString stringWithFormat: @"%@/", [self _baseCalDAVURL]];
|
||||
}
|
||||
|
||||
- (NSString *) webDavICSURL
|
||||
{
|
||||
return [NSString stringWithFormat: @"%@.ics", [self _baseCalDAVURL]];
|
||||
}
|
||||
|
||||
- (NSString *) webDavXMLURL
|
||||
{
|
||||
return [NSString stringWithFormat: @"%@.xml", [self _baseCalDAVURL]];
|
||||
}
|
||||
|
||||
- (NSString *) publicCalDavURL
|
||||
{
|
||||
return [NSString stringWithFormat: @"%@/", [self _basePublicCalDAVURL]];
|
||||
}
|
||||
|
||||
- (NSString *) publicWebDavICSURL
|
||||
{
|
||||
return [NSString stringWithFormat: @"%@.ics", [self _basePublicCalDAVURL]];
|
||||
}
|
||||
|
||||
- (NSString *) publicWebDavXMLURL
|
||||
{
|
||||
return [NSString stringWithFormat: @"%@.xml", [self _basePublicCalDAVURL]];
|
||||
}
|
||||
|
||||
/* folder management */
|
||||
- (BOOL) create
|
||||
{
|
||||
@@ -2914,12 +2928,6 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
|
||||
refArray = [moduleSettings objectForKey: @"InactiveFolders"];
|
||||
[refArray removeObject: nameInContainer];
|
||||
|
||||
refDict = [moduleSettings objectForKey: @"FolderSyncTags"];
|
||||
[refDict removeObjectForKey: reference];
|
||||
|
||||
refDict = [moduleSettings objectForKey: @"FolderSynchronize"];
|
||||
[refDict removeObjectForKey: reference];
|
||||
|
||||
[super removeFolderSettings: moduleSettings
|
||||
withReference: reference];
|
||||
@@ -3132,6 +3140,11 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
return (![inactiveFolders containsObject: nameInContainer]);
|
||||
}
|
||||
|
||||
- (BOOL) isWebCalendar
|
||||
{
|
||||
return ([self isKindOfClass: [SOGoWebAppointmentFolder class]]);
|
||||
}
|
||||
|
||||
- (NSString *) importComponent: (iCalEntityObject *) event
|
||||
timezone: (iCalTimeZone *) timezone
|
||||
{
|
||||
@@ -3157,6 +3170,8 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
inContainer: self];
|
||||
[object setIsNew: YES];
|
||||
content = [NSMutableString stringWithString: @"BEGIN:VCALENDAR\n"];
|
||||
[content appendFormat: @"PRODID:-//Inverse inc./SOGo %@//EN\n", SOGoVersion];
|
||||
|
||||
if (timezone)
|
||||
[content appendFormat: @"%@\n", [timezone versitString]];
|
||||
[content appendFormat: @"%@\nEND:VCALENDAR", [event versitString]];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2007-2014 Inverse inc.
|
||||
Copyright (C) 2007-2015 Inverse inc.
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
This file is part of SOGo
|
||||
@@ -39,6 +39,7 @@
|
||||
#import <NGCards/iCalEventChanges.h>
|
||||
#import <NGCards/iCalPerson.h>
|
||||
#import <NGCards/iCalRecurrenceCalculator.h>
|
||||
#import <NGCards/iCalToDo.h>
|
||||
#import <NGCards/NSCalendarDate+NGCards.h>
|
||||
#import <SaxObjC/XMLNamespaces.h>
|
||||
|
||||
@@ -317,7 +318,7 @@
|
||||
enumerator = [attendees objectEnumerator];
|
||||
while ((currentAttendee = [enumerator nextObject]))
|
||||
{
|
||||
currentUID = [currentAttendee uid];
|
||||
currentUID = [currentAttendee uidInContext: context];
|
||||
if (currentUID)
|
||||
[self _removeEventFromUID: currentUID
|
||||
owner: owner
|
||||
@@ -398,7 +399,7 @@
|
||||
enumerator = [updateAttendees objectEnumerator];
|
||||
while ((currentAttendee = [enumerator nextObject]))
|
||||
{
|
||||
currentUID = [currentAttendee uid];
|
||||
currentUID = [currentAttendee uidInContext: context];
|
||||
if (currentUID)
|
||||
[self _addOrUpdateEvent: newEvent
|
||||
forUID: currentUID
|
||||
@@ -438,7 +439,7 @@
|
||||
|
||||
while ((currentAttendee = [enumerator nextObject]))
|
||||
{
|
||||
currentUID = [currentAttendee uid];
|
||||
currentUID = [currentAttendee uidInContext: context];
|
||||
|
||||
if (currentUID)
|
||||
{
|
||||
@@ -520,7 +521,7 @@
|
||||
enumerator = [theAttendees objectEnumerator];
|
||||
while ((currentAttendee = [enumerator nextObject]))
|
||||
{
|
||||
currentUID = [currentAttendee uid];
|
||||
currentUID = [currentAttendee uidInContext: context];
|
||||
if (currentUID)
|
||||
{
|
||||
[attendees addObject: currentUID];
|
||||
@@ -635,7 +636,7 @@
|
||||
for (i = 0; i < [theAttendees count]; i++)
|
||||
{
|
||||
currentAttendee = [theAttendees objectAtIndex: i];
|
||||
if ([[currentAttendee uid] isEqualToString: currentUID])
|
||||
if ([[currentAttendee uidInContext: context] isEqualToString: currentUID])
|
||||
break;
|
||||
else
|
||||
currentAttendee = nil;
|
||||
@@ -713,7 +714,7 @@
|
||||
enumerator = [attendees objectEnumerator];
|
||||
while ((currentAttendee = [enumerator nextObject]))
|
||||
{
|
||||
currentUID = [currentAttendee uid];
|
||||
currentUID = [currentAttendee uidInContext: context];
|
||||
if (currentUID)
|
||||
[self _addOrUpdateEvent: newEvent
|
||||
forUID: currentUID
|
||||
@@ -838,7 +839,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
enumerator = [updatedAttendees objectEnumerator];
|
||||
while ((currentAttendee = [enumerator nextObject]))
|
||||
{
|
||||
currentUID = [currentAttendee uid];
|
||||
currentUID = [currentAttendee uidInContext: context];
|
||||
if (currentUID)
|
||||
[self _addOrUpdateEvent: newEvent
|
||||
forUID: currentUID
|
||||
@@ -1215,7 +1216,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
{
|
||||
[delegates addObject: otherDelegate];
|
||||
|
||||
delegatedUID = [otherDelegate uid];
|
||||
delegatedUID = [otherDelegate uidInContext: context];
|
||||
if (delegatedUID)
|
||||
// Delegate attendee is a local user; remove event from their calendar
|
||||
[self _removeEventFromUID: delegatedUID
|
||||
@@ -1244,7 +1245,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
|
||||
if (addDelegate)
|
||||
{
|
||||
delegatedUID = [delegate uid];
|
||||
delegatedUID = [delegate uidInContext: context];
|
||||
delegates = [NSArray arrayWithObject: delegate];
|
||||
[event addToAttendees: delegate];
|
||||
|
||||
@@ -1271,11 +1272,11 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
[self sendResponseToOrganizer: event
|
||||
from: ownerUser];
|
||||
|
||||
organizerUID = [[event organizer] uid];
|
||||
organizerUID = [[event organizer] uidInContext: context];
|
||||
|
||||
// Event is an exception to a recurring event; retrieve organizer from master event
|
||||
if (!organizerUID)
|
||||
organizerUID = [[(iCalEntityObject*)[[event parent] firstChildWithTag: [self componentTag]] organizer] uid];
|
||||
organizerUID = [[(iCalEntityObject*)[[event parent] firstChildWithTag: [self componentTag]] organizer] uidInContext: context];
|
||||
|
||||
if (organizerUID)
|
||||
// Update the attendee in organizer's calendar.
|
||||
@@ -1302,7 +1303,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
for (i = 0; i < [attendees count]; i++)
|
||||
{
|
||||
att = [attendees objectAtIndex: i];
|
||||
uid = [att uid];
|
||||
uid = [att uidInContext: context];
|
||||
if (uid && att != attendee && ![uid isEqualToString: delegatedUID])
|
||||
[self _updateAttendee: attendee
|
||||
withDelegate: delegate
|
||||
@@ -1461,7 +1462,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
{
|
||||
if (delegate && ![[delegate email] isEqualToString: [attendee delegatedTo]])
|
||||
{
|
||||
delegatedUid = [delegate uid];
|
||||
delegatedUid = [delegate uidInContext: context];
|
||||
if (delegatedUid)
|
||||
delegatedUser = [SOGoUser userWithLogin: delegatedUid];
|
||||
if (delegatedUser != nil && [event userIsOrganizer: delegatedUser])
|
||||
@@ -1820,9 +1821,28 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
[self warnWithFormat: @"Invalid event: no end date; setting duration to %@", [event duration]];
|
||||
}
|
||||
|
||||
if ([event organizer] && ![[[event organizer] cn] length])
|
||||
if ([event organizer])
|
||||
{
|
||||
[[event organizer] setCn: [[event organizer] rfc822Email]];
|
||||
NSString *uid;
|
||||
|
||||
if (![[[event organizer] cn] length])
|
||||
{
|
||||
[[event organizer] setCn: [[event organizer] rfc822Email]];
|
||||
}
|
||||
|
||||
// We now make sure that the organizer, if managed by SOGo, is using
|
||||
// its default email when creating events and inviting attendees.
|
||||
uid = [[event organizer] uidInContext: context];
|
||||
if (uid)
|
||||
{
|
||||
NSDictionary *defaultIdentity;
|
||||
SOGoUser *organizer;
|
||||
|
||||
organizer = [SOGoUser userWithLogin: uid];
|
||||
defaultIdentity = [organizer defaultIdentity];
|
||||
[[event organizer] setCn: [defaultIdentity objectForKey: @"fullName"]];
|
||||
[[event organizer] setEmail: [defaultIdentity objectForKey: @"email"]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2112,32 +2132,23 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
//
|
||||
if ([[newEvent attendees] count] || [[oldEvent attendees] count])
|
||||
{
|
||||
NSString *uid;
|
||||
|
||||
BOOL userIsOrganizer;
|
||||
|
||||
// newEvent might be nil here, if we're deleting a RECURRENCE-ID with attendees
|
||||
// If that's the case, we use the oldEvent for now just to obtain the organizer
|
||||
// and we'll swap it back to nil once we're done.
|
||||
if (!newEvent)
|
||||
newEvent = oldEvent;
|
||||
|
||||
// We fetch the organizer's uid. Sometimes, the recurrence-id will
|
||||
// have it, sometimes not. If it doesn't, we fetch it from the master event.
|
||||
uid = [[newEvent organizer] uid];
|
||||
|
||||
if (!uid && !recurrenceId)
|
||||
uid = [[[[[newEvent parent] events] objectAtIndex: 0] organizer] uid];
|
||||
|
||||
// If that's the case, we use the oldEvent to obtain the organizer
|
||||
if (newEvent)
|
||||
userIsOrganizer = [newEvent userIsOrganizer: ownerUser];
|
||||
else
|
||||
userIsOrganizer = [oldEvent userIsOrganizer: ownerUser];
|
||||
|
||||
// With Thunderbird 10, if you create a recurring event with an exception
|
||||
// occurence, and invite someone, the PUT will have the organizer in the
|
||||
// recurrence-id and not in the master event. We must fix this, otherwise
|
||||
// SOGo will break.
|
||||
if (!recurrenceId && ![[[[[newEvent parent] events] objectAtIndex: 0] organizer] uid])
|
||||
if (!recurrenceId && ![[[[[newEvent parent] events] objectAtIndex: 0] organizer] uidInContext: context])
|
||||
[[[[newEvent parent] events] objectAtIndex: 0] setOrganizer: [newEvent organizer]];
|
||||
|
||||
if (newEvent == oldEvent)
|
||||
newEvent = nil;
|
||||
|
||||
if (uid && [uid caseInsensitiveCompare: owner] == NSOrderedSame)
|
||||
|
||||
if (userIsOrganizer)
|
||||
{
|
||||
// A RECCURENCE-ID was removed
|
||||
if (!newEvent && oldEvent)
|
||||
|
||||
@@ -11,16 +11,16 @@ vtodo_class2 = "(Dôverná úloha)";
|
||||
"The event \"%{Summary}\" was created" = "Udalosť \"%{Summary}\" bola vytvorená";
|
||||
"The event \"%{Summary}\" was deleted" = "Udalosť \"%{Summary}\" bola vymazaná";
|
||||
"The event \"%{Summary}\" was updated" = "Udalosť \"%{Summary}\" bola aktualizovaná";
|
||||
"The following attendees(s) were notified:" = "Nasledujúci účastník(ci) bol upozornený:";
|
||||
"The following attendees(s) were added:" = "Nasledujúci účastník(ci) bol pridaný:";
|
||||
"The following attendees(s) were removed:" = "Nasledujúci účastník(ci) bol odstránený:";
|
||||
"The following attendees(s) were notified" = "Nasledujúci účastník(ci) bol upozornený";
|
||||
"The following attendees(s) were added" = "Nasledujúci účastník(ci) bol pridaný";
|
||||
"The following attendees(s) were removed" = "Nasledujúci účastník(ci) bol odstránený";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Kalendár:";
|
||||
"startDate_label" = "Začiatok:";
|
||||
"endDate_label" = "Koniec:";
|
||||
"calendar_label" = "Kalendár";
|
||||
"startDate_label" = "Začiatok";
|
||||
"endDate_label" = "Koniec";
|
||||
"due_label" = "Platnosť:";
|
||||
"location_label" = "Miesto:";
|
||||
"location_label" = "Miesto";
|
||||
"summary_label" = "Zhrnutie:";
|
||||
"comment_label" = "Komentár:";
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
"Inviting the following persons is prohibited:" = "Povabilo za naslednje osebe je prepovedano:";
|
||||
"Personal Calendar" = "Osebni koledar";
|
||||
vevent_class0 = "(Javni dogodek)";
|
||||
vevent_class1 = "(Osebni dogodek)";
|
||||
vevent_class2 = "(Zaupni dogodek)";
|
||||
|
||||
vtodo_class0 = "(Javno opravilo)";
|
||||
vtodo_class1 = "(Osebno opravilo)";
|
||||
vtodo_class2 = "(Zaupno opravilo)";
|
||||
|
||||
/* Receipts */
|
||||
"The event \"%{Summary}\" was created" = "Dogodek \"%{Summary}\" je bil ustvarjen";
|
||||
"The event \"%{Summary}\" was deleted" = "Dogodek \"%{Summary}\" je bil izbrisan";
|
||||
"The event \"%{Summary}\" was updated" = "Dogodek \"%{Summary}\" je bil posodobljen";
|
||||
"The following attendees(s) were notified" = "Naslednji udeleženci so bili obveščeni";
|
||||
"The following attendees(s) were added" = "Naslednji udeleženci so bili dodani";
|
||||
"The following attendees(s) were removed" = "Naslednji udeleženci so bili odstranjeni";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Koledar";
|
||||
"startDate_label" = "Začetek";
|
||||
"endDate_label" = "Konec";
|
||||
"due_label" = "Datum zapadlosti:";
|
||||
"location_label" = "Mesto";
|
||||
"summary_label" = "Povzetek:";
|
||||
"comment_label" = "Komentar:";
|
||||
|
||||
/* Invitation */
|
||||
"Event Invitation: \"%{Summary}\"" = "Povabila na dogodek: \"%{Summary}\"";
|
||||
"(sent by %{SentBy}) " = "(posladno od %{SentBy})";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText} te je povabil na %{Summary}.\n\n\nZačetek: %{StartDate}\nKonec: %{EndDate}\nOpis: %{Description}";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} te je povabil na %{Summary}.\n\n\nZačetek: %{StartDate} at %{StartTime}\nKonec: %{EndDate} at %{EndTime}\nOpis: %{Description}";
|
||||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Dogodek odpovedan: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText} je odpovedal ta dogodek: %{Summary}.\n\n\nZačetek: %{StartDate}\nKonec: %{EndDate}\nOpis: %{Description}";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText} je odpovedal ta dogodek: %{Summary}.\n\n\nZačetek: %{StartDate} at %{StartTime}\nKonec: %{EndDate} at %{EndTime}\nOpis: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
|
||||
= "Sestanek \"%{Summary}\" za %{OldStartDate} se je spremenil";
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
= "Sestanek \"%{Summary}\" za %{OldStartDate} at %{OldStartTime} se je spremenil";
|
||||
"The following parameters have changed in the \"%{Summary}\" meeting:"
|
||||
= "Naslednji parametri so se spremenil v \"%{Summary}\" srečanju:";
|
||||
"Please accept or decline those changes."
|
||||
= "Prosim sprejmi ali zavrni te spremembe.";
|
||||
|
||||
/* Reply */
|
||||
"Accepted invitation: \"%{Summary}\"" = "Sprejeto povabilo: \"%{Summary}\"";
|
||||
"Declined invitation: \"%{Summary}\"" = "Zavrnjeno povabilo: \"%{Summary}\"";
|
||||
"Delegated invitation: \"%{Summary}\"" = "Dodeljeno povabilo: \"%{Summary}\"";
|
||||
"Not yet decided on invitation: \"%{Summary}\"" = "Še neodločeno glede povabila: \"%{Summary}\"";
|
||||
"%{Attendee} %{SentByText}has accepted your event invitation."
|
||||
= "%{Attendee} %{SentByText} je sprejel tvoje povabilo.";
|
||||
"%{Attendee} %{SentByText}has declined your event invitation."
|
||||
= "%{Attendee} %{SentByText} je zavrnil tvoje povabilo.";
|
||||
"%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}."
|
||||
= "%{Attendee} %{SentByText} je posredoval povabilo k %{Delegate}.";
|
||||
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
|
||||
= "%{Attendee} %{SentByText} se še ni odločil glede tvojega povabila.";
|
||||
|
||||
/* Resources */
|
||||
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Ne morem dostopati do vira: \"%{Cn} %{SystemEmail}\"";
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Doseženo je največje število sočasnih rezervacij (%{NumberOfSimultaneousBookings}) za vir \"%{Cn} %{SystemEmail}\". Konfliktni dogodek je \"%{EventTitle}\", začel se je %{StartDate}.";
|
||||
@@ -12,16 +12,16 @@ vtodo_class2 = "(Tarea confidencial)";
|
||||
"The event \"%{Summary}\" was created" = "Se creó el evento \"%{Summary}\"";
|
||||
"The event \"%{Summary}\" was deleted" = "Se borró el evento \"%{Summary}\"";
|
||||
"The event \"%{Summary}\" was updated" = "Se actualizó la información del evento \"%{Summary}\"";
|
||||
"The following attendees(s) were notified:" = "Se notificó a los siguientes participantes:";
|
||||
"The following attendees(s) were added:" = "Se agregaron los siguientes participantes:";
|
||||
"The following attendees(s) were removed:" = "Se removieron los siguientes participantes:";
|
||||
"The following attendees(s) were notified" = "Se notificó a los siguientes participantes";
|
||||
"The following attendees(s) were added" = "Se agregaron los siguientes participantes";
|
||||
"The following attendees(s) were removed" = "Se removieron los siguientes participantes";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Calendario:";
|
||||
"startDate_label" = "Inicio:";
|
||||
"endDate_label" = "Fin:";
|
||||
"calendar_label" = "Calendario";
|
||||
"startDate_label" = "Inicio";
|
||||
"endDate_label" = "Fin";
|
||||
"due_label" = "Fecha de finalización:";
|
||||
"location_label" = "Lugar:";
|
||||
"location_label" = "Lugar";
|
||||
"summary_label" = "Resumen:";
|
||||
"comment_label" = "Comentario:";
|
||||
|
||||
|
||||
@@ -12,16 +12,16 @@ vtodo_class2 = "(Tarea confidencial)";
|
||||
"The event \"%{Summary}\" was created" = "El evento \"%{Summary}\" ha sido creado";
|
||||
"The event \"%{Summary}\" was deleted" = "El evento \"%{Summary}\" ha sido borrado";
|
||||
"The event \"%{Summary}\" was updated" = "El evento \"%{Summary}\" ha sido actualizado";
|
||||
"The following attendees(s) were notified:" = "Los invitados siguientes han sido notificado:";
|
||||
"The following attendees(s) were added:" = "Los invitados siguientes han sido añadido:";
|
||||
"The following attendees(s) were removed:" = "Los invitados siguientes han sido quitado:";
|
||||
"The following attendees(s) were notified" = "Los invitados siguientes han sido notificado";
|
||||
"The following attendees(s) were added" = "Los invitados siguientes han sido añadido";
|
||||
"The following attendees(s) were removed" = "Los invitados siguientes han sido quitado";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Calendario:";
|
||||
"startDate_label" = "Inicio:";
|
||||
"endDate_label" = "Fin:";
|
||||
"calendar_label" = "Calendario";
|
||||
"startDate_label" = "Inicio";
|
||||
"endDate_label" = "Fin";
|
||||
"due_label" = "Fecha Fin:";
|
||||
"location_label" = "Localización:";
|
||||
"location_label" = "Localización";
|
||||
"summary_label" = "Resumen:";
|
||||
"comment_label" = "Comentario:";
|
||||
|
||||
|
||||
@@ -25,10 +25,10 @@ vtodo_class2 = "(Konfidentiell uppgift)";
|
||||
"... from the following event:" = "... från följande händelse:";
|
||||
|
||||
/* IMIP messages */
|
||||
"startDate_label" = "Startdatum:";
|
||||
"endDate_label" = "Slutdatum:";
|
||||
"startDate_label" = "Startdatum";
|
||||
"endDate_label" = "Slutdatum";
|
||||
"due_label" = "Datum:";
|
||||
"location_label" = "Plats:";
|
||||
"location_label" = "Plats";
|
||||
"summary_label" = "Sammanfattning:";
|
||||
"comment_label" = "Kommentar:";
|
||||
|
||||
|
||||
@@ -25,10 +25,10 @@ vtodo_class2 = "(Конфіденційне завдання)";
|
||||
"... from the following event:" = "... з участі в події:";
|
||||
|
||||
/* IMIP messages */
|
||||
"startDate_label" = "Початок:";
|
||||
"endDate_label" = "Кінець:";
|
||||
"startDate_label" = "Початок";
|
||||
"endDate_label" = "Кінець";
|
||||
"due_label" = "Триватиме до:";
|
||||
"location_label" = "Місце:";
|
||||
"location_label" = "Місце";
|
||||
"summary_label" = "Резюме:";
|
||||
"comment_label" = "Коментар:";
|
||||
|
||||
|
||||
@@ -25,10 +25,10 @@ vtodo_class2 = "(Tasg gyfrinachol)";
|
||||
"... from the following event:" = "... from the following event:";
|
||||
|
||||
/* IMIP messages */
|
||||
"startDate_label" = "Start:";
|
||||
"endDate_label" = "End:";
|
||||
"startDate_label" = "Start";
|
||||
"endDate_label" = "End";
|
||||
"due_label" = "Due Date:";
|
||||
"location_label" = "Location:";
|
||||
"location_label" = "Location";
|
||||
"summary_label" = "Summary:";
|
||||
"comment_label" = "Comment:";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* iCalAlarm+SOGo.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2014 Inverse inc.
|
||||
* Copyright (C) 2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,11 +20,11 @@
|
||||
|
||||
#import <NGCards/iCalAlarm.h>
|
||||
|
||||
@class iCalRepeatableEntityObject;
|
||||
@class iCalEntityObject;
|
||||
|
||||
@interface iCalAlarm (SOGoExtensions)
|
||||
|
||||
+ (id) alarmForEvent: (iCalRepeatableEntityObject *) theEntity
|
||||
+ (id) alarmForEvent: (iCalEntityObject *) theEntity
|
||||
owner: (NSString *) theOwner
|
||||
action: (NSString *) reminderAction
|
||||
unit: (NSString *) reminderUnit
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* iCalAlarm+SOGo.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2014 Inverse inc.
|
||||
* Copyright (C) 2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#import <NGCards/iCalPerson.h>
|
||||
#import <NGCards/iCalTrigger.h>
|
||||
#import <NGCards/iCalEntityObject.h>
|
||||
|
||||
@implementation iCalAlarm (SOGoExtensions)
|
||||
|
||||
@@ -65,7 +66,7 @@
|
||||
[alarm addChild: aAttendee];
|
||||
}
|
||||
|
||||
+ (id) alarmForEvent: (iCalRepeatableEntityObject *) theEntity
|
||||
+ (id) alarmForEvent: (iCalEntityObject *) theEntity
|
||||
owner: (NSString *) theOwner
|
||||
action: (NSString *) reminderAction
|
||||
unit: (NSString *) reminderUnit
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* iCalEntityObject+SOGo.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2014 Inverse inc.
|
||||
* Copyright (C) 2007-2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -26,6 +26,7 @@
|
||||
@class iCalAlarm;
|
||||
@class NSMutableDictionary;
|
||||
@class SOGoUser;
|
||||
@class WOContext;
|
||||
|
||||
extern NSCalendarDate *iCalDistantFuture;
|
||||
extern NSNumber *iCalDistantFutureNumber;
|
||||
@@ -34,12 +35,15 @@ extern NSNumber *iCalDistantFutureNumber;
|
||||
|
||||
+ (void) initializeSOGoExtensions;
|
||||
|
||||
- (NSDictionary *) attributesInContext: (WOContext *) context;
|
||||
- (void) setAttributes: (NSDictionary *) data
|
||||
inContext: (WOContext *) context;
|
||||
|
||||
- (BOOL) userIsAttendee: (SOGoUser *) user;
|
||||
- (BOOL) userIsOrganizer: (SOGoUser *) user;
|
||||
|
||||
- (iCalPerson *) userAsAttendee: (SOGoUser *) user;
|
||||
|
||||
- (NSArray *) attendeeUIDs;
|
||||
- (BOOL) isStillRelevant;
|
||||
|
||||
- (id) itipEntryWithMethod: (NSString *) method;
|
||||
@@ -47,7 +51,13 @@ extern NSNumber *iCalDistantFutureNumber;
|
||||
- (NSArray *) attendeesWithoutUser: (SOGoUser *) user;
|
||||
|
||||
- (int) priorityNumber;
|
||||
- (NSString *) createdBy;
|
||||
- (NSDictionary *) createdBy;
|
||||
|
||||
- (NSCalendarDate *) dateFromString: (NSString *) dateString
|
||||
inContext: (WOContext *) context;
|
||||
- (void) adjustDate: (NSCalendarDate **) date
|
||||
withTimeString: (NSString *) timeString
|
||||
inContext: (WOContext *) context;
|
||||
|
||||
- (NSNumber *) quickRecordDateAsNumber: (NSCalendarDate *) _date
|
||||
withOffset: (int) offset
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* iCalEntityObject+SOGo.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2014 Inverse inc.
|
||||
* Copyright (C) 2007-2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,19 +20,24 @@
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSEnumerator.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
|
||||
#import <NGCards/iCalAlarm.h>
|
||||
#import <NGCards/iCalCalendar.h>
|
||||
#import <NGCards/iCalDateTime.h>
|
||||
#import <NGCards/iCalPerson.h>
|
||||
#import <NGCards/iCalTrigger.h>
|
||||
#import <NGCards/iCalRepeatableEntityObject.h>
|
||||
#import <NGCards/NSString+NGCards.h>
|
||||
|
||||
#import <NGExtensions/NGCalendarDateRange.h>
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import "SOGoAppointmentFolder.h"
|
||||
|
||||
@@ -40,11 +45,13 @@
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <SOGo/SOGoSource.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
#import <SOGo/SOGoUserManager.h>
|
||||
|
||||
#import "iCalPerson+SOGo.h"
|
||||
|
||||
#import "iCalAlarm+SOGo.h"
|
||||
#import "iCalCalendar+SOGo.h"
|
||||
#import "iCalEntityObject+SOGo.h"
|
||||
|
||||
@@ -63,6 +70,336 @@ NSNumber *iCalDistantFutureNumber = nil;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see [UIxAppointmentEditor viewAction]
|
||||
* @see [UIxTaskEditor viewAction]
|
||||
*/
|
||||
- (NSDictionary *) attributesInContext: (WOContext *) context
|
||||
{
|
||||
NSArray *elements;
|
||||
NSMutableArray *attendees;
|
||||
NSDictionary *contactData;
|
||||
NSMutableDictionary *data, *organizerData, *attendeeData;
|
||||
NSEnumerator *attendeesList;
|
||||
NSString *uid, *domain, *sentBy;
|
||||
NSObject <SOGoSource> *source;
|
||||
SOGoUserManager *um;
|
||||
iCalPerson *organizer, *currentAttendee;
|
||||
id value;
|
||||
|
||||
um = [SOGoUserManager sharedUserManager];
|
||||
|
||||
data = [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||
[[self tag] lowercaseString], @"component",
|
||||
[self summary], @"summary",
|
||||
nil];
|
||||
|
||||
value = [self priority];
|
||||
if ([value length]) [data setObject: value forKey: @"priority"];
|
||||
|
||||
value = [self location];
|
||||
if ([value length]) [data setObject: value forKey: @"location"];
|
||||
|
||||
value = [self comment];
|
||||
if ([value length]) [data setObject: value forKey: @"comment"];
|
||||
|
||||
value = [self accessClass];
|
||||
if ([value length]) [data setObject: [value lowercaseString] forKey: @"classification"];
|
||||
|
||||
value = [self status];
|
||||
if ([value length]) [data setObject: [value lowercaseString] forKey: @"status"];
|
||||
|
||||
value = [self createdBy];
|
||||
if (value) [data setObject: value forKey: @"createdBy"];
|
||||
|
||||
// Categories
|
||||
elements = [self categories];
|
||||
if ([elements count])
|
||||
[data setObject: elements forKey: @"categories"];
|
||||
|
||||
// Send appointment notifications when the custom tag is *not* set
|
||||
value = [self firstChildWithTag: @"X-SOGo-Send-Appointment-Notifications"];
|
||||
[data setObject: [NSNumber numberWithBool: (value? 0:1)] forKey: @"sendAppointmentNotifications"];
|
||||
|
||||
// Organizer
|
||||
organizer = [self organizer];
|
||||
if ([[organizer email] length])
|
||||
{
|
||||
organizerData = [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||
[organizer rfc822Email], @"email",
|
||||
[organizer cnWithoutQuotes], @"name",
|
||||
nil];
|
||||
uid = [organizer uid];
|
||||
if ([uid length]) [organizerData setObject: uid forKey: @"uid"];
|
||||
sentBy = [organizer sentBy];
|
||||
if ([sentBy length]) [organizerData setObject: sentBy forKey: @"sentBy"];
|
||||
[data setObject: organizerData forKey: @"organizer"];
|
||||
}
|
||||
|
||||
// Attendees
|
||||
attendees = [NSMutableArray array];
|
||||
attendeesList = [[self attendees] objectEnumerator];
|
||||
while ((currentAttendee = [attendeesList nextObject]))
|
||||
{
|
||||
attendeeData = [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||
[currentAttendee rfc822Email], @"email",
|
||||
[currentAttendee cnWithoutQuotes], @"name",
|
||||
nil];
|
||||
if ((uid = [currentAttendee uid]))
|
||||
{
|
||||
[attendeeData setObject: uid forKey: @"uid"];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Search for attendee in global contacts sources that are associated with a MS Exchange server
|
||||
domain = [[context activeUser] domain];
|
||||
elements = [um fetchContactsMatching: [currentAttendee rfc822Email] inDomain: domain];
|
||||
if ([elements count] == 1)
|
||||
{
|
||||
contactData = [elements lastObject];
|
||||
source = [contactData objectForKey: @"source"];
|
||||
if ([source conformsToProtocol: @protocol (SOGoDNSource)] &&
|
||||
[[(NSObject <SOGoDNSource>*) source MSExchangeHostname] length])
|
||||
{
|
||||
uid = [NSString stringWithFormat: @"%@:%@", [[context activeUser] login],
|
||||
[contactData valueForKey: @"c_uid"]];
|
||||
[attendeeData setObject: uid forKey: @"uid"];
|
||||
}
|
||||
}
|
||||
}
|
||||
[attendeeData setObject: [[currentAttendee partStat] lowercaseString] forKey: @"status"];
|
||||
[attendeeData setObject: [[currentAttendee role] lowercaseString] forKey: @"role"];
|
||||
if ([[currentAttendee delegatedTo] length])
|
||||
[attendeeData setObject: [[currentAttendee delegatedTo] rfc822Email] forKey: @"delegatedTo"];
|
||||
if ([[currentAttendee delegatedFrom] length])
|
||||
[attendeeData setObject: [[currentAttendee delegatedFrom] rfc822Email] forKey: @"delegatedFrom"];
|
||||
|
||||
[attendees addObject: attendeeData];
|
||||
}
|
||||
if ([attendees count])
|
||||
[data setObject: attendees forKey: @"attendees"];
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// From [UIxDatePicker takeValuesFromRequest:inContext:]
|
||||
- (NSCalendarDate *) dateFromString: (NSString *) dateString
|
||||
inContext: (WOContext *) context
|
||||
{
|
||||
NSInteger dateTZOffset, userTZOffset;
|
||||
NSTimeZone *systemTZ, *userTZ;
|
||||
SOGoUserDefaults *ud;
|
||||
NSCalendarDate *date;
|
||||
|
||||
date = [NSCalendarDate dateWithString: dateString
|
||||
calendarFormat: @"%Y-%m-%d"];
|
||||
if (!date)
|
||||
[self warnWithFormat: @"Could not parse dateString: '%@'", dateString];
|
||||
|
||||
// We must adjust the date timezone because "dateWithString:..." uses the
|
||||
// system timezone, which can be different from the user's. */
|
||||
ud = [[context activeUser] userDefaults];
|
||||
systemTZ = [date timeZone];
|
||||
dateTZOffset = [systemTZ secondsFromGMTForDate: date];
|
||||
userTZ = [ud timeZone];
|
||||
userTZOffset = [userTZ secondsFromGMTForDate: date];
|
||||
if (dateTZOffset != userTZOffset)
|
||||
date = [date dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: (dateTZOffset - userTZOffset)];
|
||||
[date setTimeZone: userTZ];
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
// From [UIxTimeDatePicker takeValuesFromRequest:inContext:]
|
||||
- (void) adjustDate: (NSCalendarDate **) date
|
||||
withTimeString: (NSString *) timeString
|
||||
inContext: (WOContext *) context
|
||||
{
|
||||
unsigned _year, _month, _day, _hour, _minute;
|
||||
SOGoUserDefaults *ud;
|
||||
NSArray *_time;
|
||||
|
||||
_year = [*date yearOfCommonEra];
|
||||
_month = [*date monthOfYear];
|
||||
_day = [*date dayOfMonth];
|
||||
_time = [timeString componentsSeparatedByString: @":"];
|
||||
_hour = [[_time objectAtIndex: 0] intValue];
|
||||
_minute = [[_time objectAtIndex: 1] intValue];
|
||||
|
||||
ud = [[context activeUser] userDefaults];
|
||||
*date = [NSCalendarDate dateWithYear: _year month: _month day: _day
|
||||
hour: _hour minute: _minute second: 0
|
||||
timeZone: [ud timeZone]];
|
||||
}
|
||||
|
||||
- (void) _setAttendees: (NSArray *) attendees
|
||||
{
|
||||
NSMutableArray *newAttendees;
|
||||
NSUInteger count, max;
|
||||
NSString *currentEmail;
|
||||
iCalPerson *currentAttendee;
|
||||
NSString *role, *partstat;
|
||||
NSDictionary *currentData;
|
||||
|
||||
if (attendees)
|
||||
{
|
||||
newAttendees = [NSMutableArray array];
|
||||
max = [attendees count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
currentData = [attendees objectAtIndex: count];
|
||||
currentEmail = [currentData objectForKey: @"email"];
|
||||
if ([currentEmail length] > 0)
|
||||
{
|
||||
role = [[currentData objectForKey: @"role"] uppercaseString];
|
||||
if (!role)
|
||||
role = @"REQ-PARTICIPANT";
|
||||
if ([role isEqualToString: @"NON-PARTICIPANT"])
|
||||
partstat = @"";
|
||||
else
|
||||
{
|
||||
partstat = [[currentData objectForKey: @"partstat"] uppercaseString];
|
||||
if (!partstat)
|
||||
partstat = @"NEEDS-ACTION";
|
||||
}
|
||||
currentAttendee = [self findAttendeeWithEmail: currentEmail];
|
||||
if (!currentAttendee)
|
||||
{
|
||||
currentAttendee = [iCalPerson elementWithTag: @"attendee"];
|
||||
[currentAttendee setCn: [currentData objectForKey: @"name"]];
|
||||
[currentAttendee setEmail: currentEmail];
|
||||
}
|
||||
[currentAttendee
|
||||
setRsvp: ([role isEqualToString: @"NON-PARTICIPANT"]
|
||||
? @"FALSE"
|
||||
: @"TRUE")];
|
||||
[currentAttendee setRole: role];
|
||||
[currentAttendee setPartStat: partstat];
|
||||
[newAttendees addObject: currentAttendee];
|
||||
}
|
||||
}
|
||||
[self setAttendees: newAttendees];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) _appendAttendeesToEmailAlarm: (iCalAlarm *) alarm
|
||||
{
|
||||
NSArray *attendees;
|
||||
NSMutableArray *aAttendees;
|
||||
int count, max;
|
||||
iCalPerson *currentAttendee, *aAttendee;
|
||||
|
||||
attendees = [self attendees];
|
||||
max = [attendees count];
|
||||
aAttendees = [NSMutableArray arrayWithCapacity: max];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
currentAttendee = [attendees objectAtIndex: count];
|
||||
aAttendee = [iCalPerson elementWithTag: @"attendee"];
|
||||
[aAttendee setCn: [currentAttendee cn]];
|
||||
[aAttendee setEmail: [currentAttendee rfc822Email]];
|
||||
[aAttendees addObject: aAttendee];
|
||||
}
|
||||
[alarm setAttendees: aAttendees];
|
||||
}
|
||||
|
||||
- (void) _setAlarm: (NSDictionary *) alarm
|
||||
forOwner: (NSString *) owner
|
||||
{
|
||||
iCalAlarm *anAlarm;
|
||||
NSString *reminderAction, *reminderUnit, *reminderQuantity, *reminderReference, *reminderRelation;
|
||||
BOOL reminderEmailAttendees, reminderEmailOrganizer;
|
||||
|
||||
reminderAction = [alarm objectForKey: @"action"];
|
||||
reminderUnit = [alarm objectForKey: @"unit"];
|
||||
reminderQuantity = [alarm objectForKey: @"quantity"];
|
||||
reminderReference = [alarm objectForKey: @"reference"];
|
||||
reminderRelation = [alarm objectForKey: @"relation"];
|
||||
reminderEmailAttendees = [[alarm objectForKey: @"attendees"] boolValue];
|
||||
reminderEmailOrganizer = [[alarm objectForKey: @"organizer"] boolValue];
|
||||
anAlarm = [iCalAlarm alarmForEvent: self
|
||||
owner: owner
|
||||
action: reminderAction
|
||||
unit: reminderUnit
|
||||
quantity: reminderQuantity
|
||||
reference: reminderReference
|
||||
reminderRelation: reminderRelation
|
||||
emailAttendees: reminderEmailAttendees
|
||||
emailOrganizer: reminderEmailOrganizer];
|
||||
|
||||
// If there was an unsupported alarm defined in the event, it will be deleted.
|
||||
[self removeAllAlarms];
|
||||
if (anAlarm)
|
||||
{
|
||||
[self addToAlarms: anAlarm];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see [UIxAppointmentEditor saveAction]
|
||||
*/
|
||||
- (void) setAttributes: (NSDictionary *) data
|
||||
inContext: (WOContext *) context
|
||||
{
|
||||
NSString *owner;
|
||||
id o, sendAppointmentNotifications;
|
||||
|
||||
o = [data objectForKey: @"summary"];
|
||||
if ([o isKindOfClass: [NSString class]])
|
||||
[self setSummary: o];
|
||||
|
||||
o = [data objectForKey: @"priority"];
|
||||
if ([o isKindOfClass: [NSNumber class]])
|
||||
[self setPriority: [NSString stringWithFormat: @"%d", [o intValue]]];
|
||||
|
||||
o = [data objectForKey: @"location"];
|
||||
if ([o isKindOfClass: [NSString class]])
|
||||
[self setLocation: o];
|
||||
|
||||
o = [data objectForKey: @"comment"];
|
||||
if ([o isKindOfClass: [NSString class]])
|
||||
[self setComment: [o stringByReplacingString: @"\r\n" withString: @"\n"]];
|
||||
|
||||
o = [data objectForKey: @"classification"];
|
||||
if ([o isKindOfClass: [NSString class]])
|
||||
[self setAccessClass: [o uppercaseString]];
|
||||
|
||||
o = [data objectForKey: @"status"];
|
||||
if ([o isKindOfClass: [NSString class]])
|
||||
[self setStatus: [o uppercaseString]];
|
||||
|
||||
o = [data objectForKey: @"categories"];
|
||||
if ([o isKindOfClass: [NSArray class]])
|
||||
[self setCategories: o];
|
||||
|
||||
o = [data objectForKey: @"sendAppointmentNotifications"];
|
||||
if ([o isKindOfClass: [NSNumber class]])
|
||||
{
|
||||
sendAppointmentNotifications = [self firstChildWithTag: @"X-SOGo-Send-Appointment-Notifications"];
|
||||
if (!sendAppointmentNotifications && ![o boolValue])
|
||||
[self addChild: [CardElement simpleElementWithTag: @"X-SOGo-Send-Appointment-Notifications" value: @"NO"]];
|
||||
else if (sendAppointmentNotifications && [o boolValue])
|
||||
[self removeChild: sendAppointmentNotifications];
|
||||
}
|
||||
|
||||
o = [data objectForKey: @"attendees"];
|
||||
if ([o isKindOfClass: [NSArray class]])
|
||||
[self _setAttendees: o];
|
||||
|
||||
o = [data objectForKey: @"alarm"];
|
||||
if ([o isKindOfClass: [NSDictionary class]])
|
||||
{
|
||||
owner = [data objectForKey: @"owner"];
|
||||
[self _setAlarm: o forOwner: owner];
|
||||
}
|
||||
|
||||
// Other attributes depend on the client object and therefore are set in [UIxComponentEditor setAttributes:]:
|
||||
// - organizer & "created-by"
|
||||
// - timestamps (creation/modification)
|
||||
}
|
||||
|
||||
- (BOOL) userIsAttendee: (SOGoUser *) user
|
||||
{
|
||||
NSEnumerator *attendees;
|
||||
@@ -113,10 +450,11 @@ NSNumber *iCalDistantFutureNumber = nil;
|
||||
|
||||
if ([mail length])
|
||||
return [user hasEmail: mail];
|
||||
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
/*
|
||||
- (NSArray *) attendeeUIDs
|
||||
{
|
||||
NSEnumerator *attendees;
|
||||
@@ -136,6 +474,7 @@ NSNumber *iCalDistantFutureNumber = nil;
|
||||
|
||||
return uids;
|
||||
}
|
||||
*/
|
||||
|
||||
#warning this method should be implemented in a category of iCalToDo
|
||||
- (BOOL) isStillRelevant
|
||||
@@ -152,11 +491,11 @@ NSNumber *iCalDistantFutureNumber = nil;
|
||||
|
||||
NSArray *events;
|
||||
int i, count;
|
||||
|
||||
|
||||
newCalendar = [parent mutableCopy];
|
||||
[newCalendar autorelease];
|
||||
[newCalendar setMethod: method];
|
||||
|
||||
|
||||
events = [newCalendar childrenWithTag: tag];
|
||||
count = [events count];
|
||||
if (count > 1)
|
||||
@@ -184,16 +523,17 @@ NSNumber *iCalDistantFutureNumber = nil;
|
||||
NSArray *oldAttendees;
|
||||
unsigned int count, max;
|
||||
iCalPerson *currentAttendee;
|
||||
NSString *userID;
|
||||
NSString *userID, *domain;
|
||||
|
||||
userID = [user login];
|
||||
domain = [user domain];
|
||||
oldAttendees = [self attendees];
|
||||
max = [oldAttendees count];
|
||||
newAttendees = [NSMutableArray arrayWithCapacity: max];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
currentAttendee = [oldAttendees objectAtIndex: count];
|
||||
if (![[currentAttendee uid] isEqualToString: userID])
|
||||
if (![[currentAttendee uidInDomain: domain] isEqualToString: userID])
|
||||
[newAttendees addObject: currentAttendee];
|
||||
}
|
||||
|
||||
@@ -249,19 +589,39 @@ NSNumber *iCalDistantFutureNumber = nil;
|
||||
return priorityNumber;
|
||||
}
|
||||
|
||||
- (NSString *) createdBy
|
||||
- (NSDictionary *) createdBy
|
||||
{
|
||||
NSString *created_by;
|
||||
NSString *created_by, *created_by_name, *login;
|
||||
NSDictionary *createdByData;
|
||||
SOGoUser *user;
|
||||
|
||||
created_by_name = nil;
|
||||
createdByData = nil;
|
||||
created_by = [[self firstChildWithTag: @"X-SOGo-Component-Created-By"] flattenedValuesForKey: @""];
|
||||
|
||||
// We consider "SENT-BY" in case our custom header isn't found
|
||||
|
||||
if (![created_by length])
|
||||
{
|
||||
// We consider "SENT-BY" in case our custom header isn't found
|
||||
created_by = [[self organizer] sentBy];
|
||||
}
|
||||
|
||||
return created_by;
|
||||
if ([created_by length])
|
||||
{
|
||||
login = [[SOGoUserManager sharedUserManager] getUIDForEmail: created_by];
|
||||
if (login)
|
||||
{
|
||||
user = [SOGoUser userWithLogin: login];
|
||||
if (user)
|
||||
created_by_name = [user cn];
|
||||
}
|
||||
|
||||
createdByData = [NSDictionary dictionaryWithObjectsAndKeys: created_by, @"email",
|
||||
created_by_name, @"name",
|
||||
nil];
|
||||
}
|
||||
|
||||
return createdByData;
|
||||
;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -348,20 +708,20 @@ NSNumber *iCalDistantFutureNumber = nil;
|
||||
NSCalendarDate *start, *end;
|
||||
NGCalendarDateRange *range;
|
||||
NSMutableArray *alarms;
|
||||
|
||||
|
||||
alarms = [NSMutableArray array];
|
||||
start = [NSCalendarDate date];
|
||||
end = [start addYear:1 month:0 day:0 hour:0 minute:0 second:0];
|
||||
range = [NGCalendarDateRange calendarDateRangeWithStartDate: start
|
||||
endDate: end];
|
||||
|
||||
|
||||
// Always check if container is defined. If not, that means this method
|
||||
// call was reentrant.
|
||||
if (theContainer)
|
||||
{
|
||||
NSTimeInterval now;
|
||||
int i, v, delta, c_startdate, c_nextalarm;
|
||||
|
||||
|
||||
//
|
||||
// Here is the logic:
|
||||
//
|
||||
@@ -376,12 +736,12 @@ NSNumber *iCalDistantFutureNumber = nil;
|
||||
// If we don't have a match, we pick the event for which its trigger is the closest to the c_nextalarm.
|
||||
//
|
||||
nextAlarmDate = nil;
|
||||
|
||||
|
||||
[theContainer flattenCycleRecord: (id)row
|
||||
forRange: range
|
||||
intoArray: alarms
|
||||
withCalendar: [self parent]];
|
||||
|
||||
|
||||
// We pickup the closest one from now. We remove the actual reminder (ie., 15 mins before - plus 1 minute for roundups)
|
||||
// so we don't pickup the same alarm over and over. This could happen if our alarm popups and the user clicks on "Cancel".
|
||||
// In case of a repetitive event, we want to pickup the next one (next day for example), and not the one that has just
|
||||
@@ -393,11 +753,11 @@ NSNumber *iCalDistantFutureNumber = nil;
|
||||
c_startdate = [[[alarms objectAtIndex: i] objectForKey: @"c_startdate"] intValue];
|
||||
c_nextalarm = [[[alarms objectAtIndex: i] objectForKey: @"c_nextalarm"] intValue];
|
||||
delta = (c_startdate - now - (c_startdate - c_nextalarm)) - 60;
|
||||
|
||||
|
||||
// If value is not initialized, we grab it right away
|
||||
if (!v && delta > 0)
|
||||
v = delta;
|
||||
|
||||
|
||||
// If we found a smaller delta than before, use it.
|
||||
if (v > 0 && delta > 0 && delta <= v)
|
||||
{
|
||||
@@ -408,10 +768,10 @@ NSNumber *iCalDistantFutureNumber = nil;
|
||||
o = [[self parent] eventWithRecurrenceID: [[alarms objectAtIndex: i] objectForKey: @"c_recurrence_id"]];
|
||||
else
|
||||
o = [[self parent] todoWithRecurrenceID: [[alarms objectAtIndex: i] objectForKey: @"c_recurrence_id"]];
|
||||
|
||||
|
||||
if (!o)
|
||||
o = self;
|
||||
|
||||
|
||||
if ([[o alarms] count])
|
||||
{
|
||||
anAlarm = [self firstDisplayOrAudioAlarm];
|
||||
@@ -421,7 +781,7 @@ NSNumber *iCalDistantFutureNumber = nil;
|
||||
if (!webstatus
|
||||
|| ([webstatus caseInsensitiveCompare: @"TRIGGERED"]
|
||||
!= NSOrderedSame))
|
||||
|
||||
|
||||
v = delta;
|
||||
nextAlarmDate = [NSDate dateWithTimeIntervalSince1970: [[[alarms objectAtIndex: i] objectForKey: @"c_nextalarm"] intValue]];
|
||||
}
|
||||
@@ -435,7 +795,7 @@ NSNumber *iCalDistantFutureNumber = nil;
|
||||
} // if (theContainer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ([nextAlarmDate isNotNull])
|
||||
[row setObject: [NSNumber numberWithInt: [nextAlarmDate timeIntervalSince1970]]
|
||||
forKey: @"c_nextalarm"];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* iCalEvent+SOGo.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2014 Inverse inc.
|
||||
* Copyright (C) 2007-2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -28,7 +28,7 @@
|
||||
@interface iCalEvent (SOGoExtensions)
|
||||
|
||||
- (BOOL) isStillRelevant;
|
||||
- (unsigned int) occurenceInterval;
|
||||
- (NSTimeInterval) occurenceInterval;
|
||||
- (void) updateRecurrenceRulesUntilDate: (NSCalendarDate *) previousEndDate;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* iCalEvent+SOGo.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2014 Inverse inc.
|
||||
* Copyright (C) 2007-2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -22,12 +22,14 @@
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSEnumerator.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import <NGExtensions/NGCalendarDateRange.h>
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import <NGCards/iCalCalendar.h>
|
||||
#import <NGCards/iCalDateTime.h>
|
||||
#import <NGCards/iCalTimeZone.h>
|
||||
#import <NGCards/iCalEvent.h>
|
||||
@@ -36,6 +38,12 @@
|
||||
#import <NGCards/iCalTrigger.h>
|
||||
#import <NGCards/NSString+NGCards.h>
|
||||
|
||||
#import <SoObjects/SOGo/SOGoUser.h>
|
||||
#import <SoObjects/SOGo/SOGoUserDefaults.h>
|
||||
#import <SoObjects/SOGo/WOContext+SOGo.h>
|
||||
|
||||
#import <SOGo/NSCalendarDate+SOGo.h>
|
||||
|
||||
#import "SOGoAppointmentFolder.h"
|
||||
#import "iCalRepeatableEntityObject+SOGo.h"
|
||||
|
||||
@@ -47,7 +55,7 @@
|
||||
{
|
||||
NSCalendarDate *now, *lastRecurrence;
|
||||
BOOL isStillRelevent;
|
||||
|
||||
|
||||
now = [NSCalendarDate calendarDate];
|
||||
|
||||
if ([self isRecurrent])
|
||||
@@ -57,7 +65,7 @@
|
||||
}
|
||||
else
|
||||
isStillRelevent = ([[self endDate] earlierDate: now] == now);
|
||||
|
||||
|
||||
return isStillRelevent;
|
||||
}
|
||||
|
||||
@@ -114,8 +122,8 @@
|
||||
row = [NSMutableDictionary dictionaryWithCapacity:8];
|
||||
|
||||
[row setObject: @"vevent" forKey: @"c_component"];
|
||||
|
||||
if ([uid isNotNull])
|
||||
|
||||
if ([uid isNotNull])
|
||||
[row setObject:uid forKey: @"c_uid"];
|
||||
else
|
||||
[self logWithFormat: @"WARNING: could not extract a uid from event!"];
|
||||
@@ -168,11 +176,11 @@
|
||||
forAllDay: isAllDay]
|
||||
forKey: @"c_enddate"];
|
||||
}
|
||||
|
||||
|
||||
if ([self isRecurrent])
|
||||
{
|
||||
NSCalendarDate *date;
|
||||
|
||||
|
||||
date = [self lastPossibleRecurrenceStartDate];
|
||||
if (!date)
|
||||
{
|
||||
@@ -194,7 +202,7 @@
|
||||
if ([status isNotNull])
|
||||
{
|
||||
int code = 1;
|
||||
|
||||
|
||||
if ([status isEqualToString: @"TENTATIVE"])
|
||||
code = 2;
|
||||
else if ([status isEqualToString: @"CANCELLED"])
|
||||
@@ -214,12 +222,12 @@
|
||||
if (organizer)
|
||||
{
|
||||
NSString *email;
|
||||
|
||||
|
||||
email = [organizer valueForKey: @"rfc822Email"];
|
||||
if (email)
|
||||
[row setObject:email forKey: @"c_orgmail"];
|
||||
}
|
||||
|
||||
|
||||
/* construct partstates */
|
||||
count = [attendees count];
|
||||
partstates = [[NSMutableString alloc] initWithCapacity:count * 2];
|
||||
@@ -227,7 +235,7 @@
|
||||
{
|
||||
iCalPerson *p;
|
||||
iCalPersonPartStat stat;
|
||||
|
||||
|
||||
p = [attendees objectAtIndex:i];
|
||||
stat = [p participationStatus];
|
||||
if (i)
|
||||
@@ -263,7 +271,7 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Shift the "until dates" of the recurrence rules of the event
|
||||
* Shift the "until dates" of the recurrence rules of the event
|
||||
* with respect to the previous end date of the event.
|
||||
* @param previousEndDate the previous end date of the event
|
||||
*/
|
||||
@@ -312,5 +320,137 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see [iCalRepeatableEntityObject+SOGo attributes]
|
||||
* @see [iCalEntityObject+SOGo attributes]
|
||||
* @see [UIxAppointmentEditor viewAction]
|
||||
*/
|
||||
- (NSDictionary *) attributesInContext: (WOContext *) context
|
||||
{
|
||||
BOOL isAllDay;
|
||||
NSCalendarDate *eventStartDate, *eventEndDate;
|
||||
NSMutableDictionary *data;
|
||||
NSTimeZone *timeZone;
|
||||
SOGoUserDefaults *ud;
|
||||
|
||||
isAllDay = [self isAllDay];
|
||||
ud = [[context activeUser] userDefaults];
|
||||
timeZone = [ud timeZone];
|
||||
eventStartDate = [self startDate];
|
||||
eventEndDate = [self endDate];
|
||||
|
||||
if (!isAllDay)
|
||||
{
|
||||
[eventStartDate setTimeZone: timeZone];
|
||||
[eventEndDate setTimeZone: timeZone];
|
||||
}
|
||||
|
||||
data = [NSMutableDictionary dictionaryWithDictionary: [super attributesInContext: context]];
|
||||
|
||||
[data setObject: [eventStartDate iso8601DateString] forKey: @"startDate"];
|
||||
[data setObject: [eventEndDate iso8601DateString] forKey: @"endDate"];
|
||||
|
||||
[data setObject: [NSNumber numberWithBool: isAllDay] forKey: @"isAllDay"];
|
||||
[data setObject: [NSNumber numberWithBool: ![self isOpaque]] forKey: @"isTransparent"];
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see [iCalRepeatableEntityObject+SOGo setAttributes:inContext:]
|
||||
* @see [iCalEntityObject+SOGo setAttributes:inContext:]
|
||||
* @see [UIxAppointmentEditor saveAction]
|
||||
*/
|
||||
- (void) setAttributes: (NSDictionary *) data
|
||||
inContext: (WOContext *) context
|
||||
{
|
||||
NSCalendarDate *aptStartDate, *aptEndDate, *allDayStartDate;
|
||||
NSTimeZone *timeZone;
|
||||
NSInteger offset, nbrDays;
|
||||
iCalDateTime *startDate;
|
||||
iCalTimeZone *tz;
|
||||
SOGoUserDefaults *ud;
|
||||
BOOL isAllDay;
|
||||
id o;
|
||||
|
||||
[super setAttributes: data inContext: context];
|
||||
|
||||
aptStartDate = aptEndDate = nil;
|
||||
|
||||
// Handle start/end dates
|
||||
o = [data objectForKey: @"startDate"];
|
||||
if ([o isKindOfClass: [NSString class]] && [o length])
|
||||
aptStartDate = [self dateFromString: o inContext: context];
|
||||
|
||||
o = [data objectForKey: @"startTime"];
|
||||
if ([o isKindOfClass: [NSString class]] && [o length])
|
||||
[self adjustDate: &aptStartDate withTimeString: o inContext: context];
|
||||
|
||||
o = [data objectForKey: @"endDate"];
|
||||
if ([o isKindOfClass: [NSString class]] && [o length])
|
||||
aptEndDate = [self dateFromString: o inContext: context];
|
||||
|
||||
o = [data objectForKey: @"endTime"];
|
||||
if ([o isKindOfClass: [NSString class]] && [o length])
|
||||
[self adjustDate: &aptEndDate withTimeString: o inContext: context];
|
||||
|
||||
o = [data objectForKey: @"isTransparent"];
|
||||
if ([o isKindOfClass: [NSNumber class]])
|
||||
[self setTransparency: ([o boolValue]? @"TRANSPARENT" : @"OPAQUE")];
|
||||
|
||||
isAllDay = [[data objectForKey: @"isAllDay"] boolValue];
|
||||
|
||||
if (aptStartDate && aptEndDate)
|
||||
{
|
||||
if (isAllDay)
|
||||
{
|
||||
nbrDays = ((float) abs ([aptEndDate timeIntervalSinceDate: aptStartDate]) / 86400) + 1;
|
||||
// Convert all-day start date to GMT (floating date)
|
||||
ud = [[context activeUser] userDefaults];
|
||||
timeZone = [ud timeZone];
|
||||
offset = [timeZone secondsFromGMTForDate: aptStartDate];
|
||||
allDayStartDate = [aptStartDate dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: offset];
|
||||
[self setAllDayWithStartDate: allDayStartDate
|
||||
duration: nbrDays];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setStartDate: aptStartDate];
|
||||
[self setEndDate: aptEndDate];
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAllDay)
|
||||
{
|
||||
// Make sure there's a vTimeZone associated to the event unless it
|
||||
// is an all-day event.
|
||||
startDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"];
|
||||
if (![startDate timeZone])
|
||||
{
|
||||
ud = [[context activeUser] userDefaults];
|
||||
tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]];
|
||||
if ([[self parent] addTimeZone: tz])
|
||||
{
|
||||
[startDate setTimeZone: tz];
|
||||
[(iCalDateTime *)[self uniqueChildWithTag: @"dtend"] setTimeZone: tz];
|
||||
}
|
||||
}
|
||||
}
|
||||
else // if (![[self clientObject] isNew])
|
||||
{
|
||||
// Remove the vTimeZone when dealing with an all-day event.
|
||||
startDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"];
|
||||
tz = [startDate timeZone];
|
||||
if (tz)
|
||||
{
|
||||
[startDate setTimeZone: nil];
|
||||
[(iCalDateTime *)[self uniqueChildWithTag: @"dtend"] setTimeZone: nil];
|
||||
[[self parent] removeChild: tz];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,9 +1,6 @@
|
||||
/* iCalPerson+SOGo.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2012 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
* Copyright (C) 2007-2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -32,6 +29,8 @@
|
||||
|
||||
- (NSString *) mailAddress;
|
||||
- (NSString *) uid;
|
||||
- (NSString *) uidInDomain: (NSString *) domain;
|
||||
- (NSString *) uidInContext: (WOContext *) context;
|
||||
- (NSString *) contactIDInContext: (WOContext *) context;
|
||||
- (BOOL) hasSentBy;
|
||||
- (NSString *) sentBy;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
/* iCalPerson+SOGo.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2012 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
* Copyright (C) 2007-2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -67,6 +64,31 @@ static SOGoUserManager *um = nil;
|
||||
return [um getUIDForEmail: [self rfc822Email]];
|
||||
}
|
||||
|
||||
- (NSString *) uidInContext: (WOContext *) context
|
||||
{
|
||||
NSString *domain;
|
||||
|
||||
domain = [[context activeUser] domain];
|
||||
|
||||
return [self uidInDomain: domain];
|
||||
}
|
||||
|
||||
- (NSString *) uidInDomain: (NSString *) domain
|
||||
{
|
||||
NSDictionary *contact;
|
||||
NSString *uid;
|
||||
|
||||
if (!um)
|
||||
um = [SOGoUserManager sharedUserManager];
|
||||
|
||||
uid = nil;
|
||||
contact = [um contactInfosForUserWithUIDorEmail: [self rfc822Email] inDomain: domain];
|
||||
if (contact)
|
||||
uid = [contact valueForKey: @"c_uid"];
|
||||
|
||||
return uid;
|
||||
}
|
||||
|
||||
- (NSString *) contactIDInContext: (WOContext *) context
|
||||
{
|
||||
NSString *domain, *uid;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* iCalRepeatableEntityObject+SOGo.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2014 Inverse inc.
|
||||
* Copyright (C) 2007-2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* iCalRepeatableEntityObject+SOGo.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2014 Inverse inc.
|
||||
* Copyright (C) 2007-2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -25,8 +25,10 @@
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import <NGCards/iCalByDayMask.h>
|
||||
#import <NGCards/iCalDateTime.h>
|
||||
#import <NGCards/iCalEvent.h>
|
||||
#import <NGCards/iCalPerson.h>
|
||||
#import <NGCards/iCalRecurrenceRule.h>
|
||||
#import <NGCards/iCalRecurrenceCalculator.h>
|
||||
#import <NGCards/iCalTimeZone.h>
|
||||
@@ -35,6 +37,12 @@
|
||||
|
||||
#import <NGExtensions/NGCalendarDateRange.h>
|
||||
|
||||
#import <SoObjects/SOGo/SOGoUser.h>
|
||||
#import <SoObjects/SOGo/SOGoUserDefaults.h>
|
||||
#import <SoObjects/SOGo/WOContext+SOGo.h>
|
||||
|
||||
#import <SOGo/NSCalendarDate+SOGo.h>
|
||||
|
||||
#import "iCalRepeatableEntityObject+SOGo.h"
|
||||
|
||||
@implementation iCalRepeatableEntityObject (SOGoExtensions)
|
||||
@@ -69,6 +77,152 @@
|
||||
return ma;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see [iCalEntityObject+SOGo attributes]
|
||||
* @see [iCalEvent+SOGo attributes]
|
||||
* @see [UIxAppointmentEditor viewAction]
|
||||
*/
|
||||
- (NSDictionary *) attributesInContext: (WOContext *) context
|
||||
{
|
||||
NSArray *rules;
|
||||
NSCalendarDate *untilDate;
|
||||
NSMutableDictionary *data, *repeat;
|
||||
NSString *frequency;
|
||||
NSTimeZone *timeZone;
|
||||
SOGoUserDefaults *ud;
|
||||
iCalRecurrenceRule *rule;
|
||||
|
||||
data = [NSMutableDictionary dictionaryWithDictionary: [super attributesInContext: context]];
|
||||
|
||||
rules = [self recurrenceRules];
|
||||
if ([rules count] > 0)
|
||||
{
|
||||
rule = [rules objectAtIndex: 0];
|
||||
frequency = [rule frequencyForValue: [rule frequency]];
|
||||
|
||||
repeat = [NSMutableDictionary dictionary];
|
||||
[repeat setObject: [frequency lowercaseString] forKey: @"frequency"];
|
||||
[repeat setObject: [NSNumber numberWithInt: [rule repeatInterval]] forKey: @"interval"];
|
||||
if ([rule repeatCount])
|
||||
[repeat setObject: [NSNumber numberWithInt: [rule repeatCount]] forKey: @"count"];
|
||||
if ((untilDate = [rule untilDate]))
|
||||
{
|
||||
ud = [[context activeUser] userDefaults];
|
||||
timeZone = [ud timeZone];
|
||||
[untilDate setTimeZone: timeZone];
|
||||
[repeat setObject: [untilDate iso8601DateString] forKey: @"until"];
|
||||
}
|
||||
if ([[rule byDay] length])
|
||||
[repeat setObject: [[rule byDayMask] asRuleArray] forKey: @"days"];
|
||||
if ([[rule byMonthDay] count])
|
||||
[repeat setObject: [rule byMonthDay] forKey: @"monthdays"];
|
||||
if ([[rule byMonth] count])
|
||||
[repeat setObject: [rule byMonth] forKey: @"months"];
|
||||
[data setObject: repeat forKey: @"repeat"];
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see [iCalEntityObject+SOGo setAttributes:inContext:]
|
||||
* @see [UIxAppointmentEditor saveAction]
|
||||
*/
|
||||
- (void) setAttributes: (NSDictionary *) data
|
||||
inContext: (WOContext *) context
|
||||
{
|
||||
iCalRecurrenceRule *rule;
|
||||
iCalRecurrenceFrequency frequency;
|
||||
NSCalendarDate *date;
|
||||
SOGoUserDefaults *ud;
|
||||
id repeat, o;
|
||||
|
||||
[super setAttributes: data inContext: context];
|
||||
|
||||
if ([self recurrenceId])
|
||||
// Occurrence of a recurrent object can't have a recurrence rule
|
||||
return;
|
||||
|
||||
repeat = [data objectForKey: @"repeat"];
|
||||
if ([repeat isKindOfClass: [NSDictionary class]])
|
||||
{
|
||||
rule = [iCalRecurrenceRule new];
|
||||
[rule setInterval: @"1"];
|
||||
|
||||
frequency = NSNotFound;
|
||||
o = [repeat objectForKey: @"frequency"];
|
||||
if ([o isKindOfClass: [NSString class]])
|
||||
{
|
||||
frequency = [rule valueForFrequency: o];
|
||||
if ((NSUInteger) frequency == NSNotFound)
|
||||
{
|
||||
if ([o caseInsensitiveCompare: @"BI-WEEKLY"] == NSOrderedSame)
|
||||
{
|
||||
frequency = iCalRecurrenceFrequenceWeekly;
|
||||
[rule setInterval: @"2"];
|
||||
}
|
||||
else if ([o caseInsensitiveCompare: @"EVERY WEEKDAY"] == NSOrderedSame)
|
||||
{
|
||||
frequency = iCalRecurrenceFrequenceDaily;
|
||||
[rule setByDayMask: [iCalByDayMask byDayMaskWithWeekDays]];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
o = [repeat objectForKey: @"interval"];
|
||||
if ([o isKindOfClass: [NSNumber class]])
|
||||
[rule setInterval: [NSString stringWithFormat: @"%i", [o intValue]]];
|
||||
|
||||
o = [repeat objectForKey: @"count"];
|
||||
if ([o isKindOfClass: [NSNumber class]])
|
||||
[rule setRepeatCount: [o intValue]];
|
||||
|
||||
o = [repeat objectForKey: @"until"];
|
||||
if ([o isKindOfClass: [NSString class]])
|
||||
{
|
||||
date = [NSCalendarDate dateWithString: o
|
||||
calendarFormat: @"%Y-%m-%d"];
|
||||
if (date)
|
||||
{
|
||||
// Adjust timezone
|
||||
ud = [[context activeUser] userDefaults];
|
||||
date = [NSCalendarDate dateWithYear: [date yearOfCommonEra]
|
||||
month: [date monthOfYear]
|
||||
day: [date dayOfMonth]
|
||||
hour: 0 minute: 0 second: 0
|
||||
timeZone: [ud timeZone]];
|
||||
[rule setUntilDate: date];
|
||||
}
|
||||
}
|
||||
|
||||
o = [repeat objectForKey: @"days"];
|
||||
if ([o isKindOfClass: [NSArray class]])
|
||||
[rule setByDayMask: [iCalByDayMask byDayMaskWithDaysAndOccurrences: o]];
|
||||
|
||||
o = [repeat objectForKey: @"monthdays"];
|
||||
if ([o isKindOfClass: [NSArray class]])
|
||||
[rule setValues: o atIndex: 0 forKey: @"bymonthday"];
|
||||
|
||||
o = [repeat objectForKey: @"months"];
|
||||
if ([o isKindOfClass: [NSArray class]])
|
||||
[rule setValues: o atIndex: 0 forKey: @"bymonth"];
|
||||
}
|
||||
|
||||
if ((NSUInteger) frequency != NSNotFound)
|
||||
{
|
||||
[rule setFrequency: frequency];
|
||||
[self setRecurrenceRules: [NSArray arrayWithObject: rule]];
|
||||
}
|
||||
|
||||
[rule release];
|
||||
}
|
||||
}
|
||||
else if ([self hasRecurrenceRules])
|
||||
{
|
||||
[self removeAllRecurrenceRules];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *) cycleInfo
|
||||
{
|
||||
NSArray *rules;
|
||||
@@ -83,7 +237,7 @@
|
||||
rules = [self _indexedRules: [self recurrenceRules]];
|
||||
if (rules)
|
||||
[cycleInfo setObject: rules forKey: @"rules"];
|
||||
|
||||
|
||||
rules = [self _indexedRules: [self exceptionRules]];
|
||||
if (rules)
|
||||
[cycleInfo setObject: rules forKey: @"exRules"];
|
||||
@@ -122,7 +276,7 @@
|
||||
firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: start
|
||||
endDate: end];
|
||||
}
|
||||
|
||||
|
||||
return firstRange;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,10 +27,19 @@
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import <NGCards/iCalAlarm.h>
|
||||
#import <NGCards/iCalPerson.h>
|
||||
#import <NGCards/iCalTrigger.h>
|
||||
#import <NGCards/NSString+NGCards.h>
|
||||
#import <NGCards/iCalAlarm.h>
|
||||
#import <NGCards/iCalCalendar.h>
|
||||
#import <NGCards/iCalDateTime.h>
|
||||
#import <NGCards/iCalPerson.h>
|
||||
#import <NGCards/iCalTimeZone.h>
|
||||
#import <NGCards/iCalTrigger.h>
|
||||
|
||||
#import <SoObjects/SOGo/WOContext+SOGo.h>
|
||||
|
||||
#import <SOGo/NSCalendarDate+SOGo.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
|
||||
#import "iCalRepeatableEntityObject+SOGo.h"
|
||||
|
||||
@@ -38,6 +47,179 @@
|
||||
|
||||
@implementation iCalToDo (SOGoExtensions)
|
||||
|
||||
- (NSDictionary *) attributesInContext: (WOContext *) context
|
||||
{
|
||||
BOOL isAllDayStartDate, isAllDayDueDate;
|
||||
NSCalendarDate *startDate, *dueDate, *completedDate;
|
||||
NSMutableDictionary *data;
|
||||
NSTimeZone *timeZone;
|
||||
SOGoUserDefaults *ud;
|
||||
|
||||
ud = [[context activeUser] userDefaults];
|
||||
timeZone = [ud timeZone];
|
||||
|
||||
startDate = [self startDate];
|
||||
isAllDayStartDate = [(iCalDateTime *) [self uniqueChildWithTag: @"dtstart"] isAllDay];
|
||||
if (!isAllDayStartDate)
|
||||
[startDate setTimeZone: timeZone];
|
||||
|
||||
dueDate = [self due];
|
||||
isAllDayDueDate = [(iCalDateTime *) [self uniqueChildWithTag: @"due"] isAllDay];
|
||||
if (!isAllDayDueDate)
|
||||
[dueDate setTimeZone: timeZone];
|
||||
|
||||
completedDate = [self completed];
|
||||
[completedDate setTimeZone: timeZone];
|
||||
|
||||
data = [NSMutableDictionary dictionaryWithDictionary: [super attributesInContext: context]];
|
||||
|
||||
if (startDate)
|
||||
[data setObject: [startDate iso8601DateString] forKey: @"startDate"];
|
||||
if (dueDate)
|
||||
[data setObject: [dueDate iso8601DateString] forKey: @"dueDate"];
|
||||
if (completedDate)
|
||||
[data setObject: [completedDate iso8601DateString] forKey: @"completedDate"];
|
||||
|
||||
if ([[self percentComplete] length])
|
||||
[data setObject: [NSNumber numberWithInt: [[self percentComplete] intValue]] forKey: @"percentComplete"];
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see [iCalRepeatableEntityObject+SOGo setAttributes:inContext:]
|
||||
* @see [iCalEntityObject+SOGo setAttributes:inContext:]
|
||||
* @see [UIxAppointmentEditor saveAction]
|
||||
*/
|
||||
- (void) setAttributes: (NSDictionary *) data
|
||||
inContext: (WOContext *) context
|
||||
{
|
||||
BOOL isAllDayStartDate, isAllDayDueDate;
|
||||
NSCalendarDate *startDate, *dueDate, *completedDate;
|
||||
NSInteger percent;
|
||||
SOGoUserDefaults *ud;
|
||||
iCalDateTime *todoStartDate, *todoDueDate;
|
||||
iCalTimeZone *tz;
|
||||
id o;
|
||||
|
||||
[super setAttributes: data inContext: context];
|
||||
|
||||
startDate = dueDate = completedDate = nil;
|
||||
|
||||
// Handle start date
|
||||
isAllDayStartDate = YES;
|
||||
o = [data objectForKey: @"startDate"];
|
||||
if ([o isKindOfClass: [NSString class]] && [o length])
|
||||
startDate = [self dateFromString: o inContext: context];
|
||||
|
||||
o = [data objectForKey: @"startTime"];
|
||||
if ([o isKindOfClass: [NSString class]] && [o length])
|
||||
{
|
||||
isAllDayStartDate = NO;
|
||||
[self adjustDate: &startDate withTimeString: o inContext: context];
|
||||
}
|
||||
|
||||
if (startDate)
|
||||
[self setStartDate: startDate];
|
||||
else
|
||||
{
|
||||
[self setStartDate: nil];
|
||||
[self removeAllAlarms];
|
||||
}
|
||||
|
||||
// Handle due date
|
||||
isAllDayDueDate = YES;
|
||||
o = [data objectForKey: @"dueDate"];
|
||||
if ([o isKindOfClass: [NSString class]] && [o length])
|
||||
dueDate = [self dateFromString: o inContext: context];
|
||||
|
||||
o = [data objectForKey: @"dueTime"];
|
||||
if ([o isKindOfClass: [NSString class]] && [o length])
|
||||
{
|
||||
isAllDayDueDate = NO;
|
||||
[self adjustDate: &dueDate withTimeString: o inContext: context];
|
||||
}
|
||||
|
||||
if (dueDate)
|
||||
if (isAllDayDueDate)
|
||||
[self setAllDayDue: dueDate];
|
||||
else
|
||||
[self setDue: dueDate];
|
||||
else
|
||||
[self setDue: nil];
|
||||
|
||||
// Handle time zone
|
||||
todoStartDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"];
|
||||
todoDueDate = (iCalDateTime *)[self uniqueChildWithTag: @"due"];
|
||||
tz = [todoStartDate timeZone];
|
||||
if (!tz)
|
||||
tz = [todoDueDate timeZone];
|
||||
|
||||
if (isAllDayStartDate && isAllDayDueDate)
|
||||
{
|
||||
if (tz)
|
||||
[[self parent] removeChild: tz];
|
||||
[todoStartDate setTimeZone: nil];
|
||||
[todoDueDate setTimeZone: nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!tz)
|
||||
{
|
||||
ud = [[context activeUser] userDefaults];
|
||||
tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]];
|
||||
}
|
||||
if (tz)
|
||||
{
|
||||
[[self parent] addTimeZone: tz];
|
||||
if (todoStartDate)
|
||||
{
|
||||
if (isAllDayStartDate)
|
||||
[todoStartDate setTimeZone: nil];
|
||||
else if (![todoStartDate timeZone])
|
||||
[todoStartDate setTimeZone: tz];
|
||||
}
|
||||
if (todoDueDate)
|
||||
{
|
||||
if (isAllDayDueDate)
|
||||
[todoDueDate setTimeZone: nil];
|
||||
else if (![todoDueDate timeZone])
|
||||
[todoDueDate setTimeZone: tz];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle completed date
|
||||
o = [data objectForKey: @"completedDate"];
|
||||
if ([o isKindOfClass: [NSString class]] && [o length])
|
||||
completedDate = [self dateFromString: o inContext: context];
|
||||
|
||||
o = [data objectForKey: @"completedTime"];
|
||||
if ([o isKindOfClass: [NSString class]] && [o length])
|
||||
[self adjustDate: &completedDate withTimeString: o inContext: context];
|
||||
|
||||
o = [self status];
|
||||
if ([o length])
|
||||
{
|
||||
if ([o isEqualToString: @"COMPLETED"] && completedDate)
|
||||
// As specified in RFC5545, the COMPLETED property must use UTC time
|
||||
[self setCompleted: completedDate];
|
||||
else
|
||||
[self setCompleted: nil];
|
||||
}
|
||||
|
||||
// Percent complete
|
||||
o = [data objectForKey: @"percentComplete"];
|
||||
if ([o isKindOfClass: [NSNumber class]])
|
||||
{
|
||||
percent = [o intValue];
|
||||
if (percent >= 0 && percent <= 100)
|
||||
[self setPercentComplete: [NSString stringWithFormat: @"%i", percent]];
|
||||
}
|
||||
else
|
||||
[self setPercentComplete: @""];
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent
|
||||
container: (id) theContainer
|
||||
{
|
||||
@@ -53,7 +235,7 @@
|
||||
iCalAccessClass accessClass;
|
||||
|
||||
/* extract values */
|
||||
|
||||
|
||||
startDate = [self startDate];
|
||||
dueDate = [self due];
|
||||
uid = [self uid];
|
||||
@@ -78,7 +260,7 @@
|
||||
|
||||
[row setObject: @"vtodo" forKey: @"c_component"];
|
||||
|
||||
if ([uid isNotNull])
|
||||
if ([uid isNotNull])
|
||||
[row setObject:uid forKey: @"c_uid"];
|
||||
else
|
||||
[self logWithFormat: @"WARNING: could not extract a uid from event!"];
|
||||
@@ -96,7 +278,7 @@
|
||||
[row setObject: title forKey: @"c_title"];
|
||||
if ([location isNotNull]) [row setObject: location forKey: @"c_location"];
|
||||
if ([sequence isNotNull]) [row setObject: sequence forKey: @"c_sequence"];
|
||||
|
||||
|
||||
if ([startDate isNotNull])
|
||||
date = [self quickRecordDateAsNumber: startDate
|
||||
withOffset: 0 forAllDay: NO];
|
||||
@@ -146,12 +328,12 @@
|
||||
if (organizer)
|
||||
{
|
||||
NSString *email;
|
||||
|
||||
|
||||
email = [organizer valueForKey: @"rfc822Email"];
|
||||
if (email)
|
||||
[row setObject:email forKey: @"c_orgmail"];
|
||||
}
|
||||
|
||||
|
||||
/* construct partstates */
|
||||
count = [attendees count];
|
||||
partstates = [[NSMutableString alloc] initWithCapacity:count * 2];
|
||||
@@ -159,7 +341,7 @@
|
||||
{
|
||||
iCalPerson *p;
|
||||
iCalPersonPartStat stat;
|
||||
|
||||
|
||||
p = [attendees objectAtIndex:i];
|
||||
stat = [p participationStatus];
|
||||
if(i != 0)
|
||||
@@ -171,7 +353,7 @@
|
||||
|
||||
/* handle alarms */
|
||||
[self updateNextAlarmDateInRow: row forContainer: theContainer];
|
||||
|
||||
|
||||
categories = [self categories];
|
||||
if ([categories count] > 0)
|
||||
[row setObject: [categories componentsJoinedByString: @","]
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
"Personal Address Book" = "個人通訊錄";
|
||||
"Collected Address Book" = "公用通訊錄";
|
||||
@@ -27,7 +27,7 @@ Contacts_OBJC_FILES = \
|
||||
Contacts_RESOURCE_FILES += \
|
||||
product.plist \
|
||||
|
||||
Contacts_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
|
||||
Contacts_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
|
||||
|
||||
Contacts_LOCALIZED_RESOURCE_FILES = Localizable.strings
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* NGVCard+SOGo.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2009-2014 Inverse inc.
|
||||
* Copyright (C) 2009-2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -187,29 +187,59 @@ convention:
|
||||
return element;
|
||||
}
|
||||
|
||||
- (void) addElementWithTag: (NSString *) elementTag
|
||||
ofType: (NSString *) type
|
||||
withValue: (id) value
|
||||
{
|
||||
NSArray *allValues;
|
||||
NSEnumerator *list;
|
||||
CardElement *element;
|
||||
|
||||
// value is either an array or a string
|
||||
if ([value isKindOfClass: [NSString class]])
|
||||
allValues = [NSArray arrayWithObject: value];
|
||||
else
|
||||
allValues = value;
|
||||
|
||||
// Add all values as separate elements
|
||||
list = [allValues objectEnumerator];
|
||||
while ((value = [list nextObject]))
|
||||
{
|
||||
element = [CardElement simpleElementWithTag: elementTag
|
||||
singleType: type
|
||||
value: value];
|
||||
[self addChild: element];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) _setPhoneValues: (NSDictionary *) ldifRecord
|
||||
{
|
||||
CardElement *phone;
|
||||
|
||||
phone = [self elementWithTag: @"tel" ofType: @"work"];
|
||||
[phone setSingleValue: [ldifRecord objectForKey: @"telephonenumber"] forKey: @""];
|
||||
phone = [self elementWithTag: @"tel" ofType: @"home"];
|
||||
[phone setSingleValue: [ldifRecord objectForKey: @"homephone"] forKey: @""];
|
||||
phone = [self elementWithTag: @"tel" ofType: @"cell"];
|
||||
[phone setSingleValue: [ldifRecord objectForKey: @"mobile"] forKey: @""];
|
||||
phone = [self elementWithTag: @"tel" ofType: @"fax"];
|
||||
[phone setSingleValue: [ldifRecord objectForKey: @"facsimiletelephonenumber"]
|
||||
forKey: @""];
|
||||
phone = [self elementWithTag: @"tel" ofType: @"pager"];
|
||||
[phone setSingleValue: [ldifRecord objectForKey: @"pager"] forKey: @""];
|
||||
[self addElementWithTag: @"tel"
|
||||
ofType: @"work"
|
||||
withValue: [ldifRecord objectForKey: @"telephonenumber"]];
|
||||
[self addElementWithTag: @"tel"
|
||||
ofType: @"home"
|
||||
withValue: [ldifRecord objectForKey: @"homephone"]];
|
||||
[self addElementWithTag: @"tel"
|
||||
ofType: @"cell"
|
||||
withValue: [ldifRecord objectForKey: @"mobile"]];
|
||||
[self addElementWithTag: @"tel"
|
||||
ofType: @"fax"
|
||||
withValue: [ldifRecord objectForKey: @"facsimiletelephonenumber"]];
|
||||
[self addElementWithTag: @"tel"
|
||||
ofType: @"pager"
|
||||
withValue: [ldifRecord objectForKey: @"pager"]];
|
||||
}
|
||||
|
||||
- (void) _setEmails: (NSDictionary *) ldifRecord
|
||||
{
|
||||
CardElement *mail, *homeMail;
|
||||
CardElement *homeMail;
|
||||
|
||||
mail = [self elementWithTag: @"email" ofType: @"work"];
|
||||
[mail setSingleValue: [ldifRecord objectForKey: @"mail"] forKey: @""];
|
||||
// Emails from the configured mail fields of the source have already been extracted in
|
||||
// [LDAPSource _fillEmailsOfEntry:intoLDIFRecord:]
|
||||
[self addElementWithTag: @"email"
|
||||
ofType: @"work"
|
||||
withValue: [ldifRecord objectForKey: @"c_emails"]];
|
||||
homeMail = [self elementWithTag: @"email" ofType: @"home"];
|
||||
[homeMail setSingleValue: [ldifRecord objectForKey: @"mozillasecondemail"] forKey: @""];
|
||||
[[self uniqueChildWithTag: @"x-mozilla-html"]
|
||||
|
||||
@@ -209,10 +209,26 @@ static NSArray *folderListingFields = nil;
|
||||
return qualifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize keys of dictionary representing a contact.
|
||||
* @param contactRecord a dictionary with pairs from the quick table
|
||||
* @see [UIxContactView dataAction]
|
||||
*/
|
||||
- (void) fixupContactRecord: (NSMutableDictionary *) contactRecord
|
||||
{
|
||||
NSString *data;
|
||||
|
||||
// c_categories => categories
|
||||
data = [contactRecord objectForKey: @"c_categories"];
|
||||
if ([data length])
|
||||
[contactRecord setObject: data forKey: @"categories"];
|
||||
|
||||
// c_name => id
|
||||
data = [contactRecord objectForKey: @"c_name"];
|
||||
if ([data length])
|
||||
[contactRecord setObject: data forKey: @"id"];
|
||||
|
||||
// c_cn => fn
|
||||
data = [contactRecord objectForKey: @"c_cn"];
|
||||
if (![data length])
|
||||
{
|
||||
@@ -224,17 +240,34 @@ static NSArray *folderListingFields = nil;
|
||||
else
|
||||
{
|
||||
data = [contactRecord objectForKey: @"c_o"];
|
||||
[contactRecord setObject: data forKey: @"c_cn"];
|
||||
[contactRecord setObject: data forKey: @"c_cn"];
|
||||
}
|
||||
}
|
||||
|
||||
if (![contactRecord objectForKey: @"c_mail"])
|
||||
[contactRecord setObject: @"" forKey: @"c_mail"];
|
||||
// c_screenname => X-AIM
|
||||
if (![contactRecord objectForKey: @"c_screenname"])
|
||||
[contactRecord setObject: @"" forKey: @"c_screenname"];
|
||||
if (![contactRecord objectForKey: @"c_o"])
|
||||
[contactRecord setObject: @"" forKey: @"c_o"];
|
||||
if (![contactRecord objectForKey: @"c_telephonenumber"])
|
||||
|
||||
// c_mail => emails[]
|
||||
data = [contactRecord objectForKey: @"c_mail"];
|
||||
if ([data length])
|
||||
{
|
||||
NSDictionary *email;
|
||||
email = [NSDictionary dictionaryWithObjectsAndKeys: @"pref", @"type", data, @"value", nil];
|
||||
[contactRecord setObject: [NSArray arrayWithObject: email] forKey: @"emails"];
|
||||
}
|
||||
else
|
||||
[contactRecord setObject: @"" forKey: @"c_mail"];
|
||||
|
||||
// c_telephonenumber => phones
|
||||
data = [contactRecord objectForKey: @"c_telephonenumber"];
|
||||
if ([data length])
|
||||
{
|
||||
NSDictionary *phonenumber;
|
||||
phonenumber = [NSDictionary dictionaryWithObjectsAndKeys: @"pref", @"type", data, @"value", nil];
|
||||
[contactRecord setObject: [NSArray arrayWithObject: phonenumber] forKey: @"phones"];
|
||||
}
|
||||
else
|
||||
[contactRecord setObject: @"" forKey: @"c_telephonenumber"];
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
[vcard setProdID: [NSString
|
||||
stringWithFormat: @"-//Inverse inc./SOGo %@//EN",
|
||||
SOGoVersion]];
|
||||
[vcard updateFromLDIFRecord: [self simplifiedLDIFRecord]];
|
||||
[vcard updateFromLDIFRecord: [self ldifRecord]];
|
||||
|
||||
return vcard;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
- (NSDictionary *) simplifiedLDIFRecord;
|
||||
|
||||
- (NSException *) save;
|
||||
- (NSException *) delete;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SOGoContactSourceFolder.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2006-2013 Inverse inc.
|
||||
* Copyright (C) 2006-2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -217,6 +217,11 @@
|
||||
return [source removeContactEntryWithID: [ldifEntry nameInContainer]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize keys of dictionary representing a contact.
|
||||
* @param oldRecord a dictionary with pairs from the source folder (LDAP or SQL)
|
||||
* @see [SOGoContactGCSFolder _fixupContactRecord]
|
||||
*/
|
||||
- (NSDictionary *) _flattenedRecord: (NSDictionary *) oldRecord
|
||||
{
|
||||
NSMutableDictionary *newRecord;
|
||||
@@ -226,26 +231,54 @@
|
||||
newRecord = [NSMutableDictionary dictionaryWithCapacity: 8];
|
||||
[newRecord setObject: [oldRecord objectForKey: @"c_uid"]
|
||||
forKey: @"c_uid"];
|
||||
|
||||
// c_name => id
|
||||
[newRecord setObject: [oldRecord objectForKey: @"c_name"]
|
||||
forKey: @"c_name"];
|
||||
[newRecord setObject: [oldRecord objectForKey: @"c_name"]
|
||||
forKey: @"id"];
|
||||
|
||||
// displayname || c_cn => fn
|
||||
data = [oldRecord objectForKey: @"displayname"];
|
||||
if (!data)
|
||||
data = [oldRecord objectForKey: @"c_cn"];
|
||||
if (!data)
|
||||
if (data)
|
||||
[newRecord setObject: data forKey: @"fn"];
|
||||
else
|
||||
data = @"";
|
||||
[newRecord setObject: data forKey: @"c_cn"];
|
||||
|
||||
data = [oldRecord objectForKey: @"mail"];
|
||||
if (!data)
|
||||
data = @"";
|
||||
else if ([data isKindOfClass: [NSArray class]])
|
||||
// mail => emails[]
|
||||
data = [oldRecord objectForKey: @"c_emails"];
|
||||
if (data)
|
||||
{
|
||||
if ([data count] > 0)
|
||||
data = [data objectAtIndex: 0];
|
||||
if ([data isKindOfClass: [NSArray class]])
|
||||
{
|
||||
if ([data count] > 0)
|
||||
{
|
||||
NSEnumerator *emails;
|
||||
NSMutableArray *recordEmails;
|
||||
NSString *email;
|
||||
emails = [(NSArray *)data objectEnumerator];
|
||||
recordEmails = [NSMutableArray arrayWithCapacity: [data count]];
|
||||
while ((email = [emails nextObject]))
|
||||
{
|
||||
[recordEmails addObject: [NSDictionary dictionaryWithObject: email forKey: @"value"]];
|
||||
}
|
||||
[newRecord setObject: recordEmails forKey: @"emails"];
|
||||
}
|
||||
}
|
||||
else if (data)
|
||||
{
|
||||
NSDictionary *email;
|
||||
email = [NSDictionary dictionaryWithObjectsAndKeys: @"pref", @"type", data, @"value", nil];
|
||||
[newRecord setObject: [NSArray arrayWithObject: email] forKey: @"emails"];
|
||||
}
|
||||
else
|
||||
data = @"";
|
||||
}
|
||||
else
|
||||
data = @"";
|
||||
[newRecord setObject: data forKey: @"c_mail"];
|
||||
|
||||
data = [oldRecord objectForKey: @"nsaimid"];
|
||||
@@ -255,25 +288,28 @@
|
||||
data = @"";
|
||||
[newRecord setObject: data forKey: @"c_screenname"];
|
||||
|
||||
// o => org
|
||||
data = [oldRecord objectForKey: @"o"];
|
||||
if (!data)
|
||||
if (data)
|
||||
[newRecord setObject: data forKey: @"org"];
|
||||
else
|
||||
data = @"";
|
||||
[newRecord setObject: data forKey: @"c_o"];
|
||||
|
||||
// telephonenumber || cellphone || homephone => phones[]
|
||||
data = [oldRecord objectForKey: @"telephonenumber"];
|
||||
if (![data length])
|
||||
data = [oldRecord objectForKey: @"cellphone"];
|
||||
if (![data length])
|
||||
data = [oldRecord objectForKey: @"homephone"];
|
||||
if (![data length])
|
||||
data = @"";
|
||||
else if ([data isKindOfClass: [NSArray class]])
|
||||
if (data)
|
||||
{
|
||||
if ([data count] > 0)
|
||||
data = [data objectAtIndex: 0];
|
||||
else
|
||||
data = @"";
|
||||
NSDictionary *phonenumber;
|
||||
phonenumber = [NSDictionary dictionaryWithObjectsAndKeys: @"pref", @"type", data, @"value", nil];
|
||||
[newRecord setObject: [NSArray arrayWithObject: phonenumber] forKey: @"phones"];
|
||||
}
|
||||
else
|
||||
data = @"";
|
||||
[newRecord setObject: data forKey: @"c_telephonenumber"];
|
||||
|
||||
// Custom attribute for group-lookups. See LDAPSource.m where
|
||||
@@ -290,9 +326,13 @@
|
||||
[newRecord setObject: @"vcard" forKey: @"c_component"];
|
||||
}
|
||||
|
||||
// c_info => note
|
||||
data = [oldRecord objectForKey: @"c_info"];
|
||||
if ([data length] > 0)
|
||||
[newRecord setObject: data forKey: @"contactInfo"];
|
||||
{
|
||||
[newRecord setObject: data forKey: @"note"];
|
||||
[newRecord setObject: data forKey: @"contactInfo"];
|
||||
}
|
||||
|
||||
recordSource = [oldRecord objectForKey: @"source"];
|
||||
if ([recordSource conformsToProtocol: @protocol (SOGoDNSource)] &&
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
"Personal Address Book" = "Osebni adresar";
|
||||
"Collected Address Book" = "Zbrani adresar";
|
||||
Loaded 100 of 1249 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user