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

X2Go dev team git-admin at x2go.org
Tue Jan 7 16:21:02 CET 2014


The branch, brokerclient 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