Merge lp:~urbanape/bindwood/dbus-and-local-uuids into lp:~urbanape/bindwood/trunk
- dbus-and-local-uuids
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Zachery Bir | ||||
Approved revision: | 13 | ||||
Merged at revision: | not available | ||||
Proposed branch: | lp:~urbanape/bindwood/dbus-and-local-uuids | ||||
Merge into: | lp:~urbanape/bindwood/trunk | ||||
Diff against target: | None lines | ||||
To merge this branch: | bzr merge lp:~urbanape/bindwood/dbus-and-local-uuids | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Stuart Langridge (community) | Needs Fixing | ||
Zachery Bir | Abstain | ||
Elliot Murphy | Pending | ||
Review via email: mp+8359@code.launchpad.net |
Commit message
Description of the change
Zachery Bir (urbanape) wrote : | # |
Zachery Bir (urbanape) wrote : | # |
Also, missed in my checkin messages, I've removed the menuitem from the Bookmarks menu. We no longer have any need for manually triggering the pull.
I've also pushed the interval back to 30sec, though this isn't set in stone, naturally. I just got tired of seeing my console zoom by.
Stuart Langridge (sil) wrote : | # |
I'm getting an error:
Error: guid is not defined
Source File: chrome:
Line: 385
Where is "guid" coming from?
Zachery Bir (urbanape) wrote : | # |
On Jul 9, 2009, at 8:03 AM, Stuart Langridge wrote:
> Review: Needs Fixing
> I'm getting an error:
> Error: guid is not defined
> Source File: chrome:
> Line: 385
>
> doc.application
> couch.save(doc);
> doc.application
>
> Where is "guid" coming from?
Must have been from the merge. One moment.
Zac
- 13. By Zachery Bir <email address hidden>
-
Fix merge cruft
Preview Diff
1 | === modified file 'chrome.manifest' |
2 | --- chrome.manifest 2009-06-30 13:47:50 +0000 |
3 | +++ chrome.manifest 2009-07-02 13:41:07 +0000 |
4 | @@ -1,2 +1,3 @@ |
5 | -content ubuntuone-firefox-bookmark-sync chrome/content/ |
6 | -overlay chrome://browser/content/browser.xul chrome://ubuntuone-firefox-bookmark-sync/content/browserOverlay.xul |
7 | +content bindwood chrome/content/ |
8 | +locale bindwood en chrome/locale/en/ |
9 | +overlay chrome://browser/content/browser.xul chrome://bindwood/content/browserOverlay.xul |
10 | |
11 | === modified file 'chrome/content/browserOverlay.xul' |
12 | --- chrome/content/browserOverlay.xul 2009-06-30 13:47:50 +0000 |
13 | +++ chrome/content/browserOverlay.xul 2009-07-07 21:45:47 +0000 |
14 | @@ -1,15 +1,20 @@ |
15 | <?xml version="1.0"?> |
16 | |
17 | -<!DOCTYPE overlay SYSTEM "chrome://ubuntuone-firefox-bookmark-sync/locale/ubuntuone-firefox-bookmark-sync.dtd"> |
18 | +<!DOCTYPE overlay SYSTEM "chrome://bindwood/locale/en/bindwood.dtd"> |
19 | |
20 | -<overlay id="ubuntuone-firefox-bookmark-sync" |
21 | +<overlay id="bindwood" |
22 | xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> |
23 | |
24 | - <menupopup id="menu_ToolsPopup"> |
25 | - <menuitem id="UbuntuOneFirefoxBookmarkSync" |
26 | - label="Sync Bookmarks (well, alert)" |
27 | - insertbefore="javascriptConsole" |
28 | - oncommand="alert('syncing...');" /> |
29 | - </menupopup> |
30 | + <script type="application/x-javascript" src="chrome://bindwood/content/couch.js" /> |
31 | + <script type="application/x-javascript" src="chrome://bindwood/content/sync.js" /> |
32 | + |
33 | + <window id="main-window"> |
34 | + <script type="application/x-javascript"> |
35 | + window.addEventListener("load", Bindwood.init, true); |
36 | + |
37 | + var bmsvc = Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Components.interfaces.nsINavBookmarksService); |
38 | + window.addEventListener("load", function() { bmsvc.addObserver(Bindwood.Observer, false); }, false); |
39 | + </script> |
40 | + </window> |
41 | |
42 | </overlay> |
43 | |
44 | === added file 'chrome/content/couch.js' |
45 | --- chrome/content/couch.js 1970-01-01 00:00:00 +0000 |
46 | +++ chrome/content/couch.js 2009-07-08 01:24:33 +0000 |
47 | @@ -0,0 +1,397 @@ |
48 | +// Licensed under the Apache License, Version 2.0 (the "License"); you may not |
49 | +// use this file except in compliance with the License. You may obtain a copy |
50 | +// of the License at |
51 | +// |
52 | +// http://www.apache.org/licenses/LICENSE-2.0 |
53 | +// |
54 | +// Unless required by applicable law or agreed to in writing, software |
55 | +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
56 | +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
57 | +// License for the specific language governing permissions and limitations under |
58 | +// the License. |
59 | + |
60 | +// A simple class to represent a database. Uses XMLHttpRequest to interface with |
61 | +// the CouchDB server. |
62 | + |
63 | +function CouchDB(name, httpHeaders) { |
64 | + this.name = name; |
65 | + this.uri = "/" + encodeURIComponent(name) + "/"; |
66 | + |
67 | + // The XMLHttpRequest object from the most recent request. Callers can |
68 | + // use this to check result http status and headers. |
69 | + this.last_req = null; |
70 | + |
71 | + this.request = function(method, uri, requestOptions) { |
72 | + requestOptions = requestOptions || {} |
73 | + requestOptions.headers = combine(requestOptions.headers, httpHeaders) |
74 | + return CouchDB.request(method, uri, requestOptions); |
75 | + } |
76 | + |
77 | + // Creates the database on the server |
78 | + this.createDb = function() { |
79 | + this.last_req = this.request("PUT", this.uri); |
80 | + CouchDB.maybeThrowError(this.last_req); |
81 | + return JSON.parse(this.last_req.responseText); |
82 | + } |
83 | + |
84 | + // Deletes the database on the server |
85 | + this.deleteDb = function() { |
86 | + this.last_req = this.request("DELETE", this.uri); |
87 | + if (this.last_req.status == 404) |
88 | + return false; |
89 | + CouchDB.maybeThrowError(this.last_req); |
90 | + return JSON.parse(this.last_req.responseText); |
91 | + } |
92 | + |
93 | + // Save a document to the database |
94 | + this.save = function(doc, options) { |
95 | + if (doc._id == undefined) |
96 | + doc._id = CouchDB.newUuids(1)[0]; |
97 | + |
98 | + this.last_req = this.request("PUT", this.uri + |
99 | + encodeURIComponent(doc._id) + encodeOptions(options), |
100 | + {body: JSON.stringify(doc)}); |
101 | + CouchDB.maybeThrowError(this.last_req); |
102 | + var result = JSON.parse(this.last_req.responseText); |
103 | + doc._rev = result.rev; |
104 | + return result; |
105 | + } |
106 | + |
107 | + // Open a document from the database |
108 | + this.open = function(docId, options) { |
109 | + this.last_req = this.request("GET", this.uri + encodeURIComponent(docId) + encodeOptions(options)); |
110 | + if (this.last_req.status == 404) |
111 | + return null; |
112 | + CouchDB.maybeThrowError(this.last_req); |
113 | + return JSON.parse(this.last_req.responseText); |
114 | + } |
115 | + |
116 | + // Deletes a document from the database |
117 | + this.deleteDoc = function(doc) { |
118 | + this.last_req = this.request("DELETE", this.uri + encodeURIComponent(doc._id) + "?rev=" + doc._rev); |
119 | + CouchDB.maybeThrowError(this.last_req); |
120 | + var result = JSON.parse(this.last_req.responseText); |
121 | + doc._rev = result.rev; //record rev in input document |
122 | + doc._deleted = true; |
123 | + return result; |
124 | + } |
125 | + |
126 | + // Deletes an attachment from a document |
127 | + this.deleteDocAttachment = function(doc, attachment_name) { |
128 | + this.last_req = this.request("DELETE", this.uri + encodeURIComponent(doc._id) + "/" + attachment_name + "?rev=" + doc._rev); |
129 | + CouchDB.maybeThrowError(this.last_req); |
130 | + var result = JSON.parse(this.last_req.responseText); |
131 | + doc._rev = result.rev; //record rev in input document |
132 | + return result; |
133 | + } |
134 | + |
135 | + this.bulkSave = function(docs, options) { |
136 | + // first prepoulate the UUIDs for new documents |
137 | + var newCount = 0 |
138 | + for (var i=0; i<docs.length; i++) { |
139 | + if (docs[i]._id == undefined) |
140 | + newCount++; |
141 | + } |
142 | + var newUuids = CouchDB.newUuids(docs.length); |
143 | + var newCount = 0 |
144 | + for (var i=0; i<docs.length; i++) { |
145 | + if (docs[i]._id == undefined) |
146 | + docs[i]._id = newUuids.pop(); |
147 | + } |
148 | + var json = {"docs": docs}; |
149 | + // put any options in the json |
150 | + for (var option in options) { |
151 | + json[option] = options[option]; |
152 | + } |
153 | + this.last_req = this.request("POST", this.uri + "_bulk_docs", { |
154 | + body: JSON.stringify(json) |
155 | + }); |
156 | + if (this.last_req.status == 417) { |
157 | + return {errors: JSON.parse(this.last_req.responseText)}; |
158 | + } |
159 | + else { |
160 | + CouchDB.maybeThrowError(this.last_req); |
161 | + var results = JSON.parse(this.last_req.responseText); |
162 | + for (var i = 0; i < docs.length; i++) { |
163 | + if(results[i].rev) |
164 | + docs[i]._rev = results[i].rev; |
165 | + } |
166 | + return results; |
167 | + } |
168 | + } |
169 | + |
170 | + this.ensureFullCommit = function() { |
171 | + this.last_req = this.request("POST", this.uri + "_ensure_full_commit"); |
172 | + CouchDB.maybeThrowError(this.last_req); |
173 | + return JSON.parse(this.last_req.responseText); |
174 | + } |
175 | + |
176 | + // Applies the map function to the contents of database and returns the results. |
177 | + this.query = function(mapFun, reduceFun, options, keys) { |
178 | + var body = {language: "javascript"}; |
179 | + if(keys) { |
180 | + body.keys = keys ; |
181 | + } |
182 | + if (typeof(mapFun) != "string") |
183 | + mapFun = mapFun.toSource ? mapFun.toSource() : "(" + mapFun.toString() + ")"; |
184 | + body.map = mapFun; |
185 | + if (reduceFun != null) { |
186 | + if (typeof(reduceFun) != "string") |
187 | + reduceFun = reduceFun.toSource ? reduceFun.toSource() : "(" + reduceFun.toString() + ")"; |
188 | + body.reduce = reduceFun; |
189 | + } |
190 | + if (options && options.options != undefined) { |
191 | + body.options = options.options; |
192 | + delete options.options; |
193 | + } |
194 | + this.last_req = this.request("POST", this.uri + "_temp_view" + encodeOptions(options), { |
195 | + headers: {"Content-Type": "application/json"}, |
196 | + body: JSON.stringify(body) |
197 | + }); |
198 | + CouchDB.maybeThrowError(this.last_req); |
199 | + return JSON.parse(this.last_req.responseText); |
200 | + } |
201 | + |
202 | + this.view = function(viewname, options, keys) { |
203 | + var viewParts = viewname.split('/'); |
204 | + var viewPath = this.uri + "_design/" + viewParts[0] + "/_view/" |
205 | + + viewParts[1] + encodeOptions(options); |
206 | + if(!keys) { |
207 | + this.last_req = this.request("GET", viewPath); |
208 | + } else { |
209 | + this.last_req = this.request("POST", viewPath, { |
210 | + headers: {"Content-Type": "application/json"}, |
211 | + body: JSON.stringify({keys:keys}) |
212 | + }); |
213 | + } |
214 | + if (this.last_req.status == 404) |
215 | + return null; |
216 | + CouchDB.maybeThrowError(this.last_req); |
217 | + return JSON.parse(this.last_req.responseText); |
218 | + } |
219 | + |
220 | + // gets information about the database |
221 | + this.info = function() { |
222 | + this.last_req = this.request("GET", this.uri); |
223 | + CouchDB.maybeThrowError(this.last_req); |
224 | + return JSON.parse(this.last_req.responseText); |
225 | + } |
226 | + |
227 | + this.allDocs = function(options,keys) { |
228 | + if(!keys) { |
229 | + this.last_req = this.request("GET", this.uri + "_all_docs" + encodeOptions(options)); |
230 | + } else { |
231 | + this.last_req = this.request("POST", this.uri + "_all_docs" + encodeOptions(options), { |
232 | + headers: {"Content-Type": "application/json"}, |
233 | + body: JSON.stringify({keys:keys}) |
234 | + }); |
235 | + } |
236 | + CouchDB.maybeThrowError(this.last_req); |
237 | + return JSON.parse(this.last_req.responseText); |
238 | + } |
239 | + |
240 | + this.designDocs = function() { |
241 | + return this.allDocs({startkey:"_design", endkey:"_design0"}); |
242 | + }; |
243 | + |
244 | + this.allDocsBySeq = function(options,keys) { |
245 | + var req = null; |
246 | + if(!keys) { |
247 | + req = this.request("GET", this.uri + "_all_docs_by_seq" + encodeOptions(options)); |
248 | + } else { |
249 | + req = this.request("POST", this.uri + "_all_docs_by_seq" + encodeOptions(options), { |
250 | + headers: {"Content-Type": "application/json"}, |
251 | + body: JSON.stringify({keys:keys}) |
252 | + }); |
253 | + } |
254 | + CouchDB.maybeThrowError(req); |
255 | + return JSON.parse(req.responseText); |
256 | + } |
257 | + |
258 | + this.compact = function() { |
259 | + this.last_req = this.request("POST", this.uri + "_compact"); |
260 | + CouchDB.maybeThrowError(this.last_req); |
261 | + return JSON.parse(this.last_req.responseText); |
262 | + } |
263 | + |
264 | + this.setDbProperty = function(propId, propValue) { |
265 | + this.last_req = this.request("PUT", this.uri + propId,{ |
266 | + body:JSON.stringify(propValue) |
267 | + }); |
268 | + CouchDB.maybeThrowError(this.last_req); |
269 | + return JSON.parse(this.last_req.responseText); |
270 | + } |
271 | + |
272 | + this.getDbProperty = function(propId) { |
273 | + this.last_req = this.request("GET", this.uri + propId); |
274 | + CouchDB.maybeThrowError(this.last_req); |
275 | + return JSON.parse(this.last_req.responseText); |
276 | + } |
277 | + |
278 | + this.setAdmins = function(adminsArray) { |
279 | + this.last_req = this.request("PUT", this.uri + "_admins",{ |
280 | + body:JSON.stringify(adminsArray) |
281 | + }); |
282 | + CouchDB.maybeThrowError(this.last_req); |
283 | + return JSON.parse(this.last_req.responseText); |
284 | + } |
285 | + |
286 | + this.getAdmins = function() { |
287 | + this.last_req = this.request("GET", this.uri + "_admins"); |
288 | + CouchDB.maybeThrowError(this.last_req); |
289 | + return JSON.parse(this.last_req.responseText); |
290 | + } |
291 | + |
292 | + // Convert a options object to an url query string. |
293 | + // ex: {key:'value',key2:'value2'} becomes '?key="value"&key2="value2"' |
294 | + function encodeOptions(options) { |
295 | + var buf = [] |
296 | + if (typeof(options) == "object" && options !== null) { |
297 | + for (var name in options) { |
298 | + if (!options.hasOwnProperty(name)) continue; |
299 | + var value = options[name]; |
300 | + if (name == "key" || name == "startkey" || name == "endkey") { |
301 | + value = toJSON(value); |
302 | + } |
303 | + buf.push(encodeURIComponent(name) + "=" + encodeURIComponent(value)); |
304 | + } |
305 | + } |
306 | + if (!buf.length) { |
307 | + return ""; |
308 | + } |
309 | + return "?" + buf.join("&"); |
310 | + } |
311 | + |
312 | + function toJSON(obj) { |
313 | + return obj !== null ? JSON.stringify(obj) : null; |
314 | + } |
315 | + |
316 | + function combine(object1, object2) { |
317 | + if (!object2) |
318 | + return object1; |
319 | + if (!object1) |
320 | + return object2; |
321 | + |
322 | + for (var name in object2) |
323 | + object1[name] = object2[name]; |
324 | + |
325 | + return object1; |
326 | + } |
327 | + |
328 | + |
329 | +} |
330 | + |
331 | +CouchDB.PORT_NUMBER = 5984; // default |
332 | + |
333 | +// this is the XMLHttpRequest object from last request made by the following |
334 | +// CouchDB.* functions (except for calls to request itself). |
335 | +// Use this from callers to check HTTP status or header values of requests. |
336 | +CouchDB.last_req = null; |
337 | + |
338 | + |
339 | +CouchDB.allDbs = function() { |
340 | + CouchDB.last_req = CouchDB.request("GET", "/_all_dbs"); |
341 | + CouchDB.maybeThrowError(CouchDB.last_req); |
342 | + return JSON.parse(CouchDB.last_req.responseText); |
343 | +} |
344 | + |
345 | +CouchDB.allDesignDocs = function() { |
346 | + var ddocs = {}, dbs = CouchDB.allDbs(); |
347 | + for (var i=0; i < dbs.length; i++) { |
348 | + var db = new CouchDB(dbs[i]); |
349 | + ddocs[dbs[i]] = db.designDocs(); |
350 | + }; |
351 | + return ddocs; |
352 | +}; |
353 | + |
354 | +CouchDB.getVersion = function() { |
355 | + CouchDB.last_req = CouchDB.request("GET", "/"); |
356 | + CouchDB.maybeThrowError(CouchDB.last_req); |
357 | + return JSON.parse(CouchDB.last_req.responseText).version; |
358 | +} |
359 | + |
360 | +CouchDB.replicate = function(source, target, rep_options) { |
361 | + rep_options = rep_options || {}; |
362 | + var headers = rep_options.headers || {}; |
363 | + CouchDB.last_req = CouchDB.request("POST", "/_replicate", { |
364 | + headers: headers, |
365 | + body: JSON.stringify({source: source, target: target}) |
366 | + }); |
367 | + CouchDB.maybeThrowError(CouchDB.last_req); |
368 | + return JSON.parse(CouchDB.last_req.responseText); |
369 | +} |
370 | + |
371 | +CouchDB.request = function(method, uri, options) { |
372 | + options = options || {}; |
373 | + var req = null; |
374 | + if (typeof(XMLHttpRequest) != "undefined") { |
375 | + req = new XMLHttpRequest(); |
376 | + } else if (typeof(ActiveXObject) != "undefined") { |
377 | + req = new ActiveXObject("Microsoft.XMLHTTP"); |
378 | + } else { |
379 | + throw new Error("No XMLHTTPRequest support detected"); |
380 | + } |
381 | + req.open(method, "http://localhost:" + CouchDB.PORT_NUMBER + uri, false); |
382 | + if (options.headers) { |
383 | + var headers = options.headers; |
384 | + for (var headerName in headers) { |
385 | + if (!headers.hasOwnProperty(headerName)) continue; |
386 | + req.setRequestHeader(headerName, headers[headerName]); |
387 | + } |
388 | + } |
389 | + req.send(options.body || ""); |
390 | + return req; |
391 | +} |
392 | + |
393 | +CouchDB.requestStats = function(module, key, test) { |
394 | + var query_arg = ""; |
395 | + if(test !== null) { |
396 | + query_arg = "?flush=true"; |
397 | + } |
398 | + |
399 | + var stat = CouchDB.request("GET", "/_stats/" + module + "/" + key + query_arg).responseText; |
400 | + return JSON.parse(stat)[module][key]; |
401 | +} |
402 | + |
403 | +CouchDB.uuids_cache = []; |
404 | + |
405 | +CouchDB.newUuids = function(n) { |
406 | + if (CouchDB.uuids_cache.length >= n) { |
407 | + var uuids = CouchDB.uuids_cache.slice(CouchDB.uuids_cache.length - n); |
408 | + if(CouchDB.uuids_cache.length - n == 0) { |
409 | + CouchDB.uuids_cache = []; |
410 | + } else { |
411 | + CouchDB.uuids_cache = |
412 | + CouchDB.uuids_cache.slice(0, CouchDB.uuids_cache.length - n); |
413 | + } |
414 | + return uuids; |
415 | + } else { |
416 | + CouchDB.last_req = CouchDB.request("GET", "/_uuids?count=" + (100 + n)); |
417 | + CouchDB.maybeThrowError(CouchDB.last_req); |
418 | + var result = JSON.parse(CouchDB.last_req.responseText); |
419 | + CouchDB.uuids_cache = |
420 | + CouchDB.uuids_cache.concat(result.uuids.slice(0, 100)); |
421 | + return result.uuids.slice(100); |
422 | + } |
423 | +} |
424 | + |
425 | +CouchDB.maybeThrowError = function(req) { |
426 | + if (req.status >= 400) { |
427 | + try { |
428 | + var result = JSON.parse(req.responseText); |
429 | + } catch (ParseError) { |
430 | + var result = {error:"unknown", reason:req.responseText}; |
431 | + } |
432 | + throw result; |
433 | + } |
434 | +} |
435 | + |
436 | +CouchDB.params = function(options) { |
437 | + options = options || {}; |
438 | + var returnArray = []; |
439 | + for(var key in options) { |
440 | + var value = options[key]; |
441 | + returnArray.push(key + "=" + value); |
442 | + } |
443 | + return returnArray.join("&"); |
444 | +} |
445 | |
446 | === added file 'chrome/content/dbus.sh' |
447 | --- chrome/content/dbus.sh 1970-01-01 00:00:00 +0000 |
448 | +++ chrome/content/dbus.sh 2009-07-07 10:47:14 +0000 |
449 | @@ -0,0 +1,11 @@ |
450 | +#!/bin/bash |
451 | +OUT=$1 |
452 | +PORT=$(dbus-send --session --dest=org.desktopcouch.CouchDB --print-reply --type=method_call / org.desktopcouch.CouchDB.getPort 2>/dev/null | grep int32 | awk '{print $2}' ) |
453 | +if [ -z "$PORT" ]; then |
454 | + # D-Bus call failed for some reason, so use default port |
455 | + echo 5984 > $OUT |
456 | +else |
457 | + echo $PORT > $OUT |
458 | +fi |
459 | + |
460 | + |
461 | |
462 | === added file 'chrome/content/sync.js' |
463 | --- chrome/content/sync.js 1970-01-01 00:00:00 +0000 |
464 | +++ chrome/content/sync.js 2009-07-08 01:24:33 +0000 |
465 | @@ -0,0 +1,524 @@ |
466 | +/* Lots and lots of debugging information */ |
467 | +var Bindwood = { |
468 | + bookmarksService: Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"] |
469 | + .getService(Components.interfaces.nsINavBookmarksService), |
470 | + uuidService: Components.classes["@mozilla.org/uuid-generator;1"] |
471 | + .getService(Components.interfaces.nsIUUIDGenerator), |
472 | + annotationService: Components.classes["@mozilla.org/browser/annotation-service;1"] |
473 | + .getService(Components.interfaces.nsIAnnotationService), |
474 | + consoleService: Components.classes["@mozilla.org/consoleservice;1"] |
475 | + .getService(Components.interfaces.nsIConsoleService), |
476 | + historyService: Components.classes["@mozilla.org/browser/nav-history-service;1"] |
477 | + .getService(Components.interfaces.nsINavHistoryService), |
478 | + ioService: Components.classes["@mozilla.org/network/io-service;1"] |
479 | + .getService(Components.interfaces.nsIIOService), |
480 | + |
481 | + annotationKey: "bindwood/uuid", |
482 | + uuidItemIdMap: {}, |
483 | + |
484 | + generateUUIDString: function() { |
485 | + return Bindwood.uuidService.generateUUID().toString(); |
486 | + }, |
487 | + |
488 | + annotateItemWithUUID: function(itemId) { |
489 | + var uuid = Bindwood.generateUUIDString(); |
490 | + Bindwood.annotationService.setItemAnnotation(itemId, Bindwood.annotationKey, uuid, 0, Bindwood.annotationService.EXPIRE_NEVER); |
491 | + // Whenever we create a new UUID, stash it and the itemId in |
492 | + // our local cache. |
493 | + Bindwood.uuidItemIdMap[uuid] = itemId; |
494 | + return uuid; |
495 | + }, |
496 | + |
497 | + itemIdForUUID: function(uuid) { |
498 | + // First, try to look it up in our local cache, barring that |
499 | + // (which shouldn't happen), look it up slowly. |
500 | + var itemId = Bindwood.uuidItemIdMap[uuid]; |
501 | + |
502 | + if (!itemId) { |
503 | + var items = Bindwood.annotationService.getItemsWithAnnotation(Bindwood.annotationKey, {}); |
504 | + for (var i = 0; i < items.length; i++) { |
505 | + if (Bindwood.annotationService.getItemAnnotation(items[i], Bindwood.annotationKey) == uuid) { |
506 | + Bindwood.uuidItemIdMap[uuid] = itemId = items[i]; |
507 | + break; |
508 | + } |
509 | + } |
510 | + } |
511 | + return itemId; |
512 | + }, |
513 | + |
514 | + uuidForItemId: function(itemId) { |
515 | + // Try to look up the uuid, and failing that, assign a new one |
516 | + // and return it. |
517 | + var uuid; |
518 | + var found = false; |
519 | + try { |
520 | + uuid = Bindwood.annotationService.getItemAnnotation(itemId, Bindwood.annotationKey); |
521 | + found = true; |
522 | + } catch(e) { |
523 | + uuid = Bindwood.annotateItemWithUUID(itemId); |
524 | + } |
525 | + |
526 | + return uuid; |
527 | + }, |
528 | + |
529 | + writeMessage: function(aMessage) { |
530 | + // convenience method for logging. Way better than alert()s. |
531 | + Bindwood.consoleService.logStringMessage("Bindwood: " + aMessage); |
532 | + }, |
533 | + |
534 | + writeError: function(aMessage, e) { |
535 | + Bindwood.writeMessage(aMessage + "message: '" + e.message + "', reason: '" + e.reason + "', description: '" + e.description + "', error: '" + e.error + "'"); |
536 | + }, |
537 | + |
538 | + |
539 | + |
540 | + init: function() { |
541 | + // Start the process and de-register ourself |
542 | + // http://forums.mozillazine.org/viewtopic.php?f=19&t=657911&start=0 |
543 | + // It ensures that we're only running that code on the first window. |
544 | + |
545 | + if(Components.classes["@mozilla.org/appshell/window-mediator;1"] |
546 | + .getService(Components.interfaces.nsIWindowMediator) |
547 | + .getEnumerator("").getNext() == window) { |
548 | + Bindwood.getCouchPortNumber(Bindwood.startProcess); |
549 | + } |
550 | + }, |
551 | + |
552 | + getCouchPortNumber: function(continueFunction) { |
553 | + // find the desktop Couch port number by making a D-Bus call |
554 | + // we call D-Bus by shelling out to a bash script which calls |
555 | + // it for us, and writes the port number into a temp file |
556 | + |
557 | + // find OS temp dir to put the tempfile in |
558 | + // https://developer.mozilla.org/index.php?title=File_I%2F%2FO#Getting_special_files |
559 | + var tmpdir = Components.classes["@mozilla.org/file/directory_service;1"] |
560 | + .getService(Components.interfaces.nsIProperties) |
561 | + .get("TmpD", Components.interfaces.nsIFile); |
562 | + // create a randomly named tempfile in the tempdir |
563 | + var tmpfile = Components.classes["@mozilla.org/file/local;1"] |
564 | + .createInstance(Components.interfaces.nsILocalFile); |
565 | + tmpfile.initWithPath(tmpdir.path + "/desktopcouch." + Math.random()); |
566 | + tmpfile.createUnique(tmpfile.NORMAL_FILE_TYPE, 0600); |
567 | + Bindwood.writeMessage("Tempfile for Couch port number: " + tmpfile.path); |
568 | + |
569 | + // find the D-Bus bash script, which is in our extension folder |
570 | + var MY_ID = "bindwood@ubuntu.com"; |
571 | + var em = Components.classes["@mozilla.org/extensions/manager;1"]. |
572 | + getService(Components.interfaces.nsIExtensionManager); |
573 | + var dbus_script = em.getInstallLocation(MY_ID).getItemFile(MY_ID, |
574 | + "chrome/content/dbus.sh"); |
575 | + // create an nsILocalFile for the executable |
576 | + var nsifile = Components.classes["@mozilla.org/file/local;1"] |
577 | + .createInstance(Components.interfaces.nsILocalFile); |
578 | + nsifile.initWithPath(dbus_script.path); |
579 | + |
580 | + // create an nsIProcess2 to execute this bash script |
581 | + var process = Components.classes["@mozilla.org/process/util;1"] |
582 | + .createInstance(Components.interfaces.nsIProcess2); |
583 | + process.init(nsifile); |
584 | + |
585 | + // Run the process, passing the tmpfile path |
586 | + var args = [tmpfile.path]; |
587 | + process.runAsync(args, args.length, { |
588 | + observe: function(process, finishState, data) { |
589 | + var port = 5984; |
590 | + if (finishState == "process-finished") { |
591 | + // read temp file to find port number |
592 | + // https://developer.mozilla.org/en/Code_snippets/File_I%2f%2fO#Reading_from_a_file |
593 | + var data = ""; |
594 | + var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]. |
595 | + createInstance(Components.interfaces.nsIFileInputStream); |
596 | + var cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]. |
597 | + createInstance(Components.interfaces.nsIConverterInputStream); |
598 | + fstream.init(tmpfile, -1, 0, 0); |
599 | + cstream.init(fstream, "UTF-8", 0, 0); |
600 | + let (str = {}) { |
601 | + cstream.readString(-1, str); // read the whole file and put it in str.value |
602 | + data = str.value; |
603 | + } |
604 | + cstream.close(); // this closes fstream |
605 | + data = data.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); |
606 | + if (/^[0-9]+$/.test(data)) { |
607 | + port = data; |
608 | + } else { |
609 | + Bindwood.writeMessage("D-Bus port data is not a number (" + data + ")"); |
610 | + } |
611 | + } else { |
612 | + // fall back to system CouchDB |
613 | + Bindwood.writeMessage("D-Bus port find failed"); |
614 | + } |
615 | + tmpfile.remove(false); |
616 | + continueFunction(port); |
617 | + } |
618 | + }); |
619 | + }, |
620 | + |
621 | + startProcess: function(couchPortNumber) { |
622 | + Bindwood.writeMessage("Starting process with Couch on port " + couchPortNumber); |
623 | + CouchDB.PORT_NUMBER = couchPortNumber; |
624 | + try { |
625 | + Bindwood.pushBookmarks(); |
626 | + } catch(e) { |
627 | + Bindwood.writeError("Error when calling pushBookmarks: ", e); |
628 | + } |
629 | + Bindwood.createView(); |
630 | + Bindwood.pullBookmarks(); |
631 | + }, |
632 | + |
633 | + createView: function() { |
634 | + var DDID = "_design/views"; |
635 | + var couch = new CouchDB('bookmarks'); |
636 | + var current_doc; |
637 | + try { |
638 | + current_doc = couch.open(DDID); |
639 | + if (current_doc !== null) { |
640 | + Bindwood.writeMessage("View already exists; leaving it alone"); |
641 | + } else { |
642 | + new_doc = { |
643 | + _id: DDID, |
644 | + views: { |
645 | + display: { |
646 | + map: "function(doc) { " + |
647 | + "var scheme = doc.uri.split(':',1)[0]; " + |
648 | + "var uri; " + |
649 | + "if (scheme == 'http' || scheme == 'https') {" + |
650 | + "uri = doc.uri.split('/')[2];" + |
651 | + "if (uri.length < 30) {" + |
652 | + " uri += '/' + " + |
653 | + "doc.uri.split('/',4)[3].substr(0,30-uri.length) + '...';" + |
654 | + "}" + |
655 | + "} else {" + |
656 | + "uri = scheme + ' URL';" + |
657 | + "}" + |
658 | + "emit(doc.title, uri);" + |
659 | + "}" |
660 | + } |
661 | + } |
662 | + }; |
663 | + try { |
664 | + couch.save(new_doc); |
665 | + } catch(e) { |
666 | + Bindwood.writeError("Problem saving view: ", e); |
667 | + } |
668 | + } |
669 | + } catch(e) { |
670 | + // some kind of error fetching the existing design doc |
671 | + Bindwood.writeError("Problem checking for view: ", e); |
672 | + } |
673 | + }, |
674 | + |
675 | + pushBookmarks: function() { |
676 | + // Prime the pump, so to speak, by uploading all our local |
677 | + // bookmarks to CouchDB (if they're not there already). |
678 | + var couch = new CouchDB('bookmarks'); |
679 | + // Create the DB if it doesn't already exist |
680 | + try { |
681 | + couch.createDb(); |
682 | + } catch (e) { |
683 | + Bindwood.writeError("Error when creating database in pushBookmarks (file_exists is OK here): ", e); |
684 | + } |
685 | + |
686 | + Bindwood.pushBookmarksFromList(Bindwood.bookmarksService.toolbarFolder, |
687 | + "toolbarFolder", couch); |
688 | + Bindwood.pushBookmarksFromList(Bindwood.bookmarksService.bookmarksMenuFolder, |
689 | + "bookmarksMenuFolder", couch); |
690 | + }, |
691 | + |
692 | + getBookmarksFromList: function(bookmarksList) { |
693 | + var retval = []; |
694 | + var options = Bindwood.historyService.getNewQueryOptions(); |
695 | + var query = Bindwood.historyService.getNewQuery(); |
696 | + query.setFolders([bookmarksList], 1); |
697 | + var result = Bindwood.historyService.executeQuery(query, options); |
698 | + var rootNode = result.root; |
699 | + rootNode.containerOpen = true; |
700 | + for (var i=0; i<rootNode.childCount; i++) { |
701 | + var node = rootNode.getChild(i); |
702 | + if (Bindwood.bookmarksService.getItemType(node.itemId) != |
703 | + Bindwood.bookmarksService.TYPE_BOOKMARK) { |
704 | + continue; |
705 | + } |
706 | + |
707 | + var title = Bindwood.bookmarksService.getItemTitle(node.itemId); |
708 | + try { |
709 | + var metadata = Bindwood.bookmarksService.getBookmarkURI(node.itemId); |
710 | + } catch(e) { |
711 | + Bindwood.writeError("problem fetching metadata for bookmark '" + title + "': ", e); |
712 | + continue; |
713 | + } |
714 | + var uuid = Bindwood.uuidForItemId(node.itemId); |
715 | + retval.push({ |
716 | + title: title, |
717 | + metadata: metadata, |
718 | + uuid: uuid, |
719 | + id: node.itemId}); |
720 | + } |
721 | + rootNode.containerOpen = false; |
722 | + return retval; |
723 | + }, |
724 | + |
725 | + pushBookmarksFromList: function(bookmarksList, bookmarksListName, db) { |
726 | + var bookmarkData = Bindwood.getBookmarksFromList(bookmarksList); |
727 | + for (var i = 0; i < bookmarkData.length; i++) { |
728 | + // find this bookmark in CouchDB |
729 | + var uuid = Bindwood.uuidForItemId(bookmarkData[i].id); |
730 | + var uri = bookmarkData[i].metadata.spec; |
731 | + var title = bookmarkData[i].title; |
732 | + |
733 | + var results = db.query(function(doc) { |
734 | + if (doc.application_annotations && |
735 | + doc.application_annotations.Firefox && |
736 | + doc.application_annotations.Firefox.uuid) { |
737 | + emit(doc.application_annotations.Firefox.uuid, doc); |
738 | + } |
739 | + }, null, { |
740 | + startkey: uuid, endkey: uuid |
741 | + }); |
742 | + |
743 | + if (results.rows.length === 0) { |
744 | + // this bookmark is not in CouchDB, so write it |
745 | + var record = { |
746 | + record_type: "http://example.com/bookmark", |
747 | + uri: uri, |
748 | + title: title, |
749 | + application_annotations: { |
750 | + Firefox: { |
751 | + uuid: uuid, |
752 | + list: bookmarksListName |
753 | + } |
754 | + } |
755 | + }; |
756 | + try { |
757 | + db.save(record); |
758 | + } catch(e) { |
759 | + Bindwood.writeError("Problem saving bookmark to CouchDB; bookmark is " + JSON.stringify(record) + ": ", e); |
760 | + } |
761 | + } else { |
762 | + // bookmark is already in CouchDB, so do nothing |
763 | + } |
764 | + } |
765 | + }, |
766 | + |
767 | + pullBookmarks: function() { |
768 | + var couch = new CouchDB('bookmarks'); |
769 | + // Fetch all bookmark documents from the database |
770 | + // the query function is evaluated by Couch, which doesn't know |
771 | + // what Bindwood.RECORD_TYPE is, so we string-encode it first to |
772 | + // include the literal value |
773 | + try { |
774 | + var rows = couch.query(function (doc) { |
775 | + if (doc.record_type == "http://example.com/bookmark") { |
776 | + emit(doc._id,doc); |
777 | + } |
778 | + }); |
779 | + } catch(e) { |
780 | + Bindwood.writeError("Problem fetching all bookmarks from Couch:", e); |
781 | + } |
782 | + for (var i = 0; i < rows.rows.length; i++) { |
783 | + var recordid = rows.rows[i].id; |
784 | + var bm = rows.rows[i].value; |
785 | + if (bm.application_annotations && |
786 | + bm.application_annotations.Firefox && |
787 | + bm.application_annotations.Firefox.uuid) { |
788 | + // this bookmark has a uuid, so check its values haven't changed |
789 | + // find the bookmark with this uuid |
790 | + var itemId = Bindwood.itemIdForUUID( |
791 | + bm.application_annotations.Firefox.uuid); |
792 | + if (!itemId) { |
793 | + // This bookmark has a uuid, but it's not one of ours. |
794 | + // We need to work out whether (a) it's the same as one |
795 | + // of ours but with a different uuid (so we need to |
796 | + // make the uuids the same), or (b) it's a new one |
797 | + // that happens to have been created on a different |
798 | + // machine. |
799 | + // For the moment, punt on this. |
800 | + } else { |
801 | + var title = Bindwood.bookmarksService.getItemTitle(itemId); |
802 | + var metadata = Bindwood.bookmarksService.getBookmarkURI(itemId); |
803 | + if (title != bm.title) { |
804 | + Bindwood.bookmarksService.setItemTitle(itemId, bm.title); |
805 | + } |
806 | + if (metadata.spec != bm.uri) { |
807 | + try { |
808 | + metadata = Bindwood.ioService.newURI(bm.uri, null, null); |
809 | + } catch(e) { |
810 | + Bindwood.writeError("Problem creating a new URI for bookmark", e); |
811 | + } |
812 | + Bindwood.bookmarksService.changeBookmarkURI(itemId, metadata); |
813 | + } |
814 | + } |
815 | + } else { |
816 | + // this bookmark has no uuid, so create it from fresh in Firefox |
817 | + var list; |
818 | + if (bm.application_annotations && |
819 | + bm.application_annotations.Firefox && |
820 | + bm.application_annotations.Firefox.list) { |
821 | + switch (bm.application_annotations.Firefox.list) { |
822 | + case "toolbarFolder": |
823 | + list = Bindwood.bookmarksService.toolbarFolder; |
824 | + break; |
825 | + case "bookmarksMenuFolder": |
826 | + list = Bindwood.bookmarksService.bookmarksMenuFolder; |
827 | + break; |
828 | + default: |
829 | + list = Bindwood.bookmarksService.toolbarFolder; |
830 | + break; |
831 | + } |
832 | + } else { |
833 | + list = Bindwood.bookmarksService.toolbarFolder; |
834 | + } |
835 | + var metadata = Bindwood.ioService.newURI(bm.uri, null, null); |
836 | + |
837 | + var itemId = Bindwood.bookmarksService.insertBookmark(list, |
838 | + metadata, -1, bm.title); |
839 | + // and then write the new uuid back to the record |
840 | + var uuid = Bindwood.uuidForItemId(itemId); |
841 | + var doc = couch.open(recordid); |
842 | + if (!doc.application_annotations) { |
843 | + doc.application_annotations = {}; |
844 | + } |
845 | + if (!doc.application_annotations.Firefox) { |
846 | + doc.application_annotations.Firefox = {}; |
847 | + } |
848 | + doc.application_annotations.Firefox.uuid = uuid; |
849 | + couch.save(doc); |
850 | + doc.application_annotations.Firefox.guid = guid; |
851 | + try { |
852 | + couch.save(doc); |
853 | + } catch(e) { |
854 | + Bindwood.writeError("Problem writing record for new bookmark: ",e); |
855 | + } |
856 | + } |
857 | + } |
858 | + // reschedule ourself |
859 | + setTimeout(Bindwood.pullBookmarks, 30000); |
860 | + }, |
861 | + |
862 | + Observer: { |
863 | + // An nsINavBookmarkObserver |
864 | + onItemAdded: function(aItemId, aFolder, aIndex) { |
865 | + // A bookmark has been added, so we create a blank entry |
866 | + // in Couch with our local itemId attached. |
867 | + netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead UniversalBrowserWrite"); |
868 | + |
869 | + var couch = new CouchDB('bookmarks'); |
870 | + |
871 | + var uuid = Bindwood.uuidForItemId(aItemId); |
872 | + |
873 | + var list; |
874 | + switch(aFolder) { |
875 | + case Bindwood.bookmarksService.toolbarFolder: |
876 | + list = "toolbarFolder"; |
877 | + break; |
878 | + case Bindwood.bookmarksService.bookmarksMenuFolder: |
879 | + list = "bookmarksMenuFolder"; |
880 | + break; |
881 | + default: |
882 | + list = "toolbarFolder"; |
883 | + break; |
884 | + } |
885 | + |
886 | + var doc = { |
887 | + record_type: "http://example.com/bookmark", |
888 | + application_annotations: { |
889 | + Firefox: { |
890 | + uuid: uuid, |
891 | + list: list |
892 | + } |
893 | + } |
894 | + }; |
895 | + |
896 | + try { |
897 | + var result = couch.save(doc); |
898 | + } catch(e) { |
899 | + Bindwood.writeError("Problem saving new bookmark to Couch", e); |
900 | + } |
901 | + }, |
902 | + onBeforeItemRemoved: function(aItemId) { |
903 | + // A bookmark has been removed. This is called before it's |
904 | + // been removed locally, though we're passed the itemId, |
905 | + // which we use to delete from Couch. |
906 | + netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead UniversalBrowserWrite"); |
907 | + |
908 | + var couch = new CouchDB('bookmarks'); |
909 | + |
910 | + var uuid = Bindwood.uuidForItemId(aItemId); |
911 | + |
912 | + var results = couch.query(function(doc) { |
913 | + if (doc.application_annotations && |
914 | + doc.application_annotations.Firefox && |
915 | + doc.application_annotations.Firefox.uuid) { |
916 | + emit(doc.application_annotations.Firefox.uuid, doc); |
917 | + } |
918 | + }, null, { |
919 | + startkey: uuid, endkey: uuid |
920 | + }); |
921 | + |
922 | + if (results.rows.length === 0) { |
923 | + Bindwood.writeMessage("A bookmark was deleted, but this bookmark isn't in CouchDB. This isn't supposed to happen."); |
924 | + return; |
925 | + } |
926 | + |
927 | + var doc = couch.open(results.rows[0].id); |
928 | + |
929 | + try { |
930 | + var result = couch.deleteDoc(doc); |
931 | + // Also remove from our local cache and remove |
932 | + // annotation from service. |
933 | + delete Bindwood.uuidItemIdMap[uuid]; |
934 | + } catch(e) { |
935 | + Bindwood.writeError("Problem deleting record from Couch", e); |
936 | + } |
937 | + }, |
938 | + onItemRemoved: function(aItemId, aFolder, aIndex) { |
939 | + Bindwood.annotationService.removeItemAnnotation(aItemId, Bindwood.annotationKey); |
940 | + }, |
941 | + onItemChanged: function(aBookmarkId, aProperty, aIsAnnotationProperty, aValue) { |
942 | + // A property of a bookmark has changed. On multiple |
943 | + // property updates, this will be called multiple times, |
944 | + // once per property (i.e., for title and URI) |
945 | + netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead UniversalBrowserWrite"); |
946 | + |
947 | + var couch = new CouchDB('bookmarks'); |
948 | + |
949 | + var uuid = Bindwood.uuidForItemId(aBookmarkId); |
950 | + |
951 | + var results = couch.query(function(doc) { |
952 | + if (doc.application_annotations && |
953 | + doc.application_annotations.Firefox && |
954 | + doc.application_annotations.Firefox.uuid) { |
955 | + emit(doc.application_annotations.Firefox.uuid, doc); |
956 | + } |
957 | + }, null, { |
958 | + startkey: uuid, endkey: uuid |
959 | + }); |
960 | + |
961 | + if (results.rows.length === 0) { |
962 | + Bindwood.writeMessage("a bookmark has changed, but this bookmark isn't in CouchDB. this isn't supposed to happen."); |
963 | + return; |
964 | + } |
965 | + |
966 | + var doc = couch.open(results.rows[0].id); |
967 | + doc[aProperty.toString()] = aValue.toString(); |
968 | + |
969 | + try { |
970 | + var result = couch.save(doc); |
971 | + } catch(e) { |
972 | + Bindwood.writeError("Problem saving updated bookmark to Couch", e); |
973 | + } |
974 | + }, |
975 | + |
976 | + onBeginUpdateBatch: function() {}, |
977 | + onEndUpdateBatch: function() {}, |
978 | + onItemVisited: function(aBookmarkId, aVisitID, time) {}, |
979 | + onItemMoved: function(aItemId, aOldParent, aOldIndex, aNewParent, aNewIndex) {}, |
980 | + QueryInterface: function(iid) { |
981 | + if (iid.equals(Components.interfaces.nsINavBookmarkObserver) || |
982 | + iid.equals(Components.interfaces.nsINavBookmarkObserver_MOZILLA_1_9_1_ADDITIONS) || |
983 | + iid.equals(Components.interfaces.nsISupports)) { |
984 | + return this; |
985 | + } |
986 | + throw Cr.NS_ERROR_NO_INTERFACE; |
987 | + } |
988 | + } |
989 | +}; |
990 | |
991 | === added file 'chrome/content/tutorial.html' |
992 | --- chrome/content/tutorial.html 1970-01-01 00:00:00 +0000 |
993 | +++ chrome/content/tutorial.html 2009-07-02 13:41:07 +0000 |
994 | @@ -0,0 +1,8 @@ |
995 | +<html> |
996 | + <head> |
997 | + <title>BindWood Tutorial</title> |
998 | + </head> |
999 | + <body> |
1000 | + This is BindWood. |
1001 | + </body> |
1002 | +</html> |
1003 | |
1004 | === added directory 'chrome/locale' |
1005 | === added directory 'chrome/locale/en' |
1006 | === added file 'chrome/locale/en/bindwood.dtd' |
1007 | === modified file 'install.rdf' |
1008 | --- install.rdf 2009-06-30 13:47:50 +0000 |
1009 | +++ install.rdf 2009-07-06 20:00:16 +0000 |
1010 | @@ -3,7 +3,7 @@ |
1011 | xmlns:em="http://www.mozilla.org/2004/em-rdf#"> |
1012 | |
1013 | <Description about="urn:mozilla:install-manifest"> |
1014 | - <em:id>ubuntuone-firefox-bookmark-sync@ubuntu.com</em:id> |
1015 | + <em:id>bindwood@ubuntu.com</em:id> |
1016 | <em:version>1.0</em:version> |
1017 | <em:type>2</em:type> |
1018 | |
1019 | @@ -18,9 +18,9 @@ |
1020 | </em:targetApplication> |
1021 | |
1022 | <!-- Front End MetaData --> |
1023 | - <em:name>UbuntuOne Bookmarks Sync</em:name> |
1024 | - <em:description>An extension to synchronize your bookmarks to UbuntuOne.</em:description> |
1025 | + <em:name>Bindwood</em:name> |
1026 | + <em:description>An extension to synchronize your bookmarks to a local CouchDB.</em:description> |
1027 | <em:creator>Zachery Bir</em:creator> |
1028 | - <em:homepageURL>http://launchpad.net/ubuntuone-firefox-bookmark-sync</em:homepageURL> |
1029 | + <em:homepageURL>http://launchpad.net/bindwood</em:homepageURL> |
1030 | </Description> |
1031 | </RDF> |
This has lp:~sil/bindwood/design-view and lp:~sil/bindwood/find-desktop-couch-with-dbus merged in, as well as the work I did supporting internally- generated uuids for bookmarks. There are still a few rough spots to be worked out:
=== TODO ===
This code has been fixed to use the suggested style (creating a new nsIURI, rather than just poking the .spec struct member):
... sync.js +341
if (metadata.spec != bm.uri) {
try {
metadata = Bindwood. ioService. newURI( bm.uri, null, null);
} catch(e) {
Bindwood. writeError( "Problem creating a new URI for bookmark", e);
}
Bindwood. bookmarksServic e.changeBookmar kURI(itemId, metadata);
}
...
This fails when dealing with a bookmark whose URI looks like this: folder= BOOKMARKS_ MENU&folder= UNFILED_ BOOKMARKS& folder= TOOLBAR& queryType= 1&sort= 12&excludeItemI fParentHasAnnot ation=livemark% 2FfeedURI& maxResults= 10&excludeQueri es=1`
`place:
The problem is that while bookmarksServic e.getBookmarkUR I(thatBookmark) will produce the `place:folder=...` URI spec, the IIOService.newURI() function fails when given that spec to create a URI for the updated bookmark. So, we'll likely need to special case these and either ignore them or find the proper way to spell BookmarkURIs for livemarks, &c.