[X2Go-Commits] python-x2go.git - build-baikal (branch) updated: 0.1.1.4-138-ge6596c5

X2Go dev team git-admin at x2go.org
Wed Jan 8 15:28:57 CET 2014


The branch, build-baikal has been updated
       via  e6596c551489ab155e241299d30cc90a74fd25a1 (commit)
      from  83b74e308c0b9dfd47c9f8815be2c161e21c7c54 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
-----------------------------------------------------------------------

Summary of changes:
 debian/changelog                  |    2 +
 x2go/backends/control/_stdout.py  |  155 ++++++++++++++++++++++++++++++++++---
 x2go/backends/terminal/_stdout.py |    1 +
 x2go/session.py                   |   35 ++++++---
 4 files changed, 171 insertions(+), 22 deletions(-)

The diff of changes is:
diff --git a/debian/changelog b/debian/changelog
index 7e942d1..e92b8d4 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -43,6 +43,8 @@ python-x2go (0.1.2.0-0~x2go1) UNRELEASED; urgency=low
     - Fix base64 encoded icon string.
     - Fix master session recognition.
     - Handle empty control session in the session list cache.
+    - Render and cache dictionary based published applications menu tree in
+      Python X2Go. Cache the tree once rendered.
   * Depend on python-xlib.
 
  -- Mike Gabriel <mike.gabriel at das-netzwerkteam.de>  Sat, 28 Sep 2012 01:44:21 +0100
diff --git a/x2go/backends/control/_stdout.py b/x2go/backends/control/_stdout.py
index b04dda3..4366f5f 100644
--- a/x2go/backends/control/_stdout.py
+++ b/x2go/backends/control/_stdout.py
@@ -30,11 +30,10 @@ import os
 import types
 import paramiko
 import gevent
-
 import copy
-
 import string
 import random
+import re
 
 from gevent import socket
 
@@ -141,7 +140,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         self.sessions_rootdir = sessions_rootdir
         self.ssh_rootdir = ssh_rootdir
 
-        self._published_applications_menu = None
+        self._published_applications_menu = {}
 
         paramiko.SSHClient.__init__(self, *args, **kwargs)
         if self.add_to_known_hosts:
@@ -597,16 +596,46 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
             return True
         return False
 
-    def get_published_applications(self):
+    def get_published_applications(self, lang='en', refresh=False, raw=False, very_raw=False):
         """\
         Retrieve the menu tree of published applications from X2Go server.
 
+        The C{raw} option lets this method return a C{list} of C{dict} elements. Each C{dict} elements has a 
+        C{desktop} key containing the text output of a .desktop file and an C{icon} key which contains the desktop
+        icon data base64 encoded.
+
+        The {very_raw} lets this method return the output of the C{x2gogetapps} script as is.
+
+        @param lang: locale/language identifier
+        @type lang: C{str}
+        @param refresh: force reload of the menu tree from X2Go server
+        @type refresh: C{bool}
+        @param raw: retrieve a raw output of the server list of published applications
+        @type raw: C{bool}
+        @param very_raw: retrieve a very raw output of the server list of published applications
+        @type very_raw: C{bool}
+
+        @return: an i18n capable menu tree packed as a Python dictionary
+        @rtype: C{list}
+
         """
+
         if 'X2GO_PUBLISHED_APPLICATIONS' in self._x2go_server_features:
-            if self._published_applications_menu is None:
+            if self._published_applications_menu is {} or not self._published_applications_menu.has_key(lang) or raw or very_raw or refresh:
+
+                ### STAGE 1: retrieve menu from server
+
                 self.logger('querying server (%s) for list of published applications' % self.profile_name, loglevel=log.loglevel_NOTICE)
                 (stdin, stdout, stderr) = self._x2go_exec_command('which x2gogetapps >/dev/null && x2gogetapps')
-                _raw_menu_items = stdout.read().split('</desktop>\n')
+                _raw_output = stdout.read()
+
+                if very_raw:
+                    self.logger('published applications query for %s finished, return very raw output' % self.profile_name, loglevel=log.loglevel_NOTICE)
+                    return _raw_output
+
+                ### STAGE 2: dissect the text file retrieved from server, cut into single menu elements
+
+                _raw_menu_items = _raw_output.split('</desktop>\n')
                 _raw_menu_items = [ i.replace('<desktop>\n', '') for i in _raw_menu_items ]
                 _menu = []
                 for _raw_menu_item in _raw_menu_items:
@@ -621,11 +650,115 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
                         _menu_item = None
                         _icon_base64 = None
 
-                self._published_applications_menu = _menu
-                self.logger('published applications query for %s finished' % self.profile_name, loglevel=log.loglevel_NOTICE)
-                return self._published_applications_menu
-            else:
-                return self._published_applications_menu
+                if raw:
+                    self.logger('published applications query for %s finished, returning raw output' % self.profile_name, loglevel=log.loglevel_NOTICE)
+                    return _menu
+
+
+                # STAGE 3: create menu structure in a Python dictionary
+
+                _category_map = {
+                    lang: {
+                        'Multimedia': [],
+                        'Development': [],
+                        'Education': [],
+                        'Games': [],
+                        'Graphics': [],
+                        'Internet': [],
+                        'Office': [],
+                        'System': [],
+                        'Utilities': [],
+                        'Other Applications': [],
+                    }
+                }
+                _empty_menus = _category_map[lang].keys()
+
+                for item in _menu:
+
+                    _menu_entry_name = ''
+                    _menu_entry_fallback_name = ''
+                    _menu_entry_comment = ''
+                    _menu_entry_fallback_comment = ''
+                    _menu_entry_exec = ''
+                    _menu_entry_cat = ''
+
+                    lang_regio = lang
+                    lang_only = lang_regio.split('_')[0]
+
+                    for line in item['desktop'].split('\n'):
+                        if re.match('^Name\[%s\]=.*' % lang_regio, line) or re.match('Name\[%s\]=.*' % lang_only, line):
+                            _menu_entry_name = line.split("=")[1].strip()
+                        elif re.match('^Comment\[%s\]=.*' % lang_regio, line) or re.match('Comment\[%s\]=.*' % lang_only, line):
+                            _menu_entry_comment = line.split("=")[1].strip()
+                        elif re.match('^Name=.*', line):
+                            _menu_entry_fallback_name = line.split("=")[1].strip()
+                        elif re.match('^Comment=.*', line):
+                            _menu_entry_fallback_comment = line.split("=")[1].strip()
+                        elif re.match('^Exec=.*', line):
+                            _menu_entry_exec = line.split("=")[1].strip()
+                        elif re.match('^Categories=.*', line):
+                            if 'Audio' in line or 'Video' in line:
+                                _menu_entry_cat = 'Multimedia'
+                            elif 'Development' in line:
+                                _menu_entry_cat = 'Development'
+                            elif 'Education' in line:
+                                _menu_entry_cat = 'Education'
+                            elif 'Game' in line:
+                                _menu_entry_cat = 'Games'
+                            elif 'Graphics' in line:
+                                _menu_entry_cat = 'Graphics'
+                            elif 'Network' in line:
+                                _menu_entry_cat = 'Internet'
+                            elif 'Office' in line:
+                                _menu_entry_cat = 'Office'
+                            elif 'Settings' in line:
+                                continue
+                            elif 'System' in line:
+                                _menu_entry_cat = 'System'
+                            elif 'Utilities' in line:
+                                _menu_entry_cat = 'Utilities'
+                            else:
+                                _menu_entry_cat = 'Other Applications'
+
+                    if not _menu_entry_exec:
+                        continue
+                    else:
+                        # FIXME: strip off any noted options (%f, %F, %u, %U, ...), this can be more intelligent
+                        _menu_entry_exec = _menu_entry_exec.replace('%f', '').replace('%F','').replace('%u','').replace('%U','')
+
+                    if not _menu_entry_cat:
+                        _menu_entry_cat = 'Other Applications'
+
+                    if _menu_entry_cat in _empty_menus:
+                        _empty_menus.remove(_menu_entry_cat)
+
+                    if not _menu_entry_name: _menu_entry_name = _menu_entry_fallback_name
+                    if not _menu_entry_comment: _menu_entry_comment = _menu_entry_fallback_comment
+                    if not _menu_entry_comment: _menu_entry_comment = _menu_entry_name
+
+                    _menu_entry_icon = item['icon']
+
+                    _category_map[lang][_menu_entry_cat].append(
+                        {
+                            'name': _menu_entry_name,
+                            'comment': _menu_entry_comment,
+                            'exec': _menu_entry_exec,
+                            'icon': _menu_entry_icon,
+                        }
+                    )
+
+                for _cat in _empty_menus:
+                    del _category_map[lang][_cat]
+
+                for _cat in _category_map[lang].keys():
+                    _sorted = sorted(_category_map[lang][_cat], key=lambda k: k['name'])
+                    _category_map[lang][_cat] = _sorted
+
+                self._published_applications_menu.update(_category_map)
+                self.logger('published applications query for %s finished, return menu tree' % self.profile_name, loglevel=log.loglevel_NOTICE)
+
+            return self._published_applications_menu
+
         else:
             # FIXME: ignoring the absence of the published applications feature for now, handle it appropriately later
             pass
diff --git a/x2go/backends/terminal/_stdout.py b/x2go/backends/terminal/_stdout.py
index 2155d2c..32798af 100644
--- a/x2go/backends/terminal/_stdout.py
+++ b/x2go/backends/terminal/_stdout.py
@@ -345,6 +345,7 @@ class X2goTerminalSessionSTDOUT(object):
         self.release_proxy()
 
         try:
+
             if self.control_session.get_transport() is not None:
                 try:
                     for _tunnel in [ _tun[1] for _tun in self.reverse_tunnels[self.session_info.name].values() ]:
diff --git a/x2go/session.py b/x2go/session.py
index c966ac9..1079a59 100644
--- a/x2go/session.py
+++ b/x2go/session.py
@@ -724,7 +724,7 @@ class X2goSession(object):
         @rtype: C{str}
 
         """
-        if self.terminal_session is not None:
+        if self.has_terminal_session():
             return self.terminal_session.session_title
         else:
             return 'X2GO-%s' % self.get_session_name()
@@ -1225,18 +1225,27 @@ class X2goSession(object):
             return self.terminal_session.is_desktop_session()
         return False
 
-    def get_published_applications(self):
+    def get_published_applications(self, lang='en', refresh=False, raw=False, very_raw=False):
         """\
         Return a list of published menu items from the X2Go server
         for session type published applications.
 
+        @param lang: locale/language identifier
+        @type lang: C{str}
+        @param refresh: force reload of the menu tree from X2Go server
+        @type refresh: C{bool}
+        @param raw: retrieve a raw output of the server list of published applications
+        @type raw: C{bool}
+        @param very_raw: retrieve a very raw output of the server list of published applications (as-is output of x2gogetapps script)
+        @type very_raw: C{bool}
+
         @return: A C{list} of C{dict} elements. Each C{dict} elements has a 
             C{desktop} key containing the text output of a .desktop file and
             an C{icon} key which contains the desktop icon data base64 encoded 
         @rtype: C{list}
 
         """
-        return self.control_session.get_published_applications()
+        return self.control_session.get_published_applications(lang=lang, refresh=refresh, raw=raw, very_raw=very_raw)
 
     def exec_published_application(self, exec_name):
         """\
@@ -1280,7 +1289,7 @@ class X2goSession(object):
 
             try:
                 if self.published_applications:
-                    self.published_applications_menu = self.get_published_applications()
+                    self.published_applications_menu = gevent.spawn(self.get_published_applications)
             except:
                 # FIXME: test the code to see what exceptions may occur here...
                 raise
@@ -1303,13 +1312,13 @@ class X2goSession(object):
 
                 # only run the session startup command if we do not resume...
                 if _new_session:
-                    self.terminal_session.run_command(env=self.session_environment)
+                    self.has_terminal_session() and self.terminal_session.run_command(env=self.session_environment)
 
                 if self.get_session_cmd() != 'PUBLISHED':
                     self.published_applications = False
 
                 if self._SUPPORTED_SOUND and self.terminal_session.params.snd_system is not 'none':
-                    self.terminal_session and not self.faulty and self.terminal_session.start_sound()
+                    self.has_terminal_session() and not self.faulty and self.terminal_session.start_sound()
                 else:
                     self._SUPPORTED_SOUND = False
 
@@ -1317,7 +1326,7 @@ class X2goSession(object):
                     if (self._SUPPORTED_PRINTING and self.printing) or \
                        (self._SUPPORTED_MIMEBOX and self.allow_mimebox) or \
                        (self._SUPPORTED_FOLDERSHARING and self.allow_share_local_folders):
-                        self.terminal_session and not self.faulty and self.terminal_session.start_sshfs()
+                        self.has_terminal_session() and not self.faulty and self.terminal_session.start_sshfs()
                 except x2go_exceptions.X2goUserException, e:
                     self.logger('%s' % str(e), loglevel=log.loglevel_WARN)
                     self.HOOK_sshfs_not_available()
@@ -1327,8 +1336,8 @@ class X2goSession(object):
 
                 try:
                     if self._SUPPORTED_PRINTING and self.printing:
-                        self.terminal_session and not self.faulty and self.terminal_session.start_printing()
-                        self.terminal_session and not self.faulty and self.session_environment.update({'X2GO_SPOOLDIR': self.terminal_session.get_printing_spooldir(), })
+                        self.has_terminal_session() and not self.faulty and self.terminal_session.start_printing()
+                        self.has_terminal_session() and not self.faulty and self.session_environment.update({'X2GO_SPOOLDIR': self.terminal_session.get_printing_spooldir(), })
                 except x2go_exceptions.X2goUserException, e:
                     self.logger('%s' % str(e), loglevel=log.loglevel_WARN)
                     self.HOOK_printing_not_available()
@@ -1336,8 +1345,8 @@ class X2goSession(object):
 
                 try:
                     if self._SUPPORTED_MIMEBOX and self.allow_mimebox:
-                        self.terminal_session and not self.faulty and self.terminal_session.start_mimebox(mimebox_extensions=self.mimebox_extensions, mimebox_action=self.mimebox_action)
-                        self.terminal_session and self.session_environment.update({'X2GO_MIMEBOX': self.terminal_session.get_mimebox_spooldir(), })
+                        self.has_terminal_session() and not self.faulty and self.terminal_session.start_mimebox(mimebox_extensions=self.mimebox_extensions, mimebox_action=self.mimebox_action)
+                        self.has_terminal_session() and self.session_environment.update({'X2GO_MIMEBOX': self.terminal_session.get_mimebox_spooldir(), })
                 except x2go_exceptions.X2goUserException, e:
                     self.logger('%s' % str(e), loglevel=log.loglevel_WARN)
                     self.HOOK_mimebox_not_available()
@@ -1471,6 +1480,8 @@ class X2goSession(object):
                     self.terminated = False
                     self.faults = False
                     self.session_cleanup()
+                    del self.terminal_session
+                    self.terminal_session = None
                     return True
 
             elif self.has_control_session() and self.session_name:
@@ -1512,6 +1523,8 @@ class X2goSession(object):
                     self.terminated = True
                     self.faulty = False
                     self.session_cleanup()
+                    del self.terminal_session
+                    self.terminal_session = None
                     return True
 
             elif self.has_control_session() and self.session_name:


hooks/post-receive
-- 
python-x2go.git (Python X2Go Client API)

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "python-x2go.git" (Python X2Go Client API).




More information about the x2go-commits mailing list