From: Ed Page Date: Tue, 14 Apr 2009 02:13:06 +0000 (-0500) Subject: Adding persisting of credentials X-Git-Url: https://vcs.maemo.org/git/?p=doneit;a=commitdiff_plain;h=6443b60d15827484cc87e1abe39fa1021f7692da Adding persisting of credentials --- diff --git a/src/doneit.glade b/src/doneit.glade index 350ef5c..044873c 100644 --- a/src/doneit.glade +++ b/src/doneit.glade @@ -1,6 +1,6 @@ - + 800 @@ -28,6 +28,14 @@ + + True + gtk-disconnect + True + True + + + True gtk-quit @@ -279,32 +287,19 @@ True 2 - - True - - - False - False - 1 - - - True 2 2 - - True - Username - - - - + True - Password + True + False + 1 + 2 1 2 @@ -320,23 +315,36 @@ - + True - True - False + Password - 1 - 2 1 2 + + + True + Username + + 1 + + + True + + + False + False + 1 + + True @@ -413,43 +421,46 @@ 3 2 - + True - - - True - True - True - True - True - True - True - - - - - True - - - True - True - True - gtk-paste - True - 0 - - - - - False - False - 1 - - + Name + + + + + True + Priority + + + 1 + 2 + + + + + True + Due Date + + + 2 + 3 + + + + + True + True + None +1 +2 +3 1 2 + 1 + 2 + @@ -501,48 +512,45 @@ - + True - True - None -1 -2 -3 + + + True + True + True + True + True + True + True + + + + + True + + + True + True + True + gtk-paste + True + 0 + + + + + False + False + 1 + + 1 2 - 1 - 2 - - - - - - True - Due Date - - - 2 - 3 - - - True - Priority - - - 1 - 2 - - - - - True - Name - - 1 diff --git a/src/doneit.py b/src/doneit.py index 662f39e..e7ddec6 100755 --- a/src/doneit.py +++ b/src/doneit.py @@ -9,4 +9,4 @@ sys.path.insert(0,"/usr/lib/doneit/") import doneit_glade -doneit.run_doneit() +doneit_glade.run_doneit() diff --git a/src/doneit_glade.py b/src/doneit_glade.py index 132155c..eef0c6f 100755 --- a/src/doneit_glade.py +++ b/src/doneit_glade.py @@ -9,6 +9,7 @@ import gc import os import threading import warnings +import ConfigParser import gtk import gtk.glade @@ -38,11 +39,13 @@ class DoneIt(object): _user_settings = "%s/settings.ini" % _user_data def __init__(self): - self._todoUIs = [] + self._todoUIs = {} self._todoUI = None self._osso = None self._deviceIsOnline = True self._connection = None + self._fallbackUIName = "" + self._defaultUIName = "" for path in self._glade_files: if os.path.isfile(path): @@ -104,13 +107,12 @@ class DoneIt(object): def _idle_setup(self): # Barebones UI handlers import gtk_null - gtk.gdk.threads_enter() - try: - self._todoUIs = [ - gtk_null.GtkNull(self._widgetTree), - ] - finally: - gtk.gdk.threads_leave() + with gtk_toolbox.gtk_lock(): + nullView = gtk_null.GtkNull(self._widgetTree) + self._todoUIs[nullView.name()] = nullView + self._todoUI = nullView + self._todoUI.enable() + self._fallbackUIName = nullView.name() # Setup maemo specifics try: @@ -139,17 +141,20 @@ class DoneIt(object): # Setup costly backends import gtk_rtmilk - gtk.gdk.threads_enter() - try: - self._todoUIs.extend([ - gtk_rtmilk.GtkRtMilk(self._widgetTree), - ]) - self._todoUI = self._todoUIs[1] - self._todoUI.enable() - finally: - gtk.gdk.threads_leave() + with gtk_toolbox.gtk_lock(): + rtmView = gtk_rtmilk.GtkRtMilk(self._widgetTree) + self._todoUIs[rtmView.name()] = rtmView + self._defaultUIName = rtmView.name() + + config = ConfigParser.SafeConfigParser() + config.read(self._user_settings) + with gtk_toolbox.gtk_lock(): + self.load_settings(config) def display_error_message(self, msg): + """ + @note UI Thread + """ error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg) def close(dialog, response, editor): @@ -158,6 +163,75 @@ class DoneIt(object): error_dialog.connect("response", close, self) error_dialog.run() + def load_settings(self, config): + """ + @note UI Thread + """ + for todoUI in self._todoUIs.itervalues(): + try: + todoUI.load_settings(config) + except ConfigParser.NoSectionError, e: + warnings.warn( + "Settings file %s is missing section %s" % ( + self._user_settings, + e.section, + ), + stacklevel=2 + ) + + try: + activeUIName = config.get(self.__pretty_app_name__, "active") + except ConfigParser.NoSectionError, e: + activeUIName = "" + warnings.warn( + "Settings file %s is missing section %s" % ( + self._user_settings, + e.section, + ), + stacklevel=2 + ) + + try: + self._switch_ui(activeUIName) + except KeyError, e: + self._switch_ui(self._defaultUIName) + + def save_settings(self, config): + """ + @note Thread Agnostic + """ + config.add_section(self.__pretty_app_name__) + config.set(self.__pretty_app_name__, "active", self._todoUI.name()) + + for todoUI in self._todoUIs.itervalues(): + todoUI.save_settings(config) + + def _switch_ui(self, uiName): + """ + @note UI Thread + """ + newActiveUI = self._todoUIs[uiName] + try: + newActiveUI.login() + except RuntimeError: + return # User cancelled the operation + + self._todoUI.disable() + self._todoUI = newActiveUI + self._todoUI.enable() + + if uiName != self._fallbackUIName: + self._defaultUIName = uiName + + def _save_settings(self): + """ + @note Thread Agnostic + """ + config = ConfigParser.SafeConfigParser() + self.save_settings(config) + with open(self._user_settings, "wb") as configFile: + config.write(configFile) + def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData): """ For system_inactivity, we have no background tasks to pause @@ -168,7 +242,7 @@ class DoneIt(object): gc.collect() if save_unsaved_data or shutdown: - pass + self._save_settings() def _on_connection_change(self, connection, event, magicIdentifier): """ @@ -183,8 +257,10 @@ class DoneIt(object): if status == conic.STATUS_CONNECTED: self._deviceIsOnline = True + self._switch_ui(self._defaultUIName) elif status == conic.STATUS_DISCONNECTED: self._deviceIsOnline = False + self._switch_ui(self._fallbackUIName) def _on_window_state_change(self, widget, event, *args): """ @@ -196,11 +272,11 @@ class DoneIt(object): self._isFullScreen = False def _on_close(self, *args, **kwds): - if self._osso is not None: - self._osso.close() - try: - pass + if self._osso is not None: + self._osso.close() + + self._save_settings() finally: gtk.main_quit() @@ -217,6 +293,10 @@ class DoneIt(object): else: self.__window.fullscreen() + def _on_logout(self, *args): + self._todoUI.logout() + self._switch_ui(self._fallbackUIName) + def _on_about_activate(self, *args): dlg = gtk.AboutDialog() dlg.set_name(self.__pretty_app_name__) diff --git a/src/gtk_null.py b/src/gtk_null.py index 04b537c..ab33b0b 100644 --- a/src/gtk_null.py +++ b/src/gtk_null.py @@ -7,40 +7,37 @@ class GtkNull(object): """ @note Thread agnostic """ - self._todoItemTree = widgetTree.get_widget("todoItemTree") - self._todoDetailsTree = widgetTree.get_widget("todoDetailsTree") - self._todoDetailsScroll = widgetTree.get_widget("todoDetailsScroll") + self._projectsCombo = widgetTree.get_widget("projectsCombo") + self._addTaskButton = widgetTree.get_widget("add-addTaskButton") - self._completeButton = widgetTree.get_widget("completeButton") - self._editButton = widgetTree.get_widget("editButton") - self._addButton = widgetTree.get_widget("addButton") - - self._manager = None + self._manager = null.NullManager("", "") @staticmethod def name(): return "None" + def load_settings(self, config): + pass + + def save_settings(self, config): + pass + + def login(self): + pass + + def logout(self): + pass + def enable(self): """ @note UI Thread """ - self._manager = null.NullManager("", "") - - self._todoDetailsScroll.hide() - - self._completeButton.set_sensitive(False) - self._editButton.set_sensitive(False) - self._addButton.set_sensitive(False) + self._projectsCombo.set_sensitive(False) + self._addTaskButton.set_sensitive(False) def disable(self): """ @note UI Thread """ - self._todoDetailsScroll.hide() - - self._completeButton.set_sensitive(True) - self._editButton.set_sensitive(True) - self._addButton.set_sensitive(True) - - self._manager = None + self._projectsCombo.set_sensitive(True) + self._addTaskButton.set_sensitive(True) diff --git a/src/gtk_rtmilk.py b/src/gtk_rtmilk.py index 54508e7..bab46aa 100644 --- a/src/gtk_rtmilk.py +++ b/src/gtk_rtmilk.py @@ -1,6 +1,7 @@ import webbrowser import datetime import urlparse +import base64 import gobject import gtk @@ -54,11 +55,11 @@ def get_token(username, apiKey, secret): return token -def start_session(credentialsDialog): +def get_credentials(credentialsDialog): # @todo Figure out storage of credentials username, password = credentialsDialog.request_credentials() token = get_token(username, rtmilk.RtMilkManager.API_KEY, rtmilk.RtMilkManager.SECRET) - return rtmilk.RtMilkManager(username, password, token) + return username, password, token class GtkRtMilk(object): @@ -138,19 +139,61 @@ class GtkRtMilk(object): self._onClearId = None self._onPasteId = None - self._credentials = gtk_toolbox.LoginWindow(widgetTree) + self._credentialsDialog = gtk_toolbox.LoginWindow(widgetTree) + self._credentials = "", "", "" self._manager = None @staticmethod def name(): return "Remember The Milk" - def enable(self): + def load_settings(self, config): + """ + @note Thread Agnostic + """ + username = config.get(self.name(), "username") + password = None + blobbedToken = config.get(self.name(), "bin_blob") + token = base64.b64decode(blobbedToken) + self._credentials = username, password, token + + def save_settings(self, config): + """ + @note Thread Agnostic + """ + config.add_section(self.name()) + config.set(self.name(), "username", self._credentials[0]) + blobbedToken = base64.b64encode(self._credentials[2]) + config.set(self.name(), "bin_blob", blobbedToken) + + def login(self): """ @note UI Thread """ - self._manager = start_session(self._credentials) + if self._manager is not None: + return + + credentials = self._credentials + while True: + try: + self._manager = rtmilk.RtMilkManager(*credentials) + self._credentials = credentials + return # Login succeeded + except rtmapi.AuthStateMachine.NoData: + # Login failed, grab new credentials + credentials = get_credentials(self._credentialsDialog) + + def logout(self): + """ + @note Thread Agnostic + """ + self._credentials = "", "", "" + self._manager = None + def enable(self): + """ + @note UI Thread + """ self._projectsList.clear() self._populate_projects()