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