Merge lp:~urbanape/bindwood/with-couch-libraries into lp:~urbanape/bindwood/trunk
- with-couch-libraries
- Merge into trunk
Proposed by
Zachery Bir
Status: | Merged |
---|---|
Merge reported by: | Zachery Bir |
Merged at revision: | not available |
Proposed branch: | lp:~urbanape/bindwood/with-couch-libraries |
Merge into: | lp:~urbanape/bindwood/trunk |
Diff against target: | None lines |
To merge this branch: | bzr merge lp:~urbanape/bindwood/with-couch-libraries |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Joshua Blount (community) | Approve | ||
Stuart Langridge (community) | Approve | ||
Review via email: mp+8150@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Zachery Bir (urbanape) wrote : | # |
Revision history for this message
Stuart Langridge (sil) wrote : | # |
1. it's an extension to save your bookmarks to your desktop CouchDB, not to Ubuntu One
2. it would be useful to add some comments explaining it a bit, perhaps?
3. localhost:5984 is hardcoded (this will obviously be changed later, so it's probably OK now)
review:
Approve
Revision history for this message
Joshua Blount (jblount) wrote : | # |
This looks fine, noting that sil's comments are valid.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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-02 15:11:14 +0000 |
14 | @@ -1,15 +1,25 @@ |
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 | + <script type="application/x-javascript" src="chrome://bindwood/content/couch.js" /> |
25 | + <script type="application/x-javascript" src="chrome://bindwood/content/sync.js" /> |
26 | + |
27 | + <window id="main-window"> |
28 | + <script type="application/x-javascript"> |
29 | + var bmsvc = Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Components.interfaces.nsINavBookmarksService); |
30 | + window.addEventListener("load", function() { bmsvc.addObserver(BindWoodListener, false); }, false); |
31 | + </script> |
32 | + </window> |
33 | + |
34 | <menupopup id="menu_ToolsPopup"> |
35 | - <menuitem id="UbuntuOneFirefoxBookmarkSync" |
36 | + <menuitem id="BindWoodSync" |
37 | label="Sync Bookmarks (well, alert)" |
38 | insertbefore="javascriptConsole" |
39 | - oncommand="alert('syncing...');" /> |
40 | + oncommand="syncBookmarks();" /> |
41 | </menupopup> |
42 | |
43 | </overlay> |
44 | |
45 | === added file 'chrome/content/couch.js' |
46 | --- chrome/content/couch.js 1970-01-01 00:00:00 +0000 |
47 | +++ chrome/content/couch.js 2009-07-02 15:11:14 +0000 |
48 | @@ -0,0 +1,395 @@ |
49 | +// Licensed under the Apache License, Version 2.0 (the "License"); you may not |
50 | +// use this file except in compliance with the License. You may obtain a copy |
51 | +// of the License at |
52 | +// |
53 | +// http://www.apache.org/licenses/LICENSE-2.0 |
54 | +// |
55 | +// Unless required by applicable law or agreed to in writing, software |
56 | +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
57 | +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
58 | +// License for the specific language governing permissions and limitations under |
59 | +// the License. |
60 | + |
61 | +// A simple class to represent a database. Uses XMLHttpRequest to interface with |
62 | +// the CouchDB server. |
63 | + |
64 | +function CouchDB(name, httpHeaders) { |
65 | + this.name = name; |
66 | + this.uri = "/" + encodeURIComponent(name) + "/"; |
67 | + |
68 | + // The XMLHttpRequest object from the most recent request. Callers can |
69 | + // use this to check result http status and headers. |
70 | + this.last_req = null; |
71 | + |
72 | + this.request = function(method, uri, requestOptions) { |
73 | + requestOptions = requestOptions || {} |
74 | + requestOptions.headers = combine(requestOptions.headers, httpHeaders) |
75 | + return CouchDB.request(method, uri, requestOptions); |
76 | + } |
77 | + |
78 | + // Creates the database on the server |
79 | + this.createDb = function() { |
80 | + this.last_req = this.request("PUT", this.uri); |
81 | + CouchDB.maybeThrowError(this.last_req); |
82 | + return JSON.parse(this.last_req.responseText); |
83 | + } |
84 | + |
85 | + // Deletes the database on the server |
86 | + this.deleteDb = function() { |
87 | + this.last_req = this.request("DELETE", this.uri); |
88 | + if (this.last_req.status == 404) |
89 | + return false; |
90 | + CouchDB.maybeThrowError(this.last_req); |
91 | + return JSON.parse(this.last_req.responseText); |
92 | + } |
93 | + |
94 | + // Save a document to the database |
95 | + this.save = function(doc, options) { |
96 | + if (doc._id == undefined) |
97 | + doc._id = CouchDB.newUuids(1)[0]; |
98 | + |
99 | + this.last_req = this.request("PUT", this.uri + |
100 | + encodeURIComponent(doc._id) + encodeOptions(options), |
101 | + {body: JSON.stringify(doc)}); |
102 | + CouchDB.maybeThrowError(this.last_req); |
103 | + var result = JSON.parse(this.last_req.responseText); |
104 | + doc._rev = result.rev; |
105 | + return result; |
106 | + } |
107 | + |
108 | + // Open a document from the database |
109 | + this.open = function(docId, options) { |
110 | + this.last_req = this.request("GET", this.uri + encodeURIComponent(docId) + encodeOptions(options)); |
111 | + if (this.last_req.status == 404) |
112 | + return null; |
113 | + CouchDB.maybeThrowError(this.last_req); |
114 | + return JSON.parse(this.last_req.responseText); |
115 | + } |
116 | + |
117 | + // Deletes a document from the database |
118 | + this.deleteDoc = function(doc) { |
119 | + this.last_req = this.request("DELETE", this.uri + encodeURIComponent(doc._id) + "?rev=" + doc._rev); |
120 | + CouchDB.maybeThrowError(this.last_req); |
121 | + var result = JSON.parse(this.last_req.responseText); |
122 | + doc._rev = result.rev; //record rev in input document |
123 | + doc._deleted = true; |
124 | + return result; |
125 | + } |
126 | + |
127 | + // Deletes an attachment from a document |
128 | + this.deleteDocAttachment = function(doc, attachment_name) { |
129 | + this.last_req = this.request("DELETE", this.uri + encodeURIComponent(doc._id) + "/" + attachment_name + "?rev=" + doc._rev); |
130 | + CouchDB.maybeThrowError(this.last_req); |
131 | + var result = JSON.parse(this.last_req.responseText); |
132 | + doc._rev = result.rev; //record rev in input document |
133 | + return result; |
134 | + } |
135 | + |
136 | + this.bulkSave = function(docs, options) { |
137 | + // first prepoulate the UUIDs for new documents |
138 | + var newCount = 0 |
139 | + for (var i=0; i<docs.length; i++) { |
140 | + if (docs[i]._id == undefined) |
141 | + newCount++; |
142 | + } |
143 | + var newUuids = CouchDB.newUuids(docs.length); |
144 | + var newCount = 0 |
145 | + for (var i=0; i<docs.length; i++) { |
146 | + if (docs[i]._id == undefined) |
147 | + docs[i]._id = newUuids.pop(); |
148 | + } |
149 | + var json = {"docs": docs}; |
150 | + // put any options in the json |
151 | + for (var option in options) { |
152 | + json[option] = options[option]; |
153 | + } |
154 | + this.last_req = this.request("POST", this.uri + "_bulk_docs", { |
155 | + body: JSON.stringify(json) |
156 | + }); |
157 | + if (this.last_req.status == 417) { |
158 | + return {errors: JSON.parse(this.last_req.responseText)}; |
159 | + } |
160 | + else { |
161 | + CouchDB.maybeThrowError(this.last_req); |
162 | + var results = JSON.parse(this.last_req.responseText); |
163 | + for (var i = 0; i < docs.length; i++) { |
164 | + if(results[i].rev) |
165 | + docs[i]._rev = results[i].rev; |
166 | + } |
167 | + return results; |
168 | + } |
169 | + } |
170 | + |
171 | + this.ensureFullCommit = function() { |
172 | + this.last_req = this.request("POST", this.uri + "_ensure_full_commit"); |
173 | + CouchDB.maybeThrowError(this.last_req); |
174 | + return JSON.parse(this.last_req.responseText); |
175 | + } |
176 | + |
177 | + // Applies the map function to the contents of database and returns the results. |
178 | + this.query = function(mapFun, reduceFun, options, keys) { |
179 | + var body = {language: "javascript"}; |
180 | + if(keys) { |
181 | + body.keys = keys ; |
182 | + } |
183 | + if (typeof(mapFun) != "string") |
184 | + mapFun = mapFun.toSource ? mapFun.toSource() : "(" + mapFun.toString() + ")"; |
185 | + body.map = mapFun; |
186 | + if (reduceFun != null) { |
187 | + if (typeof(reduceFun) != "string") |
188 | + reduceFun = reduceFun.toSource ? reduceFun.toSource() : "(" + reduceFun.toString() + ")"; |
189 | + body.reduce = reduceFun; |
190 | + } |
191 | + if (options && options.options != undefined) { |
192 | + body.options = options.options; |
193 | + delete options.options; |
194 | + } |
195 | + this.last_req = this.request("POST", this.uri + "_temp_view" + encodeOptions(options), { |
196 | + headers: {"Content-Type": "application/json"}, |
197 | + body: JSON.stringify(body) |
198 | + }); |
199 | + CouchDB.maybeThrowError(this.last_req); |
200 | + return JSON.parse(this.last_req.responseText); |
201 | + } |
202 | + |
203 | + this.view = function(viewname, options, keys) { |
204 | + var viewParts = viewname.split('/'); |
205 | + var viewPath = this.uri + "_design/" + viewParts[0] + "/_view/" |
206 | + + viewParts[1] + encodeOptions(options); |
207 | + if(!keys) { |
208 | + this.last_req = this.request("GET", viewPath); |
209 | + } else { |
210 | + this.last_req = this.request("POST", viewPath, { |
211 | + headers: {"Content-Type": "application/json"}, |
212 | + body: JSON.stringify({keys:keys}) |
213 | + }); |
214 | + } |
215 | + if (this.last_req.status == 404) |
216 | + return null; |
217 | + CouchDB.maybeThrowError(this.last_req); |
218 | + return JSON.parse(this.last_req.responseText); |
219 | + } |
220 | + |
221 | + // gets information about the database |
222 | + this.info = function() { |
223 | + this.last_req = this.request("GET", this.uri); |
224 | + CouchDB.maybeThrowError(this.last_req); |
225 | + return JSON.parse(this.last_req.responseText); |
226 | + } |
227 | + |
228 | + this.allDocs = function(options,keys) { |
229 | + if(!keys) { |
230 | + this.last_req = this.request("GET", this.uri + "_all_docs" + encodeOptions(options)); |
231 | + } else { |
232 | + this.last_req = this.request("POST", this.uri + "_all_docs" + encodeOptions(options), { |
233 | + headers: {"Content-Type": "application/json"}, |
234 | + body: JSON.stringify({keys:keys}) |
235 | + }); |
236 | + } |
237 | + CouchDB.maybeThrowError(this.last_req); |
238 | + return JSON.parse(this.last_req.responseText); |
239 | + } |
240 | + |
241 | + this.designDocs = function() { |
242 | + return this.allDocs({startkey:"_design", endkey:"_design0"}); |
243 | + }; |
244 | + |
245 | + this.allDocsBySeq = function(options,keys) { |
246 | + var req = null; |
247 | + if(!keys) { |
248 | + req = this.request("GET", this.uri + "_all_docs_by_seq" + encodeOptions(options)); |
249 | + } else { |
250 | + req = this.request("POST", this.uri + "_all_docs_by_seq" + encodeOptions(options), { |
251 | + headers: {"Content-Type": "application/json"}, |
252 | + body: JSON.stringify({keys:keys}) |
253 | + }); |
254 | + } |
255 | + CouchDB.maybeThrowError(req); |
256 | + return JSON.parse(req.responseText); |
257 | + } |
258 | + |
259 | + this.compact = function() { |
260 | + this.last_req = this.request("POST", this.uri + "_compact"); |
261 | + CouchDB.maybeThrowError(this.last_req); |
262 | + return JSON.parse(this.last_req.responseText); |
263 | + } |
264 | + |
265 | + this.setDbProperty = function(propId, propValue) { |
266 | + this.last_req = this.request("PUT", this.uri + propId,{ |
267 | + body:JSON.stringify(propValue) |
268 | + }); |
269 | + CouchDB.maybeThrowError(this.last_req); |
270 | + return JSON.parse(this.last_req.responseText); |
271 | + } |
272 | + |
273 | + this.getDbProperty = function(propId) { |
274 | + this.last_req = this.request("GET", this.uri + propId); |
275 | + CouchDB.maybeThrowError(this.last_req); |
276 | + return JSON.parse(this.last_req.responseText); |
277 | + } |
278 | + |
279 | + this.setAdmins = function(adminsArray) { |
280 | + this.last_req = this.request("PUT", this.uri + "_admins",{ |
281 | + body:JSON.stringify(adminsArray) |
282 | + }); |
283 | + CouchDB.maybeThrowError(this.last_req); |
284 | + return JSON.parse(this.last_req.responseText); |
285 | + } |
286 | + |
287 | + this.getAdmins = function() { |
288 | + this.last_req = this.request("GET", this.uri + "_admins"); |
289 | + CouchDB.maybeThrowError(this.last_req); |
290 | + return JSON.parse(this.last_req.responseText); |
291 | + } |
292 | + |
293 | + // Convert a options object to an url query string. |
294 | + // ex: {key:'value',key2:'value2'} becomes '?key="value"&key2="value2"' |
295 | + function encodeOptions(options) { |
296 | + var buf = [] |
297 | + if (typeof(options) == "object" && options !== null) { |
298 | + for (var name in options) { |
299 | + if (!options.hasOwnProperty(name)) continue; |
300 | + var value = options[name]; |
301 | + if (name == "key" || name == "startkey" || name == "endkey") { |
302 | + value = toJSON(value); |
303 | + } |
304 | + buf.push(encodeURIComponent(name) + "=" + encodeURIComponent(value)); |
305 | + } |
306 | + } |
307 | + if (!buf.length) { |
308 | + return ""; |
309 | + } |
310 | + return "?" + buf.join("&"); |
311 | + } |
312 | + |
313 | + function toJSON(obj) { |
314 | + return obj !== null ? JSON.stringify(obj) : null; |
315 | + } |
316 | + |
317 | + function combine(object1, object2) { |
318 | + if (!object2) |
319 | + return object1; |
320 | + if (!object1) |
321 | + return object2; |
322 | + |
323 | + for (var name in object2) |
324 | + object1[name] = object2[name]; |
325 | + |
326 | + return object1; |
327 | + } |
328 | + |
329 | + |
330 | +} |
331 | + |
332 | +// this is the XMLHttpRequest object from last request made by the following |
333 | +// CouchDB.* functions (except for calls to request itself). |
334 | +// Use this from callers to check HTTP status or header values of requests. |
335 | +CouchDB.last_req = null; |
336 | + |
337 | + |
338 | +CouchDB.allDbs = function() { |
339 | + CouchDB.last_req = CouchDB.request("GET", "/_all_dbs"); |
340 | + CouchDB.maybeThrowError(CouchDB.last_req); |
341 | + return JSON.parse(CouchDB.last_req.responseText); |
342 | +} |
343 | + |
344 | +CouchDB.allDesignDocs = function() { |
345 | + var ddocs = {}, dbs = CouchDB.allDbs(); |
346 | + for (var i=0; i < dbs.length; i++) { |
347 | + var db = new CouchDB(dbs[i]); |
348 | + ddocs[dbs[i]] = db.designDocs(); |
349 | + }; |
350 | + return ddocs; |
351 | +}; |
352 | + |
353 | +CouchDB.getVersion = function() { |
354 | + CouchDB.last_req = CouchDB.request("GET", "/"); |
355 | + CouchDB.maybeThrowError(CouchDB.last_req); |
356 | + return JSON.parse(CouchDB.last_req.responseText).version; |
357 | +} |
358 | + |
359 | +CouchDB.replicate = function(source, target, rep_options) { |
360 | + rep_options = rep_options || {}; |
361 | + var headers = rep_options.headers || {}; |
362 | + CouchDB.last_req = CouchDB.request("POST", "/_replicate", { |
363 | + headers: headers, |
364 | + body: JSON.stringify({source: source, target: target}) |
365 | + }); |
366 | + CouchDB.maybeThrowError(CouchDB.last_req); |
367 | + return JSON.parse(CouchDB.last_req.responseText); |
368 | +} |
369 | + |
370 | +CouchDB.request = function(method, uri, options) { |
371 | + options = options || {}; |
372 | + var req = null; |
373 | + if (typeof(XMLHttpRequest) != "undefined") { |
374 | + req = new XMLHttpRequest(); |
375 | + } else if (typeof(ActiveXObject) != "undefined") { |
376 | + req = new ActiveXObject("Microsoft.XMLHTTP"); |
377 | + } else { |
378 | + throw new Error("No XMLHTTPRequest support detected"); |
379 | + } |
380 | + req.open(method, "http://localhost:5984" + uri, false); |
381 | + if (options.headers) { |
382 | + var headers = options.headers; |
383 | + for (var headerName in headers) { |
384 | + if (!headers.hasOwnProperty(headerName)) continue; |
385 | + req.setRequestHeader(headerName, headers[headerName]); |
386 | + } |
387 | + } |
388 | + req.send(options.body || ""); |
389 | + return req; |
390 | +} |
391 | + |
392 | +CouchDB.requestStats = function(module, key, test) { |
393 | + var query_arg = ""; |
394 | + if(test !== null) { |
395 | + query_arg = "?flush=true"; |
396 | + } |
397 | + |
398 | + var stat = CouchDB.request("GET", "/_stats/" + module + "/" + key + query_arg).responseText; |
399 | + return JSON.parse(stat)[module][key]; |
400 | +} |
401 | + |
402 | +CouchDB.uuids_cache = []; |
403 | + |
404 | +CouchDB.newUuids = function(n) { |
405 | + if (CouchDB.uuids_cache.length >= n) { |
406 | + var uuids = CouchDB.uuids_cache.slice(CouchDB.uuids_cache.length - n); |
407 | + if(CouchDB.uuids_cache.length - n == 0) { |
408 | + CouchDB.uuids_cache = []; |
409 | + } else { |
410 | + CouchDB.uuids_cache = |
411 | + CouchDB.uuids_cache.slice(0, CouchDB.uuids_cache.length - n); |
412 | + } |
413 | + return uuids; |
414 | + } else { |
415 | + CouchDB.last_req = CouchDB.request("GET", "/_uuids?count=" + (100 + n)); |
416 | + CouchDB.maybeThrowError(CouchDB.last_req); |
417 | + var result = JSON.parse(CouchDB.last_req.responseText); |
418 | + CouchDB.uuids_cache = |
419 | + CouchDB.uuids_cache.concat(result.uuids.slice(0, 100)); |
420 | + return result.uuids.slice(100); |
421 | + } |
422 | +} |
423 | + |
424 | +CouchDB.maybeThrowError = function(req) { |
425 | + if (req.status >= 400) { |
426 | + try { |
427 | + var result = JSON.parse(req.responseText); |
428 | + } catch (ParseError) { |
429 | + var result = {error:"unknown", reason:req.responseText}; |
430 | + } |
431 | + throw result; |
432 | + } |
433 | +} |
434 | + |
435 | +CouchDB.params = function(options) { |
436 | + options = options || {}; |
437 | + var returnArray = []; |
438 | + for(var key in options) { |
439 | + var value = options[key]; |
440 | + returnArray.push(key + "=" + value); |
441 | + } |
442 | + return returnArray.join("&"); |
443 | +} |
444 | \ No newline at end of file |
445 | |
446 | === added file 'chrome/content/sync.js' |
447 | --- chrome/content/sync.js 1970-01-01 00:00:00 +0000 |
448 | +++ chrome/content/sync.js 2009-07-02 17:56:29 +0000 |
449 | @@ -0,0 +1,68 @@ |
450 | +function _writeMessage(aMessage) { |
451 | + var consoleService = Components.classes["@mozilla.org/consoleservice;1"] |
452 | + .getService(Components.interfaces.nsIConsoleService); |
453 | + consoleService.logStringMessage("BindWood: " + aMessage); |
454 | +} |
455 | + |
456 | +function syncBookmarks() { |
457 | + alert('Syncing bookmarks...'); |
458 | + var couch = new CouchDB('bookmarks'); |
459 | + var total_documents = couch.allDocs().total_rows; |
460 | + var all_documents = new Array(); |
461 | + if (total_documents > 0) { |
462 | + all_documents = dc.all('bookmarks').rows; |
463 | + } |
464 | + |
465 | + for (var i = 0; i < all_documents.length; i++) { |
466 | + alert(all_documents[i].id); |
467 | + } |
468 | +} |
469 | + |
470 | +// An nsINavBookmarkObserver |
471 | +var BindWoodListener = { |
472 | + onBeginUpdateBatch: function() {}, |
473 | + onEndUpdateBatch: function() {}, |
474 | + onItemAdded: function(aItemId, aFolder, aIndex) { |
475 | + netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead UniversalBrowserWrite"); |
476 | + var couch = new CouchDB('bookmarks'); |
477 | + |
478 | + var doc = {_id: aItemId.toString()}; |
479 | + try { |
480 | + var result = couch.save(doc); |
481 | + _writeMessage("Saved record: " + result.rev); |
482 | + } catch(e) { |
483 | + _writeMessage("Error '" + e.error + "': " + e.reason); |
484 | + } |
485 | + return true; |
486 | + }, |
487 | + onItemRemoved: function(aItemId, aFolder, aIndex) { |
488 | + netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead UniversalBrowserWrite"); |
489 | + var couch = new CouchDB('bookmarks'); |
490 | + |
491 | + var doc = couch.open(aItemId.toString()); |
492 | + try { |
493 | + var result = couch.deleteDoc(doc); |
494 | + _writeMessage("Deleted record: " + result.rev); |
495 | + } catch(e) { |
496 | + _writeMessage("Error '" + e.error + "': " + e.reason); |
497 | + } |
498 | + return true; |
499 | + }, |
500 | + onItemChanged: function(aBookmarkId, aProperty, aIsAnnotationProperty, aValue) { |
501 | + netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead UniversalBrowserWrite"); |
502 | + var couch = new CouchDB('bookmarks'); |
503 | + |
504 | + var doc = couch.open(aBookmarkId.toString()); |
505 | + doc[aProperty.toString()] = aValue.toString(); |
506 | + try { |
507 | + var result = couch.save(doc); |
508 | + _writeMessage("Updated record: " + result.rev + ", setting (" + aProperty.toString() + ") to (" + aValue.toString() + ")"); |
509 | + } catch(e) { |
510 | + _writeMessage("Error '" + e.error + "': " + e.reason); |
511 | + } |
512 | + return true; |
513 | + }, |
514 | + onItemVisited: function(aBookmarkId, aVisitID, time) {}, |
515 | + onItemMoved: function(aItemId, aOldParent, aOldIndex, aNewParent, aNewIndex) {}, |
516 | + QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsINavBookmarkObserver]) |
517 | +}; |
518 | |
519 | === added file 'chrome/content/tutorial.html' |
520 | --- chrome/content/tutorial.html 1970-01-01 00:00:00 +0000 |
521 | +++ chrome/content/tutorial.html 2009-07-02 13:41:07 +0000 |
522 | @@ -0,0 +1,8 @@ |
523 | +<html> |
524 | + <head> |
525 | + <title>BindWood Tutorial</title> |
526 | + </head> |
527 | + <body> |
528 | + This is BindWood. |
529 | + </body> |
530 | +</html> |
531 | |
532 | === added directory 'chrome/locale' |
533 | === added directory 'chrome/locale/en' |
534 | === added file 'chrome/locale/en/bindwood.dtd' |
535 | === modified file 'install.rdf' |
536 | --- install.rdf 2009-06-30 13:47:50 +0000 |
537 | +++ install.rdf 2009-07-02 13:41:07 +0000 |
538 | @@ -3,7 +3,7 @@ |
539 | xmlns:em="http://www.mozilla.org/2004/em-rdf#"> |
540 | |
541 | <Description about="urn:mozilla:install-manifest"> |
542 | - <em:id>ubuntuone-firefox-bookmark-sync@ubuntu.com</em:id> |
543 | + <em:id>bindwood@ubuntu.com</em:id> |
544 | <em:version>1.0</em:version> |
545 | <em:type>2</em:type> |
546 | |
547 | @@ -18,7 +18,7 @@ |
548 | </em:targetApplication> |
549 | |
550 | <!-- Front End MetaData --> |
551 | - <em:name>UbuntuOne Bookmarks Sync</em:name> |
552 | + <em:name>BindWood</em:name> |
553 | <em:description>An extension to synchronize your bookmarks to UbuntuOne.</em:description> |
554 | <em:creator>Zachery Bir</em:creator> |
555 | <em:homepageURL>http://launchpad.net/ubuntuone-firefox-bookmark-sync</em:homepageURL> |
We now have an extension that will propagate additions to CouchDB as well as deletions and modifications to bookmarks currently in CouchDB.
To test:
1) Ensure you have a local CouchDB running on port 5984 with a database called 'bookmarks'.
2) (Re)start Firefox with the extension in place
3) Add a bookmark
4) Verify it exists in CouchDB
5) Change the bookmark in Firefox (title or URI)
6) Verify the change was recorded (new rev #)
7) Delete the bookmark in Firefox
8) Verify the record no longer exists in CouchDB