Esempio n. 1
0
    def next_step(self):
        # We already sent the details, but may still be on the same step.
        if self.wizard.tef_request_done:
            return StoqAdminPasswordStep(self.wizard, self.previous)

        webapi = WebService()
        response = webapi.tef_request(self.model.name, self.model.email,
                                      self.model.phone)
        response.addCallback(self._on_response_done)
        response.addErrback(self._on_response_error)

        # FIXME: This is a hack, remove it when we can avoid
        #        calling dialog.run()
        if not reactor.running:
            reactor.run()

        self.send_progress.show()
        self.send_progress.set_text(_('Sending...'))
        self.send_progress.set_pulse_step(0.05)
        self.details_table.set_sensitive(False)
        self.wizard.next_button.set_sensitive(False)
        glib.timeout_add(50, self._pulse)

        # Cancel the request after 30 seconds without a reply
        glib.timeout_add(30000, self._cancel_request)

        # Stay on the same step while sending the details
        return self
Esempio n. 2
0
    def download_plugin(self, plugin_name):
        """Download a plugin from webservice

        :param plugin_name: the name of the plugin to download
        :returns: a deferred
        """
        from stoqlib.lib.webservice import WebService

        def callback(filename):
            md5sum = unicode(md5sum_for_filename(filename))
            with open(filename) as f:
                with new_store() as store:
                    existing_egg = store.find(PluginEgg,
                                              plugin_name=plugin_name).one()
                    if existing_egg is not None:
                        existing_egg.egg_content = f.read()
                        existing_egg.md5sum = md5sum
                    else:
                        PluginEgg(
                            store=store,
                            plugin_name=plugin_name,
                            egg_md5sum=md5sum,
                            egg_content=f.read(),
                        )

            self._reload()

        default_store = get_default_store()
        existing_egg = default_store.find(PluginEgg,
                                          plugin_name=plugin_name).one()
        md5sum = existing_egg and existing_egg.egg_md5sum

        webapi = WebService()
        return webapi.download_plugin(plugin_name, callback=callback,
                                      md5sum=md5sum)
Esempio n. 3
0
    def next_step(self):
        # We already sent the details, but may still be on the same step.
        # Also, if the user didn't choose to "register now", respect his
        # decision
        if not self.model.register_now or self.wizard.link_request_done:
            return FinishInstallationStep(self.wizard)

        webapi = WebService()
        response = webapi.link_registration(
            self.model.name, self.model.email, self.model.phone)
        response.addCallback(self._on_response_done)
        response.addErrback(self._on_response_error)

        # FIXME: This is a hack, remove it when we can avoid
        #        calling dialog.run()
        if not reactor.running:
            reactor.run()

        self.send_progress.show()
        self.send_progress.set_text(_('Sending...'))
        self.send_progress.set_pulse_step(0.05)
        self.wizard.next_button.set_sensitive(False)
        glib.timeout_add(50, self._pulse)

        # Cancel the request after 30 seconds without a reply
        glib.timeout_add(30000, self._cancel_request)

        # Stay on the same step while sending the details
        return self
Esempio n. 4
0
    def download_plugin(self, plugin_name):
        """Download a plugin from webservice

        :param plugin_name: the name of the plugin to download
        :returns: a deferred
        """
        from stoqlib.lib.webservice import WebService

        default_store = get_default_store()
        existing_egg = default_store.find(PluginEgg,
                                          plugin_name=plugin_name).one()
        md5sum = existing_egg and existing_egg.egg_md5sum
        webapi = WebService()
        r = webapi.download_plugin(plugin_name, md5sum=md5sum)

        try:
            response = r.get_response()
        except Exception as e:
            return False, _("Failed to do the request: %s" % (e, ))

        code = response.status_code
        if code == 204:
            msg = _("No update needed. The plugin is already up to date.")
            log.info(msg)
            return True, msg

        if code != 200:
            return_messages = {
                400: _("Plugin not available for this stoq version"),
                401: _("The instance is not authorized to download the plugin"),
                404: _("Plugin does not exist"),
                405: _("This instance has not acquired the specified plugin"),
            }
            msg = return_messages.get(code, str(code))
            log.warning(msg)
            return False, msg

        content = response.content
        md5sum = unicode(hashlib.md5(content).hexdigest())
        with new_store() as store:
            existing_egg = store.find(PluginEgg,
                                      plugin_name=plugin_name).one()
            if existing_egg is not None:
                existing_egg.egg_content = content
                existing_egg.egg_md5sum = md5sum
            else:
                PluginEgg(
                    store=store,
                    plugin_name=plugin_name,
                    egg_md5sum=md5sum,
                    egg_content=content,
                )

        self._reload()
        return True, _("Plugin download successful")
Esempio n. 5
0
    def validate_confirm(self):
        if not self._can_submit_feedback():
            return False

        webapi = WebService()
        d = webapi.feedback(self.application_screen,
                            self.model.email,
                            self.model.feedback)
        d.addCallback(self._on_feedback_reply)
        self.disable_ok()
        return False
Esempio n. 6
0
    def _check_information(self):
        """Check some information with Stoq Web API

        - Check if there are new versions of Stoq Available
        - Check if this Stoq Instance uses Stoq Link (and send data to us if
          it does).
        """
        # Check version
        self._version_checker = VersionChecker(self.store, self)
        self._version_checker.check_new_version()
        if not api.sysparam.get_bool('ONLINE_SERVICES'):
            return
        # Check Stoq Link usage
        webapi = WebService()
        webapi.link_update(self.store)
Esempio n. 7
0
    def download_plugin(self, plugin_name):
        """Download a plugin from webservice

        :param plugin_name: the name of the plugin to download
        :returns: a deferred
        """
        from stoqlib.lib.webservice import WebService

        def callback(filename):
            md5sum = unicode(md5sum_for_filename(filename))
            with open(filename) as f:
                with new_store() as store:
                    PluginEgg(
                        store=store,
                        plugin_name=plugin_name,
                        egg_md5sum=md5sum,
                        egg_content=f.read(),
                    )
            self._reload()

        webapi = WebService()
        return webapi.download_plugin(plugin_name, callback=callback)
Esempio n. 8
0
    def next_step(self):
        # We already sent the details, but may still be on the same step.
        # Also, if the user didn't choose to "register now", respect his
        # decision
        if not self.model.register_now or self.wizard.link_request_done:
            return FinishInstallationStep(self.wizard)

        webapi = WebService()
        webapi.link_registration(
            self.model.name, self.model.email, self.model.phone,
            callback=lambda r: schedule_in_main_thread(self._on_response_done, r),
            errback=lambda e: schedule_in_main_thread(self._on_response_error, e))

        self.send_progress.show()
        self.send_progress.set_text(_('Sending...'))
        self.send_progress.set_pulse_step(0.05)
        self.wizard.next_button.set_sensitive(False)
        GLib.timeout_add(50, self._pulse)

        # Cancel the request after 30 seconds without a reply
        GLib.timeout_add(30000, self._cancel_request)

        # Stay on the same step while sending the details
        return self
Esempio n. 9
0
    class ReportSubmitter(GObject.GObject):
        gsignal('failed', object)
        gsignal('submitted', object)

        def __init__(self):
            GObject.GObject.__init__(self)

            self._count = 0
            self._api = WebService()
            self.report = collect_report()

        def _done(self, args):
            self.emit('submitted', args)

        def _error(self, args):
            self.emit('failed', args)

        def submit(self):
            return self._api.bug_report(self.report,
                                        callback=self._on_report__callback,
                                        errback=self._on_report__errback)

        def _on_report__callback(self, response):
            if response.status_code == 200:
                self._on_success(response.json())
            else:
                self._on_error()

        def _on_report__errback(self, failure):
            self._on_error(failure)

        def _on_error(self, data=None):
            log.info('Failed to report bug: %r count=%d' % (data, self._count))
            if self._count < _N_TRIES:
                self.submit()
            else:
                schedule_in_main_thread(self.emit, 'failed', data)
            self._count += 1

        def _on_success(self, data):
            log.info('Finished sending bugreport: %r' % (data, ))
            schedule_in_main_thread(self.emit, 'submitted', data)
Esempio n. 10
0
class ReportSubmitter(gobject.GObject):
    gsignal("failed", object)
    gsignal("submitted", object)

    def __init__(self):
        gobject.GObject.__init__(self)

        self._count = 0
        self._api = WebService()
        self.report = collect_report()

    def _done(self, args):
        self.emit("submitted", args)

    def _error(self, args):
        self.emit("failed", args)

    def submit(self):
        return self._api.bug_report(self.report, callback=self._on_report__callback, errback=self._on_report__errback)

    def _on_report__callback(self, response):
        if response.status_code == 200:
            self._on_success(response.json())
        else:
            self._on_error()

    def _on_report__errback(self, failure):
        self._on_error(failure)

    def _on_error(self, data=None):
        log.info("Failed to report bug: %r count=%d" % (data, self._count))
        if self._count < _N_TRIES:
            self.submit()
        else:
            schedule_in_main_thread(self.emit, "failed", data)
        self._count += 1

    def _on_success(self, data):
        log.info("Finished sending bugreport: %r" % (data,))
        schedule_in_main_thread(self.emit, "submitted", data)
Esempio n. 11
0
class ReportSubmitter(gobject.GObject):
    gsignal("failed", object)
    gsignal("submitted", object)

    def __init__(self):
        gobject.GObject.__init__(self)

        self._api = WebService()
        self._report = collect_report()
        self._count = 0

    def _done(self, args):
        self.emit("submitted", args)

    def _error(self, args):
        self.emit("failed", args)

    @property
    def report(self):
        return self._report

    def submit(self):
        response = self._api.bug_report(self._report)
        response.addCallback(self._on_report__callback)
        response.addErrback(self._on_report__errback)
        return response

    def _on_report__callback(self, data):
        log.info("Finished sending bugreport: %r" % (data,))
        self._done(data)

    def _on_report__errback(self, failure):
        log.info("Failed to report bug: %r count=%d" % (failure, self._count))
        if self._count < _N_TRIES:
            self.submit()
        else:
            self._error(failure)
        self._count += 1
Esempio n. 12
0
class ReportSubmitter(gobject.GObject):
    gsignal('failed', object)
    gsignal('submitted', object)

    def __init__(self):
        gobject.GObject.__init__(self)

        self._api = WebService()
        self._report = collect_report()
        self._count = 0

    def _done(self, args):
        self.emit('submitted', args)

    def _error(self, args):
        self.emit('failed', args)

    @property
    def report(self):
        return self._report

    def submit(self):
        response = self._api.bug_report(self._report)
        response.addCallback(self._on_report__callback)
        response.addErrback(self._on_report__errback)
        return response

    def _on_report__callback(self, data):
        log.info('Finished sending bugreport: %r' % (data, ))
        self._done(data)

    def _on_report__errback(self, failure):
        log.info('Failed to report bug: %r count=%d' % (failure, self._count))
        if self._count < _N_TRIES:
            self.submit()
        else:
            self._error(failure)
        self._count += 1
Esempio n. 13
0
 def __init__(self):
     ResourceStatus.__init__(self)
     self._webservice = WebService()
     self._server = ServerProxy()
Esempio n. 14
0
class _BackupStatus(ResourceStatus):

    name = "backup"
    label = _("Backup")
    priority = 98

    def __init__(self):
        ResourceStatus.__init__(self)
        self._webservice = WebService()
        self._server = ServerProxy()

    @threaded
    def _get_key(self):
        return self._server.call('get_backup_key')

    @threaded
    def _get_server_status(self):
        request = self._webservice.status()
        return request.get_response()

    def refresh(self):
        if stoq.trial_mode:
            self.status = ResourceStatus.STATUS_NA
            self.reason = (_('Online features are not available in trial mode'))
            self.reason_long = _('Online features require a subscription of Stoq.link')
            return

        if not api.sysparam.get_bool('ONLINE_SERVICES'):
            self.status = ResourceStatus.STATUS_NA
            self.reason = _('Backup service not running because '
                            '"Online Services" is disabled')
            self.reason_long = _('Enable the parameter "Online Services" '
                                 'on the "Admin" app to solve this issue')
            return

        try:
            key = self._get_key()
        except ServerError:
            pass
        else:
            if not key:
                self.status = self.STATUS_WARNING
                self.reason = _("Backup key not configured")
                self.reason_long = _('Click on "Configure" button to '
                                     'configure the backup key')
                return

        try:
            response = self._get_server_status()
        except Exception as e:
            self.status = self.STATUS_WARNING
            self.reason = _("Could not communicate with Stoq.link")
            self.reason_long = str(e)
            return

        if response.status_code != 200:
            self.status = self.STATUS_WARNING
            self.reason = _("Could not communicate with Stoq.link")
            self.reason_long = None
            return

        data = response.json()
        if data['latest_backup_date']:
            backup_date = dateutil.parser.parse(data['latest_backup_date'])
            delta = datetime.datetime.today() - backup_date

            if delta.days > 3:
                self.status = self.STATUS_WARNING
                self.reason = _("Backup is late. Last backup date is %s") % (
                    backup_date.strftime('%x'))
                self.reason_long = _("Check your Stoq Server logs to see if "
                                     "there's any problem with it")
            else:
                self.status = self.STATUS_OK
                self.reason = _("Backup is up-to-date. Last backup date is %s") % (
                    backup_date.strftime('%x'))
                self.reason_long = None
        else:
            self.status = self.STATUS_WARNING
            self.reason = _("There's no backup data yet")
            self.reason_long = None

    def get_actions(self):
        if self.status != ResourceStatus.STATUS_NA:
            yield ResourceStatusAction(
                self, 'backup-now',
                _("Backup now"), self._on_backup_now, threaded=True)
            yield ResourceStatusAction(
                self, 'configure',
                _("Configure"), self._on_configure, threaded=False)

    def _on_configure(self):
        key = self._server.call('get_backup_key')

        with api.new_store() as store:
            rv = run_dialog(BackupSettingsEditor, None,
                            store, Settable(key=key))

        if rv:
            key = self._server.call('set_backup_key', rv.key)

    def _on_backup_now(self):
        self._server.call('backup_database')
Esempio n. 15
0
 def _download_details(self):
     log.debug('Downloading new version information')
     webapi = WebService()
     response = webapi.version(self.store, stoq.version)
     response.addCallback(self._on_response_done)
Esempio n. 16
0
 def _setup_stoq_link(self):
     from stoqlib.domain.events import SaleStatusChangedEvent
     from stoqlib.lib.webservice import WebService
     self._api = WebService()
     SaleStatusChangedEvent.connect(self._update_stoq_link)
Esempio n. 17
0
    def __init__(self):
        gobject.GObject.__init__(self)

        self._count = 0
        self._api = WebService()
        self.report = collect_report()
Esempio n. 18
0
 def _download_details(self):
     log.debug('Downloading new version information')
     webapi = WebService()
     webapi.version(self.store,
                    stoq.version,
                    callback=self._on_response_done)
Esempio n. 19
0
class ShellBootstrap(object):
    """Bootstraps the Stoq application, it's responsible for:
    - Setting up log files
    - Checking dependencies
    - Setting up libraries (gobject, gtk, kiwi, twisted)
    - Checking version
    - Locale
    - User settings and keybindings

    When this is completed Stoq is ready to connect to a database.
    """
    def __init__(self, options, initial):
        self._initial = initial
        self._log_filename = None
        self._options = options
        self.entered_main = False
        self.stream = None

    def bootstrap(self):
        self._setup_gobject()
        self._set_uptime()
        # Do this as soon as possible, before we attempt to use the
        # external libraries/resources
        self._set_user_locale()
        # Do this as early as possible to get as much as possible into the
        # log file itself, which means we cannot depend on the config or
        # anything else
        self._prepare_logfiles()
        self._set_app_info()
        self._check_dependencies()
        self._setup_exception_hook()
        self._setup_gtk()
        self._setup_kiwi()
        self._show_splash()
        self._setup_twisted()
        self._setup_psycopg()
        self._check_version_policy()
        self._setup_ui_dialogs()
        self._setup_cookiefile()
        self._register_stock_icons()
        self._setup_domain_slave_mapper()
        self._load_key_bindings()
        self._setup_debug_options()
        self._check_locale()
        self._setup_autoreload()
        self._setup_stoq_link()

    def _setup_gobject(self):
        if not self._initial:
            return
        assert not 'gobject' in sys.modules
        assert not 'gtk' in sys.modules

        if 'STOQ_USE_GI' in os.environ:
            from stoq.lib import gicompat
            gicompat.enable()

        import gobject
        gobject.threads_init()

    def _set_uptime(self):
        from stoqlib.lib.uptime import set_initial
        set_initial()

    def _set_user_locale(self):
        from stoqlib.lib.settings import get_settings
        from stoqlib.lib.translation import stoqlib_gettext as _

        self._locale_error = None
        settings = get_settings()
        lang = settings.get('user-locale', None)
        if not lang:
            return

        lang += '.UTF-8'
        try:
            locale.setlocale(locale.LC_ALL, lang)
        except locale.Error as err:
            msg = _("Could not set locale to %s. Make sure that you have "
                    "the packages for this locale installed.") % lang[:-6]
            self._locale_error = (msg, err)
            log.warning(msg)
        else:
            os.environ['LC_ALL'] = lang
            os.environ['LANGUAGE'] = lang

    def _setup_autoreload(self):
        if not self._options.autoreload:
            return

        from stoqlib.lib.autoreload import install_autoreload
        install_autoreload()

    def _setup_stoq_link(self):
        from stoqlib.domain.events import SaleStatusChangedEvent
        from stoqlib.lib.webservice import WebService
        self._api = WebService()
        SaleStatusChangedEvent.connect(self._update_stoq_link)

    def _update_stoq_link(self, sale, old_status):
        if sale.status != sale.STATUS_CONFIRMED:
            return
        self._api.link_update(sale.store)

    def _prepare_logfiles(self):
        from stoqlib.lib.osutils import get_application_dir

        stoqdir = get_application_dir("stoq")
        log_dir = os.path.join(stoqdir, 'logs', time.strftime('%Y'),
                               time.strftime('%m'))
        if not os.path.exists(log_dir):
            os.makedirs(log_dir)

        filename = 'stoq_%s.%s.log' % (time.strftime('%Y-%m-%d_%H-%M-%S'),
                                       os.getpid())
        self._log_filename = os.path.join(log_dir, filename)

        from kiwi.log import set_log_file
        self._stream = set_log_file(self._log_filename, 'stoq*')

        if hasattr(os, 'symlink'):
            link_file = os.path.join(stoqdir, 'stoq.log')
            if os.path.exists(link_file):
                os.unlink(link_file)
            os.symlink(self._log_filename, link_file)

        # We want developers to see deprecation warnings.
        from stoqlib.lib.environment import is_developer_mode
        if is_developer_mode():
            import warnings
            if self._options.non_fatal_warnings:
                action = "default"
            else:
                action = "error"
            warnings.filterwarnings(action,
                                    category=DeprecationWarning,
                                    module="^(stoq|kiwi)")

    def _set_app_info(self):
        from kiwi.component import provide_utility
        from stoqlib.lib.appinfo import AppInfo
        from stoqlib.lib.kiwilibrary import library
        from stoqlib.lib.interfaces import IAppInfo
        import stoq
        # FIXME: use only stoq.stoq_version here and all other callsites of
        # IAppInfo
        stoq_version = stoq.version
        stoq_ver = stoq.stoq_version
        if hasattr(library, 'get_revision'):
            rev = library.get_revision()
            if rev is not None:
                stoq_version += ' ' + rev
                stoq_ver += (rev, )
        info = AppInfo()
        info.set("name", "Stoq")
        info.set("version", stoq_version)
        info.set("ver", stoq_ver)
        info.set("log", self._log_filename)
        provide_utility(IAppInfo, info)

    def _check_dependencies(self):
        from stoq.lib.dependencies import check_dependencies
        check_dependencies()

    def _setup_exception_hook(self):
        if self._options.debug:
            hook = self._debug_hook
        else:
            hook = self._write_exception_hook
        sys.excepthook = hook

    def _setup_gtk(self):
        import gtk
        from kiwi.environ import environ

        # Total madness to make sure we can draw treeview lines,
        # this affects the GtkTreeView::grid-line-pattern style property
        #
        # Two bytes are sent in, see gtk_tree_view_set_grid_lines in gtktreeview.c
        # Byte 1 should be as high as possible, gtk+ 0x7F appears to be
        #        the highest allowed for Gtk+ 2.22 while 0xFF worked in
        #        earlier versions
        # Byte 2 should ideally be allowed to be 0, but neither C nor Python
        #        allows that.
        #
        data = environ.get_resource_string("stoq", "misc", "stoq.gtkrc")
        data = data.replace('\\x7f\\x01', '\x7f\x01')

        gtk.rc_parse_string(data)

        # Creating a button as a temporary workaround for bug
        # https://bugzilla.gnome.org/show_bug.cgi?id=632538, until gtk 3.0
        gtk.Button()
        settings = gtk.settings_get_default()
        settings.props.gtk_button_images = True

        from stoqlib.lib.environment import is_developer_mode
        if is_developer_mode() and gtk.gtk_version[0] == 2:
            from gtk import gdk

            # Install a Control-Q handler that forcefully exits
            # the program without saving any kind of state
            def event_handler(event):
                if (event.type == gdk.KEY_PRESS
                        and event.state & gdk.CONTROL_MASK
                        and event.keyval == gtk.keysyms.q):
                    os._exit(0)
                gtk.main_do_event(event)

            gdk.event_handler_set(event_handler)

    def _setup_kiwi(self):
        from kiwi.datatypes import get_localeconv
        from kiwi.ui.widgets.label import ProxyLabel
        ProxyLabel.replace('$CURRENCY', get_localeconv()['currency_symbol'])

    def _show_splash(self):
        if not self._options.splashscreen:
            return
        from stoqlib.gui.widgets.splash import show_splash
        show_splash()

    def _setup_twisted(self, raise_=True):
        # FIXME: figure out why twisted is already loaded
        # assert not 'twisted' in sys.modules
        from stoqlib.net import gtk2reactor
        from twisted.internet.error import ReactorAlreadyInstalledError
        try:
            gtk2reactor.install()
        except ReactorAlreadyInstalledError:
            if self._initial and raise_:
                raise

    def _setup_psycopg(self):
        # This will only be required when we use uuid.UUID instances
        # for UUIDCol
        #from psycopg2.extras import register_uuid
        #register_uuid()
        return

    def _check_version_policy(self):
        # No need to bother version checking when not running in developer mode
        from stoqlib.lib.environment import is_developer_mode
        if not is_developer_mode():
            return

        import stoq

        #
        # Policies for stoq/stoqlib versions,
        # All these policies here are made so that stoqlib version is tightly
        # tied to the stoq versioning
        #

        # We reserve the first 89 for the stable series.
        FIRST_UNSTABLE_MICRO_VERSION = 90

        # Stable series of Stoq must:
        # 1) have extra_version set to < 90
        # 2) Depend on a stoqlib version with extra_version < 90
        #
        if stoq.stable:
            if (stoq.micro_version >= FIRST_UNSTABLE_MICRO_VERSION
                    and not 'rc' in stoq.extra_version):
                raise SystemExit(
                    "Stable stoq release should set micro_version to "
                    "%d or lower" % (FIRST_UNSTABLE_MICRO_VERSION, ))
        # Unstable series of Stoq must have:
        # 1) have extra_version set to >= 90
        # 2) Must depend stoqlib version with extra_version >= 90
        #
        else:
            if stoq.micro_version < FIRST_UNSTABLE_MICRO_VERSION:
                raise SystemExit(
                    "Unstable stoq (%s) must set micro_version to %d or higher, "
                    "or did you forget to set stoq.stable to True?" %
                    (stoq.version, FIRST_UNSTABLE_MICRO_VERSION))

    def _setup_ui_dialogs(self):
        # This needs to be here otherwise we can't install the dialog
        if 'STOQ_TEST_MODE' in os.environ:
            return
        log.debug('providing graphical notification dialogs')
        from stoqlib.gui.base.dialogs import DialogSystemNotifier
        from stoqlib.lib.interfaces import ISystemNotifier
        from kiwi.component import provide_utility
        provide_utility(ISystemNotifier, DialogSystemNotifier(), replace=True)

        import gtk
        from kiwi.environ import environ
        from kiwi.ui.pixbufutils import pixbuf_from_string
        data = environ.get_resource_string('stoq', 'pixmaps',
                                           'stoq-stock-app-24x24.png')
        gtk.window_set_default_icon(pixbuf_from_string(data))

        if platform.system() == 'Darwin':
            from AppKit import NSApplication, NSData, NSImage
            bytes = environ.get_resource_string('stoq', 'pixmaps',
                                                'stoq-stock-app-48x48.png')
            data = NSData.alloc().initWithBytes_length_(bytes, len(bytes))
            icon = NSImage.alloc().initWithData_(data)
            app = NSApplication.sharedApplication()
            app.setApplicationIconImage_(icon)

    def _setup_cookiefile(self):
        log.debug('setting up cookie file')
        from kiwi.component import provide_utility
        from stoqlib.lib.cookie import Base64CookieFile
        from stoqlib.lib.interfaces import ICookieFile
        from stoqlib.lib.osutils import get_application_dir
        app_dir = get_application_dir()
        cookiefile = os.path.join(app_dir, "cookie")
        provide_utility(ICookieFile, Base64CookieFile(cookiefile))

    def _register_stock_icons(self):
        from stoqlib.gui.stockicons import register

        log.debug('register stock icons')
        register()

    def _setup_domain_slave_mapper(self):
        from kiwi.component import provide_utility
        from stoqlib.gui.interfaces import IDomainSlaveMapper
        from stoqlib.gui.slaves.domainslavemapper import DefaultDomainSlaveMapper
        provide_utility(IDomainSlaveMapper,
                        DefaultDomainSlaveMapper(),
                        replace=True)

    def _load_key_bindings(self):
        from stoqlib.gui.utils.keybindings import load_user_keybindings
        load_user_keybindings()

    def _check_locale(self):
        if not self._locale_error:
            return

        from stoqlib.lib.message import warning
        warning(self._locale_error[0], str(self._locale_error[1]))

    def _setup_debug_options(self):
        if not self._options.debug:
            return
        from gtk import keysyms
        from stoqlib.gui.utils.introspection import introspect_slaves
        from stoqlib.gui.utils.keyboardhandler import install_global_keyhandler
        install_global_keyhandler(keysyms.F12, introspect_slaves)

    #
    # Global functions
    #

    def _debug_hook(self, exctype, value, tb):
        self._write_exception_hook(exctype, value, tb)
        traceback.print_exception(exctype, value, tb)
        print()
        print('-- Starting debugger --')
        print()
        import pdb
        pdb.pm()

    def _write_exception_hook(self, exctype, value, tb):
        # NOTE: This exception hook depends on gtk, kiwi, twisted being present
        #       In the future we might want it to run without some of these
        #       dependencies, so we can crash reports that happens really
        #       really early on for users with weird environments.
        if not self.entered_main:
            self._setup_twisted(raise_=False)

        try:
            from psycopg2 import OperationalError
            if exctype == OperationalError:
                from stoqlib.lib.message import error
                from stoqlib.lib.translation import stoqlib_gettext as _
                return error(_('There was an error quering the database'),
                             str(value))
        except ImportError:
            pass

        appname = 'unknown'
        try:
            from stoq.gui.shell.shell import get_shell
            shell = get_shell()
            if shell:
                appname = shell.get_current_app_name()
        except ImportError:
            pass

        window_name = 'unknown'
        try:
            from stoqlib.gui.base.dialogs import get_current_toplevel
            window = get_current_toplevel()
            if window:
                window_name = window.get_name()
        except ImportError:
            pass

        log.info('An error occurred in application "%s", toplevel window=%s' %
                 (appname, window_name))

        exc_lines = traceback.format_exception(exctype, value, tb)
        for line in ''.join(exc_lines).split('\n')[:-1]:
            log.error(line)

        from stoqlib.lib.crashreport import collect_traceback
        collect_traceback((exctype, value, tb))

        if self.entered_main:
            return

        from stoqlib.gui.dialogs.crashreportdialog import show_dialog
        d = show_dialog()
        from twisted.internet import reactor
        d.addCallback(lambda *x: reactor.stop())
        reactor.run()
        raise SystemExit
Esempio n. 20
0
File: status.py Progetto: stoq/stoq
 def __init__(self):
     ResourceStatus.__init__(self)
     self._webservice = WebService()
     self._server = ServerProxy()
Esempio n. 21
0
File: status.py Progetto: stoq/stoq
class _BackupStatus(ResourceStatus):

    name = "backup"
    label = _("Backup")
    priority = 98

    def __init__(self):
        ResourceStatus.__init__(self)
        self._webservice = WebService()
        self._server = ServerProxy()

    def refresh(self):
        if not api.sysparam.get_bool("ONLINE_SERVICES"):
            self.status = ResourceStatus.STATUS_NA
            self.reason = _("Backup service not running because " '"Online Services" is disabled')
            self.reason_long = _('Enable the parameter "Online Services" ' 'on the "Admin" app to solve this issue')
            return

        try:
            key = self._server.call("get_backup_key")
        except ServerError:
            pass
        else:
            if not key:
                self.status = self.STATUS_WARNING
                self.reason = _("Backup key not configured")
                self.reason_long = _('Click on "Configure" button to ' "configure the backup key")
                return

        request = self._webservice.status()
        try:
            response = request.get_response()
        except Exception as e:
            self.status = self.STATUS_WARNING
            self.reason = _("Could not communicate with Stoq.link")
            self.reason_long = str(e)
            return

        if response.status_code != 200:
            self.status = self.STATUS_WARNING
            self.reason = _("Could not communicate with Stoq.link")
            self.reason_long = None
            return

        data = response.json()
        if data["latest_backup_date"]:
            backup_date = dateutil.parser.parse(data["latest_backup_date"])
            delta = datetime.datetime.today() - backup_date

            if delta.days > 3:
                self.status = self.STATUS_WARNING
                self.reason = _("Backup is late. Last backup date is %s") % (backup_date.strftime("%x"))
                self.reason_long = _("Check your Stoq Server logs to see if " "there's any problem with it")
            else:
                self.status = self.STATUS_OK
                self.reason = _("Backup is up-to-date. Last backup date is %s") % (backup_date.strftime("%x"))
                self.reason_long = None
        else:
            self.status = self.STATUS_WARNING
            self.reason = _("There's no backup data yet")
            self.reason_long = None

    def get_actions(self):
        if self.status != ResourceStatus.STATUS_NA:
            yield ResourceStatusAction(self, "backup-now", _("Backup now"), self._on_backup_now, threaded=True)
            yield ResourceStatusAction(self, "configure", _("Configure"), self._on_configure, threaded=False)

    def _on_configure(self):
        key = self._server.call("get_backup_key")

        with api.new_store() as store:
            rv = run_dialog(BackupSettingsEditor, None, store, Settable(key=key))

        if rv:
            key = self._server.call("set_backup_key", rv.key)

    def _on_backup_now(self):
        self._server.call("backup_database")
Esempio n. 22
0
    def __init__(self):
        gobject.GObject.__init__(self)

        self._api = WebService()
        self._report = collect_report()
        self._count = 0
Esempio n. 23
0
    def download_plugin(self, plugin_name, channel=None):
        """Download a plugin from webservice

        :param plugin_name: the name of the plugin to download
        :param channel: the channel the plugin belongs
        :returns: a deferred
        """
        from stoqlib.lib.webservice import WebService

        default_store = get_default_store()
        existing_egg = default_store.find(PluginEgg,
                                          plugin_name=plugin_name).one()
        md5sum = existing_egg and existing_egg.egg_md5sum
        webapi = WebService()
        r = webapi.download_plugin(plugin_name, md5sum=md5sum, channel=channel)

        try:
            response = r.get_response()
        except Exception as e:
            return False, _("Failed to do the request: %s" % (e, ))

        code = response.status_code
        if code == 204:
            msg = _("No update needed. The plugin is already up to date.")
            log.info(msg)
            return True, msg

        if code != 200:
            return_messages = {
                400: _("Plugin not available for this stoq version"),
                401: _("The instance is not authorized to download the plugin"),
                404: _("Plugin does not exist"),
                405: _("This instance has not acquired the specified plugin"),
            }
            msg = return_messages.get(code, str(code))
            log.warning(msg)
            return False, msg

        try:
            with io.BytesIO() as f:
                f.write(response.content)
                with ZipFile(f) as egg:
                    if egg.testzip() is not None:
                        raise BadZipfile

                md5sum = hashlib.md5(f.getvalue()).hexdigest()
                with new_store() as store:
                    existing_egg = store.find(PluginEgg,
                                              plugin_name=plugin_name).one()
                    if existing_egg is not None:
                        existing_egg.egg_content = f.getvalue()
                        existing_egg.egg_md5sum = md5sum
                    else:
                        PluginEgg(
                            store=store,
                            plugin_name=plugin_name,
                            egg_md5sum=md5sum,
                            egg_content=f.getvalue(),
                        )
        except BadZipfile:
            return False, _("The downloaded plugin is corrupted")

        self._reload()
        return True, _("Plugin download successful")
Esempio n. 24
0
 def _setup_stoq_link(self):
     from stoqlib.domain.events import SaleStatusChangedEvent
     from stoqlib.lib.webservice import WebService
     self._api = WebService()
     SaleStatusChangedEvent.connect(self._update_stoq_link)
Esempio n. 25
0
    def download_plugin(self, plugin_name, channel=u'stable'):
        """Download a plugin from webservice

        :param plugin_name: the name of the plugin to download
        :param channel: the channel the plugin belongs
        :returns: a deferred
        """
        from stoqlib.lib.webservice import WebService

        default_store = get_default_store()
        existing_egg = default_store.find(PluginEgg,
                                          plugin_name=plugin_name).one()
        md5sum = existing_egg and existing_egg.egg_md5sum
        webapi = WebService()
        r = webapi.download_plugin(plugin_name, md5sum=md5sum, channel=channel)

        try:
            response = r.get_response()
        except Exception as e:
            return False, _("Failed to do the request: %s" % (e, ))

        code = response.status_code
        if code == 204:
            msg = _("No update needed. The plugin is already up to date.")
            log.info(msg)
            return True, msg

        if code != 200:
            return_messages = {
                400: _("Plugin not available for this stoq version"),
                401: _("The instance is not authorized to download the plugin"),
                404: _("Plugin does not exist"),
                405: _("This instance has not acquired the specified plugin"),
            }
            msg = return_messages.get(code, str(code))
            log.warning(msg)
            return False, msg

        try:
            with io.BytesIO() as f:
                f.write(response.content)
                with ZipFile(f) as egg:
                    if egg.testzip() is not None:
                        raise BadZipfile

                md5sum = hashlib.md5(f.getvalue()).hexdigest()
                with new_store() as store:
                    existing_egg = store.find(PluginEgg,
                                              plugin_name=plugin_name).one()
                    if existing_egg is not None:
                        existing_egg.egg_content = f.getvalue()
                        existing_egg.egg_md5sum = md5sum
                    else:
                        PluginEgg(
                            store=store,
                            plugin_name=plugin_name,
                            egg_md5sum=md5sum,
                            egg_content=f.getvalue(),
                        )
        except BadZipfile:
            return False, _("The downloaded plugin is corrupted")

        self._reload()
        return True, _("Plugin download successful")
Esempio n. 26
0
class ShellBootstrap(object):
    """Bootstraps the Stoq application, it's responsible for:
    - Setting up log files
    - Checking dependencies
    - Setting up libraries (gobject, gtk, kiwi, twisted)
    - Checking version
    - Locale
    - User settings and keybindings

    When this is completed Stoq is ready to connect to a database.
    """

    def __init__(self, options, initial):
        self._initial = initial
        self._log_filename = None
        self._options = options
        self.entered_main = False
        self.stream = None

    def bootstrap(self):
        self._setup_gobject()
        self._set_uptime()
        # Do this as soon as possible, before we attempt to use the
        # external libraries/resources
        self._set_user_locale()
        # Do this as early as possible to get as much as possible into the
        # log file itself, which means we cannot depend on the config or
        # anything else
        self._prepare_logfiles()
        self._set_app_info()
        self._check_dependencies()
        self._setup_exception_hook()
        self._setup_gtk()
        self._setup_kiwi()
        self._show_splash()
        self._setup_twisted()
        self._setup_psycopg()
        self._check_version_policy()
        self._setup_ui_dialogs()
        self._setup_cookiefile()
        self._register_stock_icons()
        self._setup_domain_slave_mapper()
        self._load_key_bindings()
        self._setup_debug_options()
        self._check_locale()
        self._setup_autoreload()
        self._setup_stoq_link()

    def _setup_gobject(self):
        if not self._initial:
            return
        assert not 'gobject' in sys.modules
        assert not 'gtk' in sys.modules

        if 'STOQ_USE_GI' in os.environ:
            from stoq.lib import gicompat
            gicompat.enable()

        import gobject
        gobject.threads_init()

    def _set_uptime(self):
        from stoqlib.lib.uptime import set_initial
        set_initial()

    def _set_user_locale(self):
        from stoqlib.lib.settings import get_settings
        from stoqlib.lib.translation import stoqlib_gettext as _

        self._locale_error = None
        settings = get_settings()
        lang = settings.get('user-locale', None)
        if not lang:
            return

        lang += '.UTF-8'
        try:
            locale.setlocale(locale.LC_ALL, lang)
        except locale.Error as err:
            msg = _("Could not set locale to %s. Make sure that you have "
                    "the packages for this locale installed.") % lang[:-6]
            self._locale_error = (msg, err)
            log.warning(msg)
        else:
            os.environ['LC_ALL'] = lang
            os.environ['LANGUAGE'] = lang

    def _setup_autoreload(self):
        if not self._options.autoreload:
            return

        from stoqlib.lib.autoreload import install_autoreload
        install_autoreload()

    def _setup_stoq_link(self):
        from stoqlib.domain.events import SaleStatusChangedEvent
        from stoqlib.lib.webservice import WebService
        self._api = WebService()
        SaleStatusChangedEvent.connect(self._update_stoq_link)

    def _update_stoq_link(self, sale, old_status):
        if sale.status != sale.STATUS_CONFIRMED:
            return
        self._api.link_update(sale.store)

    def _prepare_logfiles(self):
        from stoqlib.lib.osutils import get_application_dir

        stoqdir = get_application_dir("stoq")
        log_dir = os.path.join(stoqdir, 'logs', time.strftime('%Y'),
                               time.strftime('%m'))
        if not os.path.exists(log_dir):
            os.makedirs(log_dir)

        filename = 'stoq_%s.%s.log' % (time.strftime('%Y-%m-%d_%H-%M-%S'), os.getpid())
        self._log_filename = os.path.join(log_dir, filename)

        from kiwi.log import set_log_file
        self._stream = set_log_file(self._log_filename, 'stoq*')

        if hasattr(os, 'symlink'):
            link_file = os.path.join(stoqdir, 'stoq.log')
            if os.path.exists(link_file):
                os.unlink(link_file)
            os.symlink(self._log_filename, link_file)

        # We want developers to see deprecation warnings.
        from stoqlib.lib.environment import is_developer_mode
        if is_developer_mode():
            import warnings
            if self._options.non_fatal_warnings:
                action = "default"
            else:
                action = "error"
            warnings.filterwarnings(
                action, category=DeprecationWarning,
                module="^(stoq|kiwi)")

    def _set_app_info(self):
        from kiwi.component import provide_utility
        from stoqlib.lib.appinfo import AppInfo
        from stoqlib.lib.kiwilibrary import library
        from stoqlib.lib.interfaces import IAppInfo
        import stoq
        # FIXME: use only stoq.stoq_version here and all other callsites of
        # IAppInfo
        stoq_version = stoq.version
        stoq_ver = stoq.stoq_version
        if hasattr(library, 'get_revision'):
            rev = library.get_revision()
            if rev is not None:
                stoq_version += ' ' + rev
                stoq_ver += (rev,)
        info = AppInfo()
        info.set("name", "Stoq")
        info.set("version", stoq_version)
        info.set("ver", stoq_ver)
        info.set("log", self._log_filename)
        provide_utility(IAppInfo, info)

    def _check_dependencies(self):
        from stoq.lib.dependencies import check_dependencies
        check_dependencies()

    def _setup_exception_hook(self):
        if self._options.debug:
            hook = self._debug_hook
        else:
            hook = self._write_exception_hook
        sys.excepthook = hook

    def _setup_gtk(self):
        import gtk
        from kiwi.environ import environ

        # Total madness to make sure we can draw treeview lines,
        # this affects the GtkTreeView::grid-line-pattern style property
        #
        # Two bytes are sent in, see gtk_tree_view_set_grid_lines in gtktreeview.c
        # Byte 1 should be as high as possible, gtk+ 0x7F appears to be
        #        the highest allowed for Gtk+ 2.22 while 0xFF worked in
        #        earlier versions
        # Byte 2 should ideally be allowed to be 0, but neither C nor Python
        #        allows that.
        #
        data = environ.get_resource_string("stoq", "misc", "stoq.gtkrc")
        data = data.replace('\\x7f\\x01', '\x7f\x01')

        gtk.rc_parse_string(data)

        # Creating a button as a temporary workaround for bug
        # https://bugzilla.gnome.org/show_bug.cgi?id=632538, until gtk 3.0
        gtk.Button()
        settings = gtk.settings_get_default()
        settings.props.gtk_button_images = True

        from stoqlib.lib.environment import is_developer_mode
        if is_developer_mode() and gtk.gtk_version[0] == 2:
            from gtk import gdk

            # Install a Control-Q handler that forcefully exits
            # the program without saving any kind of state
            def event_handler(event):
                if (event.type == gdk.KEY_PRESS and
                        event.state & gdk.CONTROL_MASK and
                        event.keyval == gtk.keysyms.q):
                    os._exit(0)
                gtk.main_do_event(event)
            gdk.event_handler_set(event_handler)

    def _setup_kiwi(self):
        from kiwi.datatypes import get_localeconv
        from kiwi.ui.widgets.label import ProxyLabel
        ProxyLabel.replace('$CURRENCY', get_localeconv()['currency_symbol'])

    def _show_splash(self):
        if not self._options.splashscreen:
            return
        from stoqlib.gui.widgets.splash import show_splash
        show_splash()

    def _setup_twisted(self, raise_=True):
        # FIXME: figure out why twisted is already loaded
        # assert not 'twisted' in sys.modules
        from stoqlib.net import gtk2reactor
        from twisted.internet.error import ReactorAlreadyInstalledError
        try:
            gtk2reactor.install()
        except ReactorAlreadyInstalledError:
            if self._initial and raise_:
                raise

    def _setup_psycopg(self):
        # This will only be required when we use uuid.UUID instances
        # for UUIDCol
        #from psycopg2.extras import register_uuid
        #register_uuid()
        return

    def _check_version_policy(self):
        # No need to bother version checking when not running in developer mode
        from stoqlib.lib.environment import is_developer_mode
        if not is_developer_mode():
            return

        import stoq

        #
        # Policies for stoq/stoqlib versions,
        # All these policies here are made so that stoqlib version is tightly
        # tied to the stoq versioning
        #

        # We reserve the first 89 for the stable series.
        FIRST_UNSTABLE_MICRO_VERSION = 90

        # Stable series of Stoq must:
        # 1) have extra_version set to < 90
        # 2) Depend on a stoqlib version with extra_version < 90
        #
        if stoq.stable:
            if (stoq.micro_version >= FIRST_UNSTABLE_MICRO_VERSION and
                    not 'rc' in stoq.extra_version):
                raise SystemExit(
                    "Stable stoq release should set micro_version to "
                    "%d or lower" % (FIRST_UNSTABLE_MICRO_VERSION, ))
        # Unstable series of Stoq must have:
        # 1) have extra_version set to >= 90
        # 2) Must depend stoqlib version with extra_version >= 90
        #
        else:
            if stoq.micro_version < FIRST_UNSTABLE_MICRO_VERSION:
                raise SystemExit(
                    "Unstable stoq (%s) must set micro_version to %d or higher, "
                    "or did you forget to set stoq.stable to True?" % (
                        stoq.version, FIRST_UNSTABLE_MICRO_VERSION))

    def _setup_ui_dialogs(self):
        # This needs to be here otherwise we can't install the dialog
        if 'STOQ_TEST_MODE' in os.environ:
            return
        log.debug('providing graphical notification dialogs')
        from stoqlib.gui.base.dialogs import DialogSystemNotifier
        from stoqlib.lib.interfaces import ISystemNotifier
        from kiwi.component import provide_utility
        provide_utility(ISystemNotifier, DialogSystemNotifier(), replace=True)

        import gtk
        from kiwi.environ import environ
        from kiwi.ui.pixbufutils import pixbuf_from_string
        data = environ.get_resource_string(
            'stoq', 'pixmaps', 'stoq-stock-app-24x24.png')
        gtk.window_set_default_icon(pixbuf_from_string(data))

        if platform.system() == 'Darwin':
            from AppKit import NSApplication, NSData, NSImage
            bytes = environ.get_resource_string(
                'stoq', 'pixmaps', 'stoq-stock-app-48x48.png')
            data = NSData.alloc().initWithBytes_length_(bytes, len(bytes))
            icon = NSImage.alloc().initWithData_(data)
            app = NSApplication.sharedApplication()
            app.setApplicationIconImage_(icon)

    def _setup_cookiefile(self):
        log.debug('setting up cookie file')
        from kiwi.component import provide_utility
        from stoqlib.lib.cookie import Base64CookieFile
        from stoqlib.lib.interfaces import ICookieFile
        from stoqlib.lib.osutils import get_application_dir
        app_dir = get_application_dir()
        cookiefile = os.path.join(app_dir, "cookie")
        provide_utility(ICookieFile, Base64CookieFile(cookiefile))

    def _register_stock_icons(self):
        from stoqlib.gui.stockicons import register

        log.debug('register stock icons')
        register()

    def _setup_domain_slave_mapper(self):
        from kiwi.component import provide_utility
        from stoqlib.gui.interfaces import IDomainSlaveMapper
        from stoqlib.gui.slaves.domainslavemapper import DefaultDomainSlaveMapper
        provide_utility(IDomainSlaveMapper, DefaultDomainSlaveMapper(),
                        replace=True)

    def _load_key_bindings(self):
        from stoqlib.gui.utils.keybindings import load_user_keybindings
        load_user_keybindings()

    def _check_locale(self):
        if not self._locale_error:
            return

        from stoqlib.lib.message import warning
        warning(self._locale_error[0], str(self._locale_error[1]))

    def _setup_debug_options(self):
        if not self._options.debug:
            return
        from gtk import keysyms
        from stoqlib.gui.utils.introspection import introspect_slaves
        from stoqlib.gui.utils.keyboardhandler import install_global_keyhandler
        install_global_keyhandler(keysyms.F12, introspect_slaves)

    #
    # Global functions
    #

    def _debug_hook(self, exctype, value, tb):
        self._write_exception_hook(exctype, value, tb)
        traceback.print_exception(exctype, value, tb)
        print()
        print('-- Starting debugger --')
        print()
        import pdb
        pdb.pm()

    def _write_exception_hook(self, exctype, value, tb):
        # NOTE: This exception hook depends on gtk, kiwi, twisted being present
        #       In the future we might want it to run without some of these
        #       dependencies, so we can crash reports that happens really
        #       really early on for users with weird environments.
        if not self.entered_main:
            self._setup_twisted(raise_=False)

        try:
            from psycopg2 import OperationalError
            if exctype == OperationalError:
                from stoqlib.lib.message import error
                from stoqlib.lib.translation import stoqlib_gettext as _
                return error(_('There was an error quering the database'),
                             str(value))
        except ImportError:
            pass

        appname = 'unknown'
        try:
            from stoq.gui.shell.shell import get_shell
            shell = get_shell()
            if shell:
                appname = shell.get_current_app_name()
        except ImportError:
            pass

        window_name = 'unknown'
        try:
            from stoqlib.gui.base.dialogs import get_current_toplevel
            window = get_current_toplevel()
            if window:
                window_name = window.get_name()
        except ImportError:
            pass

        log.info('An error occurred in application "%s", toplevel window=%s' % (
            appname, window_name))

        exc_lines = traceback.format_exception(exctype, value, tb)
        for line in ''.join(exc_lines).split('\n')[:-1]:
            log.error(line)

        from stoqlib.lib.crashreport import collect_traceback
        collect_traceback((exctype, value, tb))

        if self.entered_main:
            return

        from stoqlib.gui.dialogs.crashreportdialog import show_dialog
        d = show_dialog()
        from twisted.internet import reactor
        d.addCallback(lambda *x: reactor.stop())
        reactor.run()
        raise SystemExit
Esempio n. 27
0
 def _download_details(self):
     log.debug('Downloading new version information')
     api = WebService()
     response = api.version(self.store, stoq.version)
     response.addCallback(self._on_response_done)