Merge lp:~igors/do-plugins/Thunderbird-fix into lp:do-plugins
- Thunderbird-fix
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 757 |
Proposed branch: | lp:~igors/do-plugins/Thunderbird-fix |
Merge into: | lp:do-plugins |
Diff against target: |
367 lines (+213/-44) 3 files modified
Thunderbird/Makefile.am (+2/-1) Thunderbird/Thunderbird.mdp (+1/-0) Thunderbird/src/ThunderbirdContactItemSource.cs (+210/-43) |
To merge this branch: | bzr merge lp:~igors/do-plugins/Thunderbird-fix |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Chris Halse Rogers | Needs Information | ||
Review via email: mp+17102@code.launchpad.net |
Commit message
Description of the change
Igor Slepchin (igors) wrote : | # |
Chris Halse Rogers (raof) wrote : | # |
Bah. Sometimes, I hate bzr.
This branch looks like it's got some valuable improvements, but I can't actually pull it because of a repository version mismatch! If you could hit the “upgrade” button on your branch, it should convert it to a 2a repository, which I'll be able to pull.
It looks good, with the exception of the EmailList class. I'm not sure why you don't just pass a Dictionary<string, uint> around directly? The only value of EmailList appears to be .Add, which is only called once, and looks like it could replace with TryGetValue?
Igor Slepchin (igors) wrote : | # |
Hey Chris - I clicked "upgrade" half a dozen times now but I'm not sure it's changed anything (upgrade button is still available and the repo is still marked as RepositoryForma
As for EmailList class, the primary motivation for it was overwriting Add method so that I didn't have to check if the key was already present in the Dictionary whenever I needed to update "popularity" value.
Chris Halse Rogers (raof) wrote : | # |
Bah. No, it hasn't upgraded, so I'm still unable to pull from it.
Running “bzr upgrade --2a lp:~igors/do-plugins/Thunderbird-fix” might upgrade it properly, or you could (hopefully) push a new branch.
Ah. Somehow I didn't notice all the .Adds when doing my cursory review. I still find the class a bit of a heavyweight solution, so I might play around with some other options. I don't expect you to need to do any work there, though.
Igor Slepchin (igors) wrote : | # |
bzr upgrade apparently worked, thanks!
I also merged this to the current trunk and re-pushed to https:/
Chris Halse Rogers (raof) wrote : | # |
Excellent. That works.
I'll perform a more thorough review and merge.
Igor Slepchin (igors) wrote : | # |
While I have your attention: do you think you could commit the fix for bug 698078 while at it? Thunderbird plugin is fairly useless if the email action itself does not work :p
Preview Diff
1 | === modified file 'Thunderbird/Makefile.am' | |||
2 | --- Thunderbird/Makefile.am 2009-01-15 06:27:03 +0000 | |||
3 | +++ Thunderbird/Makefile.am 2011-02-08 03:44:13 +0000 | |||
4 | @@ -11,4 +11,5 @@ | |||
5 | 11 | 11 | ||
6 | 12 | REFERENCES = \ | 12 | REFERENCES = \ |
7 | 13 | System \ | 13 | System \ |
9 | 14 | $(DO_UNIVERSE_LIBS) | 14 | $(DO_UNIVERSE_LIBS) \ |
10 | 15 | $(DO_PLATFORM_LIBS) | ||
11 | 15 | 16 | ||
12 | === modified file 'Thunderbird/Thunderbird.mdp' | |||
13 | --- Thunderbird/Thunderbird.mdp 2009-06-23 00:52:54 +0000 | |||
14 | +++ Thunderbird/Thunderbird.mdp 2011-02-08 03:44:13 +0000 | |||
15 | @@ -22,5 +22,6 @@ | |||
16 | 22 | <References> | 22 | <References> |
17 | 23 | <ProjectReference type="Gac" localcopy="True" refto="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> | 23 | <ProjectReference type="Gac" localcopy="True" refto="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> |
18 | 24 | <ProjectReference type="Gac" localcopy="True" refto="Do.Universe, Version=0.9.0.0, Culture=neutral" /> | 24 | <ProjectReference type="Gac" localcopy="True" refto="Do.Universe, Version=0.9.0.0, Culture=neutral" /> |
19 | 25 | <ProjectReference type="Gac" localcopy="True" refto="Do.Platform, Version=0.9.0.0, Culture=neutral" /> | ||
20 | 25 | </References> | 26 | </References> |
21 | 26 | </Project> | 27 | </Project> |
22 | 27 | \ No newline at end of file | 28 | \ No newline at end of file |
23 | 28 | 29 | ||
24 | === modified file 'Thunderbird/src/ThunderbirdContactItemSource.cs' | |||
25 | --- Thunderbird/src/ThunderbirdContactItemSource.cs 2009-05-29 09:39:41 +0000 | |||
26 | +++ Thunderbird/src/ThunderbirdContactItemSource.cs 2011-02-08 03:44:13 +0000 | |||
27 | @@ -17,14 +17,13 @@ | |||
28 | 17 | // You should have received a copy of the GNU General Public License | 17 | // You should have received a copy of the GNU General Public License |
29 | 18 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | 18 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
30 | 19 | 19 | ||
31 | 20 | // | ||
32 | 21 | |||
33 | 22 | using System; | 20 | using System; |
34 | 23 | using System.IO; | 21 | using System.IO; |
35 | 24 | using System.Collections; | 22 | using System.Collections; |
36 | 25 | using System.Collections.Generic; | 23 | using System.Collections.Generic; |
37 | 26 | 24 | ||
38 | 27 | using Do.Universe; | 25 | using Do.Universe; |
39 | 26 | using Do.Platform; | ||
40 | 28 | 27 | ||
41 | 29 | using Beagle.Util; | 28 | using Beagle.Util; |
42 | 30 | 29 | ||
43 | @@ -33,16 +32,107 @@ | |||
44 | 33 | 32 | ||
45 | 34 | public class ThunderbirdContactItemSource : ItemSource | 33 | public class ThunderbirdContactItemSource : ItemSource |
46 | 35 | { | 34 | { |
49 | 36 | 35 | ||
50 | 37 | const string BeginProfileName = "Path="; | 36 | class EmailContactDetail: Item, IContactDetailItem |
51 | 37 | { | ||
52 | 38 | readonly string detail, description; | ||
53 | 39 | readonly ContactItem owner; | ||
54 | 40 | |||
55 | 41 | public EmailContactDetail (ContactItem owner, string detail) | ||
56 | 42 | { | ||
57 | 43 | this.owner = owner; | ||
58 | 44 | this.detail = detail; | ||
59 | 45 | description = string.IsNullOrEmpty (owner["name"]) | ||
60 | 46 | ? owner[detail] | ||
61 | 47 | : owner["name"]; | ||
62 | 48 | } | ||
63 | 49 | |||
64 | 50 | public override string Name | ||
65 | 51 | { | ||
66 | 52 | get { return owner[detail]; } | ||
67 | 53 | } | ||
68 | 54 | |||
69 | 55 | public override string Description | ||
70 | 56 | { | ||
71 | 57 | get { return description; } | ||
72 | 58 | } | ||
73 | 59 | |||
74 | 60 | public override string Icon | ||
75 | 61 | { | ||
76 | 62 | get { return "thunderbird"; } | ||
77 | 63 | } | ||
78 | 64 | |||
79 | 65 | public string Key | ||
80 | 66 | { | ||
81 | 67 | get { return detail; } | ||
82 | 68 | } | ||
83 | 69 | |||
84 | 70 | public string Value | ||
85 | 71 | { | ||
86 | 72 | get { return owner[detail]; } | ||
87 | 73 | } | ||
88 | 74 | } | ||
89 | 75 | |||
90 | 76 | class EmailList | ||
91 | 77 | { | ||
92 | 78 | private Dictionary<string, uint> set; | ||
93 | 79 | |||
94 | 80 | public EmailList () { set = new Dictionary<string, uint> (); } | ||
95 | 81 | |||
96 | 82 | public void Add (string email, uint popularity) | ||
97 | 83 | { | ||
98 | 84 | if (!set.ContainsKey (email)) | ||
99 | 85 | { | ||
100 | 86 | set.Add (email, popularity); | ||
101 | 87 | } | ||
102 | 88 | else | ||
103 | 89 | { | ||
104 | 90 | set[email] += popularity; | ||
105 | 91 | } | ||
106 | 92 | } | ||
107 | 93 | |||
108 | 94 | public bool Contains (string email) | ||
109 | 95 | { | ||
110 | 96 | return set.ContainsKey (email); | ||
111 | 97 | } | ||
112 | 98 | |||
113 | 99 | public uint this [string email] | ||
114 | 100 | { | ||
115 | 101 | get { return set[email]; } | ||
116 | 102 | } | ||
117 | 103 | |||
118 | 104 | public int Count { | ||
119 | 105 | get { return set.Count; } | ||
120 | 106 | } | ||
121 | 107 | |||
122 | 108 | public ICollection<string> Keys { | ||
123 | 109 | get { return set.Keys; } | ||
124 | 110 | } | ||
125 | 111 | } | ||
126 | 112 | |||
127 | 113 | class ThunderbirdEmail | ||
128 | 114 | { | ||
129 | 115 | public readonly string email; | ||
130 | 116 | public readonly uint popularity; | ||
131 | 117 | |||
132 | 118 | public ThunderbirdEmail (string email, uint popularity) | ||
133 | 119 | { | ||
134 | 120 | this.email = email; | ||
135 | 121 | this.popularity = popularity; | ||
136 | 122 | } | ||
137 | 123 | } | ||
138 | 124 | |||
139 | 125 | |||
140 | 126 | const string BeginProfileName = "Path="; | ||
141 | 38 | const string BeginDefaultProfile = "Name=default"; | 127 | const string BeginDefaultProfile = "Name=default"; |
144 | 39 | 128 | const string THUNDERBIRD_EMAIL = "email.thunderbird"; | |
145 | 40 | List<Item> contacts; | 129 | static readonly char[] nameDelimiters = { ' ', '\'', '"' }; |
146 | 130 | |||
147 | 131 | Dictionary<string, Item> contacts; // name => ContactItem | ||
148 | 41 | 132 | ||
149 | 42 | public ThunderbirdContactItemSource () | 133 | public ThunderbirdContactItemSource () |
150 | 43 | { | 134 | { |
153 | 44 | contacts = new List<Item> (); | 135 | contacts = new Dictionary<string, Item> (); |
152 | 45 | UpdateItems (); | ||
154 | 46 | } | 136 | } |
155 | 47 | 137 | ||
156 | 48 | public override IEnumerable<Type> SupportedItemTypes { | 138 | public override IEnumerable<Type> SupportedItemTypes { |
157 | @@ -50,7 +140,7 @@ | |||
158 | 50 | return new Type[] { | 140 | return new Type[] { |
159 | 51 | typeof (ContactItem), | 141 | typeof (ContactItem), |
160 | 52 | }; | 142 | }; |
162 | 53 | } | 143 | } |
163 | 54 | } | 144 | } |
164 | 55 | 145 | ||
165 | 56 | public override string Name { get { return "Thunderbird Contacts"; } } | 146 | public override string Name { get { return "Thunderbird Contacts"; } } |
166 | @@ -62,73 +152,135 @@ | |||
167 | 62 | try { | 152 | try { |
168 | 63 | _UpdateItems (); | 153 | _UpdateItems (); |
169 | 64 | } catch (Exception e) { | 154 | } catch (Exception e) { |
171 | 65 | Console.Error.WriteLine ("Cannot index Thunderbird contacts because a {0} was thrown: {1}", e.GetType (), e.Message); | 155 | Log<ThunderbirdContactItemSource>.Error ("Cannot index Thunderbird contacts because a {0} was thrown: {1}", e.GetType (), e.Message); |
172 | 66 | return; | 156 | return; |
173 | 67 | } | 157 | } |
174 | 68 | } | 158 | } |
175 | 69 | 159 | ||
176 | 70 | public override IEnumerable<Item> Items { | 160 | public override IEnumerable<Item> Items { |
178 | 71 | get { return contacts; } | 161 | get { return contacts.Values; } |
179 | 72 | } | 162 | } |
180 | 73 | 163 | ||
181 | 74 | public override IEnumerable<Item> ChildrenOfItem (Item item) | 164 | public override IEnumerable<Item> ChildrenOfItem (Item item) |
182 | 75 | { | 165 | { |
183 | 166 | ContactItem contact = item as ContactItem; | ||
184 | 167 | |||
185 | 168 | foreach (string detail in contact.Details) | ||
186 | 169 | { | ||
187 | 170 | if (detail.StartsWith (THUNDERBIRD_EMAIL)) | ||
188 | 171 | { | ||
189 | 172 | yield return new EmailContactDetail (contact, detail); | ||
190 | 173 | } | ||
191 | 174 | } | ||
192 | 76 | yield break; | 175 | yield break; |
193 | 77 | } | 176 | } |
194 | 78 | 177 | ||
195 | 79 | void _UpdateItems () | 178 | void _UpdateItems () |
196 | 80 | { | 179 | { |
199 | 81 | MorkDatabase database; | 180 | MorkDatabase abook, history; |
200 | 82 | 181 | Dictionary<string, EmailList> emails = new Dictionary<string, EmailList> (); | |
201 | 182 | |||
202 | 183 | abook = new MorkDatabase (GetThunderbirdAddressBookFilePath ()); | ||
203 | 184 | abook.Read (); | ||
204 | 185 | abook.EnumNamespace = "ns:addrbk:db:row:scope:card:all"; | ||
205 | 186 | |||
206 | 187 | history = new MorkDatabase (GetThunderbirdHistoryFilePath ()); | ||
207 | 188 | history.Read (); | ||
208 | 189 | history.EnumNamespace = "ns:addrbk:db:row:scope:card:all"; | ||
209 | 190 | |||
210 | 191 | addEmails (emails, history); | ||
211 | 192 | addEmails (emails, abook); | ||
212 | 193 | |||
213 | 83 | contacts.Clear (); | 194 | contacts.Clear (); |
217 | 84 | database = new MorkDatabase (GetThunderbirdAddressBookFilePath ()); | 195 | foreach (string name in emails.Keys) |
218 | 85 | database.Read (); | 196 | { |
219 | 86 | database.EnumNamespace = "ns:addrbk:db:row:scope:card:all"; | 197 | CreateThunderbirdContactItem (name, emails[name]); |
220 | 198 | } | ||
221 | 199 | } | ||
222 | 87 | 200 | ||
231 | 88 | foreach (string id in database) { | 201 | void addEmails (Dictionary<string, EmailList> emails, MorkDatabase database) |
232 | 89 | Hashtable contact_row; | 202 | { |
233 | 90 | ContactItem contact; | 203 | foreach (string id in database) |
234 | 91 | 204 | { | |
235 | 92 | contact_row = database.Compile (id, database.EnumNamespace); | 205 | Hashtable contact_row = database.Compile (id, database.EnumNamespace); |
236 | 93 | contact = CreateThunderbirdContactItem (contact_row); | 206 | AddThunderbirdEmail (emails, contact_row); |
229 | 94 | if (contact != null) | ||
230 | 95 | contacts.Add (contact); | ||
237 | 96 | } | 207 | } |
238 | 97 | } | 208 | } |
239 | 98 | 209 | ||
242 | 99 | ContactItem CreateThunderbirdContactItem (Hashtable row) { | 210 | void AddThunderbirdEmail (Dictionary<string, EmailList> emails, Hashtable row) |
243 | 100 | ContactItem contact; | 211 | { |
244 | 101 | string name, email; | 212 | string name, email; |
248 | 102 | 213 | uint popularity; | |
246 | 103 | // foreach (object o in row.Keys) | ||
247 | 104 | // Console.WriteLine ("\t{0} --> {1}", o, row[o]); | ||
249 | 105 | 214 | ||
250 | 106 | // I think this will detect deleted contacts... Hmm... | 215 | // I think this will detect deleted contacts... Hmm... |
251 | 107 | if (row["table"] == null || row["table"] as string == "C6") | 216 | if (row["table"] == null || row["table"] as string == "C6") |
253 | 108 | return null; | 217 | return; |
254 | 109 | 218 | ||
255 | 110 | // Name | 219 | // Name |
256 | 111 | name = row["DisplayName"] as string; | 220 | name = row["DisplayName"] as string; |
257 | 221 | if (name != null) { | ||
258 | 222 | name = name.Trim (nameDelimiters); | ||
259 | 223 | } | ||
260 | 112 | if (name == null || name == string.Empty) | 224 | if (name == null || name == string.Empty) |
261 | 113 | name = string.Format ("{0} {1}", row["FirstName"], row["LastName"]); | 225 | name = string.Format ("{0} {1}", row["FirstName"], row["LastName"]); |
262 | 114 | contact = ContactItem.Create (name); | ||
263 | 115 | 226 | ||
264 | 116 | 227 | ||
268 | 117 | email = row["PrimaryEmail"] as string; | 228 | email = row["PrimaryEmail"] as string; |
269 | 118 | if (email != null && email != string.Empty) | 229 | string p = row["PopularityIndex"] as string; |
270 | 119 | contact["email"] = email; | 230 | try { |
271 | 231 | popularity = UInt32.Parse (p, System.Globalization.NumberStyles.HexNumber); | ||
272 | 232 | } | ||
273 | 233 | catch (Exception) { | ||
274 | 234 | popularity = 0; | ||
275 | 235 | } | ||
276 | 120 | 236 | ||
281 | 121 | return contact; | 237 | if (name == null || name.Trim () == string.Empty) |
282 | 122 | } | 238 | name = email; |
283 | 123 | 239 | ||
284 | 124 | string GetThunderbirdAddressBookFilePath () | 240 | if (string.IsNullOrEmpty (email)) |
285 | 241 | return; | ||
286 | 242 | |||
287 | 243 | if (!emails.ContainsKey (name)) | ||
288 | 244 | { | ||
289 | 245 | emails[name] = new EmailList (); | ||
290 | 246 | } | ||
291 | 247 | emails[name].Add (email, popularity); | ||
292 | 248 | } | ||
293 | 249 | |||
294 | 250 | void CreateThunderbirdContactItem (string name, EmailList emails) | ||
295 | 251 | { | ||
296 | 252 | int emailCount = emails.Count; | ||
297 | 253 | ThunderbirdEmail[] sortedEmails = new ThunderbirdEmail[emailCount]; | ||
298 | 254 | |||
299 | 255 | int i = 0; | ||
300 | 256 | foreach (string key in emails.Keys) | ||
301 | 257 | { | ||
302 | 258 | sortedEmails[i] = new ThunderbirdEmail (key, emails[key]); | ||
303 | 259 | i++; | ||
304 | 260 | } | ||
305 | 261 | Array.Sort (sortedEmails, (x, y) => (int) (y.popularity - x.popularity)); | ||
306 | 262 | |||
307 | 263 | ContactItem contact = ContactItem.Create (name); | ||
308 | 264 | for (i = 0; i < emailCount; i++) | ||
309 | 265 | { | ||
310 | 266 | string detail = THUNDERBIRD_EMAIL + "." + i; | ||
311 | 267 | contact[detail] = sortedEmails[i].email; | ||
312 | 268 | } | ||
313 | 269 | |||
314 | 270 | if (!contacts.ContainsKey (name.ToLower ())) | ||
315 | 271 | { | ||
316 | 272 | contacts.Add (name.ToLower (), contact); | ||
317 | 273 | } | ||
318 | 274 | } | ||
319 | 275 | |||
320 | 276 | string GetThunderbirdDefaultProfilePath () | ||
321 | 125 | { | 277 | { |
322 | 126 | string home, path, profile; | 278 | string home, path, profile; |
323 | 127 | StreamReader reader; | 279 | StreamReader reader; |
324 | 128 | 280 | ||
325 | 129 | profile = null; | 281 | profile = null; |
326 | 130 | home = System.Environment.GetFolderPath (System.Environment.SpecialFolder.Personal); | 282 | home = System.Environment.GetFolderPath (System.Environment.SpecialFolder.Personal); |
328 | 131 | path = System.IO.Path.Combine (home, ".mozilla-thunderbird/profiles.ini"); | 283 | path = System.IO.Path.Combine (home, ".thunderbird/profiles.ini"); |
329 | 132 | try { | 284 | try { |
330 | 133 | reader = System.IO.File.OpenText (path); | 285 | reader = System.IO.File.OpenText (path); |
331 | 134 | } catch { | 286 | } catch { |
332 | @@ -148,16 +300,31 @@ | |||
333 | 148 | } | 300 | } |
334 | 149 | } | 301 | } |
335 | 150 | reader.Close (); | 302 | reader.Close (); |
337 | 151 | 303 | return profile; | |
338 | 304 | } | ||
339 | 305 | |||
340 | 306 | string GetThunderbirdFilePath (string filename) | ||
341 | 307 | { | ||
342 | 308 | string path, home, profile; | ||
343 | 309 | home = System.Environment.GetFolderPath (System.Environment.SpecialFolder.Personal); | ||
344 | 310 | profile = GetThunderbirdDefaultProfilePath (); | ||
345 | 152 | if (profile == null) { | 311 | if (profile == null) { |
346 | 153 | return null; | 312 | return null; |
347 | 154 | } | 313 | } |
349 | 155 | path = System.IO.Path.Combine (home, ".mozilla-thunderbird"); | 314 | path = System.IO.Path.Combine (home, ".thunderbird"); |
350 | 156 | path = System.IO.Path.Combine (path, profile); | 315 | path = System.IO.Path.Combine (path, profile); |
352 | 157 | path = System.IO.Path.Combine (path, "abook.mab"); | 316 | path = System.IO.Path.Combine (path, filename); |
353 | 158 | return path; | 317 | return path; |
354 | 159 | |||
355 | 160 | } | 318 | } |
356 | 161 | 319 | ||
357 | 320 | string GetThunderbirdHistoryFilePath () | ||
358 | 321 | { | ||
359 | 322 | return GetThunderbirdFilePath ("history.mab"); | ||
360 | 323 | } | ||
361 | 324 | |||
362 | 325 | string GetThunderbirdAddressBookFilePath () | ||
363 | 326 | { | ||
364 | 327 | return GetThunderbirdFilePath ("abook.mab"); | ||
365 | 328 | } | ||
366 | 162 | } | 329 | } |
367 | 163 | } | 330 | } |
This fixes Thunderbird plugin. The original problem was described in https:/ /bugs.launchpad .net/do- plugins/ +bug/305778 and was fixed by the first commit on the branch.
The rest of the changes add the following:
* Support for multiple Thunderbird emails on a single ContactItem (ContactDetailItems are introduced)
* Importing emails from Thunderbird history in addition to Thunderbird address book
* Using the most used email for main ContactItem (as determined by Thunderbird's PopularityIndex)
I know the plugin is marked as "Community" but people still seem to distribute it (e.g., Fedora does).