def __init__(self, parent): Gtk.Dialog.__init__(self) self.set_default_size(420, 400) self.set_transient_for(parent) self.set_title(_("Terms of Use")) # buttons self.add_button(_("Decline"), Gtk.ResponseType.NO) self.add_button(_("Accept"), Gtk.ResponseType.YES) # label self.label = Gtk.Label(_(u"One moment, please\u2026")) self.label.show() # add the label box = self.get_action_area() box.pack_start(self.label, False, False, 0) box.set_child_secondary(self.label, True) # hrm, hrm, there really should be a better way for itm in box.get_children(): if itm.get_label() == _("Accept"): self.button_accept = itm break self.button_accept.set_sensitive(False) # webkit wb = ScrolledWebkitWindow() wb.show_all() self.webkit = wb.webkit self.webkit.connect("notify::load-status", self._on_load_status_changed) # content content = self.get_content_area() self.spinner = SpinnerNotebook(wb) self.spinner.show_all() content.pack_start(self.spinner, True, True, 0)
def __init__(self, parent): Gtk.Dialog.__init__(self) self.set_default_size(420, 400) self.set_transient_for(parent) self.set_title(_("Terms of Use")) # buttons self.add_button(_("Decline"), Gtk.ResponseType.NO) self.add_button(_("Accept"), Gtk.ResponseType.YES) # label self.label = Gtk.Label(_(u"One moment, please\u2026")) self.label.show() # add the label box = self.get_action_area() box.pack_start(self.label, False, False, 0) box.set_child_secondary(self.label, True) # hrm, hrm, there really should be a better way for itm in box.get_children(): if itm.get_label() == _("Accept"): self.button_accept = itm break self.button_accept.set_sensitive(False) # webkit wb = ScrolledWebkitWindow() wb.show_all() self.webkit = wb.webkit self.webkit.connect( "notify::load-status", self._on_load_status_changed) # content content = self.get_content_area() self.spinner = SpinnerNotebook(wb) self.spinner.show_all() content.pack_start(self.spinner, True, True, 0)
def init_view(self): if self.wk is None: self.wk = ScrolledWebkitWindow() #self.wk.webkit.connect("new-window-policy-decision-requested", # self._on_new_window) self.wk.webkit.connect("create-web-view", self._on_create_web_view) self.wk.webkit.connect("close-web-view", self._on_close_web_view) self.wk.webkit.connect("console-message", self._on_console_message) # a possible way to do IPC (script or title change) self.wk.webkit.connect("script-alert", self._on_script_alert) self.wk.webkit.connect("title-changed", self._on_title_changed) self.wk.webkit.connect("notify::load-status", self._on_load_status_changed) # unblock signal handlers if needed when showing the purchase webkit # view in case they were blocked after a previous purchase was # completed or canceled self._unblock_wk_handlers()
def _on_create_web_view(self, view, frame): win = Gtk.Window() win.set_size_request(400, 400) wk = ScrolledWebkitWindow(include_progress_ui=True) wk.webkit.connect("close-web-view", self._on_close_web_view) win.add(wk) win.show_all() # make sure close will work later wk.webkit.set_data("win", win) # find and set parent w = self.wk.get_parent() while w.get_parent(): w = w.get_parent() win.set_transient_for(w) return wk.webkit
class PurchaseView(Gtk.VBox): """ View that displays the webkit-based UI for purchasing an item. """ LOADING_HTML = """ <html> <head> <title></title> </head> <style type="text/css"> html { background: #fff; color: #000; font: sans-serif; font: caption; text-align: center; position: absolute; top: 0; bottom: 0; width: 100%%; height: 100%%; display: table; } body { display: table-cell; vertical-align: middle; } h1 { background: url(file:///usr/share/software-center/images/spinner.gif) top \ center no-repeat; padding-top: 48px; /* leaves room for the spinner above */ font-size: 100%%; font-weight: normal; } </style> <body> <h1>%s</h1> </body> </html> """ % _("Connecting to payment service...") __gsignals__ = { 'purchase-succeeded': (GObject.SignalFlags.RUN_LAST, None, ()), 'purchase-failed': (GObject.SignalFlags.RUN_LAST, None, ()), 'purchase-cancelled-by-user': (GObject.SignalFlags.RUN_LAST, None, ()), 'terms-of-service-declined': (GObject.SignalFlags.RUN_LAST, None, ()), 'purchase-needs-spinner': (GObject.SignalFlags.RUN_LAST, None, (bool, )), } def __init__(self): GObject.GObject.__init__(self) self.wk = None self._wk_handlers_blocked = False self._oauth_token = None self.config = get_config() def init_view(self): if self.wk is None: self.wk = ScrolledWebkitWindow() # automatically fill in the email in the login page email = "" if self.config.has_option("general", "email"): email = self.config.get("general", "email") self.wk.webkit.set_auto_insert_email(email) #self.wk.webkit.connect("new-window-policy-decision-requested", # self._on_new_window) self.wk.webkit.connect("create-web-view", self._on_create_web_view) self.wk.webkit.connect("close-web-view", self._on_close_web_view) self.wk.webkit.connect("console-message", self._on_console_message) # a possible way to do IPC (script or title change) self.wk.webkit.connect("script-alert", self._on_script_alert) self.wk.webkit.connect("title-changed", self._on_title_changed) self.wk.webkit.connect("notify::load-status", self._on_load_status_changed) # unblock signal handlers if needed when showing the purchase webkit # view in case they were blocked after a previous purchase was # completed or canceled self._unblock_wk_handlers() def _ask_for_tos_acceptance_if_needed(self): try: accepted_tos = self.config.getboolean("general", "accepted_tos") except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): accepted_tos = False if not accepted_tos: # show the dialog and ensure the user accepts it res = show_accept_tos_dialog(get_parent(self)) if not res: return False self.config.set("general", "accepted_tos", "yes") return True return True def initiate_purchase(self, app, iconname, url=None, html=None): """ initiates the purchase workflow inside the embedded webkit window for the item specified """ if not self._ask_for_tos_acceptance_if_needed(): self.emit("terms-of-service-declined") return False self.init_view() self.app = app self.iconname = iconname self.wk.webkit.load_html_string(self.LOADING_HTML, "file:///") self.wk.show() while Gtk.events_pending(): Gtk.main_iteration() if url: self.wk.webkit.load_uri(url) elif html: self.wk.webkit.load_html_string(html, "file:///") else: self.wk.webkit.load_html_string(DUMMY_HTML, "file:///") self.pack_start(self.wk, True, True, 0) # only for debugging if os.environ.get("SOFTWARE_CENTER_DEBUG_BUY"): GObject.timeout_add_seconds(1, _generate_events, self) return True def _on_new_window(self, view, frame, request, action, policy): LOG.debug("_on_new_window") import subprocess subprocess.Popen(['xdg-open', request.get_uri()]) return True def _on_close_web_view(self, view): win = view.get_data("win") win.destroy() return True def _on_create_web_view(self, view, frame): win = Gtk.Window() win.set_size_request(400, 400) wk = ScrolledWebkitWindow(include_progress_ui=True) wk.webkit.connect("close-web-view", self._on_close_web_view) win.add(wk) win.show_all() # make sure close will work later wk.webkit.set_data("win", win) # find and set parent w = self.wk.get_parent() while w.get_parent(): w = w.get_parent() win.set_transient_for(w) return wk.webkit def _on_console_message(self, view, message, line, source_id): try: # load the token from the console message self._oauth_token = json.loads(message) # compat with the regular oauth naming self._oauth_token["token"] = self._oauth_token["token_key"] except ValueError: pass for k in ["token_key", "token_secret", "consumer_secret"]: if k in message: LOG.debug( "skipping console message that contains sensitive data") return True LOG.debug("_on_console_message '%s'" % message) return False def _on_script_alert(self, view, frame, message): self._process_json(message) # stop further processing to avoid actually showing the alter return True def _on_title_changed(self, view, frame, title): #print "on_title_changed", view, frame, title # see wkwidget.py _on_title_changed() for a code example self._process_json(title) def _on_load_status_changed(self, view, property_spec): """ helper to give visual feedback while the page is loading """ prop = view.get_property(property_spec.name) window = self.get_window() if prop == webkit.LoadStatus.PROVISIONAL: self.emit("purchase-needs-spinner", True) if window: window.set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH)) elif (prop == webkit.LoadStatus.FIRST_VISUALLY_NON_EMPTY_LAYOUT or prop == webkit.LoadStatus.FAILED or prop == webkit.LoadStatus.FINISHED): self.emit("purchase-needs-spinner", False) if window: window.set_cursor(None) def _process_json(self, json_string): try: LOG.debug("server returned: '%s'" % json_string) res = json.loads(json_string) #print res except: LOG.debug("error processing json: '%s'" % json_string) return if res["successful"] == False: if (res.get("user_canceled", False) or # note the different spelling res.get("user_cancelled", False) or # COMPAT with older clients that do not send the user # canceled property (LP: #696861), this msg appears # to be not translated "CANCELLED" in res.get("failures", "")): self.emit("purchase-cancelled-by-user") self._block_wk_handlers() return # this is what the agent implements elif "failures" in res: LOG.error("the server returned a error: '%s'" % res["failures"]) # show a generic error, the "failures" string we get from the # server is way too technical to show, but we do log it self.emit("purchase-failed") self._block_wk_handlers() return else: self.emit("purchase-succeeded") self._block_wk_handlers() # gather data from response deb_line = res["deb_line"] signing_key_id = res["signing_key_id"] license_key = res.get("license_key") license_key_path = res.get("license_key_path") # add repo and key backend = get_install_backend() backend.add_repo_add_key_and_install_app( deb_line, signing_key_id, self.app, self.iconname, license_key, license_key_path, json.dumps(self._oauth_token)) def _block_wk_handlers(self): # we need to block webkit signal handlers when we hide the # purchase webkit view, this prevents e.g. handling of signals on # title_change on reloads (see LP: #696861) if not self._wk_handlers_blocked: self.wk.webkit.handler_block_by_func(self._on_script_alert) self.wk.webkit.handler_block_by_func(self._on_title_changed) self.wk.webkit.handler_block_by_func(self._on_load_status_changed) self._wk_handlers_blocked = True def _unblock_wk_handlers(self): if self._wk_handlers_blocked: self.wk.webkit.handler_unblock_by_func(self._on_script_alert) self.wk.webkit.handler_unblock_by_func(self._on_title_changed) self.wk.webkit.handler_unblock_by_func( self._on_load_status_changed) self._wk_handlers_blocked = False
class PurchaseView(Gtk.VBox): """ View that displays the webkit-based UI for purchasing an item. """ LOADING_HTML = """ <html> <head> <title></title> </head> <style type="text/css"> html { background: #fff; color: #000; font: sans-serif; font: caption; text-align: center; position: absolute; top: 0; bottom: 0; width: 100%%; height: 100%%; display: table; } body { display: table-cell; vertical-align: middle; } h1 { background: url(file:///usr/share/software-center/images/spinner.gif) top \ center no-repeat; padding-top: 48px; /* leaves room for the spinner above */ font-size: 100%%; font-weight: normal; } </style> <body> <h1>%s</h1> </body> </html> """ % _("Connecting to payment service...") __gsignals__ = { 'purchase-succeeded': (GObject.SignalFlags.RUN_LAST, None, ()), 'purchase-failed': (GObject.SignalFlags.RUN_LAST, None, ()), 'purchase-cancelled-by-user': (GObject.SignalFlags.RUN_LAST, None, ()), 'terms-of-service-declined': (GObject.SignalFlags.RUN_LAST, None, ()), 'purchase-needs-spinner': (GObject.SignalFlags.RUN_LAST, None, (bool, )), } def __init__(self): GObject.GObject.__init__(self) self.wk = None self._wk_handlers_blocked = False self._oauth_token = None self.config = get_config() def init_view(self): if self.wk is None: self.wk = ScrolledWebkitWindow() #self.wk.webkit.connect("new-window-policy-decision-requested", # self._on_new_window) self.wk.webkit.connect("create-web-view", self._on_create_web_view) self.wk.webkit.connect("close-web-view", self._on_close_web_view) self.wk.webkit.connect("console-message", self._on_console_message) # a possible way to do IPC (script or title change) self.wk.webkit.connect("script-alert", self._on_script_alert) self.wk.webkit.connect("title-changed", self._on_title_changed) self.wk.webkit.connect("notify::load-status", self._on_load_status_changed) # unblock signal handlers if needed when showing the purchase webkit # view in case they were blocked after a previous purchase was # completed or canceled self._unblock_wk_handlers() def _ask_for_tos_acceptance_if_needed(self): try: accepted_tos = self.config.getboolean("general", "accepted_tos") except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): accepted_tos = False if not accepted_tos: # show the dialog and ensure the user accepts it res = show_accept_tos_dialog(get_parent(self)) if not res: return False self.config.set("general", "accepted_tos", "yes") return True return True def initiate_purchase(self, app, iconname, url=None, html=None): """ initiates the purchase workflow inside the embedded webkit window for the item specified """ if not self._ask_for_tos_acceptance_if_needed(): self.emit("terms-of-service-declined") return False self.init_view() self.app = app self.iconname = iconname self.wk.webkit.load_html_string(self.LOADING_HTML, "file:///") self.wk.show() while Gtk.events_pending(): Gtk.main_iteration() if url: self.wk.webkit.load_uri(url) elif html: self.wk.webkit.load_html_string(html, "file:///") else: self.wk.webkit.load_html_string(DUMMY_HTML, "file:///") self.pack_start(self.wk, True, True, 0) # only for debugging if os.environ.get("SOFTWARE_CENTER_DEBUG_BUY"): GObject.timeout_add_seconds(1, _generate_events, self) return True def _on_new_window(self, view, frame, request, action, policy): LOG.debug("_on_new_window") import subprocess subprocess.Popen(['xdg-open', request.get_uri()]) return True def _on_close_web_view(self, view): win = view.get_data("win") win.destroy() return True def _on_create_web_view(self, view, frame): win = Gtk.Window() win.set_size_request(400, 400) wk = ScrolledWebkitWindow(include_progress_ui=True) wk.webkit.connect("close-web-view", self._on_close_web_view) win.add(wk) win.show_all() # make sure close will work later wk.webkit.set_data("win", win) # find and set parent w = self.wk.get_parent() while w.get_parent(): w = w.get_parent() win.set_transient_for(w) return wk.webkit def _on_console_message(self, view, message, line, source_id): try: # load the token from the console message self._oauth_token = json.loads(message) # compat with the regular oauth naming self._oauth_token["token"] = self._oauth_token["token_key"] except ValueError: pass for k in ["token_key", "token_secret", "consumer_secret"]: if k in message: LOG.debug( "skipping console message that contains sensitive data") return True LOG.debug("_on_console_message '%s'" % message) return False def _on_script_alert(self, view, frame, message): self._process_json(message) # stop further processing to avoid actually showing the alter return True def _on_title_changed(self, view, frame, title): #print "on_title_changed", view, frame, title # see wkwidget.py _on_title_changed() for a code example self._process_json(title) def _on_load_status_changed(self, view, property_spec): """ helper to give visual feedback while the page is loading """ prop = view.get_property(property_spec.name) window = self.get_window() if prop == webkit.LoadStatus.PROVISIONAL: self.emit("purchase-needs-spinner", True) if window: window.set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH)) elif (prop == webkit.LoadStatus.FIRST_VISUALLY_NON_EMPTY_LAYOUT or prop == webkit.LoadStatus.FAILED or prop == webkit.LoadStatus.FINISHED): self.emit("purchase-needs-spinner", False) if window: window.set_cursor(None) def _process_json(self, json_string): try: LOG.debug("server returned: '%s'" % json_string) res = json.loads(json_string) #print res except: LOG.debug("error processing json: '%s'" % json_string) return if res["successful"] == False: if (res.get("user_canceled", False) or # note the different spelling res.get("user_cancelled", False) or # COMPAT with older clients that do not send the user # canceled property (LP: #696861), this msg appears # to be not translated "CANCELLED" in res.get("failures", "")): self.emit("purchase-cancelled-by-user") self._block_wk_handlers() return # this is what the agent implements elif "failures" in res: LOG.error("the server returned a error: '%s'" % res["failures"]) # show a generic error, the "failures" string we get from the # server is way too technical to show, but we do log it self.emit("purchase-failed") self._block_wk_handlers() return else: self.emit("purchase-succeeded") self._block_wk_handlers() # gather data from response deb_line = res["deb_line"] signing_key_id = res["signing_key_id"] license_key = res.get("license_key") license_key_path = res.get("license_key_path") # add repo and key backend = get_install_backend() backend.add_repo_add_key_and_install_app( deb_line, signing_key_id, self.app, self.iconname, license_key, license_key_path, json.dumps(self._oauth_token)) def _block_wk_handlers(self): # we need to block webkit signal handlers when we hide the # purchase webkit view, this prevents e.g. handling of signals on # title_change on reloads (see LP: #696861) if not self._wk_handlers_blocked: self.wk.webkit.handler_block_by_func(self._on_script_alert) self.wk.webkit.handler_block_by_func(self._on_title_changed) self.wk.webkit.handler_block_by_func(self._on_load_status_changed) self._wk_handlers_blocked = True def _unblock_wk_handlers(self): if self._wk_handlers_blocked: self.wk.webkit.handler_unblock_by_func(self._on_script_alert) self.wk.webkit.handler_unblock_by_func(self._on_title_changed) self.wk.webkit.handler_unblock_by_func( self._on_load_status_changed) self._wk_handlers_blocked = False