Merge lp:~ubuntuone-control-tower/ubuntu/lucid/ubuntuone-client/trunk into lp:ubuntu/lucid/ubuntuone-client
- Lucid (10.04)
- trunk
- Merge into lucid
Proposed by
dobey
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~ubuntuone-control-tower/ubuntu/lucid/ubuntuone-client/trunk |
Merge into: | lp:ubuntu/lucid/ubuntuone-client |
Diff against target: |
6279 lines (+3033/-1028) 57 files modified
Makefile.am (+1/-0) Makefile.in (+1/-0) bin/u1sdtool (+0/-1) bin/ubuntuone-launch (+78/-0) bin/ubuntuone-preferences (+796/-480) bin/ubuntuone-syncdaemon (+9/-7) configure (+10/-10) configure.ac (+1/-1) contrib/mocker.py (+4/-1) contrib/pylint-wrapper (+2/-1) contrib/testing/testcase.py (+24/-0) data/Makefile.am (+6/-0) data/Makefile.in (+50/-22) data/oauth_registration.d/ubuntuone (+10/-0) data/source_ubuntuone-client.py (+17/-6) data/syncdaemon.conf (+16/-0) data/ubuntuone-launch.desktop.in (+7/-0) data/ubuntuone-preferences.desktop.in (+1/-1) debian/changelog (+13/-0) debian/control (+0/-1) debian/source/format (+2/-0) docs/states_manager.svg (+225/-133) docs/syncdaemon_dbus_api.txt (+9/-0) nautilus/contacts-view.c (+136/-47) nautilus/contacts-view.h (+3/-0) nautilus/u1-contacts-picker.c (+111/-12) nautilus/u1-contacts-picker.h (+3/-0) nautilus/ubuntuone-nautilus.c (+19/-2) tests/syncdaemon/test_action_queue.py (+60/-1) tests/syncdaemon/test_config.py (+39/-0) tests/syncdaemon/test_dbus.py (+17/-0) tests/syncdaemon/test_eq_inotify.py (+184/-35) tests/syncdaemon/test_eventqueue.py (+36/-0) tests/syncdaemon/test_fileshelf.py (+39/-3) tests/syncdaemon/test_fsm.py (+304/-30) tests/syncdaemon/test_hashqueue.py (+3/-2) tests/syncdaemon/test_localrescan.py (+51/-1) tests/syncdaemon/test_states.py (+8/-21) tests/syncdaemon/test_u1sdtool.py (+13/-2) tests/syncdaemon/test_vm.py (+187/-1) tests/test_preferences.py (+135/-19) ubuntuone/oauthdesktop/key_acls.py (+1/-1) ubuntuone/oauthdesktop/main.py (+3/-4) ubuntuone/syncdaemon/action_queue.py (+8/-8) ubuntuone/syncdaemon/config.py (+9/-0) ubuntuone/syncdaemon/dbus_interface.py (+42/-14) ubuntuone/syncdaemon/event_queue.py (+77/-21) ubuntuone/syncdaemon/file_shelf.py (+9/-0) ubuntuone/syncdaemon/filesystem_manager.py (+50/-27) ubuntuone/syncdaemon/hash_queue.py (+4/-3) ubuntuone/syncdaemon/local_rescan.py (+82/-55) ubuntuone/syncdaemon/logger.py (+9/-0) ubuntuone/syncdaemon/main.py (+3/-2) ubuntuone/syncdaemon/states.py (+6/-0) ubuntuone/syncdaemon/sync.py (+6/-14) ubuntuone/syncdaemon/tools.py (+36/-1) ubuntuone/syncdaemon/volume_manager.py (+58/-38) |
To merge this branch: | bzr merge lp:~ubuntuone-control-tower/ubuntu/lucid/ubuntuone-client/trunk |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu branches | Pending | ||
Review via email: mp+22582@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
- 24. By Luke Yelavich
-
* New upstream release.
- Notify user when approaching and exceeding quota (LP: #540360)
- Add instructional text to ubuntuone-preferences (LP: #539676)
- UbuntuOne needs to autostart and connect by defualt (LP: #534707)
- Impossible to infer status of file synchronization (LP: #526084)
- Devices and Services tabs functionality and development (LP: #525803)
* Add debian/source/ format.
* Remove python-httplib2 from the dependencies (LP: #535207) - 25. By Luke Yelavich
-
releasing version 1.1.90-0ubuntu1
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'Makefile.am' |
2 | --- Makefile.am 2010-02-17 23:51:29 +0000 |
3 | +++ Makefile.am 2010-03-31 23:55:45 +0000 |
4 | @@ -19,6 +19,7 @@ |
5 | # Install our scripts and extra data here |
6 | bin_SCRIPTS = \ |
7 | bin/ubuntuone-preferences \ |
8 | + bin/ubuntuone-launch \ |
9 | bin/u1sdtool \ |
10 | bin/u1sync |
11 | |
12 | |
13 | === modified file 'Makefile.in' |
14 | --- Makefile.in 2010-02-17 23:51:29 +0000 |
15 | +++ Makefile.in 2010-03-31 23:55:45 +0000 |
16 | @@ -296,6 +296,7 @@ |
17 | # Install our scripts and extra data here |
18 | bin_SCRIPTS = \ |
19 | bin/ubuntuone-preferences \ |
20 | + bin/ubuntuone-launch \ |
21 | bin/u1sdtool \ |
22 | bin/u1sync |
23 | |
24 | |
25 | === modified file 'bin/u1sdtool' |
26 | --- bin/u1sdtool 2010-03-10 23:36:53 +0000 |
27 | +++ bin/u1sdtool 2010-03-31 23:55:45 +0000 |
28 | @@ -31,7 +31,6 @@ |
29 | from twisted.internet import reactor, defer |
30 | |
31 | from ubuntuone.syncdaemon.tools import SyncDaemonTool |
32 | -from ubuntuone.syncdaemon.dbus_interface import DBUS_IFACE_NAME |
33 | from ubuntuone.syncdaemon.tools import ( |
34 | show_path_info, |
35 | show_uploads, |
36 | |
37 | === added file 'bin/ubuntuone-launch' |
38 | --- bin/ubuntuone-launch 1970-01-01 00:00:00 +0000 |
39 | +++ bin/ubuntuone-launch 2010-03-31 23:55:45 +0000 |
40 | @@ -0,0 +1,78 @@ |
41 | +#!/usr/bin/python |
42 | + |
43 | +# ubuntuone-start - Ubuntu One storage synchronization daemon startup helper |
44 | +# |
45 | +# Author: John Lenton <john.lenton@canonical.com> |
46 | +# |
47 | +# Copyright 2010 Canonical Ltd. |
48 | +# |
49 | +# This program is free software: you can redistribute it and/or modify it |
50 | +# under the terms of the GNU General Public License version 3, as published |
51 | +# by the Free Software Foundation. |
52 | +# |
53 | +# This program is distributed in the hope that it will be useful, but |
54 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
55 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
56 | +# PURPOSE. See the GNU General Public License for more details. |
57 | +# |
58 | +# You should have received a copy of the GNU General Public License along |
59 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
60 | + |
61 | +import sys |
62 | +import os |
63 | + |
64 | +U1ROOT=os.path.expanduser('~/Ubuntu One/') |
65 | + |
66 | +if __name__ == '__main__': |
67 | + # this check is done early to speed up startup on people who are not |
68 | + # (yet) using the service (this way avoids all the imports). |
69 | + if not os.path.isdir(U1ROOT): |
70 | + # no directory, just quit |
71 | + sys.exit(1) |
72 | + |
73 | +import dbus |
74 | +import glib |
75 | +import gobject |
76 | +import gnomekeyring |
77 | +from dbus.mainloop.glib import DBusGMainLoop |
78 | +from ubuntuone.syncdaemon.tools import SyncDaemonTool, is_running |
79 | +from twisted.internet import defer |
80 | + |
81 | +def stage_two(node, sync_daemon_tool): |
82 | + """do the last few checks and ask for a connect if all is ok""" |
83 | + d = None |
84 | + if node['node_id'] is not None: |
85 | + items = gnomekeyring.find_items_sync( |
86 | + gnomekeyring.ITEM_GENERIC_SECRET, |
87 | + {'ubuntuone-realm': 'https://ubuntuone.com'}) |
88 | + if items: |
89 | + d = sync_daemon_tool.connect() |
90 | + if d is None: |
91 | + d = defer.fail(RuntimeError("bad node")) |
92 | + return d |
93 | + |
94 | +def main(): |
95 | + """Start syncdaemon and ask it to connect, if possible.""" |
96 | + gobject.set_application_name("ubuntuone-launch") |
97 | + loop = DBusGMainLoop(set_as_default=True) |
98 | + bus = dbus.SessionBus(mainloop=loop) |
99 | + sync_daemon_tool = SyncDaemonTool(bus) |
100 | + |
101 | + if not is_running(): |
102 | + try: |
103 | + d = sync_daemon_tool.start() |
104 | + except dbus.exception.DBusException, e: |
105 | + d = defer.fail(e) |
106 | + else: |
107 | + d = defer.succeed(True) |
108 | + |
109 | + d.addCallback(lambda _: sync_daemon_tool.get_metadata(U1ROOT)) |
110 | + d.addCallback(stage_two, sync_daemon_tool) |
111 | + # os._exit feels like it's cheating, but it's simple and fast |
112 | + d.addCallbacks(lambda _: os._exit(0), lambda _: os._exit(1)) |
113 | + |
114 | + mainloop = glib.MainLoop() |
115 | + mainloop.run() |
116 | + |
117 | +if __name__ == '__main__': |
118 | + main() |
119 | |
120 | === modified file 'bin/ubuntuone-preferences' |
121 | --- bin/ubuntuone-preferences 2010-03-10 23:36:53 +0000 |
122 | +++ bin/ubuntuone-preferences 2010-03-31 23:55:45 +0000 |
123 | @@ -1,4 +1,5 @@ |
124 | #!/usr/bin/python |
125 | +# -*- coding: utf-8 -*- |
126 | |
127 | # ubuntuone-client-applet - Tray icon applet for managing Ubuntu One |
128 | # |
129 | @@ -23,37 +24,47 @@ |
130 | import pygtk |
131 | pygtk.require('2.0') |
132 | import gobject |
133 | -import glib |
134 | import gtk |
135 | import os |
136 | +import sys |
137 | +import time |
138 | import gettext |
139 | import gnomekeyring |
140 | -import httplib2 |
141 | import simplejson |
142 | +import subprocess |
143 | +from threading import Thread |
144 | from oauth import oauth |
145 | from ubuntuone import clientdefs |
146 | from ubuntuone.syncdaemon.tools import SyncDaemonTool |
147 | -from ubuntuone.syncdaemon.logger import LOGFOLDER |
148 | +from ubuntuone.syncdaemon.logger import (basic_formatter, LOGFOLDER, |
149 | + CustomRotatingFileHandler) |
150 | |
151 | import logging |
152 | -import sys |
153 | import httplib, urlparse, socket |
154 | |
155 | import dbus.service |
156 | -from ConfigParser import ConfigParser |
157 | from dbus.exceptions import DBusException |
158 | from dbus.mainloop.glib import DBusGMainLoop |
159 | -from xdg.BaseDirectory import xdg_config_home |
160 | |
161 | -logging.basicConfig( |
162 | - filename=os.path.join(LOGFOLDER, 'u1-prefs.log'), |
163 | - level=logging.DEBUG, |
164 | - format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") |
165 | logger = logging.getLogger("ubuntuone-preferences") |
166 | +logger.setLevel(logging.DEBUG) |
167 | +handler = CustomRotatingFileHandler(filename=os.path.join(LOGFOLDER, |
168 | + 'u1-prefs.log')) |
169 | +handler.setFormatter(basic_formatter) |
170 | +handler.setLevel(logging.DEBUG) |
171 | +logger.addHandler(handler) |
172 | + |
173 | +_ = gettext.gettext |
174 | + |
175 | +dcouch = None |
176 | +# Try importing the Ubuntu One desktopcouch enablement api |
177 | +try: |
178 | + from desktopcouch.replication_services import ubuntuone as dcouch |
179 | +except ImportError: |
180 | + logger.error(_("DesktopCouch replication API not found")) |
181 | + |
182 | DBusGMainLoop(set_as_default=True) |
183 | |
184 | -_ = gettext.gettext |
185 | - |
186 | DBUS_IFACE_NAME = "com.ubuntuone.SyncDaemon" |
187 | DBUS_IFACE_CONFIG_NAME = DBUS_IFACE_NAME + ".Config" |
188 | DBUS_IFACE_STATUS_NAME = DBUS_IFACE_NAME + ".Status" |
189 | @@ -61,6 +72,12 @@ |
190 | DBUS_IFACE_AUTH_NAME = "com.ubuntuone.Authentication" |
191 | DBUS_IFACE_AUTH_PATH = "/" |
192 | |
193 | +# Our own DBus interface |
194 | +PREFS_BUS_NAME = "com.ubuntuone.Preferences" |
195 | + |
196 | +# This is where thy music lies |
197 | +U1MSPATH = os.path.expanduser("~/.ubuntuone/Purchased from Ubuntu One") |
198 | + |
199 | # Why thank you GTK+ for enforcing style-set and breaking API |
200 | RCSTYLE = """ |
201 | style 'dialogs' { |
202 | @@ -71,19 +88,87 @@ |
203 | widget_class '*Dialog*' style 'dialogs' |
204 | """ |
205 | |
206 | -def dbus_async(*args): |
207 | - """Simple handler to make dbus do stuff async.""" |
208 | - pass |
209 | - |
210 | +# Some defines for the bindwood installation magic |
211 | +BW_PKG_NAME = "xul-ext-bindwood" |
212 | +BW_INST_ARGS = ['apturl', 'apt:%s?section=universe' % BW_PKG_NAME] |
213 | +BW_CHCK_ARGS = ['dpkg', '-l', BW_PKG_NAME] |
214 | + |
215 | +# This is a global so we can avoid creating multiple instances in some cases |
216 | +prefs_dialog = None |
217 | + |
218 | +def dbus_async(*args, **kwargs): |
219 | + """Simple handler to make dbus do stuff async.""" |
220 | + pass |
221 | + |
222 | + |
223 | +def do_login_request(bus, error_handler): |
224 | + """Make a login request to the login handling daemon.""" |
225 | + try: |
226 | + client = bus.get_object(DBUS_IFACE_AUTH_NAME, |
227 | + DBUS_IFACE_AUTH_PATH, |
228 | + follow_name_owner_changes=True) |
229 | + iface = dbus.Interface(client, DBUS_IFACE_AUTH_NAME) |
230 | + iface.login('https://ubuntuone.com', 'ubuntuone', |
231 | + reply_handler=dbus_async, |
232 | + error_handler=error_handler) |
233 | + except DBusException, e: |
234 | + error_handler(e) |
235 | |
236 | def get_access_token(keyring): |
237 | - items = [] |
238 | - items = keyring.find_items_sync( |
239 | + """Get the access token from the keyring.""" |
240 | + items = [] |
241 | + try: |
242 | + items = keyring.find_items_sync( |
243 | keyring.ITEM_GENERIC_SECRET, |
244 | {'ubuntuone-realm': "https://ubuntuone.com", |
245 | 'oauth-consumer-key': 'ubuntuone'}) |
246 | - secret = items[0].secret |
247 | - return oauth.OAuthToken.from_string(secret) |
248 | + secret = items[0].secret |
249 | + return oauth.OAuthToken.from_string(secret) |
250 | + except (gnomekeyring.NoMatchError, gnomekeyring.DeniedError): |
251 | + return None |
252 | + |
253 | +def do_rest_request(url, method, token, callback): |
254 | + """ |
255 | + Helper that handles the REST response. |
256 | + """ |
257 | + result = None |
258 | + consumer = oauth.OAuthConsumer('ubuntuone', 'hammertime') |
259 | + |
260 | + oauth_request = oauth.OAuthRequest.from_consumer_and_token( |
261 | + http_url=url, |
262 | + http_method=method, |
263 | + oauth_consumer=consumer, |
264 | + token=token, |
265 | + parameters='') |
266 | + oauth_request.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(), |
267 | + consumer, token) |
268 | + |
269 | + scheme, netloc, path, query, fragment = urlparse.urlsplit(url) |
270 | + |
271 | + conn = httplib.HTTPSConnection(netloc) |
272 | + try: |
273 | + conn.request(method, path, headers=oauth_request.to_header()) |
274 | + response = conn.getresponse() # shouldn't block |
275 | + if response.status == 200: |
276 | + data = response.read() # neither should this |
277 | + result = simplejson.loads(data) |
278 | + else: |
279 | + result = {'status' : response.status, |
280 | + 'reason' : response.reason} |
281 | + except socket.error, e: |
282 | + result = {'error' : e} |
283 | + |
284 | + gtk.gdk.threads_enter() |
285 | + callback(result) |
286 | + gtk.gdk.threads_leave() |
287 | + |
288 | +def make_rest_request(url=None, method='GET', |
289 | + callback=None, keyring=None): |
290 | + """ |
291 | + Helper that makes an oauth-wrapped REST request. |
292 | + """ |
293 | + token = get_access_token(keyring) |
294 | + Thread(target=do_rest_request, args=(url, method, token, callback)).start() |
295 | |
296 | |
297 | class DevicesWidget(gtk.Table): |
298 | @@ -125,8 +210,8 @@ |
299 | self.status_label = gtk.Label("") |
300 | self.attach(self.status_label, 0, 3, 2, 3) |
301 | |
302 | - self.description = gtk.Label(_("The devices connected to with your" |
303 | - " personal cloud network" |
304 | + self.description = gtk.Label(_("The devices connected with your" |
305 | + " pseronal cloud network" |
306 | " are listed below")) |
307 | self.description.set_alignment(0., .5) |
308 | self.description.set_line_wrap(True) |
309 | @@ -227,34 +312,6 @@ |
310 | self.status_label.set_markup("<b>Error:</b> %s" % msg) |
311 | logger.error(msg) |
312 | |
313 | - def request(self, path='', method='GET'): |
314 | - """ |
315 | - Helper that makes an oauth-wrapped rest request. |
316 | - |
317 | - XXX duplication with request_REST_info (but this one should be async). |
318 | - """ |
319 | - url = self.base_url + path |
320 | - |
321 | - token = get_access_token(self.keyring) |
322 | - |
323 | - oauth_request = oauth.OAuthRequest.from_consumer_and_token( |
324 | - http_url=url, |
325 | - http_method=method, |
326 | - oauth_consumer=self.consumer, |
327 | - token=token, |
328 | - parameters='') |
329 | - oauth_request.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(), |
330 | - self.consumer, token) |
331 | - |
332 | - scheme, netloc, path, query, fragment = urlparse.urlsplit(url) |
333 | - |
334 | - conn = httplib.HTTPSConnection(netloc) |
335 | - try: |
336 | - conn.request(method, path, headers=oauth_request.to_header()) |
337 | - except socket.error: |
338 | - return None |
339 | - return conn |
340 | - |
341 | def get_devices(self): |
342 | """ |
343 | Ask the server for a list of devices |
344 | @@ -262,36 +319,38 @@ |
345 | Hook up parse_devices to run on the result (when it gets here). |
346 | """ |
347 | try: |
348 | - token = get_access_token(self.keyring) |
349 | + get_access_token(self.keyring) |
350 | except gnomekeyring.NoMatchError: |
351 | self.error("No token in the keyring") |
352 | - self.devices = [] |
353 | + self.devices = None |
354 | else: |
355 | - self.consumer = oauth.OAuthConsumer("ubuntuone", "hammertime") |
356 | - |
357 | - self.conn = self.request() |
358 | - if self.conn is None: |
359 | - self.clear_devices_view() |
360 | - self.error('Unable to connect') |
361 | - else: |
362 | - glib.io_add_watch( |
363 | - self.conn.sock, |
364 | - glib.IO_IN | glib.IO_PRI | glib.IO_ERR | glib.IO_HUP, |
365 | - self.parse_devices) |
366 | - |
367 | - def parse_devices(self, *a): |
368 | + make_rest_request(url=self.base_url, keyring=self.keyring, |
369 | + callback=self.parse_devices) |
370 | + |
371 | + def parse_devices(self, result): |
372 | """ |
373 | Parse the list of devices, and hook up list_devices if it worked. |
374 | """ |
375 | - response = self.conn.getresponse() # shouldn't block |
376 | - if response.status == 200: |
377 | - response = response.read() # neither should this |
378 | - self.devices = simplejson.loads(response) |
379 | - gobject.idle_add(self.list_devices) |
380 | + is_error = False |
381 | + error = None |
382 | + if result and isinstance(result, list): |
383 | + self.devices = result |
384 | + elif isinstance(result, dict): |
385 | + error = result.get('error', None) |
386 | + if not error and result.get('status', None): |
387 | + is_error = True |
388 | + error = result.get('reason', None) |
389 | + else: |
390 | + is_error = True |
391 | else: |
392 | - self.clear_devices_view() |
393 | - self.error(response.reason) |
394 | - return False |
395 | + error = "Got empty result for devices list." |
396 | + is_error = True |
397 | + |
398 | + if is_error: |
399 | + self.devices = None |
400 | + self.error(error) |
401 | + |
402 | + gobject.idle_add(self.list_devices) |
403 | |
404 | def clear_devices_view(self): |
405 | """ |
406 | @@ -315,8 +374,6 @@ |
407 | If the list of devices is empty, make a fake one that refers |
408 | to the local machine (to get the connect/restart buttons). |
409 | """ |
410 | - self.resize(len(self.devices)+1, 3) |
411 | - |
412 | self.clear_devices_view() |
413 | |
414 | token = get_access_token(self.keyring) |
415 | @@ -325,8 +382,10 @@ |
416 | # a stopgap device so you can at least try to connect |
417 | self.devices = [{'kind': 'Computer', |
418 | 'description': _("<LOCAL MACHINE>"), |
419 | - 'token': token.key, |
420 | + 'token': token.key if token else '', |
421 | 'FAKE': 'YES'}] |
422 | + else: |
423 | + self.resize(len(self.devices)+1, 3) |
424 | |
425 | self.status_label.set_label("") |
426 | |
427 | @@ -340,17 +399,18 @@ |
428 | self.attach(img, 0, 1, i, i+1) |
429 | self.attach(desc, 1, 2, i, i+1) |
430 | if 'FAKE' not in row: |
431 | - # we don't include the "Remove" button for the fake entry :) |
432 | - butn = gtk.Button(_('Remove')) |
433 | - butn.connect('clicked', self.remove, |
434 | - row['kind'], row.get('token')) |
435 | - self.attach(butn, 2, 3, i, i+1, xoptions=0, yoptions=0) |
436 | - if row.get('token') == token.key: |
437 | + # we don't include the "Remove" button for the fake entry :) |
438 | + butn = gtk.Button(_("Remove")) |
439 | + butn.connect('clicked', self.remove, |
440 | + row['kind'], row.get('token')) |
441 | + self.attach(butn, 2, 3, i, i+1, xoptions=0, yoptions=0) |
442 | + if token and row.get('token') == token.key or 'FAKE' in row: |
443 | self.bw_chk = ck_btn = gtk.CheckButton( |
444 | - _("_Limit Bandwidth Usage")) |
445 | + _("_Limit Bandwidth Usage")) |
446 | ck_btn.set_active(self.bw_limited) |
447 | up_lbl = gtk.Label(_("Maximum _upload speed (KB/s):")) |
448 | up_lbl.set_alignment(0., .5) |
449 | + up_lbl.set_use_underline(True) |
450 | adj = gtk.Adjustment(value=self.up_limit/1024., |
451 | lower=0.0, upper=4096.0, |
452 | step_incr=1.0, page_incr=16.0) |
453 | @@ -359,6 +419,7 @@ |
454 | up_lbl.set_mnemonic_widget(up_btn) |
455 | dn_lbl = gtk.Label(_("Maximum _download speed (KB/s):")) |
456 | dn_lbl.set_alignment(0., .5) |
457 | + dn_lbl.set_use_underline(True) |
458 | adj = gtk.Adjustment(value=self.dn_limit/1024., |
459 | lower=0.0, upper=4096.0, |
460 | step_incr=1.0, page_incr=16.0) |
461 | @@ -370,13 +431,13 @@ |
462 | self.handle_bw_checkbox_toggled(ck_btn, |
463 | up_lbl, up_btn, dn_lbl, dn_btn) |
464 | |
465 | - self.conn_btn = gtk.Button(_('Connect')) |
466 | + self.conn_btn = gtk.Button(_("Connect")) |
467 | if self.connected is None: |
468 | self.conn_btn.set_sensitive(False) |
469 | elif self.connected: |
470 | - self.conn_btn.set_label(_('Disconnect')) |
471 | + self.conn_btn.set_label(_("Disconnect")) |
472 | self.conn_btn.connect('clicked', self.handle_connect_button) |
473 | - restart_btn = gtk.Button(_('Restart')) |
474 | + restart_btn = gtk.Button(_("Restart")) |
475 | restart_btn.connect('clicked', self.handle_restart_button) |
476 | btn_box = gtk.HButtonBox() |
477 | btn_box.add(self.conn_btn) |
478 | @@ -401,9 +462,9 @@ |
479 | """ |
480 | self.conn_btn.set_sensitive(False) |
481 | if self.connected: |
482 | - d = self.sdtool.disconnect() |
483 | + self.sdtool.disconnect() |
484 | else: |
485 | - d = self.sdtool.connect() |
486 | + self.sdtool.connect() |
487 | |
488 | def handle_restart_button(self, *a): |
489 | """ |
490 | @@ -417,402 +478,657 @@ |
491 | |
492 | Starts an async request to remove a device. |
493 | """ |
494 | - self.conn = self.request('remove/%s/%s' % (kind.lower(), token)) |
495 | - if self.conn is None: |
496 | - self.clear_devices_view() |
497 | - self.error('Unable to connect') |
498 | - else: |
499 | - glib.io_add_watch( |
500 | - self.conn.sock, |
501 | - glib.IO_IN | glib.IO_PRI | glib.IO_ERR | glib.IO_HUP, |
502 | - self.parse_devices) |
503 | + make_rest_request(url=('%sremove/%s/%s' % (self.base_url, |
504 | + kind.lower(), token)), |
505 | + keyring=self.keyring, |
506 | + callback=self.parse_devices) |
507 | + local = get_access_token(self.keyring) |
508 | + def local_removal_cb(*args, **kwargs): |
509 | + """Try to get a new token if we remove the local one.""" |
510 | + do_login_request(self.bus, self.error) |
511 | |
512 | + if token == local.key: |
513 | + try: |
514 | + client = self.bus.get_object(DBUS_IFACE_AUTH_NAME, |
515 | + DBUS_IFACE_AUTH_PATH, |
516 | + follow_name_owner_changes=True) |
517 | + iface = dbus.Interface(client, DBUS_IFACE_AUTH_NAME) |
518 | + iface.clear_token('https://ubuntuone.com', 'ubuntuone', |
519 | + reply_handler=local_removal_cb, |
520 | + error_handler=self.error) |
521 | + except DBusException, e: |
522 | + self.error(e) |
523 | |
524 | |
525 | class UbuntuOneDialog(gtk.Dialog): |
526 | - """Preferences dialog.""" |
527 | - |
528 | - def __init__(self, config=None, keyring=gnomekeyring, *args, **kw): |
529 | - """Initializes our config dialog.""" |
530 | - super(UbuntuOneDialog, self).__init__(*args, **kw) |
531 | - self.set_title(_("Ubuntu One Preferences")) |
532 | - self.set_has_separator(False) |
533 | - self.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE) |
534 | - self.set_default_response(gtk.RESPONSE_CLOSE) |
535 | - self.set_icon_name("ubuntuone") |
536 | - |
537 | - self.connect("close", self.__handle_response, gtk.RESPONSE_CLOSE) |
538 | - self.connect("response", self.__handle_response) |
539 | - |
540 | - self.__bus = dbus.SessionBus() |
541 | - self.keyring = keyring |
542 | - |
543 | - self.__bus.add_signal_receiver( |
544 | - handler_function=self.__got_state, |
545 | - signal_name='StatusChanged', |
546 | - dbus_interface=DBUS_IFACE_STATUS_NAME) |
547 | - |
548 | - try: |
549 | - client = self.__bus.get_object(DBUS_IFACE_NAME, "/config", |
550 | - follow_name_owner_changes=True) |
551 | - iface = dbus.Interface(client, DBUS_IFACE_CONFIG_NAME) |
552 | - iface.get_throttling_limits( |
553 | - reply_handler=self.__got_limits, |
554 | - error_handler=self.__dbus_error) |
555 | - iface.bandwidth_throttling_enabled( |
556 | - reply_handler=self.__got_enabled, |
557 | - error_handler=self.__dbus_error) |
558 | - except DBusException, e: |
559 | - self.__dbus_error(e) |
560 | - |
561 | - # Timeout ID to avoid spamming DBus from spinbutton changes |
562 | - self.__update_id = 0 |
563 | - |
564 | - # SD Tool object |
565 | - self.sdtool = SyncDaemonTool(self.__bus) |
566 | - self.sdtool.get_status().addCallbacks(lambda _: self.__got_state, |
567 | - self.__sd_error) |
568 | - # Build the dialog |
569 | - self.__construct() |
570 | - logger.debug("starting") |
571 | - |
572 | - def quit(self): |
573 | - """Exit the main loop.""" |
574 | - gtk.main_quit() |
575 | - |
576 | - def __dbus_error(self, error): |
577 | - """Error getting throttling config.""" |
578 | - print repr(error) |
579 | - |
580 | - def __sd_error(self, error): |
581 | - """Error talking to syncdaemon.""" |
582 | - print repr(error) |
583 | - |
584 | - def __got_state(self, state): |
585 | - """Got the state of syncdaemon.""" |
586 | - self.devices.handle_state_change(state) |
587 | - |
588 | - def __got_limits(self, limits): |
589 | - """Got the throttling limits.""" |
590 | - logger.debug("got limits: %s" % (limits,)) |
591 | - self.devices.handle_limits(limits) |
592 | - |
593 | - def __got_enabled(self, enabled): |
594 | - """Got the throttling enabled config.""" |
595 | - self.devices.handle_throttling_enabled(enabled) |
596 | - |
597 | - def __handle_response(self, dialog, response): |
598 | - """Handle the dialog's response.""" |
599 | - self.hide() |
600 | - self.devices.update_bw_settings() |
601 | - gtk.main_quit() |
602 | - |
603 | - def _format_for_gb_display(self, bytes): |
604 | - """Format bytes into reasonable gb display.""" |
605 | - gb = bytes / 1024 / 1024 / 1024 |
606 | - if gb < 1.0: |
607 | - mb = bytes / 1024 / 1024 |
608 | - if mb < 1.0: |
609 | - return (bytes / 1024, 'KB') |
610 | - return (mb, 'MB') |
611 | - return (gb, 'GB') |
612 | - |
613 | - def update_quota_display(self, used, total): |
614 | - """Update the quota display.""" |
615 | - percent = (float(used) / float(total)) * 100 |
616 | - real_used, real_type = self._format_for_gb_display(used) |
617 | - self.usage_label.set_text( |
618 | - _("%(used)0.1f %(type)s Used (%(percent)0.1f%%)") % { |
619 | - 'used' : real_used, |
620 | - 'type' : real_type, |
621 | - 'percent' : percent }) |
622 | - self.usage_graph.set_fraction(percent / 100) |
623 | - |
624 | - def request_REST_info(self, url, method): |
625 | - """Make a REST request and return the resulting dict, or None.""" |
626 | - consumer = oauth.OAuthConsumer("ubuntuone", "hammertime") |
627 | - token = get_access_token(self.keyring) |
628 | - request = oauth.OAuthRequest.from_consumer_and_token( |
629 | - http_url=url, http_method=method, oauth_consumer=consumer, |
630 | - token=token) |
631 | - request.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(), |
632 | - consumer, token) |
633 | - client = httplib2.Http() |
634 | - headers = {} |
635 | - headers.update(request.to_header()) |
636 | - resp, content = client.request(url, method, headers=headers) |
637 | - if resp['status'] == '200': |
638 | - return simplejson.loads(content) |
639 | - # FIXME: Log json parsing failures |
640 | - else: |
641 | - # FIXME: Log errors |
642 | - return None |
643 | - |
644 | - def request_quota_info(self): |
645 | - """Request new quota info from server, and update display.""" |
646 | - quota = self.request_REST_info('https://one.ubuntu.com/api/quota/', |
647 | - 'GET') |
648 | - if quota: |
649 | - self.update_quota_display(quota['used'], quota['total']) |
650 | - |
651 | - def request_account_info(self): |
652 | - """Request account info from server, and update display.""" |
653 | - user = self.request_REST_info('https://one.ubuntu.com/api/account/', |
654 | - 'GET') |
655 | - if user: |
656 | - self.name_label.set_text(user['nickname']) |
657 | - self.user_label.set_text(user['username']) |
658 | - self.mail_label.set_text(user['email']) |
659 | - |
660 | - def toggle_db_sync(self, dbname, disable=False): |
661 | - """ |
662 | - Toggle whether a db in desktopcouch is synchronized to the |
663 | - Ubuntu One couchdb server. |
664 | - """ |
665 | - # FIXME: Actually enable/disable the dbs if desktopcouch exists |
666 | - |
667 | - def __db_check_toggled(self, checkbutton): |
668 | - """Handle toggling a desktopcouch service toggling.""" |
669 | - dbname = checkbutton.get_data('dbname') |
670 | - self.toggle_db_sync(dbname, not checkbutton.get_active()) |
671 | - |
672 | - def __files_check_toggled(self, checkbutton): |
673 | - """Handle toggling the files service.""" |
674 | - enable = checkbutton.get_active() |
675 | - if enable: |
676 | - self.music_check.set_sensitive(True) |
677 | - if self.music_check.get_active(): |
678 | - self.__music_check_toggled(self.music_check) |
679 | - else: |
680 | - self.music_check.set_sensitive(False) |
681 | - # FIXME: Actually stop or start ubuntuone-syncdaemon |
682 | - |
683 | - def __music_check_toggled(self, checkbutton): |
684 | - """Handle toggling the music download service.""" |
685 | - if not self.files_check.get_active(): |
686 | - return |
687 | - # FIXME: Actually subscribe or unsubscribe to the UDF |
688 | - |
689 | - def __construct(self): |
690 | - """Construct the dialog's layout.""" |
691 | - area = self.get_content_area() |
692 | - |
693 | - vbox = gtk.VBox(spacing=12) |
694 | - vbox.set_border_width(12) |
695 | - area.add(vbox) |
696 | - vbox.show() |
697 | - |
698 | - # Usage text/progress bar |
699 | - hbox = gtk.HBox(homogeneous=True) |
700 | - vbox.pack_start(hbox, False, False) |
701 | - hbox.show() |
702 | - |
703 | - label = gtk.Label("") |
704 | - hbox.add(label) |
705 | - label.show() |
706 | - |
707 | - rbox = gtk.VBox(spacing=2) |
708 | - hbox.pack_end(rbox) |
709 | - rbox.show() |
710 | - |
711 | - self.usage_label = gtk.Label("") |
712 | - self.usage_label.set_alignment(0.5, 0.5) |
713 | - rbox.add(self.usage_label) |
714 | - self.usage_label.show() |
715 | - |
716 | - self.usage_graph = gtk.ProgressBar() |
717 | - rbox.add(self.usage_graph) |
718 | - self.usage_graph.show() |
719 | - |
720 | - self.update_quota_display(0, 2) |
721 | - |
722 | - # Notebook |
723 | - self.notebook = gtk.Notebook() |
724 | - vbox.add(self.notebook) |
725 | - self.notebook.show() |
726 | - |
727 | - # Account tab |
728 | - account = gtk.VBox(spacing=12) |
729 | - account.set_border_width(6) |
730 | - self.notebook.append_page(account) |
731 | - self.notebook.set_tab_label_text(account, _("Account")) |
732 | - account.show() |
733 | - |
734 | - # User info in account tab |
735 | - table = gtk.Table(rows=3, columns=2) |
736 | - table.set_row_spacings(6) |
737 | - table.set_col_spacings(6) |
738 | - account.pack_start(table, False, False) |
739 | - table.show() |
740 | - |
741 | - label = gtk.Label(_("_Name:")) |
742 | - label.set_use_underline(True) |
743 | - label.set_alignment(0.0, 0.5) |
744 | - table.attach(label, 0, 1, 0, 1) |
745 | - label.show() |
746 | - |
747 | - self.name_label = gtk.Label("") |
748 | - self.name_label.set_use_underline(True) |
749 | - self.name_label.set_alignment(0.0, 0.5) |
750 | - table.attach(self.name_label, 1, 2, 0, 1) |
751 | - self.name_label.show() |
752 | - |
753 | - label = gtk.Label(_("_Username:")) |
754 | - label.set_use_underline(True) |
755 | - label.set_alignment(0.0, 0.5) |
756 | - table.attach(label, 0, 1, 1, 2) |
757 | - label.show() |
758 | - |
759 | - self.user_label = gtk.Label("") |
760 | - self.user_label.set_use_underline(True) |
761 | - self.user_label.set_alignment(0.0, 0.5) |
762 | - table.attach(self.user_label, 1, 2, 1, 2) |
763 | - self.user_label.show() |
764 | - |
765 | - label = gtk.Label(_("_E-mail:")) |
766 | - label.set_use_underline(True) |
767 | - label.set_alignment(0.0, 0.5) |
768 | - table.attach(label, 0, 1, 2, 3) |
769 | - label.show() |
770 | - |
771 | - self.mail_label = gtk.Label("") |
772 | - self.mail_label.set_use_underline(True) |
773 | - self.mail_label.set_alignment(0.0, 0.5) |
774 | - table.attach(self.mail_label, 1, 2, 2, 3) |
775 | - self.mail_label.show() |
776 | - |
777 | - # Devices tab |
778 | - self.devices = DevicesWidget(self.__bus, self.keyring) |
779 | - self.notebook.append_page(self.devices) |
780 | - self.notebook.set_tab_label_text(self.devices, _("Devices")) |
781 | - self.devices.show_all() |
782 | - self.devices.get_devices() |
783 | - |
784 | - # Services tab |
785 | - services = gtk.VBox(spacing=12) |
786 | - services.set_border_width(6) |
787 | - self.notebook.append_page(services) |
788 | - self.notebook.set_tab_label_text(services, _("Services")) |
789 | - # FIXME: These are all disabled for the moment |
790 | - services.set_sensitive(False) |
791 | - services.show() |
792 | - |
793 | - self.bookmarks_check = gtk.CheckButton(_("_Bookmarks")) |
794 | - self.bookmarks_check.set_data('dbname', 'bookmarks') |
795 | - self.bookmarks_check.connect('toggled', self.__db_check_toggled) |
796 | - services.pack_start(self.bookmarks_check, False, False) |
797 | - self.bookmarks_check.show() |
798 | - |
799 | - self.abook_check = gtk.CheckButton(_("C_ontacts")) |
800 | - self.abook_check.set_data('dbname', 'contacts') |
801 | - self.abook_check.connect('toggled', self.__db_check_toggled) |
802 | - services.pack_start(self.abook_check, False, False) |
803 | - self.abook_check.show() |
804 | - |
805 | - fbox = gtk.VBox(spacing=6) |
806 | - services.pack_start(fbox, False, False) |
807 | - fbox.show() |
808 | - |
809 | - self.files_check = gtk.CheckButton(_("_File Synchronization")) |
810 | - self.files_check.set_active(True) |
811 | - self.files_check.connect('toggled', self.__files_check_toggled) |
812 | - fbox.pack_start(self.files_check, False, False) |
813 | - self.files_check.show() |
814 | - |
815 | - alignment = gtk.Alignment(0.0, 0.5) |
816 | - alignment.set_padding(0, 0, 12, 0) |
817 | - fbox.pack_start(alignment, False, False) |
818 | - alignment.show() |
819 | - |
820 | - self.music_check = gtk.CheckButton(_("_Music Download")) |
821 | - self.music_check.set_active(True) |
822 | - self.music_check.connect('toggled', self.__music_check_toggled) |
823 | - alignment.add(self.music_check) |
824 | - self.music_check.show() |
825 | - |
826 | - |
827 | -class UbuntuoneLoginHandler(object): |
828 | - """Class to handle registration/login, in case we aren't already.""" |
829 | - |
830 | - def __init__(self, *args, **kw): |
831 | - self.bus = dbus.SessionBus() |
832 | - |
833 | - self.newcreds = None |
834 | - self.oautherr = None |
835 | - self.authdeny = None |
836 | - |
837 | - def got_newcredentials(self, realm, consumer_key): |
838 | - """Show our dialog, since we can do stuff now.""" |
839 | - self.disconnect_signal_handlers() |
840 | - dialog = UbuntuOneDialog() |
841 | - dialog.request_quota_info() |
842 | - dialog.request_account_info() |
843 | - dialog.show() |
844 | - |
845 | - def got_oautherror(self, message=None): |
846 | - """Got an error during oauth.""" |
847 | - gtk.main_quit() |
848 | - |
849 | - def got_authdenied(self): |
850 | - """User denied access.""" |
851 | - gtk.main_quit() |
852 | - |
853 | - def got_dbus_error(self, error): |
854 | - """Got a DBusError.""" |
855 | - gtk.main_quit() |
856 | - |
857 | - def register_signal_handlers(self): |
858 | - """Register the dbus signal handlers.""" |
859 | - self.newcreds = self.bus.add_signal_receiver( |
860 | - handler_function=self.got_newcredentials, |
861 | - signal_name='NewCredentials', |
862 | - dbus_interface=DBUS_IFACE_AUTH_NAME) |
863 | - self.oautherr = self.bus.add_signal_receiver( |
864 | - handler_function=self.got_oautherror, |
865 | - signal_name='OAuthError', |
866 | - dbus_interface=DBUS_IFACE_AUTH_NAME) |
867 | - self.authdeny = self.bus.add_signal_receiver( |
868 | - handler_function=self.got_authdenied, |
869 | - signal_name='AuthorizationDenied', |
870 | - dbus_interface=DBUS_IFACE_AUTH_NAME) |
871 | - |
872 | - def disconnect_signal_handlers(self): |
873 | - """Disconnect the dbus signal handlers, so we don't break.""" |
874 | - self.bus.remove_signal_receiver( |
875 | - self.newcreds, |
876 | - dbus_interface=DBUS_IFACE_AUTH_NAME) |
877 | - self.bus.remove_signal_receiver( |
878 | - self.oautherr, |
879 | - dbus_interface=DBUS_IFACE_AUTH_NAME) |
880 | - self.bus.remove_signal_receiver( |
881 | - self.authdeny, |
882 | - dbus_interface=DBUS_IFACE_AUTH_NAME) |
883 | - |
884 | - def do_login_check(self): |
885 | - """Log in to U1 or validate that we already are.""" |
886 | - try: |
887 | - client = self.bus.get_object(DBUS_IFACE_AUTH_NAME, |
888 | - DBUS_IFACE_AUTH_PATH, |
889 | - follow_name_owner_changes=True) |
890 | - iface = dbus.Interface(client, DBUS_IFACE_AUTH_NAME) |
891 | - iface.login('https://ubuntuone.com', 'ubuntuone', |
892 | - reply_handler=dbus_async, |
893 | - error_handler=self.got_dbus_error) |
894 | - except DBusException, e: |
895 | - self.got_dbus_error(e) |
896 | + """Preferences dialog.""" |
897 | + |
898 | + def __init__(self, config=None, keyring=gnomekeyring, *args, **kw): |
899 | + """Initializes our config dialog.""" |
900 | + super(UbuntuOneDialog, self).__init__(*args, **kw) |
901 | + self.set_title(_("Ubuntu One Preferences")) |
902 | + self.set_has_separator(False) |
903 | + self.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE) |
904 | + self.set_default_response(gtk.RESPONSE_CLOSE) |
905 | + self.set_icon_name("ubuntuone") |
906 | + self.set_default_size(400, 300) |
907 | + |
908 | + self.connect("close", self.__handle_response, gtk.RESPONSE_CLOSE) |
909 | + self.connect("response", self.__handle_response) |
910 | + |
911 | + # desktopcouch ReplicationExclusion, or None |
912 | + self.dcouch = None |
913 | + |
914 | + # folder id for dealing with the Music folder |
915 | + self.ums_id = None |
916 | + |
917 | + self.__bus = dbus.SessionBus() |
918 | + self.keyring = keyring |
919 | + |
920 | + # Timeout ID to avoid spamming DBus from spinbutton changes |
921 | + self.__update_id = 0 |
922 | + |
923 | + # Build the dialog |
924 | + self.__construct() |
925 | + |
926 | + # SD Tool object |
927 | + self.sdtool = SyncDaemonTool(self.__bus) |
928 | + self.sdtool.get_status().addCallbacks(self.__got_state, |
929 | + self.__sd_error) |
930 | + |
931 | + # hook dbus up |
932 | + self.__bus.add_signal_receiver( |
933 | + handler_function=self.__got_state, |
934 | + signal_name='StatusChanged', |
935 | + dbus_interface=DBUS_IFACE_STATUS_NAME) |
936 | + |
937 | + try: |
938 | + client = self.__bus.get_object(DBUS_IFACE_NAME, "/config", |
939 | + follow_name_owner_changes=True) |
940 | + iface = dbus.Interface(client, DBUS_IFACE_CONFIG_NAME) |
941 | + iface.get_throttling_limits( |
942 | + reply_handler=self.__got_limits, |
943 | + error_handler=self.__dbus_error) |
944 | + iface.bandwidth_throttling_enabled( |
945 | + reply_handler=self.__got_enabled, |
946 | + error_handler=self.__dbus_error) |
947 | + except DBusException, e: |
948 | + self.__dbus_error(e) |
949 | + |
950 | + logger.debug("starting") |
951 | + |
952 | + def __dbus_error(self, error): |
953 | + """Error getting throttling config.""" |
954 | + logger.error(error) |
955 | + |
956 | + def __sd_error(self, error): |
957 | + """Error talking to syncdaemon.""" |
958 | + logger.error(error) |
959 | + |
960 | + def __got_state(self, state): |
961 | + """Got the state of syncdaemon.""" |
962 | + if not state['is_connected']: |
963 | + self.status_label.set_text(_("Disconnected")) |
964 | + else: |
965 | + try: |
966 | + status = state['name'] |
967 | + queues = state['queues'] |
968 | + except KeyError: |
969 | + status = None |
970 | + queues = None |
971 | + if status == u'QUEUE_MANAGER' and queues == u'IDLE': |
972 | + self.status_label.set_text(_("Synchronization complete")) |
973 | + else: |
974 | + self.status_label.set_text(_(u"Synchronization in progress…")) |
975 | + |
976 | + # Update the stuff in the devices tab |
977 | + self.devices.handle_state_change(state) |
978 | + |
979 | + def __got_limits(self, limits): |
980 | + """Got the throttling limits.""" |
981 | + logger.debug("got limits: %s" % (limits,)) |
982 | + self.devices.handle_limits(limits) |
983 | + |
984 | + def __got_enabled(self, enabled): |
985 | + """Got the throttling enabled config.""" |
986 | + self.devices.handle_throttling_enabled(enabled) |
987 | + |
988 | + def __handle_response(self, dialog, response): |
989 | + """Handle the dialog's response.""" |
990 | + self.hide() |
991 | + self.devices.handle_bw_controls_changed() |
992 | + gtk.main_quit() |
993 | + |
994 | + def _format_for_gb_display(self, bytes): |
995 | + """Format bytes into reasonable gb display.""" |
996 | + gb = bytes / 1024 / 1024 / 1024 |
997 | + if gb < 1.0: |
998 | + mb = bytes / 1024 / 1024 |
999 | + if mb < 1.0: |
1000 | + return (bytes / 1024, 'KB') |
1001 | + return (mb, 'MB') |
1002 | + return (gb, 'GB') |
1003 | + |
1004 | + def update_quota_display(self, used, total): |
1005 | + """Update the quota display.""" |
1006 | + percent = (float(used) / float(total)) * 100 |
1007 | + real_used, real_type = self._format_for_gb_display(used) |
1008 | + self.usage_label.set_text( |
1009 | + _("%(used)0.1f %(type)s Used (%(percent)0.1f%%)") % { |
1010 | + 'used' : real_used, |
1011 | + 'type' : real_type, |
1012 | + 'percent' : percent }) |
1013 | + self.usage_graph.set_fraction(percent / 100) |
1014 | + |
1015 | + # assumes all paid accounts will have more than 50GB |
1016 | + is_paid_account = total >= 50<<30 |
1017 | + self.plan_label.set_label(_("Paid") if is_paid_account else _("Free")) |
1018 | + if is_paid_account: |
1019 | + self.upgrade_link.hide() |
1020 | + else: |
1021 | + self.upgrade_link.show() |
1022 | + |
1023 | + if percent >= 100.: |
1024 | + self.overquota_img.set_from_icon_name('dialog-warning', |
1025 | + gtk.ICON_SIZE_DIALOG) |
1026 | + self.overquota.show_all() |
1027 | + label = _("You are using all of your Ubuntu One storage.") |
1028 | + if is_paid_account: |
1029 | + desc = _("Synchronization is now disabled. Remove files from" |
1030 | + " synchronization or upgrade your subscription. Use" |
1031 | + " the Support options if an upgrade is not available.") |
1032 | + else: |
1033 | + desc = _("Synchronization is now disabled. Remove files from" |
1034 | + " synchronization or upgrade your subscription.") |
1035 | + elif percent >= 95.: |
1036 | + self.overquota_img.set_from_icon_name('dialog-information', |
1037 | + gtk.ICON_SIZE_DIALOG) |
1038 | + self.overquota.show_all() |
1039 | + label = _("You are near your Ubuntu One storage limit.") |
1040 | + if is_paid_account: |
1041 | + desc = _("Automatic synchronization will stop when you reach" |
1042 | + " your storage limit.") |
1043 | + else: |
1044 | + desc = _("Automatic synchronization will stop when you reach" |
1045 | + " your storage limit. Please consider upgrading to" |
1046 | + " avoid a service interruption.") |
1047 | + else: |
1048 | + self.overquota_img.clear() |
1049 | + label = desc = "" |
1050 | + self.overquota.hide_all() |
1051 | + self.overquota_label.set_markup("%s\n<small>%s</small>" % (label, desc)) |
1052 | + |
1053 | + def got_quota_info(self, quota): |
1054 | + """Handle the result from the quota REST call.""" |
1055 | + if quota: |
1056 | + used = quota.get('used', 0) |
1057 | + total = quota.get('total', 2) |
1058 | + self.update_quota_display(used, total) |
1059 | + |
1060 | + def request_quota_info(self): |
1061 | + """Request new quota info from server, and update display.""" |
1062 | + make_rest_request(url='https://one.ubuntu.com/api/quota/', |
1063 | + keyring=self.keyring, |
1064 | + callback=self.got_quota_info) |
1065 | + |
1066 | + def got_account_info(self, user): |
1067 | + """Handle the result from the account REST call.""" |
1068 | + if user: |
1069 | + self.name_label.set_text(user.get('nickname', _("Unknown"))) |
1070 | + self.mail_label.set_text(user.get('email', _("Unknown"))) |
1071 | + |
1072 | + def request_account_info(self): |
1073 | + """Request account info from server, and update display.""" |
1074 | + make_rest_request(url='https://one.ubuntu.com/api/account/', |
1075 | + keyring=self.keyring, |
1076 | + callback=self.got_account_info) |
1077 | + |
1078 | + def __bw_inst_cb(self, button): |
1079 | + """Pops off the bindwood installer to a thread.""" |
1080 | + Thread(target=self.__install_bindwood).start() |
1081 | + |
1082 | + def __install_bindwood(self): |
1083 | + """Runs the tool to intall bindwood and updates the UI.""" |
1084 | + installed = subprocess.call(BW_INST_ARGS) |
1085 | + gtk.gdk.threads_enter() |
1086 | + if installed == 0: |
1087 | + self.bw_inst_box.hide() |
1088 | + else: |
1089 | + self.bw_inst_box.show() |
1090 | + logger.error(_("There was an error installing bindwood.")) |
1091 | + gtk.gdk.threads_leave() |
1092 | + |
1093 | + def __check_for_bindwood(self): |
1094 | + """ |
1095 | + Method run in a thread to check that bindwood exists, and |
1096 | + update the interface appropriately. |
1097 | + """ |
1098 | + exists = True |
1099 | + p = subprocess.Popen(BW_CHCK_ARGS, bufsize=4096, |
1100 | + stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
1101 | + stdin=subprocess.PIPE, env=os.environ) |
1102 | + result = p.wait() |
1103 | + if result == 0: |
1104 | + for line in p.stdout.readlines(): |
1105 | + if line.startswith('un'): |
1106 | + exists = False |
1107 | + else: |
1108 | + exists = False |
1109 | + |
1110 | + gtk.gdk.threads_enter() |
1111 | + if exists: |
1112 | + self.bw_inst_box.hide() |
1113 | + else: |
1114 | + self.bw_inst_btn.connect("clicked", |
1115 | + self.__bw_inst_cb) |
1116 | + self.bw_inst_box.show() |
1117 | + gtk.gdk.threads_leave() |
1118 | + |
1119 | + def connect_desktopcouch_exclusion(self): |
1120 | + """Hook up to desktopcouch enablement API, or disable the UI.""" |
1121 | + # Hook up to desktopcouch, or die trying |
1122 | + if dcouch: |
1123 | + Thread(target=self.__check_for_bindwood).start() |
1124 | + |
1125 | + self.dcouch = dcouch.ReplicationExclusion() |
1126 | + exclusions = self.dcouch.all_exclusions() |
1127 | + for check in [self.bookmarks_check, |
1128 | + self.abook_check, |
1129 | + self.gwib_check]: |
1130 | + if check.get_data('dbname') in exclusions: |
1131 | + check.set_active(False) |
1132 | + else: |
1133 | + check.set_active(True) |
1134 | + else: |
1135 | + self.bookmarks_check.set_sensitive(False) |
1136 | + self.abook_check.set_sensitive(False) |
1137 | + self.gwib_check.set_sensitive(False) |
1138 | + |
1139 | + def toggle_db_sync(self, dbname, disable=False): |
1140 | + """ |
1141 | + Toggle whether a db in desktopcouch is synchronized to the |
1142 | + Ubuntu One couchdb server. |
1143 | + """ |
1144 | + if self.dcouch: |
1145 | + if disable: |
1146 | + self.dcouch.exclude(dbname) |
1147 | + else: |
1148 | + self.dcouch.replicate(dbname) |
1149 | + else: |
1150 | + logger.error(_("Database enablement is unavailable.")) |
1151 | + |
1152 | + def __db_check_toggled(self, checkbutton): |
1153 | + """Handle toggling a desktopcouch service toggling.""" |
1154 | + dbname = checkbutton.get_data('dbname') |
1155 | + self.toggle_db_sync(dbname, not checkbutton.get_active()) |
1156 | + |
1157 | + def files_check_toggled(self, checkbutton): |
1158 | + """Handle toggling the files service.""" |
1159 | + enabled = checkbutton.get_active() |
1160 | + self.sdtool.enable_files_sync(enabled).addCallbacks( |
1161 | + lambda _: dbus_async, |
1162 | + self.__sd_error) |
1163 | + def sd_error(error): |
1164 | + self.files_check.set_active(False) |
1165 | + self.files_check.set_sensitive(False) |
1166 | + self.music_check.set_sensitive(False) |
1167 | + self.__sd_error(error) |
1168 | + |
1169 | + if enabled: |
1170 | + self.sdtool.start().addErrback(sd_error) |
1171 | + if self.ums_id: |
1172 | + self.music_check.set_sensitive(True) |
1173 | + if self.music_check.get_active(): |
1174 | + self.__music_check_toggled(self.music_check) |
1175 | + else: |
1176 | + self.sdtool.quit() |
1177 | + self.music_check.set_sensitive(False) |
1178 | + |
1179 | + def music_check_toggled(self, checkbutton): |
1180 | + """Handle toggling the music download service.""" |
1181 | + if not self.files_check.get_active() or not self.ums_id: |
1182 | + return |
1183 | + enabled = checkbutton.get_active() |
1184 | + def got_error(error): |
1185 | + checkbutton.set_sensitive(False) |
1186 | + checkbutton.set_active(not enabled) |
1187 | + self.__sd_error(error) |
1188 | + |
1189 | + if enabled: |
1190 | + d = self.sdtool.subscribe_folder(self.ums_id) |
1191 | + else: |
1192 | + d = self.sdtool.unsubscribe_folder(self.ums_id) |
1193 | + d.addErrback(got_error) |
1194 | + |
1195 | + def get_syncdaemon_sync_config(self): |
1196 | + """Update the files/music sync config from syncdaemon.""" |
1197 | + def got_fs_err(error): |
1198 | + self.files_check.set_sensitive(False) |
1199 | + self.music_check.set_sensitive(False) |
1200 | + self.__sd_error(error) |
1201 | + |
1202 | + def got_ms_err(error): |
1203 | + self.music_check.set_sensitive(False) |
1204 | + self.__sd_error(error) |
1205 | + |
1206 | + def got_info(info): |
1207 | + if not info: |
1208 | + self.music_check.set_active(False) |
1209 | + self.music_check.set_sensitive(False) |
1210 | + else: |
1211 | + self.ums_id = info['volume_id'] |
1212 | + self.music_check.set_sensitive(True) |
1213 | + if info['subscribed'] == 'True': |
1214 | + self.music_check.set_active(True) |
1215 | + else: |
1216 | + self.music_check.set_active(False) |
1217 | + |
1218 | + def got_fs(enabled): |
1219 | + self.files_check.set_active(enabled) |
1220 | + |
1221 | + self.sdtool.get_folder_info(U1MSPATH).addCallbacks(got_info, got_ms_err) |
1222 | + self.sdtool.is_files_sync_enabled().addCallbacks(got_fs, got_fs_err) |
1223 | + |
1224 | + def connect_file_sync_callbacks(self): |
1225 | + """Connect the file sync checkbox callbacks.""" |
1226 | + self.files_check.connect('toggled', self.files_check_toggled) |
1227 | + self.music_check.connect('toggled', self.music_check_toggled) |
1228 | + |
1229 | + def __construct(self): |
1230 | + """Construct the dialog's layout.""" |
1231 | + area = self.get_content_area() |
1232 | + |
1233 | + vbox = gtk.VBox(spacing=12) |
1234 | + vbox.set_border_width(12) |
1235 | + area.add(vbox) |
1236 | + vbox.show() |
1237 | + |
1238 | + # Usage text/progress bar |
1239 | + rbox = gtk.VBox(spacing=12) |
1240 | + vbox.pack_start(rbox, False, False) |
1241 | + rbox.show() |
1242 | + |
1243 | + hbox = gtk.HBox(spacing=6) |
1244 | + rbox.pack_start(hbox, False, False) |
1245 | + hbox.show() |
1246 | + |
1247 | + self.usage_graph = gtk.ProgressBar() |
1248 | + hbox.pack_start(self.usage_graph, False, False) |
1249 | + self.usage_graph.show() |
1250 | + |
1251 | + self.usage_label = gtk.Label(_("Unknown")) |
1252 | + self.usage_label.set_alignment(0.0, 0.5) |
1253 | + hbox.pack_start(self.usage_label, False, False) |
1254 | + self.usage_label.show() |
1255 | + |
1256 | + self.status_label = gtk.Label(_("Unknown")) |
1257 | + self.status_label.set_alignment(0.0, 0.5) |
1258 | + rbox.pack_start(self.status_label, False, False) |
1259 | + self.status_label.show() |
1260 | + |
1261 | + # Notebook |
1262 | + self.notebook = gtk.Notebook() |
1263 | + vbox.add(self.notebook) |
1264 | + self.notebook.show() |
1265 | + |
1266 | + # Account tab |
1267 | + account = gtk.VBox(spacing=12) |
1268 | + account.set_border_width(6) |
1269 | + self.notebook.append_page(account) |
1270 | + self.notebook.set_tab_label_text(account, _("Account")) |
1271 | + account.show() |
1272 | + |
1273 | + # overquota notice in account tab |
1274 | + self.overquota = gtk.HBox(spacing=6) |
1275 | + self.overquota.set_border_width(6) |
1276 | + account.pack_start(self.overquota, False, False) |
1277 | + |
1278 | + self.overquota_img = gtk.Image() |
1279 | + self.overquota.pack_start(self.overquota_img, False, False) |
1280 | + self.overquota_img.show() |
1281 | + |
1282 | + self.overquota_label = label = gtk.Label("\n\n") |
1283 | + label.set_line_wrap(True) |
1284 | + label.set_alignment(0.0, 0.5) |
1285 | + self.overquota.pack_start(label, False, False) |
1286 | + self.overquota_label.show() |
1287 | + |
1288 | + |
1289 | + # User info in account tab |
1290 | + table = gtk.Table(rows=6, columns=2) |
1291 | + table.set_row_spacings(6) |
1292 | + table.set_col_spacings(6) |
1293 | + account.pack_start(table, False, False) |
1294 | + table.show() |
1295 | + |
1296 | + label = gtk.Label(_("_Name:")) |
1297 | + label.set_use_underline(True) |
1298 | + label.set_alignment(0.0, 0.5) |
1299 | + table.attach(label, 0, 1, 0, 1) |
1300 | + label.show() |
1301 | + |
1302 | + self.name_label = gtk.Label(_("Unknown")) |
1303 | + self.name_label.set_use_underline(True) |
1304 | + self.name_label.set_alignment(0.0, 0.5) |
1305 | + table.attach(self.name_label, 1, 2, 0, 1) |
1306 | + self.name_label.show() |
1307 | + |
1308 | + label = gtk.Label(_("_E-mail:")) |
1309 | + label.set_use_underline(True) |
1310 | + label.set_alignment(0.0, 0.5) |
1311 | + table.attach(label, 0, 1, 1, 2) |
1312 | + label.show() |
1313 | + |
1314 | + self.mail_label = gtk.Label(_("Unknown")) |
1315 | + self.mail_label.set_use_underline(True) |
1316 | + self.mail_label.set_alignment(0.0, 0.5) |
1317 | + table.attach(self.mail_label, 1, 2, 1, 2) |
1318 | + self.mail_label.show() |
1319 | + |
1320 | + label = gtk.Label(_("Current plan:")) |
1321 | + label.set_alignment(0.0, 0.5) |
1322 | + table.attach(label, 0, 1, 2, 3) |
1323 | + label.show() |
1324 | + |
1325 | + self.plan_label = gtk.Label(_("Unknown")) |
1326 | + self.plan_label.set_alignment(0.0, 0.5) |
1327 | + table.attach(self.plan_label, 1, 2, 2, 3) |
1328 | + self.plan_label.show() |
1329 | + |
1330 | + for n, (url, label) in enumerate([ |
1331 | + ("http://one.ubuntu.com/support", _("Support options")), |
1332 | + ("http://one.ubuntu.com/account", _("Manage account")), |
1333 | + ("http://one.ubuntu.com/upgrade", |
1334 | + _("Upgrade your subscription")), |
1335 | + ]): |
1336 | + link = gtk.LinkButton(url, label) |
1337 | + link.set_relief(gtk.RELIEF_NONE) |
1338 | + link.set_alignment(0.0, 0.5) |
1339 | + table.attach(link, 0, 2, 5-n, 6-n) |
1340 | + link.show() |
1341 | + self.upgrade_link = link |
1342 | + self.upgrade_link.hide() |
1343 | + |
1344 | + # Devices tab |
1345 | + sw = gtk.ScrolledWindow() |
1346 | + sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) |
1347 | + self.notebook.append_page(sw) |
1348 | + self.notebook.set_tab_label_text(sw, _("Devices")) |
1349 | + sw.show() |
1350 | + self.devices = DevicesWidget(self.__bus, self.keyring) |
1351 | + sw.add_with_viewport(self.devices) |
1352 | + self.devices.show_all() |
1353 | + |
1354 | + # Services tab |
1355 | + services = gtk.VBox(spacing=12) |
1356 | + services.set_border_width(6) |
1357 | + self.notebook.append_page(services) |
1358 | + self.notebook.set_tab_label_text(services, _("Services")) |
1359 | + services.show() |
1360 | + |
1361 | + label = gtk.Label() |
1362 | + label.set_markup( |
1363 | + _("Ubuntu One sync options\n" |
1364 | + "<small>Choose services to synchronize" |
1365 | + " with this computer</small>")) |
1366 | + label.set_alignment(0., .5) |
1367 | + label.set_padding(12, 6) |
1368 | + label.set_line_wrap(True) |
1369 | + services.pack_start(label, False, False) |
1370 | + label.show() |
1371 | + |
1372 | + self.bookmarks_check = gtk.CheckButton(_("_Bookmarks")) |
1373 | + self.bookmarks_check.set_data('dbname', 'bookmarks') |
1374 | + self.bookmarks_check.connect('toggled', self.__db_check_toggled) |
1375 | + services.pack_start(self.bookmarks_check, False, False) |
1376 | + self.bookmarks_check.show() |
1377 | + |
1378 | + # This box is shown in the event bindwood is not installed |
1379 | + self.bw_inst_box = gtk.HBox(spacing=12) |
1380 | + services.pack_start(self.bw_inst_box, False, False) |
1381 | + |
1382 | + label = gtk.Label("") |
1383 | + self.bw_inst_box.pack_start(label, False, False) |
1384 | + label.show() |
1385 | + |
1386 | + label = gtk.Label("<i>%s</i>" % _("Firefox extension not installed.")) |
1387 | + label.set_use_markup(True) |
1388 | + label.set_alignment(0.0, 0.5) |
1389 | + self.bw_inst_box.pack_start(label, False, False) |
1390 | + label.show() |
1391 | + |
1392 | + self.bw_inst_btn = gtk.Button(_("_Install")) |
1393 | + self.bw_inst_box.pack_start(self.bw_inst_btn, False, False) |
1394 | + self.bw_inst_btn.show() |
1395 | + |
1396 | + self.gwib_check = gtk.CheckButton(_("Broadcast Messages _Archive")) |
1397 | + self.gwib_check.set_data('dbname', 'gwibber_messages') |
1398 | + self.gwib_check.connect('toggled', self.__db_check_toggled) |
1399 | + services.pack_start(self.gwib_check, False, False) |
1400 | + self.gwib_check.show() |
1401 | + |
1402 | + self.abook_check = gtk.CheckButton(_("C_ontacts")) |
1403 | + self.abook_check.set_data('dbname', 'contacts') |
1404 | + self.abook_check.connect('toggled', self.__db_check_toggled) |
1405 | + services.pack_start(self.abook_check, False, False) |
1406 | + self.abook_check.show() |
1407 | + |
1408 | + fbox = gtk.VBox(spacing=6) |
1409 | + services.pack_start(fbox, False, False) |
1410 | + fbox.show() |
1411 | + |
1412 | + self.files_check = gtk.CheckButton(_("_File Synchronization")) |
1413 | + self.files_check.set_active(True) |
1414 | + fbox.pack_start(self.files_check, False, False) |
1415 | + self.files_check.show() |
1416 | + |
1417 | + alignment = gtk.Alignment(0.0, 0.5) |
1418 | + alignment.set_padding(0, 0, 12, 0) |
1419 | + fbox.pack_start(alignment, False, False) |
1420 | + alignment.show() |
1421 | + |
1422 | + self.music_check = gtk.CheckButton(_("_Music Download")) |
1423 | + self.music_check.set_active(True) |
1424 | + alignment.add(self.music_check) |
1425 | + self.music_check.show() |
1426 | + |
1427 | + |
1428 | +class UbuntuoneLoginHandler(dbus.service.Object): |
1429 | + """Class to handle registration/login, in case we aren't already.""" |
1430 | + |
1431 | + def __init__(self, dialog, *args, **kw): |
1432 | + self.bus = dbus.SessionBus() |
1433 | + |
1434 | + # The actual UI |
1435 | + self.dialog = dialog |
1436 | + |
1437 | + # DBus object magic |
1438 | + self.path = '/' |
1439 | + bus_name = dbus.service.BusName(PREFS_BUS_NAME, bus=self.bus) |
1440 | + dbus.service.Object.__init__(self, bus_name=bus_name, |
1441 | + object_path=self.path) |
1442 | + |
1443 | + |
1444 | + @dbus.service.method(PREFS_BUS_NAME, in_signature='', out_signature='') |
1445 | + def present(self): |
1446 | + """Raise the dialog window.""" |
1447 | + self.dialog.connect_desktopcouch_exclusion() |
1448 | + self.dialog.get_syncdaemon_sync_config() |
1449 | + self.dialog.connect_file_sync_callbacks() |
1450 | + self.dialog.request_quota_info() |
1451 | + self.dialog.request_account_info() |
1452 | + self.dialog.devices.get_devices() |
1453 | + self.dialog.present_with_time(int(time.time())) |
1454 | + |
1455 | + def got_newcredentials(self, realm, consumer_key): |
1456 | + """Show our dialog, since we can do stuff now.""" |
1457 | + self.present() |
1458 | + |
1459 | + def got_oautherror(self, message=None): |
1460 | + """Got an error during oauth.""" |
1461 | + if message: |
1462 | + logger.error(message) |
1463 | + else: |
1464 | + logger.error(_("OAuthError with no message.")) |
1465 | + |
1466 | + def got_authdenied(self): |
1467 | + """User denied access.""" |
1468 | + logger.error(_("Authorization was denied.")) |
1469 | + |
1470 | + def got_dbus_error(self, error): |
1471 | + """Got a DBusError.""" |
1472 | + logger.error(error) |
1473 | + |
1474 | + def register_signal_handlers(self): |
1475 | + """Register the dbus signal handlers.""" |
1476 | + self.bus.add_signal_receiver( |
1477 | + handler_function=self.got_newcredentials, |
1478 | + signal_name='NewCredentials', |
1479 | + dbus_interface=DBUS_IFACE_AUTH_NAME) |
1480 | + self.bus.add_signal_receiver( |
1481 | + handler_function=self.got_oautherror, |
1482 | + signal_name='OAuthError', |
1483 | + dbus_interface=DBUS_IFACE_AUTH_NAME) |
1484 | + self.bus.add_signal_receiver( |
1485 | + handler_function=self.got_authdenied, |
1486 | + signal_name='AuthorizationDenied', |
1487 | + dbus_interface=DBUS_IFACE_AUTH_NAME) |
1488 | |
1489 | |
1490 | if __name__ == "__main__": |
1491 | - gettext.bindtextdomain(clientdefs.GETTEXT_PACKAGE, clientdefs.LOCALEDIR) |
1492 | - gettext.textdomain(clientdefs.GETTEXT_PACKAGE) |
1493 | - |
1494 | - gtk.rc_parse_string(RCSTYLE) |
1495 | - gobject.set_application_name("Ubuntu One") |
1496 | - |
1497 | - try: |
1498 | - login = UbuntuoneLoginHandler() |
1499 | - login.register_signal_handlers() |
1500 | - login.do_login_check() |
1501 | - gtk.main() |
1502 | - except KeyboardInterrupt: |
1503 | - pass |
1504 | + gettext.bindtextdomain(clientdefs.GETTEXT_PACKAGE, clientdefs.LOCALEDIR) |
1505 | + gettext.textdomain(clientdefs.GETTEXT_PACKAGE) |
1506 | + |
1507 | + gobject.threads_init() |
1508 | + gtk.gdk.threads_init() |
1509 | + |
1510 | + gtk.rc_parse_string(RCSTYLE) |
1511 | + gobject.set_application_name("ubuntuone-preferences") |
1512 | + |
1513 | + bus = dbus.SessionBus() |
1514 | + result = bus.request_name(PREFS_BUS_NAME, |
1515 | + dbus.bus.NAME_FLAG_DO_NOT_QUEUE) |
1516 | + if result == dbus.bus.REQUEST_NAME_REPLY_EXISTS: |
1517 | + try: |
1518 | + client = bus.get_object(PREFS_BUS_NAME, '/', |
1519 | + follow_name_owner_changes=True) |
1520 | + iface= dbus.Interface(client, PREFS_BUS_NAME) |
1521 | + iface.present() |
1522 | + except DBusException, e: |
1523 | + logger.error(e) |
1524 | + sys.exit(0) |
1525 | + |
1526 | + try: |
1527 | + # The prefs dialog |
1528 | + gtk.gdk.threads_enter() |
1529 | + prefs_dialog = UbuntuOneDialog() |
1530 | + prefs_dialog.show() |
1531 | + |
1532 | + login = UbuntuoneLoginHandler(dialog=prefs_dialog) |
1533 | + login.register_signal_handlers() |
1534 | + gobject.timeout_add_seconds(1, do_login_request, |
1535 | + bus, login.got_dbus_error) |
1536 | + gtk.main() |
1537 | + gtk.gdk.threads_leave() |
1538 | + except KeyboardInterrupt: |
1539 | + pass |
1540 | |
1541 | === modified file 'bin/ubuntuone-syncdaemon' |
1542 | --- bin/ubuntuone-syncdaemon 2010-03-04 16:47:43 +0000 |
1543 | +++ bin/ubuntuone-syncdaemon 2010-03-31 23:55:45 +0000 |
1544 | @@ -25,7 +25,6 @@ |
1545 | import dbus |
1546 | import dbus.mainloop.glib |
1547 | import gobject |
1548 | -import logging |
1549 | import os |
1550 | import signal |
1551 | import sys |
1552 | @@ -34,15 +33,10 @@ |
1553 | from ubuntuone.syncdaemon import dbus_interface, logger, config |
1554 | from ubuntuone.syncdaemon.config import ( |
1555 | get_config_files, |
1556 | - get_parsers, |
1557 | - xdg_cache_dir_parser, |
1558 | - xdg_data_dir_parser, |
1559 | - home_dir_parser, |
1560 | ) |
1561 | |
1562 | from ubuntuone.syncdaemon.main import Main |
1563 | |
1564 | -from configglue import configglue |
1565 | from twisted.internet import reactor |
1566 | from xdg.BaseDirectory import ( |
1567 | xdg_cache_home, |
1568 | @@ -95,6 +89,10 @@ |
1569 | if is_already_running(): |
1570 | die('Another instance is running') |
1571 | |
1572 | + # check if the user disabled files sync |
1573 | + if not config.get_user_config().get_files_sync_enabled(): |
1574 | + die('Files synchronization is disabled.') |
1575 | + |
1576 | # check if we are using xdg_data_home and it doesn't exists |
1577 | if xdg_data_home in options.data_dir and \ |
1578 | not os.path.exists(options.data_dir): |
1579 | @@ -132,7 +130,8 @@ |
1580 | shares_symlink_name='Shared With Me', |
1581 | read_limit=options.bandwidth_throttling_read_limit, |
1582 | write_limit=options.bandwidth_throttling_write_limit, |
1583 | - throttling_enabled=options.bandwidth_throttling_on) |
1584 | + throttling_enabled=options.bandwidth_throttling_on, |
1585 | + ignore_files=options.ignore) |
1586 | if options.oauth: |
1587 | try: |
1588 | (key, secret) = options.oauth.split(':', 2) |
1589 | @@ -164,6 +163,9 @@ |
1590 | except ImportError: |
1591 | logger.root_logger.warning('guppy-pe/heapy not available, remote ' |
1592 | 'monitor thread not started') |
1593 | + else: |
1594 | + guppy.heapy.RM.on() |
1595 | + |
1596 | main.start() |
1597 | if options.debug_lsprof_file: |
1598 | try: |
1599 | |
1600 | === modified file 'configure' |
1601 | --- configure 2010-03-10 23:36:53 +0000 |
1602 | +++ configure 2010-03-31 23:55:45 +0000 |
1603 | @@ -1,6 +1,6 @@ |
1604 | #! /bin/sh |
1605 | # Guess values for system-dependent variables and create Makefiles. |
1606 | -# Generated by GNU Autoconf 2.65 for ubuntuone-client 1.1.4. |
1607 | +# Generated by GNU Autoconf 2.65 for ubuntuone-client 1.1.90. |
1608 | # |
1609 | # |
1610 | # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, |
1611 | @@ -698,8 +698,8 @@ |
1612 | # Identity of this package. |
1613 | PACKAGE_NAME='ubuntuone-client' |
1614 | PACKAGE_TARNAME='ubuntuone-client' |
1615 | -PACKAGE_VERSION='1.1.4' |
1616 | -PACKAGE_STRING='ubuntuone-client 1.1.4' |
1617 | +PACKAGE_VERSION='1.1.90' |
1618 | +PACKAGE_STRING='ubuntuone-client 1.1.90' |
1619 | PACKAGE_BUGREPORT='' |
1620 | PACKAGE_URL='' |
1621 | |
1622 | @@ -1476,7 +1476,7 @@ |
1623 | # Omit some internal or obsolete options to make the list less imposing. |
1624 | # This message is too long to be a string in the A/UX 3.1 sh. |
1625 | cat <<_ACEOF |
1626 | -\`configure' configures ubuntuone-client 1.1.4 to adapt to many kinds of systems. |
1627 | +\`configure' configures ubuntuone-client 1.1.90 to adapt to many kinds of systems. |
1628 | |
1629 | Usage: $0 [OPTION]... [VAR=VALUE]... |
1630 | |
1631 | @@ -1547,7 +1547,7 @@ |
1632 | |
1633 | if test -n "$ac_init_help"; then |
1634 | case $ac_init_help in |
1635 | - short | recursive ) echo "Configuration of ubuntuone-client 1.1.4:";; |
1636 | + short | recursive ) echo "Configuration of ubuntuone-client 1.1.90:";; |
1637 | esac |
1638 | cat <<\_ACEOF |
1639 | |
1640 | @@ -1660,7 +1660,7 @@ |
1641 | test -n "$ac_init_help" && exit $ac_status |
1642 | if $ac_init_version; then |
1643 | cat <<\_ACEOF |
1644 | -ubuntuone-client configure 1.1.4 |
1645 | +ubuntuone-client configure 1.1.90 |
1646 | generated by GNU Autoconf 2.65 |
1647 | |
1648 | Copyright (C) 2009 Free Software Foundation, Inc. |
1649 | @@ -1938,7 +1938,7 @@ |
1650 | This file contains any messages produced by compilers while |
1651 | running configure, to aid debugging if configure makes a mistake. |
1652 | |
1653 | -It was created by ubuntuone-client $as_me 1.1.4, which was |
1654 | +It was created by ubuntuone-client $as_me 1.1.90, which was |
1655 | generated by GNU Autoconf 2.65. Invocation command line was |
1656 | |
1657 | $ $0 $@ |
1658 | @@ -2748,7 +2748,7 @@ |
1659 | |
1660 | # Define the identity of the package. |
1661 | PACKAGE='ubuntuone-client' |
1662 | - VERSION='1.1.4' |
1663 | + VERSION='1.1.90' |
1664 | |
1665 | |
1666 | cat >>confdefs.h <<_ACEOF |
1667 | @@ -12972,7 +12972,7 @@ |
1668 | # report actual input values of CONFIG_FILES etc. instead of their |
1669 | # values after options handling. |
1670 | ac_log=" |
1671 | -This file was extended by ubuntuone-client $as_me 1.1.4, which was |
1672 | +This file was extended by ubuntuone-client $as_me 1.1.90, which was |
1673 | generated by GNU Autoconf 2.65. Invocation command line was |
1674 | |
1675 | CONFIG_FILES = $CONFIG_FILES |
1676 | @@ -13038,7 +13038,7 @@ |
1677 | cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 |
1678 | ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" |
1679 | ac_cs_version="\\ |
1680 | -ubuntuone-client config.status 1.1.4 |
1681 | +ubuntuone-client config.status 1.1.90 |
1682 | configured by $0, generated by GNU Autoconf 2.65, |
1683 | with options \\"\$ac_cs_config\\" |
1684 | |
1685 | |
1686 | === modified file 'configure.ac' |
1687 | --- configure.ac 2010-03-10 23:36:53 +0000 |
1688 | +++ configure.ac 2010-03-31 23:55:45 +0000 |
1689 | @@ -1,7 +1,7 @@ |
1690 | dnl Process this file with autoconf to produce a configure script. |
1691 | AC_PREREQ(2.53) |
1692 | |
1693 | -AC_INIT([ubuntuone-client], [1.1.4]) |
1694 | +AC_INIT([ubuntuone-client], [1.1.90]) |
1695 | AC_CONFIG_SRCDIR([config.h.in]) |
1696 | |
1697 | AM_INIT_AUTOMAKE([1.10 foreign]) |
1698 | |
1699 | === modified file 'contrib/__init__.pyc' |
1700 | Binary files contrib/__init__.pyc 2010-03-10 23:36:53 +0000 and contrib/__init__.pyc 2010-03-31 23:55:45 +0000 differ |
1701 | === modified file 'contrib/dbus_util.pyc' |
1702 | Binary files contrib/dbus_util.pyc 2010-03-10 23:36:53 +0000 and contrib/dbus_util.pyc 2010-03-31 23:55:45 +0000 differ |
1703 | === modified file 'contrib/mocker.py' |
1704 | --- contrib/mocker.py 2009-06-30 12:00:00 +0000 |
1705 | +++ contrib/mocker.py 2010-03-31 23:55:45 +0000 |
1706 | @@ -1092,9 +1092,12 @@ |
1707 | |
1708 | def __nonzero__(self): |
1709 | try: |
1710 | - return self.__mocker_act__("nonzero") |
1711 | + result = self.__mocker_act__("nonzero") |
1712 | except MatchError, e: |
1713 | return True |
1714 | + if type(result) is Mock: |
1715 | + return True |
1716 | + return result |
1717 | |
1718 | def __iter__(self): |
1719 | # XXX On py3k, when next() becomes __next__(), we'll be able |
1720 | |
1721 | === modified file 'contrib/mocker.pyc' |
1722 | Binary files contrib/mocker.pyc 2010-03-10 23:36:53 +0000 and contrib/mocker.pyc 2010-03-31 23:55:45 +0000 differ |
1723 | === modified file 'contrib/pylint-wrapper' |
1724 | --- contrib/pylint-wrapper 2009-12-07 17:35:00 +0000 |
1725 | +++ contrib/pylint-wrapper 2010-03-31 23:55:45 +0000 |
1726 | @@ -71,7 +71,8 @@ |
1727 | # pylint: disable-msg=W0612 |
1728 | for root, dirs, files in os.walk(SRCDIR, topdown=False): |
1729 | for file in files: |
1730 | - if file.endswith(".py"): |
1731 | + path = "%s/" % root |
1732 | + if file.endswith(".py") or path.endswith("bin/"): |
1733 | pyfiles.append(os.path.join(root, file)) |
1734 | |
1735 | pyfiles.sort() |
1736 | |
1737 | === modified file 'contrib/testing/__init__.pyc' |
1738 | Binary files contrib/testing/__init__.pyc 2010-03-10 23:36:53 +0000 and contrib/testing/__init__.pyc 2010-03-31 23:55:45 +0000 differ |
1739 | === modified file 'contrib/testing/testcase.py' |
1740 | --- contrib/testing/testcase.py 2010-03-10 23:36:53 +0000 |
1741 | +++ contrib/testing/testcase.py 2010-03-31 23:55:45 +0000 |
1742 | @@ -616,6 +616,30 @@ |
1743 | self.remove_from_connection() |
1744 | |
1745 | |
1746 | +class FakeLogger(object): |
1747 | + """Helper logging class.""" |
1748 | + def __init__(self): |
1749 | + self.logged = dict(debug=[], warning=[], info=[]) |
1750 | + |
1751 | + def _log(self, log, txt, args): |
1752 | + """Really logs.""" |
1753 | + if args: |
1754 | + txt = txt % args |
1755 | + log.append(txt) |
1756 | + |
1757 | + def warning(self, txt, *args): |
1758 | + """WARNING logs.""" |
1759 | + self._log(self.logged['warning'], txt, args) |
1760 | + |
1761 | + def debug(self, txt, *args): |
1762 | + """DEBUG logs.""" |
1763 | + self._log(self.logged['debug'], txt, args) |
1764 | + |
1765 | + def info(self, txt, *args): |
1766 | + """INFO logs.""" |
1767 | + self._log(self.logged['info'], txt, args) |
1768 | + |
1769 | + |
1770 | class Listener(object): |
1771 | """Helper class to gather events.""" |
1772 | |
1773 | |
1774 | === modified file 'contrib/testing/testcase.pyc' |
1775 | Binary files contrib/testing/testcase.pyc 2010-03-10 23:36:53 +0000 and contrib/testing/testcase.pyc 2010-03-31 23:55:45 +0000 differ |
1776 | === modified file 'data/Makefile.am' |
1777 | --- data/Makefile.am 2010-02-17 23:51:29 +0000 |
1778 | +++ data/Makefile.am 2010-03-31 23:55:45 +0000 |
1779 | @@ -23,6 +23,10 @@ |
1780 | desktop_in_files = ubuntuone-preferences.desktop.in |
1781 | desktop_DATA = $(desktop_in_files:.desktop.in=.desktop) |
1782 | |
1783 | +autostartdir = $(sysconfdir)/xdg/autostart |
1784 | +autostart_in_files = ubuntuone-launch.desktop.in |
1785 | +autostart_DATA = $(autostart_in_files:.desktop.in=.desktop) |
1786 | + |
1787 | @INTLTOOL_DESKTOP_RULE@ |
1788 | |
1789 | %.conf: %.conf.in |
1790 | @@ -132,6 +136,7 @@ |
1791 | $(config_in_files) \ |
1792 | $(memenu_in_files) \ |
1793 | $(desktop_in_files) \ |
1794 | + $(autostart_in_files) \ |
1795 | $(service_in_files) \ |
1796 | $(emblem_in_files) \ |
1797 | $(icon_in_files) \ |
1798 | @@ -144,6 +149,7 @@ |
1799 | CLEANFILES = \ |
1800 | $(memenu_DATA) \ |
1801 | $(desktop_DATA) \ |
1802 | + $(autostart_DATA) \ |
1803 | $(service_DATA) \ |
1804 | $(emblem_files) \ |
1805 | hicolor |
1806 | |
1807 | === modified file 'data/Makefile.in' |
1808 | --- data/Makefile.in 2010-02-17 23:51:29 +0000 |
1809 | +++ data/Makefile.in 2010-03-31 23:55:45 +0000 |
1810 | @@ -70,12 +70,12 @@ |
1811 | am__base_list = \ |
1812 | sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ |
1813 | sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' |
1814 | -am__installdirs = "$(DESTDIR)$(apportdir)" "$(DESTDIR)$(configdir)" \ |
1815 | - "$(DESTDIR)$(crashdbdir)" "$(DESTDIR)$(desktopdir)" \ |
1816 | - "$(DESTDIR)$(memenudir)" "$(DESTDIR)$(oauthdir)" \ |
1817 | - "$(DESTDIR)$(servicedir)" |
1818 | -DATA = $(apport_DATA) $(config_DATA) $(crashdb_DATA) $(desktop_DATA) \ |
1819 | - $(memenu_DATA) $(oauth_DATA) $(service_DATA) |
1820 | +am__installdirs = "$(DESTDIR)$(apportdir)" "$(DESTDIR)$(autostartdir)" \ |
1821 | + "$(DESTDIR)$(configdir)" "$(DESTDIR)$(crashdbdir)" \ |
1822 | + "$(DESTDIR)$(desktopdir)" "$(DESTDIR)$(memenudir)" \ |
1823 | + "$(DESTDIR)$(oauthdir)" "$(DESTDIR)$(servicedir)" |
1824 | +DATA = $(apport_DATA) $(autostart_DATA) $(config_DATA) $(crashdb_DATA) \ |
1825 | + $(desktop_DATA) $(memenu_DATA) $(oauth_DATA) $(service_DATA) |
1826 | DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) |
1827 | ACLOCAL = @ACLOCAL@ |
1828 | ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ |
1829 | @@ -237,6 +237,9 @@ |
1830 | desktopdir = $(datadir)/applications |
1831 | desktop_in_files = ubuntuone-preferences.desktop.in |
1832 | desktop_DATA = $(desktop_in_files:.desktop.in=.desktop) |
1833 | +autostartdir = $(sysconfdir)/xdg/autostart |
1834 | +autostart_in_files = ubuntuone-launch.desktop.in |
1835 | +autostart_DATA = $(autostart_in_files:.desktop.in=.desktop) |
1836 | hicolordir = $(datadir)/icons/hicolor |
1837 | privateiconsdir = $(pkgdatadir)/icons/hicolor |
1838 | emblem_in_files = \ |
1839 | @@ -258,6 +261,7 @@ |
1840 | $(config_in_files) \ |
1841 | $(memenu_in_files) \ |
1842 | $(desktop_in_files) \ |
1843 | + $(autostart_in_files) \ |
1844 | $(service_in_files) \ |
1845 | $(emblem_in_files) \ |
1846 | $(icon_in_files) \ |
1847 | @@ -270,6 +274,7 @@ |
1848 | CLEANFILES = \ |
1849 | $(memenu_DATA) \ |
1850 | $(desktop_DATA) \ |
1851 | + $(autostart_DATA) \ |
1852 | $(service_DATA) \ |
1853 | $(emblem_files) \ |
1854 | hicolor |
1855 | @@ -337,6 +342,26 @@ |
1856 | test -n "$$files" || exit 0; \ |
1857 | echo " ( cd '$(DESTDIR)$(apportdir)' && rm -f" $$files ")"; \ |
1858 | cd "$(DESTDIR)$(apportdir)" && rm -f $$files |
1859 | +install-autostartDATA: $(autostart_DATA) |
1860 | + @$(NORMAL_INSTALL) |
1861 | + test -z "$(autostartdir)" || $(MKDIR_P) "$(DESTDIR)$(autostartdir)" |
1862 | + @list='$(autostart_DATA)'; test -n "$(autostartdir)" || list=; \ |
1863 | + for p in $$list; do \ |
1864 | + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ |
1865 | + echo "$$d$$p"; \ |
1866 | + done | $(am__base_list) | \ |
1867 | + while read files; do \ |
1868 | + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(autostartdir)'"; \ |
1869 | + $(INSTALL_DATA) $$files "$(DESTDIR)$(autostartdir)" || exit $$?; \ |
1870 | + done |
1871 | + |
1872 | +uninstall-autostartDATA: |
1873 | + @$(NORMAL_UNINSTALL) |
1874 | + @list='$(autostart_DATA)'; test -n "$(autostartdir)" || list=; \ |
1875 | + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ |
1876 | + test -n "$$files" || exit 0; \ |
1877 | + echo " ( cd '$(DESTDIR)$(autostartdir)' && rm -f" $$files ")"; \ |
1878 | + cd "$(DESTDIR)$(autostartdir)" && rm -f $$files |
1879 | install-configDATA: $(config_DATA) |
1880 | @$(NORMAL_INSTALL) |
1881 | test -z "$(configdir)" || $(MKDIR_P) "$(DESTDIR)$(configdir)" |
1882 | @@ -501,7 +526,7 @@ |
1883 | check: check-am |
1884 | all-am: Makefile $(DATA) |
1885 | installdirs: |
1886 | - for dir in "$(DESTDIR)$(apportdir)" "$(DESTDIR)$(configdir)" "$(DESTDIR)$(crashdbdir)" "$(DESTDIR)$(desktopdir)" "$(DESTDIR)$(memenudir)" "$(DESTDIR)$(oauthdir)" "$(DESTDIR)$(servicedir)"; do \ |
1887 | + for dir in "$(DESTDIR)$(apportdir)" "$(DESTDIR)$(autostartdir)" "$(DESTDIR)$(configdir)" "$(DESTDIR)$(crashdbdir)" "$(DESTDIR)$(desktopdir)" "$(DESTDIR)$(memenudir)" "$(DESTDIR)$(oauthdir)" "$(DESTDIR)$(servicedir)"; do \ |
1888 | test -z "$$dir" || $(MKDIR_P) "$$dir"; \ |
1889 | done |
1890 | install: install-am |
1891 | @@ -551,9 +576,10 @@ |
1892 | |
1893 | info-am: |
1894 | |
1895 | -install-data-am: install-apportDATA install-configDATA \ |
1896 | - install-crashdbDATA install-data-local install-desktopDATA \ |
1897 | - install-memenuDATA install-oauthDATA install-serviceDATA |
1898 | +install-data-am: install-apportDATA install-autostartDATA \ |
1899 | + install-configDATA install-crashdbDATA install-data-local \ |
1900 | + install-desktopDATA install-memenuDATA install-oauthDATA \ |
1901 | + install-serviceDATA |
1902 | |
1903 | install-dvi: install-dvi-am |
1904 | |
1905 | @@ -598,29 +624,31 @@ |
1906 | |
1907 | ps-am: |
1908 | |
1909 | -uninstall-am: uninstall-apportDATA uninstall-configDATA \ |
1910 | - uninstall-crashdbDATA uninstall-desktopDATA uninstall-local \ |
1911 | - uninstall-memenuDATA uninstall-oauthDATA uninstall-serviceDATA |
1912 | +uninstall-am: uninstall-apportDATA uninstall-autostartDATA \ |
1913 | + uninstall-configDATA uninstall-crashdbDATA \ |
1914 | + uninstall-desktopDATA uninstall-local uninstall-memenuDATA \ |
1915 | + uninstall-oauthDATA uninstall-serviceDATA |
1916 | |
1917 | .MAKE: install-am install-strip |
1918 | |
1919 | .PHONY: all all-am check check-am clean clean-generic clean-libtool \ |
1920 | dist-hook distclean distclean-generic distclean-libtool \ |
1921 | distdir dvi dvi-am html html-am info info-am install \ |
1922 | - install-am install-apportDATA install-configDATA \ |
1923 | - install-crashdbDATA install-data install-data-am \ |
1924 | - install-data-local install-desktopDATA install-dvi \ |
1925 | - install-dvi-am install-exec install-exec-am install-html \ |
1926 | - install-html-am install-info install-info-am install-man \ |
1927 | - install-memenuDATA install-oauthDATA install-pdf \ |
1928 | + install-am install-apportDATA install-autostartDATA \ |
1929 | + install-configDATA install-crashdbDATA install-data \ |
1930 | + install-data-am install-data-local install-desktopDATA \ |
1931 | + install-dvi install-dvi-am install-exec install-exec-am \ |
1932 | + install-html install-html-am install-info install-info-am \ |
1933 | + install-man install-memenuDATA install-oauthDATA install-pdf \ |
1934 | install-pdf-am install-ps install-ps-am install-serviceDATA \ |
1935 | install-strip installcheck installcheck-am installdirs \ |
1936 | maintainer-clean maintainer-clean-generic \ |
1937 | maintainer-clean-local mostlyclean mostlyclean-generic \ |
1938 | mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am \ |
1939 | - uninstall-apportDATA uninstall-configDATA \ |
1940 | - uninstall-crashdbDATA uninstall-desktopDATA uninstall-local \ |
1941 | - uninstall-memenuDATA uninstall-oauthDATA uninstall-serviceDATA |
1942 | + uninstall-apportDATA uninstall-autostartDATA \ |
1943 | + uninstall-configDATA uninstall-crashdbDATA \ |
1944 | + uninstall-desktopDATA uninstall-local uninstall-memenuDATA \ |
1945 | + uninstall-oauthDATA uninstall-serviceDATA |
1946 | |
1947 | |
1948 | %.menu: %.menu.in |
1949 | |
1950 | === modified file 'data/oauth_registration.d/ubuntuone' |
1951 | --- data/oauth_registration.d/ubuntuone 2009-06-30 12:00:00 +0000 |
1952 | +++ data/oauth_registration.d/ubuntuone 2010-03-31 23:55:45 +0000 |
1953 | @@ -10,4 +10,14 @@ |
1954 | exe_path = /usr/bin/python |
1955 | application_name = ubuntuone-syncdaemon |
1956 | |
1957 | +[control-panel] |
1958 | +realm = https://ubuntuone.com |
1959 | +consumer_key = ubuntuone |
1960 | +exe_path = /usr/bin/python |
1961 | +application_name = ubuntuone-preferences |
1962 | |
1963 | +[launcher] |
1964 | +realm = https://ubuntuone.com |
1965 | +consumer_key = ubuntuone |
1966 | +exe_path = /usr/bin/python |
1967 | +application_name = ubuntuone-launch |
1968 | |
1969 | === modified file 'data/source_ubuntuone-client.py' |
1970 | --- data/source_ubuntuone-client.py 2009-09-28 18:15:00 +0000 |
1971 | +++ data/source_ubuntuone-client.py 2010-03-31 23:55:45 +0000 |
1972 | @@ -27,8 +27,10 @@ |
1973 | # things we may want to collect for the report |
1974 | u1_client_log = os.path.join(u1_log_path, "syncdaemon.log") |
1975 | u1_except_log = os.path.join(u1_log_path, "syncdaemon-exceptions.log") |
1976 | +u1_invalidnames_log = os.path.join(u1_log_path, "syncdaemon-invalid-names.log") |
1977 | u1_oauth_log = os.path.join(u1_log_path, "oauth-login.log") |
1978 | u1_u1sync_log = os.path.join(u1_log_path, "u1sync.log") |
1979 | +u1_prefs_log = os.path.join(u1_log_path, "u1-prefs.log") |
1980 | u1_sd_conf = os.path.join("etc", "xdg", "ubuntuone", "syncdaemon.conf") |
1981 | u1_usersd_conf = os.path.join(u1_user_config_path, "syncdaemon.conf") |
1982 | u1_user_conf = os.path.join(u1_user_config_path, "ubuntuone-client.conf") |
1983 | @@ -36,11 +38,20 @@ |
1984 | |
1985 | def add_info(report): |
1986 | """add report info""" |
1987 | - attach_file_if_exists(report, u1_except_log) |
1988 | - attach_file_if_exists(report, u1_oauth_log) |
1989 | - attach_file_if_exists(report, u1_usersd_conf) |
1990 | - attach_file_if_exists(report, u1_sd_conf) |
1991 | - attach_file_if_exists(report, u1_user_conf) |
1992 | + attach_file_if_exists(report, u1_except_log, |
1993 | + "UbuntuOneSyncdaemonExceptionsLog") |
1994 | + attach_file_if_exists(report, u1_invalidnames_log, |
1995 | + "UbuntuOneSyncdaemonInvalidNamesLog") |
1996 | + attach_file_if_exists(report, u1_oauth_log, |
1997 | + "UbuntuOneOAuthLoginLog") |
1998 | + attach_file_if_exists(report, u1_prefs_log, |
1999 | + "UbuntuOnePreferencesLog") |
2000 | + attach_file_if_exists(report, u1_usersd_conf, |
2001 | + "UbuntuOneSyncdaemonConfig") |
2002 | + attach_file_if_exists(report, u1_sd_conf, |
2003 | + "UbuntuOneUserSyncdaemonConfig") |
2004 | + attach_file_if_exists(report, u1_user_conf, |
2005 | + "UbuntuOneClientConfig") |
2006 | |
2007 | if not apport.packaging.is_distro_package(report['Package'].split()[0]): |
2008 | report['ThirdParty'] = 'True' |
2009 | @@ -62,4 +73,4 @@ |
2010 | if version is None: |
2011 | version = 'N/A' |
2012 | versions += '%s %s\n' % (package, version) |
2013 | - report['UbuntuoneClientPackages'] = versions |
2014 | + report['UbuntuOneClientPackages'] = versions |
2015 | |
2016 | === modified file 'data/syncdaemon.conf' |
2017 | --- data/syncdaemon.conf 2010-03-04 16:47:43 +0000 |
2018 | +++ data/syncdaemon.conf 2010-03-31 23:55:45 +0000 |
2019 | @@ -14,6 +14,11 @@ |
2020 | port.parser = int |
2021 | port.help = The port on which to connect to the server |
2022 | |
2023 | +files_sync_enabled.default = True |
2024 | +files_sync_enabled.action = store_true |
2025 | +files_sync_enabled.parser = bool |
2026 | +files_sync_enabled.help = Toggles synchronization of files (False disables syncdaemon entirely) |
2027 | + |
2028 | root_dir.default = ~/Ubuntu One |
2029 | root_dir.parser = home_dir |
2030 | root_dir.help = Use the specified directory as the root |
2031 | @@ -62,6 +67,17 @@ |
2032 | udf_autosubscribe.help = Autosubsribe to new User Defined Folders, 'on' by default. |
2033 | (accepted values: 1/0, on/off, true/false and yes/no) |
2034 | |
2035 | +ignore.parser = lines |
2036 | +ignore.help = The list of (Python, not bash) regexes of the files that |
2037 | + SD should ignore. |
2038 | +ignore.default = \A#.*\Z |
2039 | + \A.*~\Z |
2040 | + \A.*\.py[oc]\Z |
2041 | + \A.*\.sw[nop]\Z |
2042 | + \A.*\.swpx\Z |
2043 | + \A\..*\.tmp\Z |
2044 | + |
2045 | + |
2046 | [bandwidth_throttling] |
2047 | on.default = False |
2048 | on.parser = bool |
2049 | |
2050 | === added file 'data/ubuntuone-launch.desktop.in' |
2051 | --- data/ubuntuone-launch.desktop.in 1970-01-01 00:00:00 +0000 |
2052 | +++ data/ubuntuone-launch.desktop.in 2010-03-31 23:55:45 +0000 |
2053 | @@ -0,0 +1,7 @@ |
2054 | +[Desktop Entry] |
2055 | +Name=Ubuntu One |
2056 | +Exec=/bin/sh -c '[ -d "$HOME/Ubuntu One" ] && ubuntuone-launch' |
2057 | +Type=Application |
2058 | +X-GNOME-Autostart-Delay=30 |
2059 | +Icon=ubuntuone |
2060 | +Comment=Â |
2061 | |
2062 | === modified file 'data/ubuntuone-preferences.desktop.in' |
2063 | --- data/ubuntuone-preferences.desktop.in 2010-02-17 23:51:29 +0000 |
2064 | +++ data/ubuntuone-preferences.desktop.in 2010-03-31 23:55:45 +0000 |
2065 | @@ -1,6 +1,6 @@ |
2066 | [Desktop Entry] |
2067 | Name=Ubuntu One |
2068 | -Comment=Configure and manage your Ubuntu One account |
2069 | +_Comment=Configure and manage your Ubuntu One account |
2070 | Exec=ubuntuone-preferences |
2071 | Icon=ubuntuone |
2072 | Terminal=false |
2073 | |
2074 | === modified file 'debian/changelog' |
2075 | --- debian/changelog 2010-03-11 00:04:35 +0000 |
2076 | +++ debian/changelog 2010-03-31 23:55:45 +0000 |
2077 | @@ -1,3 +1,16 @@ |
2078 | +ubuntuone-client (1.1.90-0ubuntu1) UNRELEASED; urgency=low |
2079 | + |
2080 | + * New upstream release. |
2081 | + - Notify user when approaching and exceeding quota (LP: #540360) |
2082 | + - Add instructional text to ubuntuone-preferences (LP: #539676) |
2083 | + - UbuntuOne needs to autostart and connect by defualt (LP: #534707) |
2084 | + - Impossible to infer status of file synchronization (LP: #526084) |
2085 | + - Devices and Services tabs functionality and development (LP: #525803) |
2086 | + * Add debian/source/format. |
2087 | + * Remove python-httplib2 from the dependencies (LP: #535207) |
2088 | + |
2089 | + -- Rodney Dawes <rodney.dawes@canonical.com> Wed, 31 Mar 2010 19:47:01 -0400 |
2090 | + |
2091 | ubuntuone-client (1.1.4-0ubuntu1) lucid; urgency=low |
2092 | |
2093 | * New upstream release. |
2094 | |
2095 | === modified file 'debian/control' |
2096 | --- debian/control 2010-03-10 23:50:45 +0000 |
2097 | +++ debian/control 2010-03-31 23:55:45 +0000 |
2098 | @@ -41,7 +41,6 @@ |
2099 | Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, |
2100 | ubuntuone-client (= ${source:Version}), |
2101 | python-gtk2 (>= 2.10), |
2102 | - python-httplib2, |
2103 | python-simplejson |
2104 | Replaces: ubuntuone-client (<= 1.1.1) |
2105 | Conflicts: ubuntuone-client (<= 1.1.1) |
2106 | |
2107 | === added directory 'debian/source' |
2108 | === added file 'debian/source/format' |
2109 | --- debian/source/format 1970-01-01 00:00:00 +0000 |
2110 | +++ debian/source/format 2010-03-31 23:55:45 +0000 |
2111 | @@ -0,0 +1,2 @@ |
2112 | +3.0 (quilt) |
2113 | + |
2114 | |
2115 | === modified file 'docs/states_manager.svg' |
2116 | --- docs/states_manager.svg 2010-03-04 16:47:43 +0000 |
2117 | +++ docs/states_manager.svg 2010-03-31 23:55:45 +0000 |
2118 | @@ -33,10 +33,10 @@ |
2119 | inkscape:document-units="px" |
2120 | inkscape:current-layer="layer1" |
2121 | showgrid="false" |
2122 | - inkscape:window-width="1280" |
2123 | - inkscape:window-height="728" |
2124 | + inkscape:window-width="1920" |
2125 | + inkscape:window-height="1010" |
2126 | inkscape:window-x="0" |
2127 | - inkscape:window-y="25" |
2128 | + inkscape:window-y="24" |
2129 | inkscape:window-maximized="1" |
2130 | showguides="true" |
2131 | inkscape:guide-bbox="true" /> |
2132 | @@ -5733,6 +5733,46 @@ |
2133 | inkscape:vp_y="0 : 1000 : 0" |
2134 | inkscape:vp_x="0 : 0.5 : 1" |
2135 | sodipodi:type="inkscape:persp3d" /> |
2136 | + <inkscape:perspective |
2137 | + id="perspective4002" |
2138 | + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" |
2139 | + inkscape:vp_z="1 : 0.5 : 1" |
2140 | + inkscape:vp_y="0 : 1000 : 0" |
2141 | + inkscape:vp_x="0 : 0.5 : 1" |
2142 | + sodipodi:type="inkscape:persp3d" /> |
2143 | + <marker |
2144 | + inkscape:stockid="Arrow1Mend1u64" |
2145 | + orient="auto" |
2146 | + refY="0" |
2147 | + refX="0" |
2148 | + id="Arrow1Mend1u64-857" |
2149 | + style="overflow:visible"> |
2150 | + <path |
2151 | + id="path7114-41" |
2152 | + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" |
2153 | + style="fill:#796a69;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" |
2154 | + transform="matrix(-0.4,0,0,-0.4,-4,0)" /> |
2155 | + </marker> |
2156 | + <inkscape:perspective |
2157 | + id="perspective4069" |
2158 | + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" |
2159 | + inkscape:vp_z="1 : 0.5 : 1" |
2160 | + inkscape:vp_y="0 : 1000 : 0" |
2161 | + inkscape:vp_x="0 : 0.5 : 1" |
2162 | + sodipodi:type="inkscape:persp3d" /> |
2163 | + <marker |
2164 | + inkscape:stockid="Arrow1Mend1u64" |
2165 | + orient="auto" |
2166 | + refY="0" |
2167 | + refX="0" |
2168 | + id="Arrow1Mend1u64-64" |
2169 | + style="overflow:visible"> |
2170 | + <path |
2171 | + id="path7114-30" |
2172 | + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" |
2173 | + style="fill:#796a69;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" |
2174 | + transform="matrix(-0.4,0,0,-0.4,-4,0)" /> |
2175 | + </marker> |
2176 | </defs> |
2177 | <metadata |
2178 | id="metadata7"> |
2179 | @@ -5849,63 +5889,63 @@ |
2180 | <text |
2181 | sodipodi:linespacing="125%" |
2182 | xml:space="preserve" |
2183 | - style="font-size:19.21366692px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans" |
2184 | - x="751.61261" |
2185 | - y="556.11877" |
2186 | + style="font-size:17.80227852px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans" |
2187 | + x="839.35968" |
2188 | + y="460.50702" |
2189 | id="text2467-1-5-4-7"><tspan |
2190 | sodipodi:role="line" |
2191 | id="tspan2469-4-3-8-9" |
2192 | - x="751.61261" |
2193 | - y="556.11877" |
2194 | - style="font-size:12.22687912px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono">3. SYS_NET_CONNECTED</tspan></text> |
2195 | + x="839.35968" |
2196 | + y="460.50702" |
2197 | + style="font-size:11.328722px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono">3. SYS_NET_CONNECTED</tspan></text> |
2198 | <text |
2199 | sodipodi:linespacing="125%" |
2200 | xml:space="preserve" |
2201 | - style="font-size:19.21366692px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans" |
2202 | - x="751.82153" |
2203 | - y="576.80383" |
2204 | + style="font-size:17.80227852px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans" |
2205 | + x="839.55328" |
2206 | + y="479.67267" |
2207 | id="text2467-1-5-4-4"><tspan |
2208 | sodipodi:role="line" |
2209 | id="tspan2469-4-3-8-1" |
2210 | - x="751.82153" |
2211 | - y="576.80383" |
2212 | - style="font-size:12.22687912px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono">4. SYS_USER_CONNECT</tspan></text> |
2213 | + x="839.55328" |
2214 | + y="479.67267" |
2215 | + style="font-size:11.328722px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono">4. SYS_USER_CONNECT</tspan></text> |
2216 | <text |
2217 | sodipodi:linespacing="125%" |
2218 | xml:space="preserve" |
2219 | - style="font-size:19.21366692px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans" |
2220 | - x="751.60071" |
2221 | - y="638.85907" |
2222 | + style="font-size:17.80227852px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans" |
2223 | + x="839.34869" |
2224 | + y="537.16943" |
2225 | id="text2467-1-5-4-5"><tspan |
2226 | sodipodi:role="line" |
2227 | id="tspan2469-4-3-8-3" |
2228 | - x="751.60071" |
2229 | - y="638.85907" |
2230 | - style="font-size:12.22687912px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono">7. SYS_CONNECTION_LOST</tspan></text> |
2231 | + x="839.34869" |
2232 | + y="537.16943" |
2233 | + style="font-size:11.328722px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono">7. SYS_CONNECTION_LOST</tspan></text> |
2234 | <text |
2235 | sodipodi:linespacing="125%" |
2236 | xml:space="preserve" |
2237 | - style="font-size:19.21366692px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans" |
2238 | - x="751.57678" |
2239 | - y="597.48889" |
2240 | + style="font-size:17.80227852px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans" |
2241 | + x="839.32654" |
2242 | + y="498.8382" |
2243 | id="text2467-1-5-4-3-8"><tspan |
2244 | sodipodi:role="line" |
2245 | id="tspan2469-4-3-8-38" |
2246 | - x="751.57678" |
2247 | - y="597.48889" |
2248 | - style="font-size:12.22687912px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono">5. SYS_NET_DISCONNECTED</tspan></text> |
2249 | + x="839.32654" |
2250 | + y="498.8382" |
2251 | + style="font-size:11.328722px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono">5. SYS_NET_DISCONNECTED</tspan></text> |
2252 | <text |
2253 | sodipodi:linespacing="125%" |
2254 | xml:space="preserve" |
2255 | - style="font-size:19.21366692px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans" |
2256 | - x="751.63654" |
2257 | - y="618.17401" |
2258 | + style="font-size:17.80227852px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans" |
2259 | + x="839.38184" |
2260 | + y="518.00385" |
2261 | id="text2467-1-5-4-4-0"><tspan |
2262 | sodipodi:role="line" |
2263 | id="tspan2469-4-3-8-1-3" |
2264 | - x="751.63654" |
2265 | - y="618.17401" |
2266 | - style="font-size:12.22687912px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono">6. SYS_USER_DISCONNECT</tspan></text> |
2267 | + x="839.38184" |
2268 | + y="518.00385" |
2269 | + style="font-size:11.328722px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono">6. SYS_USER_DISCONNECT</tspan></text> |
2270 | <g |
2271 | id="g8266" |
2272 | transform="matrix(0.74658494,0,0,0.74658494,-0.8603134,-224.75307)"> |
2273 | @@ -6138,27 +6178,6 @@ |
2274 | x="373.00955" |
2275 | sodipodi:role="line" |
2276 | id="tspan8676-1-0">AUTH_FAILED</tspan></text> |
2277 | - <g |
2278 | - id="g8763" |
2279 | - transform="translate(-571.53861,-16.17932)"> |
2280 | - <path |
2281 | - sodipodi:nodetypes="ccccccc" |
2282 | - id="rect8502-1-3-1-3" |
2283 | - d="m 687.97895,686.92944 19.03124,19.03125 -18.43749,18.4375 130.95537,0 -19.03125,-19.03125 18.4375,-18.4375 -130.95537,0 z" |
2284 | - style="opacity:0.80373798;color:#000000;fill:#ececec;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.33651051;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> |
2285 | - <text |
2286 | - sodipodi:linespacing="150%" |
2287 | - xml:space="preserve" |
2288 | - style="font-size:11.40619183px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:150%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans" |
2289 | - x="753.75067" |
2290 | - y="709.81586" |
2291 | - id="text2429-9-7-7-2-2-4-3"><tspan |
2292 | - id="tspan8676-1-9" |
2293 | - sodipodi:role="line" |
2294 | - x="753.75067" |
2295 | - y="709.81586" |
2296 | - style="font-size:11.40619183px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:150%;writing-mode:lr-tb;text-anchor:middle;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans">UNKNOWN</tspan></text> |
2297 | - </g> |
2298 | <path |
2299 | style="color:#000000;fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:url(#Arrow1Mend);visibility:visible;display:inline;overflow:visible;enable-background:accumulate" |
2300 | d="M 196.60308,507.23994 332.40207,379.84707" |
2301 | @@ -6498,15 +6517,15 @@ |
2302 | <text |
2303 | sodipodi:linespacing="125%" |
2304 | xml:space="preserve" |
2305 | - style="font-size:19.21366692px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans" |
2306 | - x="751.64844" |
2307 | - y="659.54419" |
2308 | + style="font-size:17.80227852px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans" |
2309 | + x="839.39288" |
2310 | + y="556.33508" |
2311 | id="text2467-1-5-4-5-5"><tspan |
2312 | sodipodi:role="line" |
2313 | id="tspan2469-4-3-8-3-7" |
2314 | - x="751.64844" |
2315 | - y="659.54419" |
2316 | - style="font-size:12.22687912px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono">8. SYS_HANDSHAKE_TIMEOUT</tspan></text> |
2317 | + x="839.39288" |
2318 | + y="556.33508" |
2319 | + style="font-size:11.328722px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono">8. SYS_HANDSHAKE_TIMEOUT</tspan></text> |
2320 | <path |
2321 | style="color:#000000;fill:none;stroke:#000000;stroke-width:1.50000000000000000;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:url(#DotM);marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" |
2322 | d="m 172.15675,355.01368 21.74975,7.69663" |
2323 | @@ -6599,22 +6618,6 @@ |
2324 | style="opacity:0.65322583;color:#000000;fill:#00ff00;fill-opacity:1;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" |
2325 | sodipodi:type="arc" /> |
2326 | <path |
2327 | - style="color:#000000;fill:none;stroke:#967b71;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:url(#Arrow1Mend1u64);visibility:visible;display:inline;overflow:visible;enable-background:accumulate" |
2328 | - d="M 41.694143,630.27619 C 191.18408,629.59254 179.79003,629.79207 183.20824,666.48091" |
2329 | - id="path8817-5-8-1-5-2" |
2330 | - sodipodi:nodetypes="cc" /> |
2331 | - <text |
2332 | - sodipodi:linespacing="125%" |
2333 | - xml:space="preserve" |
2334 | - style="font-size:12.56705952px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans Italic" |
2335 | - x="41.174591" |
2336 | - y="625.82983" |
2337 | - id="text2461-6-0-31-6-2"><tspan |
2338 | - sodipodi:role="line" |
2339 | - x="41.174591" |
2340 | - y="625.82983" |
2341 | - id="tspan2463-3-4-2-8-1">From any node</tspan></text> |
2342 | - <path |
2343 | transform="matrix(0.24411038,0,0,0.22458156,264.46156,450.51532)" |
2344 | d="m -147.4098,407.55515 a 42.646858,47.282387 0 1 1 -85.29371,0 42.646858,47.282387 0 1 1 85.29371,0 z" |
2345 | sodipodi:ry="47.282387" |
2346 | @@ -6635,16 +6638,6 @@ |
2347 | style="opacity:0.65322583;color:#000000;fill:#00ff00;fill-opacity:1;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" |
2348 | sodipodi:type="arc" /> |
2349 | <path |
2350 | - transform="matrix(0.24411038,0,0,0.22458156,229.17074,569.22208)" |
2351 | - d="m -147.4098,407.55515 a 42.646858,47.282387 0 1 1 -85.29371,0 42.646858,47.282387 0 1 1 85.29371,0 z" |
2352 | - sodipodi:ry="47.282387" |
2353 | - sodipodi:rx="42.646858" |
2354 | - sodipodi:cy="407.55515" |
2355 | - sodipodi:cx="-190.05666" |
2356 | - id="path4627-2-8-0" |
2357 | - style="opacity:0.65322583;color:#000000;fill:#00ff00;fill-opacity:1;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" |
2358 | - sodipodi:type="arc" /> |
2359 | - <path |
2360 | transform="matrix(0.24411038,0,0,0.22458156,644.85465,391.20219)" |
2361 | d="m -147.4098,407.55515 a 42.646858,47.282387 0 1 1 -85.29371,0 42.646858,47.282387 0 1 1 85.29371,0 z" |
2362 | sodipodi:ry="47.282387" |
2363 | @@ -6767,26 +6760,26 @@ |
2364 | <text |
2365 | sodipodi:linespacing="125%" |
2366 | xml:space="preserve" |
2367 | - style="font-size:10.45871924999999969px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:end;text-decoration:none;line-height:125%;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:end;color:#000000;fill:#ff9d5b;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans Italic;opacity:1" |
2368 | - x="944.04236" |
2369 | - y="682.54211" |
2370 | + style="font-size:9.69044781px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:end;text-decoration:none;line-height:125%;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:end;color:#000000;fill:#ff9d5b;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans Italic" |
2371 | + x="1017.654" |
2372 | + y="577.64355" |
2373 | id="text2461-6-0-31-6-4-5"><tspan |
2374 | sodipodi:role="line" |
2375 | - x="944.04236" |
2376 | - y="682.54211" |
2377 | + x="1017.654" |
2378 | + y="577.64355" |
2379 | id="tspan18329" |
2380 | - style="font-size:10.45871924999999969px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:end;text-decoration:none;line-height:125%;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:end;color:#000000;fill:#ff9d5b;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans Italic">Note: On all nodes 3 & 4 do anything</tspan></text> |
2381 | + style="font-size:9.69044781px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:end;text-decoration:none;line-height:125%;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:end;color:#000000;fill:#ff9d5b;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans Italic">Note: On all nodes 3 & 4 do anything</tspan></text> |
2382 | <text |
2383 | sodipodi:linespacing="125%" |
2384 | xml:space="preserve" |
2385 | - style="font-size:10.45871924999999969px;font-style:italic;font-weight:normal;text-align:end;line-height:125%;writing-mode:lr-tb;text-anchor:end;fill:#ff6600;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans Italic" |
2386 | - x="-551.41644" |
2387 | - y="739.90375" |
2388 | + style="font-size:9.69044781px;font-style:italic;font-weight:normal;text-align:end;line-height:125%;writing-mode:lr-tb;text-anchor:end;fill:#ff6600;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans Italic" |
2389 | + x="-456.15012" |
2390 | + y="828.51099" |
2391 | id="text2461-6-0-31-6-4-5-3" |
2392 | transform="matrix(0,-1,1,0,0,0)"><tspan |
2393 | sodipodi:role="line" |
2394 | - x="-551.41644" |
2395 | - y="739.90375" |
2396 | + x="-456.15012" |
2397 | + y="828.51099" |
2398 | id="tspan18329-0">ConnectionManager</tspan></text> |
2399 | <g |
2400 | id="g18554" |
2401 | @@ -6966,30 +6959,6 @@ |
2402 | x="1138.0615" |
2403 | sodipodi:role="line">AQ.disconnect()</tspan></text> |
2404 | </g> |
2405 | - <g |
2406 | - id="g18776" |
2407 | - transform="translate(-1317.1307,149.4903)"> |
2408 | - <rect |
2409 | - style="opacity:0.65322583;color:#000000;fill:#00ff00;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.847;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" |
2410 | - id="rect5912-2-48-2-9-0" |
2411 | - width="129.83394" |
2412 | - height="19.030485" |
2413 | - x="1372.1442" |
2414 | - y="549.65802" |
2415 | - ry="4.5156531" |
2416 | - rx="4.5156531" /> |
2417 | - <text |
2418 | - id="text2461-6-7-4-5-0" |
2419 | - y="562.5957" |
2420 | - x="1399.2255" |
2421 | - style="font-size:10.90068817px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans" |
2422 | - xml:space="preserve" |
2423 | - sodipodi:linespacing="125%"><tspan |
2424 | - id="tspan2463-3-5-2-7-1" |
2425 | - y="562.5957" |
2426 | - x="1399.2255" |
2427 | - sodipodi:role="line">main.restart()</tspan></text> |
2428 | - </g> |
2429 | <text |
2430 | sodipodi:linespacing="125%" |
2431 | xml:space="preserve" |
2432 | @@ -7383,18 +7352,6 @@ |
2433 | <text |
2434 | sodipodi:linespacing="125%" |
2435 | xml:space="preserve" |
2436 | - style="font-size:16.76553154px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;color:#000000;fill:#967b71;fill-opacity:1;stroke:none;stroke-width:2;marker:none;marker-end:url(#Arrow1Mend1u64);visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans" |
2437 | - x="102.40143" |
2438 | - y="647.05518" |
2439 | - id="text2467-1-5-4-7-3-64-9-9"><tspan |
2440 | - sodipodi:role="line" |
2441 | - id="tspan2469-4-3-8-9-0-4-4-7" |
2442 | - x="102.40143" |
2443 | - y="647.05518" |
2444 | - style="font-size:10.66897488px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;color:#000000;fill:#967b71;fill-opacity:1;stroke:none;stroke-width:2;marker:none;marker-end:url(#Arrow1Mend1u64);visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono">SYS_UNKNOWN_ERROR</tspan></text> |
2445 | - <text |
2446 | - sodipodi:linespacing="125%" |
2447 | - xml:space="preserve" |
2448 | style="font-size:25.45085716px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans" |
2449 | x="234.69305" |
2450 | y="438.2403" |
2451 | @@ -7404,5 +7361,140 @@ |
2452 | x="234.69305" |
2453 | y="438.2403" |
2454 | style="font-size:16.19600105px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff6600;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono">5,6</tspan></text> |
2455 | + <g |
2456 | + style="display:inline" |
2457 | + id="g8763-8" |
2458 | + transform="matrix(0.76886496,0,0,0.76886496,230.74978,145.34928)"> |
2459 | + <path |
2460 | + sodipodi:nodetypes="ccccccc" |
2461 | + id="rect8502-1-3-1-3-5" |
2462 | + d="m 687.97895,686.92944 19.03124,19.03125 -18.43749,18.4375 130.95537,0 -19.03125,-19.03125 18.4375,-18.4375 -130.95537,0 z" |
2463 | + style="opacity:0.80373798;color:#000000;fill:#ececec;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.33651051;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> |
2464 | + <text |
2465 | + sodipodi:linespacing="150%" |
2466 | + xml:space="preserve" |
2467 | + style="font-size:11.40619183px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:150%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans" |
2468 | + x="753.75067" |
2469 | + y="709.81586" |
2470 | + id="text2429-9-7-7-2-2-4-3-9"><tspan |
2471 | + id="tspan8676-1-9-7" |
2472 | + sodipodi:role="line" |
2473 | + x="753.75067" |
2474 | + y="709.81586" |
2475 | + style="font-size:11.40619183px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:150%;writing-mode:lr-tb;text-anchor:middle;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans">UNKNOWN</tspan></text> |
2476 | + </g> |
2477 | + <path |
2478 | + style="color:#000000;fill:none;stroke:#967b71;stroke-width:1.53772998;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:url(#Arrow1Mend1u64);visibility:visible;display:inline;overflow:visible;enable-background:accumulate" |
2479 | + d="m 702.24295,642.38627 c 114.93757,-0.52564 106.17709,-0.37222 108.80523,27.83654" |
2480 | + id="path8817-5-8-1-5-2-5" |
2481 | + sodipodi:nodetypes="cc" /> |
2482 | + <text |
2483 | + sodipodi:linespacing="125%" |
2484 | + xml:space="preserve" |
2485 | + style="font-size:9.66237164px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans Italic" |
2486 | + x="701.84351" |
2487 | + y="638.96759" |
2488 | + id="text2461-6-0-31-6-2-3"><tspan |
2489 | + sodipodi:role="line" |
2490 | + x="701.84351" |
2491 | + y="638.96759" |
2492 | + id="tspan2463-3-4-2-8-1-8">From any node</tspan></text> |
2493 | + <path |
2494 | + transform="matrix(0.18768792,0,0,0.17267289,846.38714,595.4439)" |
2495 | + d="m -147.4098,407.55515 a 42.646858,47.282387 0 1 1 -85.29371,0 42.646858,47.282387 0 1 1 85.29371,0 z" |
2496 | + sodipodi:ry="47.282387" |
2497 | + sodipodi:rx="42.646858" |
2498 | + sodipodi:cy="407.55515" |
2499 | + sodipodi:cx="-190.05666" |
2500 | + id="path4627-2-8-0-8" |
2501 | + style="opacity:0.65322583;color:#000000;fill:#00ff00;fill-opacity:1;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" |
2502 | + sodipodi:type="arc" /> |
2503 | + <g |
2504 | + style="display:inline" |
2505 | + id="g18776-3" |
2506 | + transform="matrix(0.76886496,0,0,0.76886496,-342.50986,272.72685)"> |
2507 | + <rect |
2508 | + style="opacity:0.65322583;color:#000000;fill:#00ff00;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.847;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" |
2509 | + id="rect5912-2-48-2-9-0-1" |
2510 | + width="129.83394" |
2511 | + height="19.030485" |
2512 | + x="1372.1442" |
2513 | + y="549.65802" |
2514 | + ry="4.5156531" |
2515 | + rx="4.5156531" /> |
2516 | + <text |
2517 | + id="text2461-6-7-4-5-0-8" |
2518 | + y="562.5957" |
2519 | + x="1399.2255" |
2520 | + style="font-size:10.90068817px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans" |
2521 | + xml:space="preserve" |
2522 | + sodipodi:linespacing="125%"><tspan |
2523 | + id="tspan2463-3-5-2-7-1-9" |
2524 | + y="562.5957" |
2525 | + x="1399.2255" |
2526 | + sodipodi:role="line">main.restart()</tspan></text> |
2527 | + </g> |
2528 | + <text |
2529 | + sodipodi:linespacing="125%" |
2530 | + xml:space="preserve" |
2531 | + style="font-size:12.8904295px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;color:#000000;fill:#967b71;fill-opacity:1;stroke:none;stroke-width:2;marker:none;marker-end:url(#Arrow1Mend1u64);visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans" |
2532 | + x="748.91864" |
2533 | + y="655.28705" |
2534 | + id="text2467-1-5-4-7-3-64-9-9-6"><tspan |
2535 | + sodipodi:role="line" |
2536 | + id="tspan2469-4-3-8-9-0-4-4-7-4" |
2537 | + x="748.91864" |
2538 | + y="655.28705" |
2539 | + style="font-size:8.20300102px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;color:#000000;fill:#967b71;fill-opacity:1;stroke:none;stroke-width:2;marker:none;marker-end:url(#Arrow1Mend1u64);visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono">SYS_UNKNOWN_ERROR</tspan></text> |
2540 | + <g |
2541 | + style="display:inline" |
2542 | + id="g8763-8-3" |
2543 | + transform="matrix(0.76886496,0,0,0.76886496,398.58213,145.34928)"> |
2544 | + <path |
2545 | + sodipodi:nodetypes="ccccccc" |
2546 | + id="rect8502-1-3-1-3-5-0" |
2547 | + d="m 687.97895,686.92944 19.03124,19.03125 -18.43749,18.4375 130.95537,0 -19.03125,-19.03125 18.4375,-18.4375 -130.95537,0 z" |
2548 | + style="opacity:0.80373798;color:#000000;fill:#ececec;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.33651051;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> |
2549 | + <text |
2550 | + sodipodi:linespacing="150%" |
2551 | + xml:space="preserve" |
2552 | + style="font-size:11.40619183px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:150%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans" |
2553 | + x="753.75067" |
2554 | + y="709.81586" |
2555 | + id="text2429-9-7-7-2-2-4-3-9-9"><tspan |
2556 | + id="tspan8676-1-9-7-2" |
2557 | + sodipodi:role="line" |
2558 | + x="753.75067" |
2559 | + y="709.81586" |
2560 | + style="font-size:11.40619183px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:150%;writing-mode:lr-tb;text-anchor:middle;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans">ROOT_MISMATCH</tspan></text> |
2561 | + </g> |
2562 | + <path |
2563 | + style="color:#000000;fill:none;stroke:#967b71;stroke-width:1.53772998;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:url(#Arrow1Mend1u64);visibility:visible;display:inline;overflow:visible;enable-background:accumulate" |
2564 | + d="m 870.0753,642.38627 c 114.93757,-0.52564 106.17709,-0.37222 108.80523,27.83654" |
2565 | + id="path8817-5-8-1-5-2-5-5" |
2566 | + sodipodi:nodetypes="cc" /> |
2567 | + <text |
2568 | + sodipodi:linespacing="125%" |
2569 | + xml:space="preserve" |
2570 | + style="font-size:9.66237164px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans Italic" |
2571 | + x="869.67584" |
2572 | + y="638.96759" |
2573 | + id="text2461-6-0-31-6-2-3-4"><tspan |
2574 | + sodipodi:role="line" |
2575 | + x="869.67584" |
2576 | + y="638.96759" |
2577 | + id="tspan2463-3-4-2-8-1-8-0">From any node</tspan></text> |
2578 | + <text |
2579 | + sodipodi:linespacing="125%" |
2580 | + xml:space="preserve" |
2581 | + style="font-size:12.8904295px;font-style:italic;font-weight:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;color:#000000;fill:#967b71;fill-opacity:1;stroke:none;stroke-width:2;marker:none;marker-end:url(#Arrow1Mend1u64);visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Bitstream Vera Sans;-inkscape-font-specification:Sans" |
2582 | + x="916.75098" |
2583 | + y="655.28705" |
2584 | + id="text2467-1-5-4-7-3-64-9-9-6-2"><tspan |
2585 | + sodipodi:role="line" |
2586 | + id="tspan2469-4-3-8-9-0-4-4-7-4-2" |
2587 | + x="916.75098" |
2588 | + y="655.28705" |
2589 | + style="font-size:8.20300102px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;color:#000000;fill:#967b71;fill-opacity:1;stroke:none;stroke-width:2;marker:none;marker-end:url(#Arrow1Mend1u64);visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono">SYS_ROOT_MISMATCH</tspan></text> |
2590 | </g> |
2591 | </svg> |
2592 | |
2593 | === modified file 'docs/syncdaemon_dbus_api.txt' |
2594 | --- docs/syncdaemon_dbus_api.txt 2010-03-10 23:36:53 +0000 |
2595 | +++ docs/syncdaemon_dbus_api.txt 2010-03-31 23:55:45 +0000 |
2596 | @@ -48,6 +48,9 @@ |
2597 | UploadFinished(path=s,info=a{ss}) |
2598 | Fire a D-BUS signal, notifying an upload has finished. |
2599 | |
2600 | + InvalidName(dirname=s,filename=ay) |
2601 | + Fire a D-BUS signal, notifying an invalid file or dir name. |
2602 | + |
2603 | DownloadStarted(path=v) |
2604 | Fire a D-BUS signal, notifying a download has started. |
2605 | |
2606 | @@ -141,6 +144,9 @@ |
2607 | udf_autosubscribe_enabled() |
2608 | Return the udf_autosubscribe config value. |
2609 | |
2610 | + files_sync_enabled() |
2611 | + No docstring |
2612 | + |
2613 | disable_bandwidth_throttling() |
2614 | Disable bandwidth throttling. |
2615 | |
2616 | @@ -154,6 +160,9 @@ |
2617 | enable_bandwidth_throttling() |
2618 | Enable bandwidth throttling. |
2619 | |
2620 | + set_files_sync_enabled(enabled=b) |
2621 | + Enable UDF autosubscribe. |
2622 | + |
2623 | disable_udf_autosubscribe() |
2624 | Enable UDF autosubscribe. |
2625 | |
2626 | |
2627 | === modified file 'nautilus/contacts-view.c' |
2628 | --- nautilus/contacts-view.c 2010-02-17 23:51:29 +0000 |
2629 | +++ nautilus/contacts-view.c 2010-03-31 23:55:45 +0000 |
2630 | @@ -38,6 +38,7 @@ |
2631 | } SelectedContactInfo; |
2632 | |
2633 | enum { |
2634 | + SELECTION_CHANGED_SIGNAL, |
2635 | CONTACTS_COUNT_CHANGED_SIGNAL, |
2636 | LAST_SIGNAL |
2637 | }; |
2638 | @@ -85,6 +86,15 @@ |
2639 | object_class->finalize = contacts_view_finalize; |
2640 | |
2641 | /* Signals */ |
2642 | + contacts_view_signals[SELECTION_CHANGED_SIGNAL] = |
2643 | + g_signal_new ("selection-changed", |
2644 | + G_OBJECT_CLASS_TYPE (object_class), |
2645 | + G_SIGNAL_RUN_LAST, |
2646 | + G_STRUCT_OFFSET (ContactsViewClass, selection_changed), |
2647 | + NULL, NULL, |
2648 | + g_cclosure_marshal_VOID__VOID, |
2649 | + G_TYPE_NONE, |
2650 | + 0); |
2651 | contacts_view_signals[CONTACTS_COUNT_CHANGED_SIGNAL] = |
2652 | g_signal_new ("contacts-count-changed", |
2653 | G_OBJECT_CLASS_TYPE (object_class), |
2654 | @@ -115,38 +125,33 @@ |
2655 | } |
2656 | |
2657 | static void |
2658 | -item_activated_cb (GtkIconView *icon_view, GtkTreePath *path, gpointer data) |
2659 | +selection_changed_cb (GtkIconView *icon_view, gpointer data) |
2660 | { |
2661 | - GtkTreeIter iter; |
2662 | GtkTreeModel *model; |
2663 | + GList *selected_items, *l; |
2664 | ContactsView *cv = CONTACTS_VIEW (data); |
2665 | |
2666 | + g_debug ("Selection_changed called"); |
2667 | + |
2668 | + /* We first remove all the previous selected items */ |
2669 | + g_hash_table_remove_all (cv->selection); |
2670 | + |
2671 | + /* Now add the new selection */ |
2672 | model = gtk_icon_view_get_model (icon_view); |
2673 | - if (gtk_tree_model_get_iter (model, &iter, path)) { |
2674 | - gchar *name, *email; |
2675 | - GdkPixbuf *icon; |
2676 | - |
2677 | - gtk_tree_model_get (model, &iter, |
2678 | - CONTACTS_VIEW_COLUMN_NAME, &name, |
2679 | - CONTACTS_VIEW_COLUMN_EMAIL, &email, |
2680 | - CONTACTS_VIEW_COLUMN_PIXBUF, &icon, |
2681 | - -1); |
2682 | - |
2683 | - /* if already selected, deselect it */ |
2684 | - if (g_hash_table_lookup (cv->selection, name)) { |
2685 | - gtk_list_store_set (GTK_LIST_STORE (model), &iter, |
2686 | - CONTACTS_VIEW_COLUMN_MARKUP, name, |
2687 | - -1); |
2688 | - g_hash_table_remove (cv->selection, name); |
2689 | - } else { |
2690 | - gchar *markup; |
2691 | + selected_items = gtk_icon_view_get_selected_items (icon_view); |
2692 | + for (l = selected_items; l != NULL; l = l->next) { |
2693 | + GtkTreeIter iter; |
2694 | + |
2695 | + if (gtk_tree_model_get_iter (model, &iter, (GtkTreePath *) l->data)) { |
2696 | + gchar *name, *email; |
2697 | + GdkPixbuf *icon; |
2698 | SelectedContactInfo *sci; |
2699 | |
2700 | - markup = g_strdup_printf ("<b><big>%s</big></b>", name); |
2701 | - gtk_list_store_set (GTK_LIST_STORE (model), &iter, |
2702 | - CONTACTS_VIEW_COLUMN_MARKUP, markup, |
2703 | + gtk_tree_model_get (model, &iter, |
2704 | + CONTACTS_VIEW_COLUMN_NAME, &name, |
2705 | + CONTACTS_VIEW_COLUMN_EMAIL, &email, |
2706 | + CONTACTS_VIEW_COLUMN_PIXBUF, &icon, |
2707 | -1); |
2708 | - g_free (markup); |
2709 | |
2710 | sci = g_new0 (SelectedContactInfo, 1); |
2711 | sci->name = g_strdup (name); |
2712 | @@ -159,6 +164,12 @@ |
2713 | save_recently_used_list (cv); |
2714 | } |
2715 | } |
2716 | + |
2717 | + /* Free memory */ |
2718 | + g_list_foreach (selected_items, (GFunc) gtk_tree_path_free, NULL); |
2719 | + g_list_free (selected_items); |
2720 | + |
2721 | + g_signal_emit_by_name (cv, "selection-changed", NULL); |
2722 | } |
2723 | |
2724 | static void |
2725 | @@ -258,7 +269,7 @@ |
2726 | } |
2727 | |
2728 | static void |
2729 | -add_contacts (ContactsView *cv, GList *contacts) |
2730 | +add_contacts (ContactsView *cv, GList *contacts, GHashTable *selection_hash) |
2731 | { |
2732 | GList *l; |
2733 | |
2734 | @@ -266,7 +277,7 @@ |
2735 | EContact *contact = l->data; |
2736 | |
2737 | /* We add the selected items when searching, so ignore them here */ |
2738 | - if (g_hash_table_lookup (cv->selection, (gconstpointer) e_contact_get_const (contact, E_CONTACT_FULL_NAME))) |
2739 | + if (g_hash_table_lookup (selection_hash, (gconstpointer) e_contact_get_const (contact, E_CONTACT_FULL_NAME))) |
2740 | continue; |
2741 | |
2742 | add_one_contact (cv, |
2743 | @@ -290,15 +301,19 @@ |
2744 | |
2745 | if (status != E_BOOK_ERROR_OK) { |
2746 | g_warning ("Error opening addressbook: %d", status); |
2747 | + g_object_unref (G_OBJECT (book)); |
2748 | return; |
2749 | } |
2750 | |
2751 | + /* Add the book to the list of opened books */ |
2752 | + cv->books = g_slist_append (cv->books, book); |
2753 | + |
2754 | /* Get all contacts for this book */ |
2755 | query = e_book_query_any_field_contains (""); |
2756 | e_book_get_contacts (book, query, &contacts, NULL); |
2757 | e_book_query_unref (query); |
2758 | |
2759 | - add_contacts (cv, contacts); |
2760 | + add_contacts (cv, contacts, cv->selection); |
2761 | |
2762 | } |
2763 | |
2764 | @@ -352,8 +367,8 @@ |
2765 | gtk_widget_show (scroll); |
2766 | |
2767 | cv->recently_used_view = gtk_icon_view_new (); |
2768 | - g_signal_connect (G_OBJECT (cv->recently_used_view), "item-activated", |
2769 | - G_CALLBACK (item_activated_cb), cv); |
2770 | + g_signal_connect (G_OBJECT (cv->recently_used_view), "selection-changed", |
2771 | + G_CALLBACK (selection_changed_cb), cv); |
2772 | gtk_icon_view_set_text_column (GTK_ICON_VIEW (cv->recently_used_view), CONTACTS_VIEW_COLUMN_NAME); |
2773 | gtk_icon_view_set_markup_column (GTK_ICON_VIEW (cv->recently_used_view), CONTACTS_VIEW_COLUMN_MARKUP); |
2774 | gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (cv->recently_used_view), CONTACTS_VIEW_COLUMN_PIXBUF); |
2775 | @@ -374,8 +389,8 @@ |
2776 | gtk_widget_show (scroll); |
2777 | |
2778 | cv->alphabetical_view = gtk_icon_view_new (); |
2779 | - g_signal_connect (G_OBJECT (cv->alphabetical_view), "item-activated", |
2780 | - G_CALLBACK (item_activated_cb), cv); |
2781 | + g_signal_connect (G_OBJECT (cv->alphabetical_view), "selection-changed", |
2782 | + G_CALLBACK (selection_changed_cb), cv); |
2783 | gtk_icon_view_set_text_column (GTK_ICON_VIEW (cv->alphabetical_view), CONTACTS_VIEW_COLUMN_NAME); |
2784 | gtk_icon_view_set_markup_column (GTK_ICON_VIEW (cv->alphabetical_view), CONTACTS_VIEW_COLUMN_MARKUP); |
2785 | gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (cv->alphabetical_view), CONTACTS_VIEW_COLUMN_PIXBUF); |
2786 | @@ -410,7 +425,6 @@ |
2787 | book = e_book_new ((ESource *) sl->data, &error); |
2788 | if (book != NULL) { |
2789 | e_book_async_open (book, TRUE, (EBookCallback) book_opened_cb, cv); |
2790 | - cv->books = g_slist_append (cv->books, book); |
2791 | } else { |
2792 | g_warning ("Could not open addressbook %s: %s", e_source_get_uri (sl->data), error->message); |
2793 | g_error_free (error); |
2794 | @@ -426,39 +440,57 @@ |
2795 | } |
2796 | |
2797 | static void |
2798 | -append_selected_to_model (GtkListStore *model, SelectedContactInfo *sli) |
2799 | +append_selected_to_model (GtkIconView *icon_view, SelectedContactInfo *sci) |
2800 | { |
2801 | - gchar *markup; |
2802 | GtkTreeIter new_row; |
2803 | - |
2804 | - markup = g_strdup_printf ("<b><big>%s</big></b>", (const gchar *) sli->name); |
2805 | + GtkListStore *model = GTK_LIST_STORE (gtk_icon_view_get_model (icon_view)); |
2806 | |
2807 | gtk_list_store_prepend (model, &new_row); |
2808 | gtk_list_store_set (GTK_LIST_STORE (model), &new_row, |
2809 | - CONTACTS_VIEW_COLUMN_NAME, sli->name, |
2810 | - CONTACTS_VIEW_COLUMN_MARKUP, markup, |
2811 | - CONTACTS_VIEW_COLUMN_EMAIL, sli->email, |
2812 | - CONTACTS_VIEW_COLUMN_PIXBUF, sli->pixbuf, |
2813 | + CONTACTS_VIEW_COLUMN_NAME, sci->name, |
2814 | + CONTACTS_VIEW_COLUMN_MARKUP, sci->name, |
2815 | + CONTACTS_VIEW_COLUMN_EMAIL, sci->email, |
2816 | + CONTACTS_VIEW_COLUMN_PIXBUF, sci->pixbuf, |
2817 | -1); |
2818 | |
2819 | - g_free (markup); |
2820 | + gtk_icon_view_select_path (icon_view, |
2821 | + gtk_tree_model_get_path (GTK_TREE_MODEL (model), &new_row)); |
2822 | } |
2823 | |
2824 | static void |
2825 | foreach_selected_to_model_cb (gpointer key, gpointer value, gpointer user_data) |
2826 | { |
2827 | - GtkListStore *model; |
2828 | - SelectedContactInfo *sli = (SelectedContactInfo *) value; |
2829 | + SelectedContactInfo *sci = (SelectedContactInfo *) value; |
2830 | ContactsView *cv = CONTACTS_VIEW (user_data); |
2831 | |
2832 | - append_selected_to_model (GTK_LIST_STORE (gtk_icon_view_get_model (GTK_ICON_VIEW (cv->recently_used_view))), sli); |
2833 | - append_selected_to_model (GTK_LIST_STORE (gtk_icon_view_get_model (GTK_ICON_VIEW (cv->alphabetical_view))), sli); |
2834 | + append_selected_to_model (GTK_ICON_VIEW (cv->recently_used_view), sci); |
2835 | + append_selected_to_model (GTK_ICON_VIEW (cv->alphabetical_view), sci); |
2836 | } |
2837 | |
2838 | void |
2839 | contacts_view_search (ContactsView *cv, const gchar *search_string) |
2840 | { |
2841 | GSList *l; |
2842 | + GHashTable *tmp_selection; |
2843 | + GHashTableIter hash_iter; |
2844 | + gpointer key, value; |
2845 | + |
2846 | + /* Make a copy of the selected items before changing the models */ |
2847 | + tmp_selection = g_hash_table_new_full (g_str_hash, g_str_equal, |
2848 | + (GDestroyNotify) g_free, |
2849 | + (GDestroyNotify) free_selected_contact_info); |
2850 | + g_hash_table_iter_init (&hash_iter, cv->selection); |
2851 | + while (g_hash_table_iter_next (&hash_iter, &key, &value)) { |
2852 | + SelectedContactInfo *new_sci, *old_sci; |
2853 | + |
2854 | + old_sci = (SelectedContactInfo *) value; |
2855 | + |
2856 | + new_sci = g_new0 (SelectedContactInfo, 1); |
2857 | + new_sci->name = g_strdup (old_sci->name); |
2858 | + new_sci->email = g_strdup (old_sci->email); |
2859 | + new_sci->pixbuf = gdk_pixbuf_ref (old_sci->pixbuf); |
2860 | + g_hash_table_insert (tmp_selection, g_strdup (old_sci->name), new_sci); |
2861 | + } |
2862 | |
2863 | /* Reset the icon views */ |
2864 | gtk_list_store_clear (GTK_LIST_STORE (gtk_icon_view_get_model (GTK_ICON_VIEW (cv->recently_used_view)))); |
2865 | @@ -481,11 +513,12 @@ |
2866 | e_book_get_contacts (book, query, &contacts, NULL); |
2867 | e_book_query_unref (query); |
2868 | |
2869 | - add_contacts (cv, contacts); |
2870 | + add_contacts (cv, contacts, tmp_selection); |
2871 | } |
2872 | |
2873 | /* Now add selected contacts */ |
2874 | - g_hash_table_foreach (cv->selection, (GHFunc) foreach_selected_to_model_cb, cv); |
2875 | + g_hash_table_foreach (tmp_selection, (GHFunc) foreach_selected_to_model_cb, cv); |
2876 | + g_hash_table_destroy (tmp_selection); |
2877 | } |
2878 | |
2879 | static void |
2880 | @@ -505,3 +538,59 @@ |
2881 | g_hash_table_foreach (cv->selection, (GHFunc) add_selection_to_list_cb, &selection); |
2882 | return selection; |
2883 | } |
2884 | + |
2885 | +guint |
2886 | +contacts_view_get_contacts_count (ContactsView *cv) |
2887 | +{ |
2888 | + return gtk_tree_model_iter_n_children (gtk_icon_view_get_model (GTK_ICON_VIEW (cv->alphabetical_view)), |
2889 | + NULL); |
2890 | +} |
2891 | + |
2892 | +void |
2893 | +contacts_view_add_contact (ContactsView *cv, const gchar *contact_name, const gchar *contact_email) |
2894 | +{ |
2895 | + SelectedContactInfo *sci; |
2896 | + GtkIconTheme *icon_theme; |
2897 | + GSList *l; |
2898 | + |
2899 | + icon_theme = gtk_icon_theme_get_default (); |
2900 | + |
2901 | + /* First add the new contact to the list of selected ones */ |
2902 | + sci = g_new0 (SelectedContactInfo, 1); |
2903 | + sci->name = g_strdup (contact_name); |
2904 | + sci->email = g_strdup (contact_email); |
2905 | + sci->pixbuf = gtk_icon_theme_load_icon (icon_theme, GTK_STOCK_ORIENTATION_PORTRAIT, 64, 0, NULL); |
2906 | + g_hash_table_insert (cv->selection, g_strdup (contact_name), sci); |
2907 | + |
2908 | + /* Add it to the recently used list */ |
2909 | + g_hash_table_insert (cv->recently_used, g_strdup (sci->name), sci->name); |
2910 | + save_recently_used_list (cv); |
2911 | + |
2912 | + /* And now add it to the icon views */ |
2913 | + append_selected_to_model (GTK_ICON_VIEW (cv->recently_used_view), sci); |
2914 | + append_selected_to_model (GTK_ICON_VIEW (cv->alphabetical_view), sci); |
2915 | + |
2916 | + /* Add the contact to the CouchDB addressbook, if possible */ |
2917 | + for (l = cv->books; l != NULL; l = l->next) { |
2918 | + const gchar *uri; |
2919 | + |
2920 | + uri = e_book_get_uri (E_BOOK (l->data)); |
2921 | + if (g_str_has_prefix (uri, "couchdb://127.0.0.1")) { |
2922 | + EContact *contact; |
2923 | + GError *error = NULL; |
2924 | + |
2925 | + contact = e_contact_new (); |
2926 | + e_contact_set (contact, E_CONTACT_FULL_NAME, (gconstpointer) contact_name); |
2927 | + e_contact_set (contact, E_CONTACT_EMAIL_1, (gconstpointer) contact_email); |
2928 | + |
2929 | + if (!e_book_add_contact (E_BOOK (l->data), contact, &error)) { |
2930 | + g_warning ("Could not add contact to %s: %s", uri, error->message); |
2931 | + g_error_free (error); |
2932 | + } |
2933 | + |
2934 | + g_object_unref (G_OBJECT (contact)); |
2935 | + |
2936 | + break; |
2937 | + } |
2938 | + } |
2939 | +} |
2940 | |
2941 | === modified file 'nautilus/contacts-view.h' |
2942 | --- nautilus/contacts-view.h 2010-02-17 23:51:29 +0000 |
2943 | +++ nautilus/contacts-view.h 2010-03-31 23:55:45 +0000 |
2944 | @@ -54,6 +54,7 @@ |
2945 | GtkNotebookClass parent_class; |
2946 | |
2947 | /* Signals */ |
2948 | + void (* selection_changed) (ContactsView *cv); |
2949 | void (* contacts_count_changed) (ContactsView *cv, gint total); |
2950 | } ContactsViewClass; |
2951 | |
2952 | @@ -62,5 +63,7 @@ |
2953 | GtkWidget *contacts_view_new (void); |
2954 | void contacts_view_search (ContactsView *cv, const gchar *search_string); |
2955 | GSList *contacts_view_get_selected_emails (ContactsView *cv); |
2956 | +guint contacts_view_get_contacts_count (ContactsView *cv); |
2957 | +void contacts_view_add_contact (ContactsView *cv, const gchar *contact_name, const gchar *contact_email); |
2958 | |
2959 | #endif |
2960 | |
2961 | === modified file 'nautilus/u1-contacts-picker.c' |
2962 | --- nautilus/u1-contacts-picker.c 2010-02-17 23:51:29 +0000 |
2963 | +++ nautilus/u1-contacts-picker.c 2010-03-31 23:55:45 +0000 |
2964 | @@ -27,7 +27,17 @@ |
2965 | GtkWidget *search_entry; |
2966 | GtkWidget *total_label; |
2967 | GtkWidget *contacts_view; |
2968 | -}; |
2969 | + |
2970 | + /* Hidden widgets to add a new contact */ |
2971 | + GtkWidget *add_contact_button; |
2972 | +}; |
2973 | + |
2974 | +enum { |
2975 | + SELECTION_CHANGED_SIGNAL, |
2976 | + LAST_SIGNAL |
2977 | +}; |
2978 | + |
2979 | +static guint u1_contacts_picker_signals[LAST_SIGNAL] = { 0, }; |
2980 | |
2981 | G_DEFINE_TYPE(U1ContactsPicker, u1_contacts_picker, GTK_TYPE_VBOX) |
2982 | |
2983 | @@ -50,14 +60,42 @@ |
2984 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
2985 | |
2986 | object_class->finalize = u1_contacts_picker_finalize; |
2987 | + |
2988 | + /* Register object signals */ |
2989 | + u1_contacts_picker_signals[SELECTION_CHANGED_SIGNAL] = |
2990 | + g_signal_new ("selection-changed", |
2991 | + G_TYPE_FROM_CLASS (klass), |
2992 | + (GSignalFlags) G_SIGNAL_RUN_LAST, |
2993 | + G_STRUCT_OFFSET (U1ContactsPickerClass, selection_changed), |
2994 | + NULL, |
2995 | + NULL, |
2996 | + g_cclosure_marshal_VOID__VOID, |
2997 | + G_TYPE_NONE, |
2998 | + 0); |
2999 | } |
3000 | |
3001 | static void |
3002 | -search_activated_cb (GtkEntry *entry, gpointer data) |
3003 | +search_activated_cb (GtkEditable *entry, gpointer data) |
3004 | { |
3005 | + const gchar *text; |
3006 | U1ContactsPicker *picker = (U1ContactsPicker *) data; |
3007 | |
3008 | - contacts_view_search (CONTACTS_VIEW (picker->priv->contacts_view), gtk_entry_get_text (entry)); |
3009 | + text = gtk_entry_get_text (GTK_ENTRY (entry)); |
3010 | + contacts_view_search (CONTACTS_VIEW (picker->priv->contacts_view), text); |
3011 | + |
3012 | + /* If no contacts offer the user to add it to the contacts database */ |
3013 | + if (contacts_view_get_contacts_count (CONTACTS_VIEW (picker->priv->contacts_view)) == 0) |
3014 | + gtk_widget_show (picker->priv->add_contact_button); |
3015 | + else |
3016 | + gtk_widget_hide (picker->priv->add_contact_button); |
3017 | +} |
3018 | + |
3019 | +static void |
3020 | +view_selection_changed_cb (ContactsView *cv, gpointer user_data) |
3021 | +{ |
3022 | + U1ContactsPicker *picker = U1_CONTACTS_PICKER (user_data); |
3023 | + |
3024 | + g_signal_emit (picker, u1_contacts_picker_signals[SELECTION_CHANGED_SIGNAL], 0); |
3025 | } |
3026 | |
3027 | static void |
3028 | @@ -66,41 +104,102 @@ |
3029 | gchar *label; |
3030 | U1ContactsPicker *picker = U1_CONTACTS_PICKER (user_data); |
3031 | |
3032 | - label = g_strdup_printf (ngettext ("%d contact", "%d contacts", total), total); |
3033 | + if (strlen (gtk_entry_get_text (GTK_ENTRY (picker->priv->search_entry))) > 0) |
3034 | + label = g_strdup_printf (ngettext ("Found %d match", "Found %d matches", total), total); |
3035 | + else |
3036 | + label = g_strdup_printf (ngettext ("%d contact", "%d contacts", total), total); |
3037 | gtk_label_set_text (GTK_LABEL (picker->priv->total_label), label); |
3038 | |
3039 | g_free (label); |
3040 | } |
3041 | |
3042 | static void |
3043 | +add_contact_cb (GtkButton *button, gpointer user_data) |
3044 | +{ |
3045 | + GtkWidget *dialog, *table, *label, *name_entry, *email_entry; |
3046 | + const gchar *search_text; |
3047 | + U1ContactsPicker *picker = (U1ContactsPicker *) user_data; |
3048 | + |
3049 | + /* Build the dialog */ |
3050 | + dialog = gtk_dialog_new_with_buttons (_("Add contact"), |
3051 | + GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (picker))), |
3052 | + GTK_DIALOG_NO_SEPARATOR, |
3053 | + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
3054 | + GTK_STOCK_ADD, GTK_RESPONSE_OK, |
3055 | + NULL); |
3056 | + table = gtk_table_new (2, 2, FALSE); |
3057 | + gtk_widget_show (table); |
3058 | + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), |
3059 | + table, TRUE, TRUE, 3); |
3060 | + |
3061 | + label = gtk_label_new (_("Contact name")); |
3062 | + gtk_widget_show (label); |
3063 | + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 3, 3); |
3064 | + name_entry = gtk_entry_new (); |
3065 | + gtk_widget_show (name_entry); |
3066 | + gtk_table_attach (GTK_TABLE (table), name_entry, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 3, 3); |
3067 | + |
3068 | + label = gtk_label_new (_("Email address")); |
3069 | + gtk_widget_show (label); |
3070 | + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 3, 3); |
3071 | + email_entry = gtk_entry_new (); |
3072 | + gtk_widget_show (email_entry); |
3073 | + gtk_table_attach (GTK_TABLE (table), email_entry, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 3, 3); |
3074 | + |
3075 | + search_text = gtk_entry_get_text (GTK_ENTRY (picker->priv->search_entry)); |
3076 | + if (g_strrstr (search_text, "@") != NULL) |
3077 | + gtk_entry_set_text (GTK_ENTRY (email_entry), search_text); |
3078 | + else |
3079 | + gtk_entry_set_text (GTK_ENTRY (name_entry), search_text); |
3080 | + |
3081 | + /* Run the dialog */ |
3082 | + gtk_widget_grab_focus (name_entry); |
3083 | + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { |
3084 | + contacts_view_add_contact (CONTACTS_VIEW (picker->priv->contacts_view), |
3085 | + gtk_entry_get_text (GTK_ENTRY (name_entry)), |
3086 | + gtk_entry_get_text (GTK_ENTRY (email_entry))); |
3087 | + gtk_entry_set_text (GTK_ENTRY (picker->priv->search_entry), ""); |
3088 | + } |
3089 | + |
3090 | + gtk_widget_destroy (dialog); |
3091 | + gtk_widget_hide (picker->priv->add_contact_button); |
3092 | +} |
3093 | + |
3094 | +static void |
3095 | u1_contacts_picker_init (U1ContactsPicker *picker) |
3096 | { |
3097 | - GtkWidget *table, *scroll; |
3098 | - GtkTreeModel *model; |
3099 | + GtkWidget *table; |
3100 | |
3101 | picker->priv = g_new0 (U1ContactsPickerPrivate, 1); |
3102 | |
3103 | /* Create the table to contain the layout */ |
3104 | - table = gtk_table_new (3, 2, FALSE); |
3105 | + table = gtk_table_new (4, 3, FALSE); |
3106 | gtk_widget_show (table); |
3107 | gtk_box_pack_start (GTK_BOX (picker), table, TRUE, TRUE, 3); |
3108 | |
3109 | /* Create the search area */ |
3110 | picker->priv->search_entry = gtk_entry_new (); |
3111 | - g_signal_connect (G_OBJECT (picker->priv->search_entry), "activate", G_CALLBACK (search_activated_cb), picker); |
3112 | + g_signal_connect (G_OBJECT (picker->priv->search_entry), "changed", G_CALLBACK (search_activated_cb), picker); |
3113 | gtk_widget_show (picker->priv->search_entry); |
3114 | gtk_table_attach (GTK_TABLE (table), picker->priv->search_entry, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 3, 3); |
3115 | |
3116 | - picker->priv->total_label = gtk_label_new (ngettext ("%d contact", "%d contacts", 0)); |
3117 | + picker->priv->add_contact_button = gtk_button_new_from_stock (GTK_STOCK_ADD); |
3118 | + g_signal_connect (G_OBJECT (picker->priv->add_contact_button), "clicked", |
3119 | + G_CALLBACK (add_contact_cb), picker); |
3120 | + gtk_table_attach (GTK_TABLE (table), picker->priv->add_contact_button, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 3, 3); |
3121 | + |
3122 | + picker->priv->total_label = gtk_label_new (ngettext ("0 contact", "0 contacts", 0)); |
3123 | gtk_widget_show (picker->priv->total_label); |
3124 | - gtk_table_attach (GTK_TABLE (table), picker->priv->total_label, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 3, 3); |
3125 | - |
3126 | + gtk_table_attach (GTK_TABLE (table), picker->priv->total_label, 2, 3, 0, 1, GTK_FILL, GTK_FILL, 3, 3); |
3127 | + |
3128 | /* Create the contacts view */ |
3129 | picker->priv->contacts_view = contacts_view_new (); |
3130 | + g_signal_connect (G_OBJECT (picker->priv->contacts_view), "selection-changed", |
3131 | + G_CALLBACK (view_selection_changed_cb), picker); |
3132 | g_signal_connect (G_OBJECT (picker->priv->contacts_view), "contacts-count-changed", |
3133 | G_CALLBACK (contacts_count_changed_cb), picker); |
3134 | gtk_widget_show (picker->priv->contacts_view); |
3135 | - gtk_table_attach (GTK_TABLE (table), picker->priv->contacts_view, 0, 2, 1, 3, |
3136 | + gtk_table_attach (GTK_TABLE (table), picker->priv->contacts_view, 0, 3, 2, 4, |
3137 | GTK_FILL | GTK_EXPAND | GTK_SHRINK, |
3138 | GTK_FILL | GTK_EXPAND | GTK_SHRINK, |
3139 | 3, 3); |
3140 | |
3141 | === modified file 'nautilus/u1-contacts-picker.h' |
3142 | --- nautilus/u1-contacts-picker.h 2010-02-17 23:51:29 +0000 |
3143 | +++ nautilus/u1-contacts-picker.h 2010-03-31 23:55:45 +0000 |
3144 | @@ -44,6 +44,9 @@ |
3145 | |
3146 | struct _U1ContactsPickerClass { |
3147 | GtkVBoxClass parent_class; |
3148 | + |
3149 | + /* Signals */ |
3150 | + void (* selection_changed) (U1ContactsPicker *picker); |
3151 | }; |
3152 | |
3153 | GType u1_contacts_picker_get_type (void); |
3154 | |
3155 | === modified file 'nautilus/ubuntuone-nautilus.c' |
3156 | --- nautilus/ubuntuone-nautilus.c 2010-03-10 23:36:53 +0000 |
3157 | +++ nautilus/ubuntuone-nautilus.c 2010-03-31 23:55:45 +0000 |
3158 | @@ -328,7 +328,7 @@ |
3159 | const char * uri, |
3160 | GtkWidget * parent) { |
3161 | UbuntuOneNautilus * uon; |
3162 | - gchar * path, * upath; |
3163 | + gchar * path; |
3164 | GtkWidget * hbox = NULL; |
3165 | GtkWidget * label; |
3166 | gchar * labeltext; |
3167 | @@ -402,6 +402,20 @@ |
3168 | data = NULL; |
3169 | } |
3170 | |
3171 | +static void |
3172 | +picker_selection_changed_cb (U1ContactsPicker *picker, gpointer user_data) |
3173 | +{ |
3174 | + GSList *selection; |
3175 | + GtkWidget * dialog = (GtkWidget *) user_data; |
3176 | + |
3177 | + selection = u1_contacts_picker_get_selected_emails (picker); |
3178 | + if (selection != NULL) { |
3179 | + gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT, TRUE); |
3180 | + u1_contacts_picker_free_selection_list (selection); |
3181 | + } else |
3182 | + gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT, FALSE); |
3183 | +} |
3184 | + |
3185 | /* Share on Ubuntu One dialog constructor */ |
3186 | static GtkWidget * ubuntuone_nautilus_share_dialog_construct (struct _CBData * data) { |
3187 | GtkWidget * dialog; |
3188 | @@ -414,10 +428,11 @@ |
3189 | gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); |
3190 | gtk_dialog_add_buttons (GTK_DIALOG (dialog), |
3191 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
3192 | - ("Share"), GTK_RESPONSE_ACCEPT, |
3193 | + (_("Share")), GTK_RESPONSE_ACCEPT, |
3194 | NULL); |
3195 | gtk_dialog_set_default_response (GTK_DIALOG (dialog), |
3196 | GTK_RESPONSE_ACCEPT); |
3197 | + gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT, FALSE); |
3198 | gtk_window_set_icon_name (GTK_WINDOW (dialog), "ubuntuone"); |
3199 | |
3200 | area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); |
3201 | @@ -436,6 +451,8 @@ |
3202 | gtk_widget_show (label); |
3203 | |
3204 | data->user_picker = u1_contacts_picker_new (); |
3205 | + g_signal_connect (G_OBJECT (data->user_picker), "selection-changed", |
3206 | + G_CALLBACK (picker_selection_changed_cb), dialog); |
3207 | gtk_table_attach (GTK_TABLE (table), data->user_picker, 0, 2, 1, 2, |
3208 | GTK_FILL | GTK_EXPAND | GTK_SHRINK, |
3209 | GTK_FILL | GTK_EXPAND | GTK_SHRINK, 3, 3); |
3210 | |
3211 | === modified file 'tests/__init__.pyc' |
3212 | Binary files tests/__init__.pyc 2010-03-10 23:36:53 +0000 and tests/__init__.pyc 2010-03-31 23:55:45 +0000 differ |
3213 | === modified file 'tests/oauthdesktop/__init__.pyc' |
3214 | Binary files tests/oauthdesktop/__init__.pyc 2010-03-10 23:36:53 +0000 and tests/oauthdesktop/__init__.pyc 2010-03-31 23:55:45 +0000 differ |
3215 | === modified file 'tests/oauthdesktop/test_auth.pyc' |
3216 | Binary files tests/oauthdesktop/test_auth.pyc 2010-03-10 23:36:53 +0000 and tests/oauthdesktop/test_auth.pyc 2010-03-31 23:55:45 +0000 differ |
3217 | === modified file 'tests/oauthdesktop/test_config.pyc' |
3218 | Binary files tests/oauthdesktop/test_config.pyc 2010-03-10 23:36:53 +0000 and tests/oauthdesktop/test_config.pyc 2010-03-31 23:55:45 +0000 differ |
3219 | === modified file 'tests/oauthdesktop/test_key_acls.pyc' |
3220 | Binary files tests/oauthdesktop/test_key_acls.pyc 2010-03-10 23:36:53 +0000 and tests/oauthdesktop/test_key_acls.pyc 2010-03-31 23:55:45 +0000 differ |
3221 | === modified file 'tests/oauthdesktop/test_main.pyc' |
3222 | Binary files tests/oauthdesktop/test_main.pyc 2010-03-10 23:36:53 +0000 and tests/oauthdesktop/test_main.pyc 2010-03-31 23:55:45 +0000 differ |
3223 | === modified file 'tests/syncdaemon/__init__.pyc' |
3224 | Binary files tests/syncdaemon/__init__.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/__init__.pyc 2010-03-31 23:55:45 +0000 differ |
3225 | === modified file 'tests/syncdaemon/fsm/__init__.pyc' |
3226 | Binary files tests/syncdaemon/fsm/__init__.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/fsm/__init__.pyc 2010-03-31 23:55:45 +0000 differ |
3227 | === modified file 'tests/syncdaemon/fsm/test_fsm.pyc' |
3228 | Binary files tests/syncdaemon/fsm/test_fsm.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/fsm/test_fsm.pyc 2010-03-31 23:55:45 +0000 differ |
3229 | === modified file 'tests/syncdaemon/fsm/test_fsm_run.pyc' |
3230 | Binary files tests/syncdaemon/fsm/test_fsm_run.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/fsm/test_fsm_run.pyc 2010-03-31 23:55:45 +0000 differ |
3231 | === modified file 'tests/syncdaemon/fsm/test_run_hello.pyc' |
3232 | Binary files tests/syncdaemon/fsm/test_run_hello.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/fsm/test_run_hello.pyc 2010-03-31 23:55:45 +0000 differ |
3233 | === modified file 'tests/syncdaemon/test_action_predicates.pyc' |
3234 | Binary files tests/syncdaemon/test_action_predicates.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_action_predicates.pyc 2010-03-31 23:55:45 +0000 differ |
3235 | === modified file 'tests/syncdaemon/test_action_queue.py' |
3236 | --- tests/syncdaemon/test_action_queue.py 2010-03-10 23:36:53 +0000 |
3237 | +++ tests/syncdaemon/test_action_queue.py 2010-03-31 23:55:45 +0000 |
3238 | @@ -47,7 +47,7 @@ |
3239 | from ubuntuone.syncdaemon.action_queue import ( |
3240 | ActionQueue, ActionQueueCommand, ChangePublicAccess, CreateUDF, |
3241 | DeleteVolume, ListDir, ListVolumes, NoisyRequestQueue, RequestQueue, |
3242 | - Upload |
3243 | + Upload, CreateShare, |
3244 | ) |
3245 | from ubuntuone.syncdaemon.event_queue import EventQueue, EVENTS |
3246 | from ubuntuone.syncdaemon.volume_manager import UDF |
3247 | @@ -1183,6 +1183,65 @@ |
3248 | self.assertEquals(events, self.command.action_queue.event_queue.events) |
3249 | |
3250 | |
3251 | +class CreateShareTestCase(ConnectedBaseTestCase): |
3252 | + """Test for CreateShare ActionQueueCommand.""" |
3253 | + |
3254 | + @defer.inlineCallbacks |
3255 | + def setUp(self): |
3256 | + """Init.""" |
3257 | + yield super(CreateShareTestCase, self).setUp() |
3258 | + self.request_queue = RequestQueue(name='foo', action_queue=self.action_queue) |
3259 | + self.orig_create_share_http = CreateShare._create_share_http |
3260 | + |
3261 | + @defer.inlineCallbacks |
3262 | + def tearDown(self): |
3263 | + yield super(CreateShareTestCase, self).tearDown() |
3264 | + CreateShare._create_share_http = self.orig_create_share_http |
3265 | + |
3266 | + @defer.inlineCallbacks |
3267 | + def test_access_level_modify_http(self): |
3268 | + """Test proper handling of the access level in the http case.""" |
3269 | + # replace _create_share_http with a fake, just to check the args |
3270 | + d = defer.Deferred() |
3271 | + def check_create_http(self, node_id, user, name, read_only, deferred): |
3272 | + """Fire the deferred with the args.""" |
3273 | + d.callback((node_id, user, name, read_only)) |
3274 | + deferred.callback(None) |
3275 | + CreateShare._create_share_http = check_create_http |
3276 | + command = CreateShare(self.request_queue, 'node_id', |
3277 | + 'share_to@example.com', 'share_name', |
3278 | + 'Modify', 'marker') |
3279 | + self.assertTrue(command.use_http, 'CreateShare should be in http mode') |
3280 | + |
3281 | + command._run() |
3282 | + node_id, user, name, read_only = yield d |
3283 | + self.assertEquals('node_id', node_id) |
3284 | + self.assertEquals('share_to@example.com', user) |
3285 | + self.assertEquals('share_name', name) |
3286 | + self.assertFalse(read_only) |
3287 | + |
3288 | + @defer.inlineCallbacks |
3289 | + def test_access_level_view_http(self): |
3290 | + """Test proper handling of the access level in the http case.""" |
3291 | + # replace _create_share_http with a fake, just to check the args |
3292 | + d = defer.Deferred() |
3293 | + def check_create_http(self, node_id, user, name, read_only, deferred): |
3294 | + """Fire the deferred with the args.""" |
3295 | + d.callback((node_id, user, name, read_only)) |
3296 | + deferred.callback(None) |
3297 | + CreateShare._create_share_http = check_create_http |
3298 | + command = CreateShare(self.request_queue, 'node_id', |
3299 | + 'share_to@example.com', 'share_name', |
3300 | + 'View', 'marker') |
3301 | + self.assertTrue(command.use_http, 'CreateShare should be in http mode') |
3302 | + command._run() |
3303 | + node_id, user, name, read_only = yield d |
3304 | + self.assertEquals('node_id', node_id) |
3305 | + self.assertEquals('share_to@example.com', user) |
3306 | + self.assertEquals('share_name', name) |
3307 | + self.assertTrue(read_only) |
3308 | + |
3309 | + |
3310 | class RequestQueueManager(FactoryBaseTestCase): |
3311 | """Test how RequestQueue manages the queues.""" |
3312 | |
3313 | |
3314 | === modified file 'tests/syncdaemon/test_action_queue.pyc' |
3315 | Binary files tests/syncdaemon/test_action_queue.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_action_queue.pyc 2010-03-31 23:55:45 +0000 differ |
3316 | === modified file 'tests/syncdaemon/test_config.py' |
3317 | --- tests/syncdaemon/test_config.py 2010-03-04 16:47:43 +0000 |
3318 | +++ tests/syncdaemon/test_config.py 2010-03-31 23:55:45 +0000 |
3319 | @@ -395,6 +395,21 @@ |
3320 | conf.read(config.get_user_config().config_file) |
3321 | self.assertEquals(conf.get(config.THROTTLING, 'on'), 'False') |
3322 | |
3323 | + def test_files_sync_enabled(self): |
3324 | + """Test for toggling files sync.""" |
3325 | + dbus_config = self.main.dbus_iface.config |
3326 | + enabled = dbus_config.files_sync_enabled() |
3327 | + self.assertTrue(enabled) |
3328 | + dbus_config.set_files_sync_enabled(False) |
3329 | + enabled = dbus_config.files_sync_enabled() |
3330 | + self.assertFalse(enabled) |
3331 | + dbus_config.set_files_sync_enabled(True) |
3332 | + enabled = dbus_config.files_sync_enabled() |
3333 | + self.assertTrue(enabled) |
3334 | + conf = ConfigParser() |
3335 | + conf.read(config.get_user_config().config_file) |
3336 | + self.assertEquals(conf.get(config.MAIN, 'files_sync_enabled'), 'True') |
3337 | + |
3338 | |
3339 | class SyncDaemonConfigParserTests(BaseTwistedTestCase): |
3340 | """Tests for SyncDaemonConfigParser""" |
3341 | @@ -474,3 +489,27 @@ |
3342 | self.assertRaises(ValueError, self.cp.add_upgrade_hook, 'foo', 'bar', |
3343 | lambda x: None) |
3344 | |
3345 | + def test_ignore_one(self): |
3346 | + """Test ignore files config, one regex.""" |
3347 | + conf_file = os.path.join(self.test_root, 'test_new_config.conf') |
3348 | + with open(conf_file, 'w') as fp: |
3349 | + fp.write('[__main__]\n') |
3350 | + fp.write('ignore = .*\\.pyc\n') # all .pyc files |
3351 | + self.assertTrue(os.path.exists(conf_file)) |
3352 | + self.cp.read([conf_file]) |
3353 | + self.cp.parse_all() |
3354 | + self.assertEquals(self.cp.get('__main__', 'ignore').value, |
3355 | + [r'.*\.pyc']) |
3356 | + |
3357 | + def test_ignore_two(self): |
3358 | + """Test ignore files config, two regexes.""" |
3359 | + conf_file = os.path.join(self.test_root, 'test_new_config.conf') |
3360 | + with open(conf_file, 'w') as fp: |
3361 | + fp.write('[__main__]\n') |
3362 | + fp.write('ignore = .*\\.pyc\n') # all .pyc files |
3363 | + fp.write(' .*\\.sw[opnx]\n') # all gvim temp files |
3364 | + self.assertTrue(os.path.exists(conf_file)) |
3365 | + self.cp.read([conf_file]) |
3366 | + self.cp.parse_all() |
3367 | + self.assertEquals(self.cp.get('__main__', 'ignore').value, |
3368 | + ['.*\\.pyc', '.*\\.sw[opnx]']) |
3369 | |
3370 | === modified file 'tests/syncdaemon/test_config.pyc' |
3371 | Binary files tests/syncdaemon/test_config.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_config.pyc 2010-03-31 23:55:45 +0000 differ |
3372 | === modified file 'tests/syncdaemon/test_dbus.py' |
3373 | --- tests/syncdaemon/test_dbus.py 2010-03-10 23:36:53 +0000 |
3374 | +++ tests/syncdaemon/test_dbus.py 2010-03-31 23:55:45 +0000 |
3375 | @@ -1133,6 +1133,23 @@ |
3376 | self.event_q.push(event_name, *args) |
3377 | return d |
3378 | |
3379 | + def test_invalid_filename(self): |
3380 | + """Test the FS_INVALID_NAME signal.""" |
3381 | + d = defer.Deferred() |
3382 | + |
3383 | + def handler(dirname, filename): |
3384 | + """Handler for InvalidName signal.""" |
3385 | + self.assertTrue(isinstance(dirname, unicode)) |
3386 | + self.assertTrue(isinstance(filename, str)) |
3387 | + d.callback(True) |
3388 | + |
3389 | + match = self.bus.add_signal_receiver(handler, |
3390 | + signal_name='InvalidName', |
3391 | + byte_arrays=True) |
3392 | + self.signal_receivers.add(match) |
3393 | + self.main.event_q.push('FS_INVALID_NAME', u'test/dir', 'testpath') |
3394 | + return d |
3395 | + |
3396 | def test_share_changed(self): |
3397 | """ Test the ShareChanged signal. """ |
3398 | share_path = os.path.join(self.main.shares_dir, 'share') |
3399 | |
3400 | === modified file 'tests/syncdaemon/test_dbus.pyc' |
3401 | Binary files tests/syncdaemon/test_dbus.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_dbus.pyc 2010-03-31 23:55:45 +0000 differ |
3402 | === modified file 'tests/syncdaemon/test_eq_inotify.py' |
3403 | --- tests/syncdaemon/test_eq_inotify.py 2010-03-04 16:47:43 +0000 |
3404 | +++ tests/syncdaemon/test_eq_inotify.py 2010-03-31 23:55:45 +0000 |
3405 | @@ -31,6 +31,18 @@ |
3406 | from tests.syncdaemon.test_eventqueue import BaseEQTestCase |
3407 | |
3408 | |
3409 | +class DontHitMe(object): |
3410 | + """We shouldn't be called.""" |
3411 | + |
3412 | + def __init__(self, test_instance): |
3413 | + self.test_instance = test_instance |
3414 | + |
3415 | + def handle_default(self, *a, **k): |
3416 | + """Something here? Error!""" |
3417 | + self.test_inst.finished_error("Don't hit me! Received %s %s" % (a, k)) |
3418 | + |
3419 | + |
3420 | + |
3421 | class WatchTests(BaseEQTestCase): |
3422 | """Test the EQ API to add and remove watchs.""" |
3423 | |
3424 | @@ -393,20 +405,13 @@ |
3425 | open(fromfile, "w").close() |
3426 | symlpath = os.path.join(testdir, "syml") |
3427 | |
3428 | - class DontHitMe(object): |
3429 | - """we shouldn't be called""" |
3430 | - # class-closure, cannot use self, pylint: disable-msg=E0213 |
3431 | - def handle_default(innerself, *a): |
3432 | - """Something here? Error!""" |
3433 | - self.finished_error("don't hit me! received %s" % (a,)) |
3434 | - |
3435 | def confirm(): |
3436 | """check result.""" |
3437 | self.finished_ok() |
3438 | |
3439 | # set up everything and freeze |
3440 | self.eq.inotify_add_watch(testdir) |
3441 | - self.eq.subscribe(DontHitMe()) |
3442 | + self.eq.subscribe(DontHitMe(self)) |
3443 | |
3444 | os.symlink(fromfile, symlpath) |
3445 | reactor.callLater(.1, confirm) |
3446 | @@ -1026,13 +1031,6 @@ |
3447 | testfile = os.path.join(testdir, "bar") |
3448 | os.mkdir(testdir) |
3449 | |
3450 | - class DontHitMe(object): |
3451 | - """we shouldn't be called""" |
3452 | - # class-closure, cannot use self, pylint: disable-msg=E0213 |
3453 | - def handle_default(innerself, *a): |
3454 | - """Something here? Error!""" |
3455 | - self.finished_error("don't hit me! received %s" % (a,)) |
3456 | - |
3457 | def freeze_commit(): |
3458 | """release and check result.""" |
3459 | d = self.eq.freeze_commit([("FS_DIR_DELETE", "foobar")]) |
3460 | @@ -1046,7 +1044,7 @@ |
3461 | |
3462 | # set up everything and freeze |
3463 | self.eq.inotify_add_watch(testdir) |
3464 | - self.eq.subscribe(DontHitMe()) |
3465 | + self.eq.subscribe(DontHitMe(self)) |
3466 | self.eq.freeze_begin(testdir) |
3467 | |
3468 | open(testfile, "w").close() |
3469 | @@ -1132,19 +1130,36 @@ |
3470 | class MutedSignalsTests(BaseTwisted): |
3471 | """Test that EQ filter some signals on demand.""" |
3472 | |
3473 | - class DontHitMe(object): |
3474 | - """we shouldn't be called""" |
3475 | - # class-closure, cannot use self, pylint: disable-msg=E0213 |
3476 | - def __init__(innerself, obj): |
3477 | - innerself.obj = obj |
3478 | - def handle_default(innerself, *a): |
3479 | - """Something here? Error!""" |
3480 | - innerself.obj.finished_error("don't hit me! received %s" % (a,)) |
3481 | - |
3482 | def check_filter(self, _=None): |
3483 | self.assertFalse(self.eq._processor._to_mute._cnt) |
3484 | self.finished_ok() |
3485 | |
3486 | + def test_mute_and_remove(self): |
3487 | + """Test add and remove the mute.""" |
3488 | + # add |
3489 | + self.eq.add_to_mute_filter('FS_FILE_OPEN', 'somepath') |
3490 | + self.assertEqual(self.eq._processor._to_mute._cnt, |
3491 | + {('FS_FILE_OPEN', 'somepath'): 1}) |
3492 | + self.eq.add_to_mute_filter('FS_FILE_OPEN', 'somepath') |
3493 | + self.assertEqual(self.eq._processor._to_mute._cnt, |
3494 | + {('FS_FILE_OPEN', 'somepath'): 2}) |
3495 | + self.eq.add_to_mute_filter('FS_FILE_OPEN', 'otherpath') |
3496 | + self.assertEqual(self.eq._processor._to_mute._cnt, |
3497 | + {('FS_FILE_OPEN', 'somepath'): 2, |
3498 | + ('FS_FILE_OPEN', 'otherpath'): 1}) |
3499 | + |
3500 | + # remove |
3501 | + self.eq.rm_from_mute_filter('FS_FILE_OPEN', 'somepath') |
3502 | + self.assertEqual(self.eq._processor._to_mute._cnt, |
3503 | + {('FS_FILE_OPEN', 'somepath'): 1, |
3504 | + ('FS_FILE_OPEN', 'otherpath'): 1}) |
3505 | + self.eq.rm_from_mute_filter('FS_FILE_OPEN', 'otherpath') |
3506 | + self.assertEqual(self.eq._processor._to_mute._cnt, |
3507 | + {('FS_FILE_OPEN', 'somepath'): 1}) |
3508 | + self.eq.rm_from_mute_filter('FS_FILE_OPEN', 'somepath') |
3509 | + self.assertEqual(self.eq._processor._to_mute._cnt, {}) |
3510 | + |
3511 | + |
3512 | def test_file_open(self): |
3513 | """Test receiving the open signal on files.""" |
3514 | testfile = os.path.join(self.root_dir, "foo") |
3515 | @@ -1153,7 +1168,7 @@ |
3516 | self.eq.add_to_mute_filter("FS_FILE_CLOSE_NOWRITE", testfile) |
3517 | |
3518 | self.eq.inotify_add_watch(self.root_dir) |
3519 | - self.eq.subscribe(self.DontHitMe(self)) |
3520 | + self.eq.subscribe(DontHitMe(self)) |
3521 | |
3522 | # generate the event |
3523 | open(testfile) |
3524 | @@ -1168,7 +1183,7 @@ |
3525 | self.eq.add_to_mute_filter("FS_FILE_CLOSE_NOWRITE", testfile) |
3526 | |
3527 | self.eq.inotify_add_watch(self.root_dir) |
3528 | - self.eq.subscribe(self.DontHitMe(self)) |
3529 | + self.eq.subscribe(DontHitMe(self)) |
3530 | |
3531 | # generate the event |
3532 | fh.close() |
3533 | @@ -1183,7 +1198,7 @@ |
3534 | self.eq.add_to_mute_filter("FS_FILE_CLOSE_WRITE", testfile) |
3535 | |
3536 | self.eq.inotify_add_watch(self.root_dir) |
3537 | - self.eq.subscribe(self.DontHitMe(self)) |
3538 | + self.eq.subscribe(DontHitMe(self)) |
3539 | |
3540 | # generate the event |
3541 | open(testfile, "w").close() |
3542 | @@ -1196,7 +1211,7 @@ |
3543 | self.eq.add_to_mute_filter("FS_DIR_CREATE", testdir) |
3544 | |
3545 | self.eq.inotify_add_watch(self.root_dir) |
3546 | - self.eq.subscribe(self.DontHitMe(self)) |
3547 | + self.eq.subscribe(DontHitMe(self)) |
3548 | |
3549 | # generate the event |
3550 | os.mkdir(testdir) |
3551 | @@ -1210,7 +1225,7 @@ |
3552 | self.eq.add_to_mute_filter("FS_FILE_DELETE", testfile) |
3553 | |
3554 | self.eq.inotify_add_watch(self.root_dir) |
3555 | - self.eq.subscribe(self.DontHitMe(self)) |
3556 | + self.eq.subscribe(DontHitMe(self)) |
3557 | |
3558 | # generate the event |
3559 | os.remove(testfile) |
3560 | @@ -1224,7 +1239,7 @@ |
3561 | self.eq.add_to_mute_filter("FS_DIR_DELETE", testdir) |
3562 | |
3563 | self.eq.inotify_add_watch(self.root_dir) |
3564 | - self.eq.subscribe(self.DontHitMe(self)) |
3565 | + self.eq.subscribe(DontHitMe(self)) |
3566 | |
3567 | # generate the event |
3568 | os.rmdir(testdir) |
3569 | @@ -1243,7 +1258,7 @@ |
3570 | self.eq.add_to_mute_filter("FS_FILE_MOVE", fromfile, tofile) |
3571 | |
3572 | self.eq.inotify_add_watch(self.root_dir) |
3573 | - self.eq.subscribe(self.DontHitMe(self)) |
3574 | + self.eq.subscribe(DontHitMe(self)) |
3575 | |
3576 | # generate the event |
3577 | os.rename(fromfile, tofile) |
3578 | @@ -1262,7 +1277,7 @@ |
3579 | self.eq.add_to_mute_filter("FS_DIR_MOVE", fromdir, todir) |
3580 | |
3581 | self.eq.inotify_add_watch(self.root_dir) |
3582 | - self.eq.subscribe(self.DontHitMe(self)) |
3583 | + self.eq.subscribe(DontHitMe(self)) |
3584 | |
3585 | # generate the event |
3586 | os.rename(fromdir, todir) |
3587 | @@ -1281,7 +1296,7 @@ |
3588 | self.eq.add_to_mute_filter("FS_FILE_MOVE", fromfile, tofile) |
3589 | |
3590 | self.eq.inotify_add_watch(self.root_dir) |
3591 | - self.eq.subscribe(self.DontHitMe(self)) |
3592 | + self.eq.subscribe(DontHitMe(self)) |
3593 | |
3594 | # generate the event |
3595 | os.rename(fromfile, tofile) |
3596 | @@ -1299,7 +1314,7 @@ |
3597 | self.eq.add_to_mute_filter("FS_FILE_CLOSE_WRITE", tofile) |
3598 | |
3599 | self.eq.inotify_add_watch(root_dir) |
3600 | - self.eq.subscribe(self.DontHitMe(self)) |
3601 | + self.eq.subscribe(DontHitMe(self)) |
3602 | |
3603 | # generate the event |
3604 | os.rename(fromfile, tofile) |
3605 | @@ -1701,6 +1716,140 @@ |
3606 | return self._deferred |
3607 | |
3608 | |
3609 | +class NonUTF8NamesTests(BaseTwisted): |
3610 | + """Test the non-utf8 name handling.""" |
3611 | + |
3612 | + invalid_name = "invalid \xff\xff name" |
3613 | + |
3614 | + def test_file_open(self): |
3615 | + """Test invalid_filename after open a file.""" |
3616 | + testfile = os.path.join(self.root_dir, self.invalid_name) |
3617 | + open(testfile, "w").close() |
3618 | + |
3619 | + self.eq.inotify_add_watch(self.root_dir) |
3620 | + should_events = [ |
3621 | + ('FS_INVALID_NAME', self.root_dir, self.invalid_name), # open |
3622 | + ('FS_INVALID_NAME', self.root_dir, self.invalid_name), # close no w |
3623 | + ] |
3624 | + self.eq.subscribe(DynamicHitMe(should_events, self)) |
3625 | + |
3626 | + # generate the event |
3627 | + open(testfile) |
3628 | + return self._deferred |
3629 | + |
3630 | + def test_file_close_nowrite(self): |
3631 | + """Test invalid_filename after a close no write.""" |
3632 | + testfile = os.path.join(self.root_dir, self.invalid_name) |
3633 | + open(testfile, "w").close() |
3634 | + fh = open(testfile) |
3635 | + |
3636 | + self.eq.inotify_add_watch(self.root_dir) |
3637 | + should_events = [ |
3638 | + ('FS_INVALID_NAME', self.root_dir, self.invalid_name), # close no w |
3639 | + ] |
3640 | + self.eq.subscribe(DynamicHitMe(should_events, self)) |
3641 | + |
3642 | + # generate the event |
3643 | + fh.close() |
3644 | + return self._deferred |
3645 | + |
3646 | + def test_file_create_close_write(self): |
3647 | + """Test invalid_filename after a create, open and close write.""" |
3648 | + testfile = os.path.join(self.root_dir, self.invalid_name) |
3649 | + |
3650 | + self.eq.inotify_add_watch(self.root_dir) |
3651 | + should_events = [ |
3652 | + ('FS_INVALID_NAME', self.root_dir, self.invalid_name), # create |
3653 | + ('FS_INVALID_NAME', self.root_dir, self.invalid_name), # open |
3654 | + ('FS_INVALID_NAME', self.root_dir, self.invalid_name), # close w |
3655 | + ] |
3656 | + self.eq.subscribe(DynamicHitMe(should_events, self)) |
3657 | + |
3658 | + # generate the event |
3659 | + open(testfile, "w").close() |
3660 | + return self._deferred |
3661 | + |
3662 | + def test_dir_create(self): |
3663 | + """Test invalid_filename after a dir create.""" |
3664 | + testdir = os.path.join(self.root_dir, self.invalid_name) |
3665 | + |
3666 | + self.eq.inotify_add_watch(self.root_dir) |
3667 | + should_events = [ |
3668 | + ('FS_INVALID_NAME', self.root_dir, self.invalid_name), # create |
3669 | + ] |
3670 | + self.eq.subscribe(DynamicHitMe(should_events, self)) |
3671 | + |
3672 | + # generate the event |
3673 | + os.mkdir(testdir) |
3674 | + return self._deferred |
3675 | + |
3676 | + def test_file_delete(self): |
3677 | + """Test invalid_filename after a file delete.""" |
3678 | + testfile = os.path.join(self.root_dir, self.invalid_name) |
3679 | + open(testfile, "w").close() |
3680 | + |
3681 | + self.eq.inotify_add_watch(self.root_dir) |
3682 | + should_events = [ |
3683 | + ('FS_INVALID_NAME', self.root_dir, self.invalid_name), # delete |
3684 | + ] |
3685 | + self.eq.subscribe(DynamicHitMe(should_events, self)) |
3686 | + |
3687 | + # generate the event |
3688 | + os.remove(testfile) |
3689 | + return self._deferred |
3690 | + |
3691 | + def test_dir_delete(self): |
3692 | + """Test invalid_filename after a dir delete.""" |
3693 | + testdir = os.path.join(self.root_dir, self.invalid_name) |
3694 | + os.mkdir(testdir) |
3695 | + |
3696 | + self.eq.inotify_add_watch(self.root_dir) |
3697 | + should_events = [ |
3698 | + ('FS_INVALID_NAME', self.root_dir, self.invalid_name), # delete |
3699 | + ] |
3700 | + self.eq.subscribe(DynamicHitMe(should_events, self)) |
3701 | + |
3702 | + # generate the event |
3703 | + os.rmdir(testdir) |
3704 | + return self._deferred |
3705 | + |
3706 | + def test_file_move_to(self): |
3707 | + """Test invalid_filename after moving a file into a watched dir.""" |
3708 | + fromfile = os.path.join(self.root_dir, self.invalid_name) |
3709 | + open(fromfile, "w").close() |
3710 | + destdir = os.path.join(self.root_dir, "watched_dir") |
3711 | + os.mkdir(destdir) |
3712 | + destfile = os.path.join(destdir, self.invalid_name) |
3713 | + |
3714 | + self.eq.inotify_add_watch(destdir) |
3715 | + should_events = [ |
3716 | + ('FS_INVALID_NAME', destdir, self.invalid_name), # move to |
3717 | + ] |
3718 | + self.eq.subscribe(DynamicHitMe(should_events, self)) |
3719 | + |
3720 | + # generate the event |
3721 | + os.rename(fromfile, destfile) |
3722 | + return self._deferred |
3723 | + |
3724 | + def test_file_move_from(self): |
3725 | + """Test invalid_filename after moving a file from a watched dir.""" |
3726 | + fromdir = os.path.join(self.root_dir, "watched_dir") |
3727 | + os.mkdir(fromdir) |
3728 | + fromfile = os.path.join(fromdir, self.invalid_name) |
3729 | + open(fromfile, "w").close() |
3730 | + destfile = os.path.join(self.root_dir, self.invalid_name) |
3731 | + |
3732 | + self.eq.inotify_add_watch(fromdir) |
3733 | + should_events = [ |
3734 | + ('FS_INVALID_NAME', fromdir, self.invalid_name), # move from |
3735 | + ] |
3736 | + self.eq.subscribe(DynamicHitMe(should_events, self)) |
3737 | + |
3738 | + # generate the event |
3739 | + os.rename(fromfile, destfile) |
3740 | + return self._deferred |
3741 | + |
3742 | + |
3743 | def test_suite(): |
3744 | # pylint: disable-msg=C0111 |
3745 | return unittest.TestLoader().loadTestsFromName(__name__) |
3746 | |
3747 | === modified file 'tests/syncdaemon/test_eq_inotify.pyc' |
3748 | Binary files tests/syncdaemon/test_eq_inotify.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_eq_inotify.pyc 2010-03-31 23:55:45 +0000 differ |
3749 | === modified file 'tests/syncdaemon/test_eventqueue.py' |
3750 | --- tests/syncdaemon/test_eventqueue.py 2010-03-04 16:47:43 +0000 |
3751 | +++ tests/syncdaemon/test_eventqueue.py 2010-03-31 23:55:45 +0000 |
3752 | @@ -382,6 +382,42 @@ |
3753 | self.assertFalse(self.mf._cnt) |
3754 | |
3755 | |
3756 | +class IgnoreFileTests(unittest.TestCase): |
3757 | + """Tests the ignore files behaviour.""" |
3758 | + |
3759 | + def test_filter_none(self): |
3760 | + """Still works ok even if not receiving a regex to ignore.""" |
3761 | + p = event_queue._GeneralINotifyProcessor(None) |
3762 | + self.assertFalse(p.is_ignored("froo.pyc")) |
3763 | + |
3764 | + def test_filter_one(self): |
3765 | + """Filters stuff that matches (or not) this one regex.""" |
3766 | + p = event_queue._GeneralINotifyProcessor(None, ['\A.*\\.pyc\Z']) |
3767 | + self.assertTrue(p.is_ignored("froo.pyc")) |
3768 | + self.assertFalse(p.is_ignored("froo.pyc.real")) |
3769 | + self.assertFalse(p.is_ignored("otherstuff")) |
3770 | + |
3771 | + def test_filter_two_simple(self): |
3772 | + """Filters stuff that matches (or not) these simple regexes.""" |
3773 | + p = event_queue._GeneralINotifyProcessor(None, ['\A.*foo\Z', |
3774 | + '\A.*bar\Z']) |
3775 | + self.assertTrue(p.is_ignored("blah_foo")) |
3776 | + self.assertTrue(p.is_ignored("blah_bar")) |
3777 | + self.assertFalse(p.is_ignored("bar_xxx")) |
3778 | + self.assertFalse(p.is_ignored("--foo--")) |
3779 | + self.assertFalse(p.is_ignored("otherstuff")) |
3780 | + |
3781 | + def test_filter_two_complex(self): |
3782 | + """Filters stuff that matches (or not) these complex regexes.""" |
3783 | + p = event_queue._GeneralINotifyProcessor(None, ['\A.*foo\Z|\Afoo.*\Z', |
3784 | + '\A.*bar\Z']) |
3785 | + self.assertTrue(p.is_ignored("blah_foo")) |
3786 | + self.assertTrue(p.is_ignored("blah_bar")) |
3787 | + self.assertTrue(p.is_ignored("foo_xxx")) |
3788 | + self.assertFalse(p.is_ignored("--foo--")) |
3789 | + self.assertFalse(p.is_ignored("otherstuff")) |
3790 | + |
3791 | + |
3792 | def test_suite(): |
3793 | # pylint: disable-msg=C0111 |
3794 | return unittest.TestLoader().loadTestsFromName(__name__) |
3795 | |
3796 | === modified file 'tests/syncdaemon/test_eventqueue.pyc' |
3797 | Binary files tests/syncdaemon/test_eventqueue.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_eventqueue.pyc 2010-03-31 23:55:45 +0000 differ |
3798 | === modified file 'tests/syncdaemon/test_eventsnanny.pyc' |
3799 | Binary files tests/syncdaemon/test_eventsnanny.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_eventsnanny.pyc 2010-03-31 23:55:45 +0000 differ |
3800 | === modified file 'tests/syncdaemon/test_fileshelf.py' |
3801 | --- tests/syncdaemon/test_fileshelf.py 2010-02-17 23:51:29 +0000 |
3802 | +++ tests/syncdaemon/test_fileshelf.py 2010-03-31 23:55:45 +0000 |
3803 | @@ -133,7 +133,7 @@ |
3804 | self.assertTrue(('foo', 'bar') and ('foo1', 'bar1') in \ |
3805 | [(k, v) for k, v in shelf.items()]) |
3806 | |
3807 | - def test_broked_metadata_without_backup(self): |
3808 | + def test_broken_metadata_without_backup(self): |
3809 | """test the shelf behavior when it hit a broken metadata file without |
3810 | backup. |
3811 | """ |
3812 | @@ -148,7 +148,7 @@ |
3813 | f.write(BROKEN_PICKLE) |
3814 | self.assertRaises(KeyError, self.shelf.__getitem__, 'broken_pickle') |
3815 | |
3816 | - def test_broked_metadata_with_backup(self): |
3817 | + def test_broken_metadata_with_backup(self): |
3818 | """test that each time a metadata file is updated a .old is kept""" |
3819 | self.shelf['bad_file'] = {'value':'old'} |
3820 | path = self.shelf.key_file('bad_file') |
3821 | @@ -254,6 +254,42 @@ |
3822 | self.assertEquals(shelf.values['foo'], |
3823 | cPickle.dumps('bar', protocol=2)) |
3824 | |
3825 | + def test_broken_metadata_iteritems(self): |
3826 | + """Test that broken metadata is ignored during iteritems.""" |
3827 | + self.shelf['ok_key'] = {'status':'this is valid metadata'} |
3828 | + self.shelf['bad_file'] = {} |
3829 | + path = self.shelf.key_file('bad_file') |
3830 | + open(path, 'w').close() |
3831 | + self.assertRaises(KeyError, self.shelf.__getitem__, 'bad_file') |
3832 | + self.assertEquals(1, len(list(self.shelf.iteritems()))) |
3833 | + self.assertFalse(os.path.exists(path)) |
3834 | + |
3835 | + self.shelf['broken_pickle'] = {} |
3836 | + path = self.shelf.key_file('broken_pickle') |
3837 | + with open(path, 'w') as f: |
3838 | + f.write(BROKEN_PICKLE) |
3839 | + self.assertRaises(KeyError, self.shelf.__getitem__, 'broken_pickle') |
3840 | + self.assertEquals(1, len(list(self.shelf.iteritems()))) |
3841 | + self.assertFalse(os.path.exists(path)) |
3842 | + |
3843 | + def test_broken_metadata_items(self): |
3844 | + """Test that broken metadata is ignored during iteritems.""" |
3845 | + self.shelf['ok_key'] = {'status':'this is valid metadata'} |
3846 | + self.shelf['bad_file'] = {} |
3847 | + path = self.shelf.key_file('bad_file') |
3848 | + open(path, 'w').close() |
3849 | + self.assertRaises(KeyError, self.shelf.__getitem__, 'bad_file') |
3850 | + self.assertEquals(1, len(list(self.shelf.items()))) |
3851 | + self.assertFalse(os.path.exists(path)) |
3852 | + |
3853 | + self.shelf['broken_pickle'] = {} |
3854 | + path = self.shelf.key_file('broken_pickle') |
3855 | + with open(path, 'w') as f: |
3856 | + f.write(BROKEN_PICKLE) |
3857 | + self.assertRaises(KeyError, self.shelf.__getitem__, 'broken_pickle') |
3858 | + self.assertEquals(1, len(list(self.shelf.items()))) |
3859 | + self.assertFalse(os.path.exists(path)) |
3860 | + |
3861 | |
3862 | class CachedFileShelfTests(TestFileShelf): |
3863 | """TestFileShelf tests but using CachedFileShelf""" |
3864 | @@ -273,7 +309,7 @@ |
3865 | self.shelf['realkey'] |
3866 | self.assertEquals(self.shelf.cache_hits, 1) |
3867 | |
3868 | - def test_broked_metadata_with_backup(self): |
3869 | + def test_broken_metadata_with_backup(self): |
3870 | """overrides parent test as we have the value in the cache.""" |
3871 | self.shelf['bad_file'] = {'value':'old'} |
3872 | path = self.shelf.key_file('bad_file') |
3873 | |
3874 | === modified file 'tests/syncdaemon/test_fileshelf.pyc' |
3875 | Binary files tests/syncdaemon/test_fileshelf.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_fileshelf.pyc 2010-03-31 23:55:45 +0000 differ |
3876 | === modified file 'tests/syncdaemon/test_fsm.py' |
3877 | --- tests/syncdaemon/test_fsm.py 2010-02-17 23:51:29 +0000 |
3878 | +++ tests/syncdaemon/test_fsm.py 2010-03-31 23:55:45 +0000 |
3879 | @@ -24,12 +24,11 @@ |
3880 | import shutil |
3881 | import unittest |
3882 | import time |
3883 | -import logging |
3884 | |
3885 | from contrib.testing.testcase import ( |
3886 | FakeVolumeManager, |
3887 | FakeMain, |
3888 | - MementoHandler, |
3889 | + MementoHandler |
3890 | ) |
3891 | |
3892 | from ubuntuone.syncdaemon.filesystem_manager import ( |
3893 | @@ -39,7 +38,6 @@ |
3894 | METADATA_VERSION, |
3895 | ) |
3896 | from ubuntuone.syncdaemon.event_queue import EventQueue |
3897 | -from ubuntuone.syncdaemon.logger import LOGFILENAME |
3898 | from ubuntuone.syncdaemon import logger |
3899 | from ubuntuone.syncdaemon.volume_manager import Share, allow_writes |
3900 | |
3901 | @@ -74,11 +72,19 @@ |
3902 | self.fsm, self.shares_dir) |
3903 | self.share_path = self.share.path |
3904 | |
3905 | + # add a in-memory logger handler |
3906 | + self.memento = MementoHandler() |
3907 | + self.memento.setLevel(0) |
3908 | + logger.root_logger.addHandler(self.memento) |
3909 | + |
3910 | def tearDown(self): |
3911 | """ Clean up the tests. """ |
3912 | self.eq.shutdown() |
3913 | self.rmtree(TESTS_DIR) |
3914 | |
3915 | + # remove the handler |
3916 | + logger.root_logger.removeHandler(self.memento) |
3917 | + |
3918 | @staticmethod |
3919 | def rmtree(path): |
3920 | """rmtree that handle ro childs.""" |
3921 | @@ -101,6 +107,16 @@ |
3922 | fsm.vm.add_share(share) |
3923 | return share |
3924 | |
3925 | + def create_node(self, name, is_dir=False, share=None): |
3926 | + """Create a node.""" |
3927 | + if share is None: |
3928 | + share = self.share |
3929 | + path = os.path.join(share.path, name) |
3930 | + mdid = self.fsm.create(path, share.volume_id, is_dir=is_dir) |
3931 | + self.fsm.set_node_id(path, "uuid1") |
3932 | + mdobj = self.fsm.get_by_mdid(mdid) |
3933 | + return mdobj |
3934 | + |
3935 | |
3936 | class StartupTests(unittest.TestCase): |
3937 | """Test the basic startup behaviour.""" |
3938 | @@ -1238,17 +1254,89 @@ |
3939 | self.fsm._set_node_id, mdobj, 'bad-uuid', path) |
3940 | |
3941 | |
3942 | +class GetMDObjectsInDirTests(FSMTestCase): |
3943 | + """Test the get_mdobjs_in_dir method.""" |
3944 | + |
3945 | + def create_some_contents(self, share): |
3946 | + a = 'a' |
3947 | + ab = os.path.join(a, 'b') |
3948 | + ab1 = os.path.join(a, 'b1') |
3949 | + ab2 = os.path.join(a, 'b2') |
3950 | + ac = os.path.join(a, 'c') |
3951 | + acd = os.path.join(ac, 'd') |
3952 | + |
3953 | + dirs = [a, ab, ab1, ab2, ac, acd] |
3954 | + for d in dirs: |
3955 | + self.create_node(d, is_dir=True, share=share) |
3956 | + |
3957 | + x = os.path.join(a, 'x.txt') |
3958 | + y = os.path.join(ab, 'y.txt') |
3959 | + z = os.path.join(ac, 'z.txt') |
3960 | + |
3961 | + files = [x, y, z] |
3962 | + for f in files: |
3963 | + self.create_node(f, is_dir=False, share=share) |
3964 | + |
3965 | + self.contents[share] = sorted(dirs + files) |
3966 | + |
3967 | + def setUp(self): |
3968 | + """Init.""" |
3969 | + FSMTestCase.setUp(self) |
3970 | + self.contents = {} |
3971 | + self.create_some_contents(self.share) |
3972 | + |
3973 | + def tearDown(self): |
3974 | + """Clean up.""" |
3975 | + self.contents = {} |
3976 | + FSMTestCase.tearDown(self) |
3977 | + |
3978 | + def test_basic(self): |
3979 | + """Test basic retrieval.""" |
3980 | + expected = ['a'] |
3981 | + actual = sorted([d.path for d in |
3982 | + self.fsm.get_mdobjs_in_dir(self.share.path)]) |
3983 | + self.assertEqual(expected, actual) |
3984 | + |
3985 | + def test_no_tree(self): |
3986 | + """Test just receiving the dir and not the tree.""" |
3987 | + expected = ['a/b', 'a/b1', 'a/b2', 'a/c', 'a/x.txt'] |
3988 | + actual = sorted([d.path for d in self.fsm.get_mdobjs_in_dir( |
3989 | + os.path.join(self.share.path, 'a'))]) |
3990 | + self.assertEqual(expected, actual) |
3991 | + |
3992 | + def test_similar_paths(self): |
3993 | + """Test having similar paths (a/b, a/b1, a/b2).""" |
3994 | + expected = ['a/b/y.txt'] |
3995 | + actual = sorted([d.path for d in self.fsm.get_mdobjs_in_dir( |
3996 | + os.path.join(self.share.path, 'a', 'b'))]) |
3997 | + self.assertEqual(expected, actual) |
3998 | + |
3999 | + def test_with_two_shares(self): |
4000 | + """Test having 2 shares.""" |
4001 | + second_share = self.create_share('second_share', 'the_second', |
4002 | + self.fsm, self.shares_dir) |
4003 | + self.create_some_contents(second_share) |
4004 | + |
4005 | + expected = ['a'] |
4006 | + actual = sorted([d.path for d in |
4007 | + self.fsm.get_mdobjs_in_dir(second_share.path)]) |
4008 | + self.assertEqual(expected, actual) |
4009 | + |
4010 | + def test_both_shares(self): |
4011 | + """Test having 2 shares and asking for mdobjs in shares_dir.""" |
4012 | + second_share = self.create_share('second_share', 'the_second', |
4013 | + self.fsm, self.shares_dir) |
4014 | + self.create_some_contents(second_share) |
4015 | + |
4016 | + expected = [] |
4017 | + actual = sorted([d.path for d in |
4018 | + self.fsm.get_mdobjs_in_dir(self.shares_dir)]) |
4019 | + self.assertEqual(expected, actual) |
4020 | + |
4021 | + |
4022 | class StatTests(FSMTestCase): |
4023 | """Test all the behaviour regarding the stats.""" |
4024 | |
4025 | - def create_node(self, name): |
4026 | - """Creates a node.""" |
4027 | - path = os.path.join(self.share.path, name) |
4028 | - mdid = self.fsm.create(path, self.share.volume_id) |
4029 | - self.fsm.set_node_id(path, "uuid1") |
4030 | - mdobj = self.fsm.get_by_mdid(mdid) |
4031 | - return mdobj |
4032 | - |
4033 | def test_create_nofile(self): |
4034 | """Test creation when there's no file.""" |
4035 | mdobj = self.create_node("foo") |
4036 | @@ -1866,13 +1954,48 @@ |
4037 | assert self.fsm.changed(path=local_file) == self.fsm.CHANGED_LOCAL |
4038 | self.assertRaises(OSError, self.fsm.delete_file, local_dir) |
4039 | |
4040 | + def test_delete_dir_when_non_empty_and_prior_conflict_on_file(self): |
4041 | + """Test that a dir is not deleted, when there is a conflicted file.""" |
4042 | + # local directory |
4043 | + local_dir = os.path.join(self.root_dir, "foo") |
4044 | + os.mkdir(local_dir) |
4045 | + self.fsm.create(local_dir, "", is_dir=True) |
4046 | + self.fsm.set_node_id(local_dir, "uuid") |
4047 | + |
4048 | + local_file = os.path.join(local_dir, |
4049 | + "bar.txt" + self.fsm.CONFLICT_SUFFIX) |
4050 | + open(local_file, 'w').close() # touch bar.txt.u1conflict |
4051 | + |
4052 | + assert not local_file in self.fsm._idx_path |
4053 | + self.assertRaises(OSError, self.fsm.delete_file, local_dir) |
4054 | + |
4055 | + infos = [record.message for record in self.memento.records |
4056 | + if record.levelname == 'INFO'] |
4057 | + self.assertTrue(len(infos) == 1) |
4058 | + self.assertTrue(local_file in infos[0]) |
4059 | + |
4060 | + def test_delete_dir_when_non_empty_and_prior_conflict_on_subdir(self): |
4061 | + """Test that a dir is not deleted, when there is a conflicted dir.""" |
4062 | + # local directory |
4063 | + local_dir = os.path.join(self.root_dir, "foo") |
4064 | + os.mkdir(local_dir) |
4065 | + self.fsm.create(local_dir, "", is_dir=True) |
4066 | + self.fsm.set_node_id(local_dir, "uuid") |
4067 | + |
4068 | + local_subdir = os.path.join(local_dir, |
4069 | + "subdir_bar" + self.fsm.CONFLICT_SUFFIX) |
4070 | + os.mkdir(local_subdir) |
4071 | + |
4072 | + assert not local_subdir in self.fsm._idx_path |
4073 | + self.assertRaises(OSError, self.fsm.delete_file, local_dir) |
4074 | + |
4075 | + infos = [record.message for record in self.memento.records |
4076 | + if record.levelname == 'INFO'] |
4077 | + self.assertTrue(len(infos) == 1) |
4078 | + self.assertTrue(local_subdir in infos[0]) |
4079 | + |
4080 | def test_no_warning_on_log_file_when_recursive_delete(self): |
4081 | """Test that sucessfully deleted dir does not log OSError.""" |
4082 | - # add a in-memory handler |
4083 | - memento = MementoHandler() |
4084 | - memento.setLevel(logging.WARNING) |
4085 | - logger.root_logger.addHandler(memento) |
4086 | - |
4087 | local_dir = os.path.join(self.root_dir, "foo") |
4088 | os.mkdir(local_dir) |
4089 | self.fsm.create(local_dir, "", is_dir=True) |
4090 | @@ -1883,20 +2006,15 @@ |
4091 | self.fsm.create(local_file, "") |
4092 | self.fsm.set_node_id(local_file, "uuid_file") |
4093 | |
4094 | + previous = self.memento.records |
4095 | self.fsm.delete_file(local_dir) |
4096 | |
4097 | - # remove the handler |
4098 | - logger.root_logger.removeHandler(memento) |
4099 | - self.assertFalse(memento.records, memento.records) |
4100 | + # no logs were added |
4101 | + self.assertEquals(previous, self.memento.records) |
4102 | |
4103 | - # FIXME: Bug #494469 |
4104 | - def SKIP_test_warning_on_log_file_when_failing_delete(self): |
4105 | + def test_warning_on_log_file_when_failing_delete(self): |
4106 | """Test that sucessfully deleted dir does not log OSError.""" |
4107 | |
4108 | - log = open(LOGFILENAME, 'r') |
4109 | - log.flush() |
4110 | - log.read() # ignore log content till now |
4111 | - |
4112 | local_dir = os.path.join(self.root_dir, "foo") |
4113 | self.fsm.create(local_dir, "", is_dir=True) |
4114 | self.fsm.set_node_id(local_dir, "uuid") |
4115 | @@ -1904,11 +2022,11 @@ |
4116 | # local_dir does not exist on the file system |
4117 | self.fsm.delete_file(local_dir) |
4118 | |
4119 | - log.flush() |
4120 | - log_content = log.read() |
4121 | - log.close() |
4122 | - log_present = 'OSError [Errno 2] No such file or directory' in log_content |
4123 | - self.assertTrue(log_present) |
4124 | + warnings = [record.message for record in self.memento.records |
4125 | + if record.levelname == 'WARNING'] |
4126 | + self.assertTrue(len(warnings) == 1) |
4127 | + self.assertTrue('OSError [Errno 2]' in warnings[0]) |
4128 | + self.assertTrue(local_dir in warnings[0]) |
4129 | |
4130 | def test_move_dir_to_conflict(self): |
4131 | """Test that the conflict to a dir removes children metadata.""" |
4132 | @@ -2779,6 +2897,162 @@ |
4133 | self.assertFalse(("", "uuid3", "") in data) |
4134 | |
4135 | |
4136 | +class MutingTestCase(FSMTestCase): |
4137 | + """Test FSM interaction with mutes.""" |
4138 | + |
4139 | + def setUp(self): |
4140 | + """Set up the test infrastructure.""" |
4141 | + FSMTestCase.setUp(self) |
4142 | + self.muted = [] |
4143 | + |
4144 | + # in-the-middle add |
4145 | + self._orig_add_mute = self.eq.add_to_mute_filter |
4146 | + def _fake_add(*a): |
4147 | + """Store what is added.""" |
4148 | + self.muted.append(a) |
4149 | + return self._orig_add_mute(*a) |
4150 | + self.eq.add_to_mute_filter = _fake_add |
4151 | + |
4152 | + # in-the-middle remove |
4153 | + self._orig_rm_mute = self.eq.rm_from_mute_filter |
4154 | + def _fake_rm(*a): |
4155 | + """Store what is deleted.""" |
4156 | + self.muted.remove(a) |
4157 | + return self._orig_rm_mute(*a) |
4158 | + self.eq.rm_from_mute_filter = _fake_rm |
4159 | + |
4160 | + def tearDown(self): |
4161 | + """Put original stuff back in.""" |
4162 | + FSMTestCase.tearDown(self) |
4163 | + self.eq.add_to_mute_filter = self._orig_add_mute |
4164 | + self.eq.rm_from_mute_filter = self._orig_rm_mute |
4165 | + |
4166 | + def test_movefile_ok(self): |
4167 | + """Move file adds a mute filter.""" |
4168 | + path1 = os.path.join(self.share.path, "thisfile1") |
4169 | + path2 = os.path.join(self.share.path, "thisfile2") |
4170 | + open(path1, "w").close() |
4171 | + self.create_node(path1) |
4172 | + |
4173 | + # move and check |
4174 | + self.fsm.move_file("share", path1, path2) |
4175 | + self.assertEqual(self.muted, [('FS_FILE_MOVE', path1, path2)]) |
4176 | + |
4177 | + def test_movefile_error(self): |
4178 | + """Move file adds and removes a mute filter.""" |
4179 | + path1 = os.path.join(self.share.path, "thisfile1") |
4180 | + path2 = os.path.join(self.share.path, "thisfile2") |
4181 | + self.create_node(path1) |
4182 | + |
4183 | + # move and check |
4184 | + self.fsm.move_file("share", path1, path2) |
4185 | + self.assertEqual(self.muted, []) |
4186 | + |
4187 | + def test_movedir_ok(self): |
4188 | + """Move dir adds a mute filter.""" |
4189 | + path1 = os.path.join(self.share.path, "thisfile1") |
4190 | + path2 = os.path.join(self.share.path, "thisfile2") |
4191 | + os.mkdir(path1) |
4192 | + self.create_node(path1, is_dir=True) |
4193 | + |
4194 | + # move and check |
4195 | + self.fsm.move_file("share", path1, path2) |
4196 | + self.assertEqual(self.muted, [('FS_DIR_MOVE', path1, path2)]) |
4197 | + |
4198 | + def test_movedir_error(self): |
4199 | + """Move dir adds and removes a mute filter.""" |
4200 | + path1 = os.path.join(self.share.path, "thisfile1") |
4201 | + path2 = os.path.join(self.share.path, "thisfile2") |
4202 | + self.create_node(path1, is_dir=True) |
4203 | + |
4204 | + # move and check |
4205 | + self.fsm.move_file("share", path1, path2) |
4206 | + self.assertEqual(self.muted, []) |
4207 | + |
4208 | + def test_deletefile_ok(self): |
4209 | + """Delete file adds a mute filter.""" |
4210 | + testfile = os.path.join(self.share_path, "path") |
4211 | + open(testfile, "w").close() |
4212 | + self.fsm.create(testfile, "share") |
4213 | + self.fsm.set_node_id(testfile, "uuid") |
4214 | + |
4215 | + # delete and check |
4216 | + self.fsm.delete_file(testfile) |
4217 | + self.assertEqual(self.muted, [('FS_FILE_DELETE', testfile)]) |
4218 | + |
4219 | + def test_deletefile_error(self): |
4220 | + """Delete file adds and removes a mute filter.""" |
4221 | + testfile = os.path.join(self.share_path, "path") |
4222 | + self.fsm.create(testfile, "share") |
4223 | + self.fsm.set_node_id(testfile, "uuid") |
4224 | + |
4225 | + # delete and check |
4226 | + self.fsm.delete_file(testfile) |
4227 | + self.assertEqual(self.muted, []) |
4228 | + |
4229 | + def test_deletedir_ok(self): |
4230 | + """Delete dir adds a mute filter.""" |
4231 | + testfile = os.path.join(self.share_path, "path") |
4232 | + os.mkdir(testfile) |
4233 | + self.fsm.create(testfile, "share", is_dir=True) |
4234 | + self.fsm.set_node_id(testfile, "uuid") |
4235 | + |
4236 | + # delete and check |
4237 | + self.fsm.delete_file(testfile) |
4238 | + self.assertEqual(self.muted, [('FS_DIR_DELETE', testfile)]) |
4239 | + |
4240 | + def test_deletedir_error(self): |
4241 | + """Delete dir adds and removes a mute filter.""" |
4242 | + testfile = os.path.join(self.share_path, "path") |
4243 | + self.fsm.create(testfile, "share", is_dir=True) |
4244 | + self.fsm.set_node_id(testfile, "uuid") |
4245 | + |
4246 | + # delete and check |
4247 | + self.fsm.delete_file(testfile) |
4248 | + self.assertEqual(self.muted, []) |
4249 | + |
4250 | + def test_conflict_movefile_ok(self): |
4251 | + """Move file when conflict adds a mute filter.""" |
4252 | + path1 = os.path.join(self.share.path, "thisfile1") |
4253 | + open(path1, "w").close() |
4254 | + mdobj = self.create_node(path1) |
4255 | + |
4256 | + # move and check |
4257 | + self.fsm.move_to_conflict(mdobj.mdid) |
4258 | + self.assertEqual(self.muted, |
4259 | + [('FS_FILE_MOVE', path1, path1 + '.u1conflict')]) |
4260 | + |
4261 | + def test_conflict_movefile_error(self): |
4262 | + """Move file when conflict adds and removes a mute filter.""" |
4263 | + path1 = os.path.join(self.share.path, "thisfile1") |
4264 | + mdobj = self.create_node(path1) |
4265 | + |
4266 | + # move and check |
4267 | + self.fsm.move_to_conflict(mdobj.mdid) |
4268 | + self.assertEqual(self.muted, []) |
4269 | + |
4270 | + def test_conflict_movedir_ok(self): |
4271 | + """Move dir when conflict adds a mute filter.""" |
4272 | + path1 = os.path.join(self.share.path, "thisfile1") |
4273 | + os.mkdir(path1) |
4274 | + mdobj = self.create_node(path1, is_dir=True) |
4275 | + |
4276 | + # move and check |
4277 | + self.fsm.move_to_conflict(mdobj.mdid) |
4278 | + self.assertEqual(self.muted, |
4279 | + [('FS_DIR_MOVE', path1, path1 + '.u1conflict')]) |
4280 | + |
4281 | + def test_conflict_movedir_error(self): |
4282 | + """Move dir when conflict adds and removes a mute filter.""" |
4283 | + path1 = os.path.join(self.share.path, "thisfile1") |
4284 | + mdobj = self.create_node(path1, is_dir=True) |
4285 | + |
4286 | + # move and check |
4287 | + self.fsm.move_to_conflict(mdobj.mdid) |
4288 | + self.assertEqual(self.muted, []) |
4289 | + |
4290 | + |
4291 | + |
4292 | def test_suite(): |
4293 | # pylint: disable-msg=C0111 |
4294 | return unittest.TestLoader().loadTestsFromName(__name__) |
4295 | |
4296 | === modified file 'tests/syncdaemon/test_fsm.pyc' |
4297 | Binary files tests/syncdaemon/test_fsm.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_fsm.pyc 2010-03-31 23:55:45 +0000 differ |
4298 | === modified file 'tests/syncdaemon/test_hashqueue.py' |
4299 | --- tests/syncdaemon/test_hashqueue.py 2010-02-17 23:51:29 +0000 |
4300 | +++ tests/syncdaemon/test_hashqueue.py 2010-03-31 23:55:45 +0000 |
4301 | @@ -531,7 +531,7 @@ |
4302 | receiver = Helper() |
4303 | hq = hash_queue.HashQueue(receiver) |
4304 | hq.shutdown() |
4305 | - self.assertRaises(RuntimeError, hq.insert, 'foo', "mdid") |
4306 | + self.assertTrue(hq._stopped) |
4307 | |
4308 | def test_shutdown_while_hashing(self): |
4309 | """Test that the HashQueue is shutdown ASAP while it's hashing.""" |
4310 | @@ -573,7 +573,8 @@ |
4311 | receiver = Helper() |
4312 | hq = hash_queue.HashQueue(receiver) |
4313 | hq.shutdown() |
4314 | - self.assertRaises(RuntimeError, hq.insert, '/foo/bar', "mdid") |
4315 | + hq.insert('foo', 'mdid') |
4316 | + self.assertFalse(hq.is_hashing('foo', 'mdid')) |
4317 | |
4318 | |
4319 | class UniqueQueueTests(TwistedTestCase): |
4320 | |
4321 | === modified file 'tests/syncdaemon/test_hashqueue.pyc' |
4322 | Binary files tests/syncdaemon/test_hashqueue.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_hashqueue.pyc 2010-03-31 23:55:45 +0000 differ |
4323 | === modified file 'tests/syncdaemon/test_localrescan.py' |
4324 | --- tests/syncdaemon/test_localrescan.py 2010-03-04 16:47:43 +0000 |
4325 | +++ tests/syncdaemon/test_localrescan.py 2010-03-31 23:55:45 +0000 |
4326 | @@ -48,7 +48,7 @@ |
4327 | def _fake(self, *a): |
4328 | """fake""" |
4329 | inotify_add_watch = inotify_has_watch = freeze_rollback = is_frozen = _fake |
4330 | - freeze_begin = add_to_mute_filter = _fake |
4331 | + inotify_rm_watch = freeze_begin = add_to_mute_filter = _fake |
4332 | |
4333 | def freeze_commit(self, events): |
4334 | """just store events""" |
4335 | @@ -302,6 +302,42 @@ |
4336 | self.assertTrue(isinstance(d, defer.Deferred)) |
4337 | return d |
4338 | |
4339 | + def test_start_without_udf_itself(self): |
4340 | + """LR start() having removed UDFs.""" |
4341 | + vol_to_unsub = self.volumes[0] |
4342 | + vol_to_keep = self.volumes[1:] |
4343 | + shutil.rmtree(vol_to_unsub.path) |
4344 | + d = self.lr.start() |
4345 | + |
4346 | + def check(_): |
4347 | + """Removed UDF should be desubscribed.""" |
4348 | + # these should remain ok |
4349 | + for vol in vol_to_keep: |
4350 | + self.assertTrue(vol.subscribed) |
4351 | + # this should be unsubscribed |
4352 | + self.assertFalse(vol_to_unsub.subscribed) |
4353 | + |
4354 | + d.addCallback(check) |
4355 | + return d |
4356 | + |
4357 | + def test_start_without_udf_ancestors(self): |
4358 | + """LR start() having removed UDFs parents.""" |
4359 | + vol_to_unsub = self.volumes[-1] # grab the one that has lot of parents |
4360 | + vol_to_keep = self.volumes[:-1] |
4361 | + shutil.rmtree(os.path.dirname(vol_to_unsub.path)) |
4362 | + d = self.lr.start() |
4363 | + |
4364 | + def check(_): |
4365 | + """Removed UDF should be desubscribed.""" |
4366 | + # these should remain ok |
4367 | + for vol in vol_to_keep: |
4368 | + self.assertTrue(vol.subscribed) |
4369 | + # this should be unsubscribed |
4370 | + self.assertFalse(vol_to_unsub.subscribed) |
4371 | + |
4372 | + d.addCallback(check) |
4373 | + return d |
4374 | + |
4375 | |
4376 | class TwistedBase(BaseTestCase): |
4377 | """Base class for twisted tests.""" |
4378 | @@ -503,6 +539,20 @@ |
4379 | self.scanTest(check) |
4380 | return self.deferred |
4381 | |
4382 | + def test_no_file_no_hash(self): |
4383 | + """Test useless metadata.""" |
4384 | + path = os.path.join(self.share.path, "b") |
4385 | + self.fsm.create(path, self.share.volume_id) |
4386 | + self.assertTrue(self.fsm.has_metadata(path=path)) |
4387 | + |
4388 | + def check(_): |
4389 | + """Check.""" |
4390 | + self.assertEqual(self.eq.pushed, []) |
4391 | + self.assertFalse(self.fsm.has_metadata(path=path)) |
4392 | + |
4393 | + self.startTest(check) |
4394 | + return self.deferred |
4395 | + |
4396 | def test_disc_less_dir_normal(self): |
4397 | """Test having less in disc than in metadata, normal volume.""" |
4398 | # create a node in the share, but no in disk |
4399 | |
4400 | === modified file 'tests/syncdaemon/test_localrescan.pyc' |
4401 | Binary files tests/syncdaemon/test_localrescan.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_localrescan.pyc 2010-03-31 23:55:45 +0000 differ |
4402 | === modified file 'tests/syncdaemon/test_logger.pyc' |
4403 | Binary files tests/syncdaemon/test_logger.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_logger.pyc 2010-03-31 23:55:45 +0000 differ |
4404 | === modified file 'tests/syncdaemon/test_main.pyc' |
4405 | Binary files tests/syncdaemon/test_main.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_main.pyc 2010-03-31 23:55:45 +0000 differ |
4406 | === modified file 'tests/syncdaemon/test_states.py' |
4407 | --- tests/syncdaemon/test_states.py 2010-03-04 16:47:43 +0000 |
4408 | +++ tests/syncdaemon/test_states.py 2010-03-31 23:55:45 +0000 |
4409 | @@ -21,6 +21,7 @@ |
4410 | from twisted.internet import defer, reactor |
4411 | from twisted.trial.unittest import TestCase as TwistedTestCase |
4412 | |
4413 | +from contrib.testing.testcase import FakeLogger |
4414 | from ubuntuone.syncdaemon.states import ( |
4415 | StateManager, ConnectionManager, QueueManager, Node |
4416 | ) |
4417 | @@ -121,27 +122,6 @@ |
4418 | return object.__getattribute__(self, name) |
4419 | |
4420 | |
4421 | - |
4422 | -class FakeLogger(object): |
4423 | - """Helper logging class.""" |
4424 | - def __init__(self): |
4425 | - self.logged = dict(debug=[], warning=[]) |
4426 | - |
4427 | - def _log(self, log, txt, args): |
4428 | - """Really logs.""" |
4429 | - if args: |
4430 | - txt = txt % args |
4431 | - log.append(txt) |
4432 | - |
4433 | - def warning(self, txt, *args): |
4434 | - """WARNING logs.""" |
4435 | - self._log(self.logged['warning'], txt, args) |
4436 | - |
4437 | - def debug(self, txt, *args): |
4438 | - """DEBUG logs.""" |
4439 | - self._log(self.logged['debug'], txt, args) |
4440 | - |
4441 | - |
4442 | class Base(TwistedTestCase): |
4443 | """Base class for state tests.""" |
4444 | def setUp(self): |
4445 | @@ -959,6 +939,13 @@ |
4446 | d.append(self.check(node, 'SYS_UNKNOWN_ERROR', 'UNKNOWN_ERROR')) |
4447 | return defer.DeferredList(d) |
4448 | |
4449 | + def test_root_mismatch_error(self): |
4450 | + """All nodes go to root_mismatch.""" |
4451 | + d = [] |
4452 | + for node in self.sm_nodes_ok: |
4453 | + d.append(self.check(node, 'SYS_ROOT_MISMATCH', 'ROOT_MISMATCH')) |
4454 | + return defer.DeferredList(d) |
4455 | + |
4456 | def test_not_exiting_from_errors(self): |
4457 | """No return from errors.""" |
4458 | d = [] |
4459 | |
4460 | === modified file 'tests/syncdaemon/test_states.pyc' |
4461 | Binary files tests/syncdaemon/test_states.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_states.pyc 2010-03-31 23:55:45 +0000 differ |
4462 | === modified file 'tests/syncdaemon/test_sync.pyc' |
4463 | Binary files tests/syncdaemon/test_sync.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_sync.pyc 2010-03-31 23:55:45 +0000 differ |
4464 | === modified file 'tests/syncdaemon/test_tools.pyc' |
4465 | Binary files tests/syncdaemon/test_tools.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_tools.pyc 2010-03-31 23:55:45 +0000 differ |
4466 | === modified file 'tests/syncdaemon/test_u1fsfsm.pyc' |
4467 | Binary files tests/syncdaemon/test_u1fsfsm.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_u1fsfsm.pyc 2010-03-31 23:55:45 +0000 differ |
4468 | === modified file 'tests/syncdaemon/test_u1sdtool.py' |
4469 | --- tests/syncdaemon/test_u1sdtool.py 2010-03-10 23:36:53 +0000 |
4470 | +++ tests/syncdaemon/test_u1sdtool.py 2010-03-31 23:55:45 +0000 |
4471 | @@ -103,6 +103,14 @@ |
4472 | |
4473 | def test_show_path_info_unicode(self): |
4474 | """test the output of --info with unicode paths """ |
4475 | + return self.generic_test_show_path_info_unicode('utf-8') |
4476 | + |
4477 | + def test_show_path_info_unicode_pipe(self): |
4478 | + """test the output of --info with unicode paths going to e.g. a pipe""" |
4479 | + return self.generic_test_show_path_info_unicode(None) |
4480 | + |
4481 | + def generic_test_show_path_info_unicode(self, encoding): |
4482 | + """generic test for the output of --info with unicode paths """ |
4483 | path = os.path.join(self.root_dir, "ñoño") |
4484 | mdid = self.fs_manager.create(path, "") |
4485 | self.fs_manager.set_node_id(path, "uuid1") |
4486 | @@ -117,7 +125,7 @@ |
4487 | self.fs_manager.remove_partial("uuid1", "") |
4488 | d = self.tool.get_metadata(path) |
4489 | out = StringIO() |
4490 | - out.encoding = 'utf-8' |
4491 | + out.encoding = encoding |
4492 | expected = """ File: %(path_info)s |
4493 | info_created: %(info_created)s |
4494 | info_is_partial: %(info_is_partial)s |
4495 | @@ -136,7 +144,10 @@ |
4496 | """ |
4497 | # the callback, pylint: disable-msg=C0111 |
4498 | def callback(result): |
4499 | - result.update(dict(path_info=path.decode(out.encoding))) |
4500 | + if encoding is not None: |
4501 | + result.update(dict(path_info=path.decode(encoding))) |
4502 | + else: |
4503 | + result.update(dict(path_info=path)) |
4504 | for k, v in result.copy().items(): |
4505 | result[k] = v.decode('utf-8') |
4506 | value = expected % result |
4507 | |
4508 | === modified file 'tests/syncdaemon/test_u1sdtool.pyc' |
4509 | Binary files tests/syncdaemon/test_u1sdtool.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_u1sdtool.pyc 2010-03-31 23:55:45 +0000 differ |
4510 | === modified file 'tests/syncdaemon/test_vm.py' |
4511 | --- tests/syncdaemon/test_vm.py 2010-03-04 16:47:43 +0000 |
4512 | +++ tests/syncdaemon/test_vm.py 2010-03-31 23:55:45 +0000 |
4513 | @@ -1819,7 +1819,8 @@ |
4514 | for new_key in new_keys: |
4515 | self.assertIn(new_key, old_keys) |
4516 | # check the old data is still there (in the backup) |
4517 | - backup_shelf = LegacyShareFileShelf(os.path.join(self.vm_data_dir, '0.bkp')) |
4518 | + bkp_dir = os.path.join(os.path.dirname(self.vm_data_dir), '5.bkp', '0.bkp') |
4519 | + backup_shelf = LegacyShareFileShelf(bkp_dir) |
4520 | backup_keys = [key for key in backup_shelf.keys()] |
4521 | for old_key in old_keys: |
4522 | self.assertIn(old_key, backup_keys) |
4523 | @@ -2420,6 +2421,183 @@ |
4524 | self.assertEquals(udf.suggested_path, old_udf.suggested_path) |
4525 | self.assertEquals(udf.subscribed, old_udf.subscribed) |
4526 | |
4527 | + def test_upgrade_5_partial_upgrade(self): |
4528 | + """Test migration from version 5 with upgrade to 6 unfinished.""" |
4529 | + # build a fake version 5 state |
4530 | + self._build_layout_version_4() |
4531 | + self.set_md_version('5') |
4532 | + self.udfs_md_dir = os.path.join(self.vm_data_dir, 'udfs') |
4533 | + # create some old shares and shared metadata |
4534 | + legacy_shares = LegacyShareFileShelf(self.share_md_dir) |
4535 | + root_share = _Share(path=self.root_dir, share_id='', |
4536 | + access_level='Modify', node_id=str(uuid.uuid4())) |
4537 | + legacy_shares[''] = root_share |
4538 | + for idx, name in enumerate(['share'] * 3): |
4539 | + sid = str(uuid.uuid4()) |
4540 | + share_name = name + '_' + str(idx) |
4541 | + share = _Share(path=os.path.join(self.shares_dir, share_name), |
4542 | + share_id=sid, name=share_name, |
4543 | + node_id=str(uuid.uuid4()), |
4544 | + other_username='username'+str(idx), |
4545 | + other_visible_name='visible name ' + str(idx)) |
4546 | + if idx == 0: |
4547 | + share.access_level = 'Modify' |
4548 | + legacy_shares[sid] = share |
4549 | + elif idx == 1: |
4550 | + share.access_level = 'View' |
4551 | + legacy_shares[sid] = share |
4552 | + else: |
4553 | + # add a 'new' Share dict to the shelf |
4554 | + share.access_level = 'Modify' |
4555 | + share = Share(path=share.path, |
4556 | + volume_id=share.id, name=share.name, |
4557 | + access_level=share.access_level, |
4558 | + other_username=share.other_username, |
4559 | + other_visible_name=share.other_visible_name, |
4560 | + node_id=share.subtree) |
4561 | + legacy_shares[sid] = share.__dict__ |
4562 | + |
4563 | + # create shared shares |
4564 | + legacy_shared = LegacyShareFileShelf(self.shared_md_dir) |
4565 | + for idx, name in enumerate(['dir'] * 3): |
4566 | + sid = str(uuid.uuid4()) |
4567 | + share_name = name + '_' + str(idx) |
4568 | + share = _Share(path=os.path.join(self.root_dir, share_name), |
4569 | + share_id=sid, node_id=str(uuid.uuid4()), |
4570 | + name=share_name, other_username='hola', |
4571 | + other_visible_name='hola') |
4572 | + if idx == 0: |
4573 | + share.access_level = 'Modify' |
4574 | + legacy_shares[sid] = share |
4575 | + elif idx == 1: |
4576 | + share.access_level = 'View' |
4577 | + legacy_shares[sid] = share |
4578 | + else: |
4579 | + # add a 'new' Shared dict to the shelf |
4580 | + share.access_level = 'Modify' |
4581 | + share = Shared(path=share.path, |
4582 | + volume_id=share.id, name=share.name, |
4583 | + access_level=share.access_level, |
4584 | + other_username=share.other_username, |
4585 | + other_visible_name=share.other_visible_name, |
4586 | + node_id=share.subtree) |
4587 | + legacy_shares[sid] = share.__dict__ |
4588 | + |
4589 | + # keep a copy of the current shares and shared metadata to check |
4590 | + # the upgrade went ok |
4591 | + legacy_shares = dict(legacy_shares.items()) |
4592 | + legacy_shared = dict(legacy_shared.items()) |
4593 | + |
4594 | + if self.md_version_None: |
4595 | + self.set_md_version('') |
4596 | + # upgrade it! |
4597 | + self.main = FakeMain(self.root_dir, self.shares_dir, |
4598 | + self.data_dir, self.partials_dir) |
4599 | + vm = self.main.vm |
4600 | + def compare_share(share, old_share): |
4601 | + """Compare two shares, new and old""" |
4602 | + old_id = getattr(old_share, 'id', None) |
4603 | + if old_id is None: |
4604 | + old_id = old_share['volume_id'] |
4605 | + self.assertEquals(share.volume_id, old_id) |
4606 | + self.assertEquals(share.path, |
4607 | + getattr(old_share, 'path', None) or old_share['path']) |
4608 | + self.assertEquals(share.node_id, |
4609 | + getattr(old_share, 'subtree', None) or old_share['node_id']) |
4610 | + if not isinstance(share, Root): |
4611 | + self.assertEquals(share.name, |
4612 | + getattr(old_share, 'name', None) or old_share['name']) |
4613 | + self.assertEquals(share.other_username, |
4614 | + getattr(old_share, 'other_username', None) \ |
4615 | + or old_share['other_username']) |
4616 | + self.assertEquals(share.other_visible_name, |
4617 | + getattr(old_share, 'other_visible_name', None) \ |
4618 | + or old_share['other_visible_name']) |
4619 | + self.assertEquals(share.access_level, |
4620 | + getattr(old_share, 'access_level', None) \ |
4621 | + or old_share['access_level']) |
4622 | + |
4623 | + for sid in vm.shares: |
4624 | + old_share = legacy_shares[sid] |
4625 | + share = vm.shares[sid] |
4626 | + self.assertTrue(isinstance(share, Share) or isinstance(share, Root)) |
4627 | + compare_share(share, old_share) |
4628 | + |
4629 | + for sid in vm.shared: |
4630 | + old_share = legacy_shared[sid] |
4631 | + share = vm.shared[sid] |
4632 | + self.assertTrue(isinstance(share, Shared)) |
4633 | + compare_share(share, old_share) |
4634 | + |
4635 | + def test_upgrade_5_critical_error(self): |
4636 | + """Test the migration from version 5 with a critical error.""" |
4637 | + # build a fake version 5 state |
4638 | + self._build_layout_version_4() |
4639 | + self.set_md_version('5') |
4640 | + # create some old shares and shared metadata |
4641 | + legacy_shares = LegacyShareFileShelf(self.share_md_dir) |
4642 | + root_share = _Share(path=self.root_dir, share_id='', |
4643 | + access_level='Modify') |
4644 | + legacy_shares[''] = root_share |
4645 | + for idx, name in enumerate(['share'] * 10): |
4646 | + sid = str(uuid.uuid4()) |
4647 | + share_name = name + '_' + str(idx) |
4648 | + share = _Share(path=os.path.join(self.shares_dir, share_name), |
4649 | + share_id=sid, name=share_name, |
4650 | + node_id=str(uuid.uuid4()), |
4651 | + other_username='username'+str(idx), |
4652 | + other_visible_name='visible name ' + str(idx)) |
4653 | + if idx % 2: |
4654 | + share.access_level = 'Modify' |
4655 | + else: |
4656 | + share.access_level = 'View' |
4657 | + legacy_shares[sid] = share |
4658 | + # create shared shares |
4659 | + legacy_shared = LegacyShareFileShelf(self.shared_md_dir) |
4660 | + for idx, name in enumerate(['dir'] * 5): |
4661 | + sid = str(uuid.uuid4()) |
4662 | + share_name = name + '_' + str(idx) |
4663 | + share = _Share(path=os.path.join(self.root_dir, share_name), |
4664 | + share_id=sid, node_id=str(uuid.uuid4()), |
4665 | + name=share_name, other_username='hola', |
4666 | + other_visible_name='hola') |
4667 | + if idx % 2: |
4668 | + share.access_level = 'Modify' |
4669 | + else: |
4670 | + share.access_level = 'View' |
4671 | + legacy_shared[sid] = share |
4672 | + |
4673 | + # keep a copy of the current shares and shared metadata to check |
4674 | + # the upgrade went ok |
4675 | + legacy_shares = dict(legacy_shares.items()) |
4676 | + legacy_shared = dict(legacy_shared.items()) |
4677 | + |
4678 | + if self.md_version_None: |
4679 | + self.set_md_version('') |
4680 | + # upgrade it! |
4681 | + old_upgrade_share_to_volume = MetadataUpgrader._upgrade_share_to_volume |
4682 | + def upgrade_share_to_volume(share, shared=False): |
4683 | + raise ValueError('FAIL!') |
4684 | + MetadataUpgrader._upgrade_share_to_volume = upgrade_share_to_volume |
4685 | + try: |
4686 | + self.assertRaises(ValueError, FakeMain, self.root_dir, self.shares_dir, |
4687 | + self.data_dir, self.partials_dir) |
4688 | + finally: |
4689 | + MetadataUpgrader._upgrade_share_to_volume = old_upgrade_share_to_volume |
4690 | + |
4691 | + shares = LegacyShareFileShelf(self.share_md_dir) |
4692 | + self.assertEquals(len(list(shares.keys())), len(legacy_shares.keys())) |
4693 | + for sid, share in shares.iteritems(): |
4694 | + old_share = legacy_shares[sid] |
4695 | + self.assertTrue(isinstance(share, _Share)) |
4696 | + self.assertTrue(isinstance(old_share, _Share)) |
4697 | + shared = LegacyShareFileShelf(self.shared_md_dir) |
4698 | + self.assertEquals(len(list(shared.keys())), len(legacy_shared.keys())) |
4699 | + for sid, share in shared.iteritems(): |
4700 | + old_share = legacy_shared[sid] |
4701 | + self.assertTrue(isinstance(share, _Share)) |
4702 | + self.assertTrue(isinstance(old_share, _Share)) |
4703 | + |
4704 | |
4705 | class BrokenOldMDVersionUpgradeTests(MetadataOldLayoutTests): |
4706 | """MetadataOldLayoutTests with broken .version file.""" |
4707 | @@ -2520,3 +2698,11 @@ |
4708 | version = self.md_upgrader._guess_metadata_version() |
4709 | self.assertEquals(version, '6') |
4710 | |
4711 | + def test_guess_mixed_metadata_5_and_6(self): |
4712 | + """Test _guess_metadata_version method for mixed version 5 and 6.""" |
4713 | + # fake a version 6 layout and metadata |
4714 | + shelf = LegacyShareFileShelf(self.share_md_dir) |
4715 | + shelf['old_share'] = _Share(path='/foo/bar', share_id='old_share') |
4716 | + shelf['new_share'] = Share(path='/bar/foo', volume_id='new_share').__dict__ |
4717 | + version = self.md_upgrader._guess_metadata_version() |
4718 | + self.assertEquals(version, '5') |
4719 | |
4720 | === modified file 'tests/syncdaemon/test_vm.pyc' |
4721 | Binary files tests/syncdaemon/test_vm.pyc 2010-03-10 23:36:53 +0000 and tests/syncdaemon/test_vm.pyc 2010-03-31 23:55:45 +0000 differ |
4722 | === modified file 'tests/test_login.pyc' |
4723 | Binary files tests/test_login.pyc 2010-03-10 23:36:53 +0000 and tests/test_login.pyc 2010-03-31 23:55:45 +0000 differ |
4724 | === modified file 'tests/test_preferences.py' |
4725 | --- tests/test_preferences.py 2010-03-10 23:36:53 +0000 |
4726 | +++ tests/test_preferences.py 2010-03-31 23:55:45 +0000 |
4727 | @@ -21,7 +21,7 @@ |
4728 | import os |
4729 | import gnomekeyring |
4730 | |
4731 | -from contrib.mocker import MockerTestCase, KWARGS |
4732 | +from contrib.mocker import MockerTestCase |
4733 | from contrib.testing.testcase import DBusTwistedTestCase, FakeLogin |
4734 | from twisted.internet import defer |
4735 | from twisted.python.failure import Failure |
4736 | @@ -48,7 +48,6 @@ |
4737 | |
4738 | # For testing keyring queries |
4739 | self.keyring = self.mocker.mock() |
4740 | - self.u1prefs.httplib2 = self.mocker.mock() |
4741 | self.item = self.mocker.mock(gnomekeyring.Found) |
4742 | |
4743 | self.item_id = 999 |
4744 | @@ -72,6 +71,8 @@ |
4745 | self.mocker.count(0, None) |
4746 | self.mocker.result(None) |
4747 | |
4748 | + self.u1prefs.make_rest_request = self.make_rest_request |
4749 | + |
4750 | def tearDown(self): |
4751 | # collect all signal receivers registered during the test |
4752 | signal_receivers = set() |
4753 | @@ -88,10 +89,19 @@ |
4754 | d.addBoth(lambda _: DBusTwistedTestCase.tearDown(self)) |
4755 | return d |
4756 | |
4757 | + def make_rest_request(self, url=None, method='GET', |
4758 | + callback=None, keyring=None): |
4759 | + """Override the real request call to mock some stuff.""" |
4760 | + if callback: |
4761 | + callback(self.content) |
4762 | + else: |
4763 | + raise Exception('No callback provided.') |
4764 | + |
4765 | def test_bw_throttling(self): |
4766 | """Test that toggling bw throttling works correctly.""" |
4767 | self.mocker.replay() |
4768 | widget = self.u1prefs.DevicesWidget(None, keyring=self.keyring) |
4769 | + widget.update_bw_settings = self.mocker.mock() |
4770 | try: |
4771 | widget.devices = [] |
4772 | widget.list_devices() |
4773 | @@ -116,6 +126,7 @@ |
4774 | def test_list_devices_fills_devices_list_with_fake_result_when_empty(self): |
4775 | self.mocker.replay() |
4776 | widget = self.u1prefs.DevicesWidget(None, keyring=self.keyring) |
4777 | + widget.update_bw_settings = self.mocker.mock() |
4778 | try: |
4779 | widget.devices = [] |
4780 | widget.list_devices() |
4781 | @@ -129,6 +140,7 @@ |
4782 | def test_list_devices_shows_devices_list(self): |
4783 | self.mocker.replay() |
4784 | widget = self.u1prefs.DevicesWidget(None, keyring=self.keyring) |
4785 | + widget.update_bw_settings = self.mocker.mock() |
4786 | try: |
4787 | widget.devices = [] |
4788 | widget.list_devices() |
4789 | @@ -159,6 +171,7 @@ |
4790 | def test_list_devices_shows_real_devices_list(self): |
4791 | self.mocker.replay() |
4792 | widget = self.u1prefs.DevicesWidget(None, keyring=self.keyring) |
4793 | + widget.update_bw_settings = self.mocker.mock() |
4794 | try: |
4795 | widget.devices = [{'kind': 'Computer', |
4796 | 'description': 'xyzzy', |
4797 | @@ -203,12 +216,7 @@ |
4798 | |
4799 | def test_request_quota_info(self): |
4800 | """Test that we can request the quota info properly.""" |
4801 | - response = { 'status' : '200' } |
4802 | - content = '{"total":2048, "used":1024}' |
4803 | - client = self.mocker.mock() |
4804 | - self.expect(self.u1prefs.httplib2.Http()).result(client) |
4805 | - self.expect(client.request('https://one.ubuntu.com/api/quota/', |
4806 | - 'GET', KWARGS)).result((response, content)) |
4807 | + self.content = {"total":2048, "used":1024} |
4808 | self.mocker.replay() |
4809 | dialog = self.u1prefs.UbuntuOneDialog(keyring=self.keyring) |
4810 | self.assertTrue(dialog is not None) |
4811 | @@ -217,23 +225,116 @@ |
4812 | self.assertEqual(dialog.usage_graph.get_fraction(), 0.5) |
4813 | dialog.destroy() |
4814 | |
4815 | + def test_no_overquota_notice_on_low_usage(self): |
4816 | + """Test that the quota notice is not visible if usage is low.""" |
4817 | + self.content = {"total":2048, "used":1024} |
4818 | + self.mocker.replay() |
4819 | + dialog = self.u1prefs.UbuntuOneDialog(keyring=self.keyring) |
4820 | + self.assertTrue(dialog is not None) |
4821 | + dialog.request_quota_info() |
4822 | + # the label should just be the blank '\n' |
4823 | + self.assertEqual(dialog.overquota_label.get_text(), '\n') |
4824 | + # and the icon should be blank, too |
4825 | + self.assertEqual(dialog.overquota_img.get_icon_name()[0], None) |
4826 | + dialog.destroy() |
4827 | + |
4828 | + def test_overquota_notice_and_upgrade_offer_on_free_high_usage(self): |
4829 | + """Test that the quota notice is visible if usage is 95%. |
4830 | + |
4831 | + Also make sure the label mentions upgrading. |
4832 | + """ |
4833 | + self.content = {"total": 100, "used": 95} |
4834 | + self.mocker.replay() |
4835 | + dialog = self.u1prefs.UbuntuOneDialog(keyring=self.keyring) |
4836 | + self.assertTrue(dialog is not None) |
4837 | + dialog.request_quota_info() |
4838 | + # don't check the exact text, as it will probably |
4839 | + # change. Check we're setting some amount of text, |
4840 | + self.assertTrue(len(dialog.overquota_label.get_text()) > 100) |
4841 | + # and check that we mention upgrading |
4842 | + self.assertTrue('upgrad' in dialog.overquota_label.get_text().lower()) |
4843 | + # and check that the icon used is just informational |
4844 | + self.assertEqual(dialog.overquota_img.get_icon_name()[0], |
4845 | + 'dialog-information') |
4846 | + dialog.destroy() |
4847 | + |
4848 | + def test_overquota_notice_and_not_upgrade_offer_on_paid_high_usage(self): |
4849 | + """Test that the quota notice is visible if usage is 95%. |
4850 | + |
4851 | + Also make sure the label does not mention upgrading. |
4852 | + """ |
4853 | + self.content = {"total": 50<<30, "used": 49<<30} |
4854 | + self.mocker.replay() |
4855 | + dialog = self.u1prefs.UbuntuOneDialog(keyring=self.keyring) |
4856 | + self.assertTrue(dialog is not None) |
4857 | + dialog.request_quota_info() |
4858 | + # don't check the exact text, as it will probably |
4859 | + # change. Check we're setting some amount of text, |
4860 | + self.assertTrue(len(dialog.overquota_label.get_text()) > 100) |
4861 | + # and check that we mention upgrading |
4862 | + self.assertTrue('upgrad' not in |
4863 | + dialog.overquota_label.get_text().lower()) |
4864 | + # and check that the icon used is just informational |
4865 | + self.assertEqual(dialog.overquota_img.get_icon_name()[0], |
4866 | + 'dialog-information') |
4867 | + dialog.destroy() |
4868 | + |
4869 | + def test_overquota_warning_and_upgrade_offer_there_on_free_over_quota(self): |
4870 | + """Test that the quota notice is visible if usage is 100%. |
4871 | + |
4872 | + Also make sure the label mentions upgrading. |
4873 | + """ |
4874 | + self.content = {"total": 100, "used": 100} |
4875 | + self.mocker.replay() |
4876 | + dialog = self.u1prefs.UbuntuOneDialog(keyring=self.keyring) |
4877 | + self.assertTrue(dialog is not None) |
4878 | + dialog.request_quota_info() |
4879 | + # don't check the exact text, as it will probably |
4880 | + # change. Check we're setting some amount of text, |
4881 | + self.assertTrue(len(dialog.overquota_label.get_text()) > 100) |
4882 | + # and check that we mention upgrading |
4883 | + self.assertTrue('upgrad' in dialog.overquota_label.get_text().lower()) |
4884 | + # and check that the icon used is a warning |
4885 | + self.assertEqual(dialog.overquota_img.get_icon_name()[0], |
4886 | + 'dialog-warning') |
4887 | + dialog.destroy() |
4888 | + |
4889 | + def test_overquota_warning_there_on_paid_over_quota(self): |
4890 | + """Test that the quota notice is visible if usage is 100%.""" |
4891 | + self.content = {"total": 50<<30, "used": 50<<30} |
4892 | + self.mocker.replay() |
4893 | + dialog = self.u1prefs.UbuntuOneDialog(keyring=self.keyring) |
4894 | + self.assertTrue(dialog is not None) |
4895 | + dialog.request_quota_info() |
4896 | + # don't check the exact text, as it will probably |
4897 | + # change. Check we're setting some amount of text, |
4898 | + self.assertTrue(len(dialog.overquota_label.get_text()) > 100) |
4899 | + # and check that the icon used is a warning |
4900 | + self.assertEqual(dialog.overquota_img.get_icon_name()[0], |
4901 | + 'dialog-warning') |
4902 | + dialog.destroy() |
4903 | + |
4904 | def test_request_account_info(self): |
4905 | """Test that we can request the account info properly.""" |
4906 | - response = { 'status' : '200' } |
4907 | - content = '''{"username": "ubuntuone", "nickname": "Ubuntu One", |
4908 | - "email": "uone@example.com"} |
4909 | - ''' |
4910 | - client = self.mocker.mock() |
4911 | - self.expect(self.u1prefs.httplib2.Http()).result(client) |
4912 | - self.expect(client.request('https://one.ubuntu.com/api/account/', |
4913 | - 'GET', KWARGS)).result((response, content)) |
4914 | + self.content = {"username": "ubuntuone", "nickname": "Ubuntu One", |
4915 | + "email": "uone@example.com"} |
4916 | self.mocker.replay() |
4917 | dialog = self.u1prefs.UbuntuOneDialog(keyring=self.keyring) |
4918 | self.assertTrue(dialog is not None) |
4919 | dialog.request_account_info() |
4920 | + self.content = {"total":2048, "used":1024} |
4921 | + dialog.request_quota_info() |
4922 | self.assertEqual(dialog.name_label.get_text(), 'Ubuntu One') |
4923 | - self.assertEqual(dialog.user_label.get_text(), 'ubuntuone') |
4924 | self.assertEqual(dialog.mail_label.get_text(), 'uone@example.com') |
4925 | + # ensure the plan label says "Free" and the upgrade button is there |
4926 | + self.assertEqual(dialog.plan_label.get_text(), 'Free') |
4927 | + self.assertTrue(dialog.upgrade_link.get_visible()) |
4928 | + # whoops, the user upgraded |
4929 | + self.content = {"total": 50<<30, "used":1024} |
4930 | + dialog.request_quota_info() |
4931 | + # ensure the plan label says "Paid" and the upgrade button is not there |
4932 | + self.assertEqual(dialog.plan_label.get_text(), 'Paid') |
4933 | + self.assertFalse(dialog.upgrade_link.get_visible()) |
4934 | dialog.destroy() |
4935 | |
4936 | def test_toggle_bookmarks(self): |
4937 | @@ -287,6 +388,20 @@ |
4938 | self.mocker.replay() |
4939 | dialog = self.u1prefs.UbuntuOneDialog(keyring=self.keyring) |
4940 | self.assertTrue(dialog is not None) |
4941 | + def files_toggled(checkbutton): |
4942 | + enabled = checkbutton.get_active() |
4943 | + if enabled: |
4944 | + dialog.music_check.set_sensitive(True) |
4945 | + else: |
4946 | + dialog.music_check.set_sensitive(False) |
4947 | + |
4948 | + def music_toggled(checkbutton): |
4949 | + pass |
4950 | + |
4951 | + dialog.files_check_toggled = files_toggled |
4952 | + dialog.music_check_toggled = music_toggled |
4953 | + dialog.connect_file_sync_callbacks() |
4954 | + |
4955 | dialog.files_check.set_active(False) |
4956 | self.assertFalse(dialog.files_check.get_active()) |
4957 | self.assertFalse(dialog.music_check.props.sensitive) |
4958 | @@ -324,12 +439,13 @@ |
4959 | |
4960 | d = defer.Deferred() |
4961 | |
4962 | - login = self.u1prefs.UbuntuoneLoginHandler() |
4963 | + login = self.u1prefs.UbuntuoneLoginHandler(dialog=None) |
4964 | login.got_newcredentials = got_new_creds |
4965 | login.got_authdenied = got_auth_denied |
4966 | login.got_oautherror = got_oauth_error |
4967 | login.got_dbus_error = got_dbus_error |
4968 | login.register_signal_handlers() |
4969 | - login.do_login_check() |
4970 | + self.u1prefs.do_login_request(bus=self.bus, |
4971 | + error_handler=got_dbus_error) |
4972 | |
4973 | return d |
4974 | |
4975 | === modified file 'tests/test_preferences.pyc' |
4976 | Binary files tests/test_preferences.pyc 2010-03-10 23:36:53 +0000 and tests/test_preferences.pyc 2010-03-31 23:55:45 +0000 differ |
4977 | === modified file 'tests/u1sync/__init__.pyc' |
4978 | Binary files tests/u1sync/__init__.pyc 2010-03-10 23:36:53 +0000 and tests/u1sync/__init__.pyc 2010-03-31 23:55:45 +0000 differ |
4979 | === modified file 'tests/u1sync/test_init.pyc' |
4980 | Binary files tests/u1sync/test_init.pyc 2010-03-10 23:36:53 +0000 and tests/u1sync/test_init.pyc 2010-03-31 23:55:45 +0000 differ |
4981 | === modified file 'tests/u1sync/test_merge.pyc' |
4982 | Binary files tests/u1sync/test_merge.pyc 2010-03-10 23:36:53 +0000 and tests/u1sync/test_merge.pyc 2010-03-31 23:55:45 +0000 differ |
4983 | === modified file 'ubuntuone/oauthdesktop/key_acls.py' |
4984 | --- ubuntuone/oauthdesktop/key_acls.py 2009-06-30 12:00:00 +0000 |
4985 | +++ ubuntuone/oauthdesktop/key_acls.py 2010-03-31 23:55:45 +0000 |
4986 | @@ -36,7 +36,7 @@ |
4987 | if use_source_tree_folder: |
4988 | source_tree_folder = os.path.join( |
4989 | os.path.split(__file__)[0], |
4990 | - "../../../oauth_registration.d") |
4991 | + "../../data/oauth_registration.d") |
4992 | if os.path.isdir(source_tree_folder): |
4993 | return os.path.join(source_tree_folder, "..") |
4994 | |
4995 | |
4996 | === modified file 'ubuntuone/oauthdesktop/main.py' |
4997 | --- ubuntuone/oauthdesktop/main.py 2010-02-17 23:51:29 +0000 |
4998 | +++ ubuntuone/oauthdesktop/main.py 2010-03-31 23:55:45 +0000 |
4999 | @@ -25,13 +25,12 @@ |
5000 | signal so they can retrieve the new token. |
The diff has been truncated for viewing.