Author: asterix
Date: 2009-02-08 14:47:28 +0100 (Sun, 08 Feb 2009)
New Revision: 11060
Modified:
trunk/src/common/helpers.py
Log:
reorder functions in common/helpers.py. Fixes #4764. Thanks Vardo!
Modified: trunk/src/common/helpers.py
===================================================================
--- trunk/src/common/helpers.py 2009-02-08 10:21:50 UTC (rev 11059)
+++ trunk/src/common/helpers.py 2009-02-08 13:47:28 UTC (rev 11060)
@@ -42,17 +42,11 @@
from encodings.punycode import punycode_encode
-import gajim
from i18n import Q_
from i18n import ngettext
import xmpp
try:
- from osx import nsapp
-except ImportError:
- pass
-
-try:
import winsound # windows-only built-in module for playing wav
import win32api
import win32con
@@ -187,65 +181,6 @@
else:
raise
-def convert_bytes(string):
- suffix = ''
- # IEC standard says KiB = 1024 bytes KB = 1000 bytes
- # but do we use the standard?
- use_kib_mib = gajim.config.get('use_kib_mib')
- align = 1024.
- bytes = float(string)
- if bytes >= align:
- bytes = round(bytes/align, 1)
- if bytes >= align:
- bytes = round(bytes/align, 1)
- if bytes >= align:
- bytes = round(bytes/align, 1)
- if use_kib_mib:
- #GiB means gibibyte
- suffix = _('%s GiB')
- else:
- #GB means gigabyte
- suffix = _('%s GB')
- else:
- if use_kib_mib:
- #MiB means mibibyte
- suffix = _('%s MiB')
- else:
- #MB means megabyte
- suffix = _('%s MB')
- else:
- if use_kib_mib:
- #KiB means kibibyte
- suffix = _('%s KiB')
- else:
- #KB means kilo bytes
- suffix = _('%s KB')
- else:
- #B means bytes
- suffix = _('%s B')
- return suffix % unicode(bytes)
-
-
-def get_contact_dict_for_account(account):
- ''' create a dict of jid, nick -> contact with all contacts of account.
- Can be used for completion lists'''
- contacts_dict = {}
- for jid in gajim.contacts.get_jid_list(account):
- contact = gajim.contacts.get_contact_with_highest_priority(account,
- jid)
- contacts_dict[jid] = contact
- name = contact.name
- if name in contacts_dict:
- contact1 = contacts_dict[name]
- del contacts_dict[name]
- contacts_dict['%s (%s)' % (name, contact1.jid)] = contact1
- contacts_dict['%s (%s)' % (name, jid)] = contact
- else:
- if contact.name == gajim.get_nick_from_jid(jid):
- del contacts_dict[jid]
- contacts_dict[name] = contact
- return contacts_dict
-
def get_uf_show(show, use_mnemonic = False):
'''returns a userfriendly string for dnd/xa/chat
and makes all strings translatable
@@ -426,6 +361,347 @@
command = '%s "%s"' % (executable, parameter)
return command
+def get_file_path_from_dnd_dropped_uri(uri):
+ path = urllib.unquote(uri) # escape special chars
+ path = path.strip('\r\n\x00') # remove \r\n and NULL
+ # get the path to file
+ if re.match('^file:///[a-zA-Z]:/', path): # windows
+ path = path[8:] # 8 is len('file:///')
+ elif path.startswith('file://'): # nautilus, rox
+ if sys.platform == 'darwin':
+ # OS/X includes hostname in file:// URI
+ path = re.sub('file://[^/]*', '', path)
+ else:
+ path = path[7:] # 7 is len('file://')
+ elif path.startswith('file:'): # xffm
+ path = path[5:] # 5 is len('file:')
+ return path
+
+def from_xs_boolean_to_python_boolean(value):
+ # this is xs:boolean so 'true', 'false', '1', '0'
+ # convert those to True/False (python booleans)
+ if value in ('1', 'true'):
+ val = True
+ else: # '0', 'false' or anything else
+ val = False
+
+ return val
+
+def get_xmpp_show(show):
+ if show in ('online', 'offline'):
+ return None
+ return show
+
+def get_output_of_command(command):
+ try:
+ child_stdin, child_stdout = os.popen2(command)
+ except ValueError:
+ return None
+
+ output = child_stdout.readlines()
+ child_stdout.close()
+ child_stdin.close()
+
+ return output
+
+def decode_string(string):
+ '''try to decode (to make it Unicode instance) given string'''
+ if isinstance(string, unicode):
+ return string
+ # by the time we go to iso15 it better be the one else we show bad characters
+ encodings = (locale.getpreferredencoding(), 'utf-8', 'iso-8859-15')
+ for encoding in encodings:
+ try:
+ string = string.decode(encoding)
+ except UnicodeError:
+ continue
+ break
+
+ return string
+
+def ensure_utf8_string(string):
+ '''make sure string is in UTF-8'''
+ try:
+ string = decode_string(string).encode('utf-8')
+ except Exception:
+ pass
+ return string
+
+def get_windows_reg_env(varname, default=''):
+ '''asks for paths commonly used but not exposed as ENVs
+ in english Windows 2003 those are:
+ 'AppData' = %USERPROFILE%\Application Data (also an ENV)
+ 'Desktop' = %USERPROFILE%\Desktop
+ 'Favorites' = %USERPROFILE%\Favorites
+ 'NetHood' = %USERPROFILE%\NetHood
+ 'Personal' = D:\My Documents (PATH TO MY DOCUMENTS)
+ 'PrintHood' = %USERPROFILE%\PrintHood
+ 'Programs' = %USERPROFILE%\Start Menu\Programs
+ 'Recent' = %USERPROFILE%\Recent
+ 'SendTo' = %USERPROFILE%\SendTo
+ 'Start Menu' = %USERPROFILE%\Start Menu
+ 'Startup' = %USERPROFILE%\Start Menu\Programs\Startup
+ 'Templates' = %USERPROFILE%\Templates
+ 'My Pictures' = D:\My Documents\My Pictures
+ 'Local Settings' = %USERPROFILE%\Local Settings
+ 'Local AppData' = %USERPROFILE%\Local Settings\Application Data
+ 'Cache' = %USERPROFILE%\Local Settings\Temporary Internet Files
+ 'Cookies' = %USERPROFILE%\Cookies
+ 'History' = %USERPROFILE%\Local Settings\History
+ '''
+
+ if os.name != 'nt':
+ return ''
+
+ val = default
+ try:
+ rkey = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER,
+r'Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders')
+ try:
+ val = str(win32api.RegQueryValueEx(rkey, varname)[0])
+ val = win32api.ExpandEnvironmentStrings(val) # expand using environ
+ except Exception:
+ pass
+ finally:
+ win32api.RegCloseKey(rkey)
+ return val
+
+def get_my_pictures_path():
+ '''windows-only atm. [Unix lives in the past]'''
+ return get_windows_reg_env('My Pictures')
+
+def get_desktop_path():
+ if os.name == 'nt':
+ path = get_windows_reg_env('Desktop')
+ else:
+ path = os.path.join(os.path.expanduser('~'), 'Desktop')
+ return path
+
+def get_documents_path():
+ if os.name == 'nt':
+ path = get_windows_reg_env('Personal')
+ else:
+ path = os.path.expanduser('~')
+ return path
+
+def sanitize_filename(filename):
+ '''makes sure the filename we will write does contain only acceptable and
+ latin characters, and is not too long (in that case hash it)'''
+ # 48 is the limit
+ if len(filename) > 48:
+ hash = hashlib.md5(filename)
+ filename = base64.b64encode(hash.digest())
+
+ filename = punycode_encode(filename) # make it latin chars only
+ filename = filename.replace('/', '_')
+ if os.name == 'nt':
+ filename = filename.replace('?', '_').replace(':', '_')\
+ .replace('\\', '_').replace('"', "'").replace('|', '_')\
+ .replace('*', '_').replace('<', '_').replace('>', '_')
+
+ return filename
+
+def reduce_chars_newlines(text, max_chars = 0, max_lines = 0):
+ '''Cut the chars after 'max_chars' on each line
+ and show only the first 'max_lines'.
+ If any of the params is not present (None or 0) the action
+ on it is not performed'''
+
+ def _cut_if_long(string):
+ if len(string) > max_chars:
+ string = string[:max_chars - 3] + '...'
+ return string
+
+ if isinstance(text, str):
+ text = text.decode('utf-8')
+
+ if max_lines == 0:
+ lines = text.split('\n')
+ else:
+ lines = text.split('\n', max_lines)[:max_lines]
+ if max_chars > 0:
+ if lines:
+ lines = [_cut_if_long(e) for e in lines]
+ if lines:
+ reduced_text = '\n'.join(lines)
+ if reduced_text != text:
+ reduced_text += '...'
+ else:
+ reduced_text = ''
+ return reduced_text
+
+def get_account_status(account):
+ status = reduce_chars_newlines(account['status_line'], 100, 1)
+ return status
+
+def get_avatar_path(prefix):
+ '''Returns the filename of the avatar, distinguishes between user- and
+ contact-provided one. Returns None if no avatar was found at all.
+ prefix is the path to the requested avatar just before the ".png" or
+ ".jpeg".'''
+ # First, scan for a local, user-set avatar
+ for type_ in ('jpeg', 'png'):
+ file_ = prefix + '_local.' + type_
+ if os.path.exists(file_):
+ return file_
+ # If none available, scan for a contact-provided avatar
+ for type_ in ('jpeg', 'png'):
+ file_ = prefix + '.' + type_
+ if os.path.exists(file_):
+ return file_
+ return None
+
+def datetime_tuple(timestamp):
+ '''Converts timestamp using strptime and the format: %Y%m%dT%H:%M:%S
+ Because of various datetime formats are used the following exceptions
+ are handled:
+ - Optional milliseconds appened to the string are removed
+ - Optional Z (that means UTC) appened to the string are removed
+ - XEP-082 datetime strings have all '-' cahrs removed to meet
+ the above format.'''
+ timestamp = timestamp.split('.')[0]
+ timestamp = timestamp.replace('-', '')
+ timestamp = timestamp.replace('z', '')
+ timestamp = timestamp.replace('Z', '')
+ from time import strptime
+ return strptime(timestamp, '%Y%m%dT%H:%M:%S')
+
+def sort_identities_func(i1, i2):
+ cat1 = i1['category']
+ cat2 = i2['category']
+ if cat1 < cat2:
+ return -1
+ if cat1 > cat2:
+ return 1
+ type1 = i1.get('type', '')
+ type2 = i2.get('type', '')
+ if type1 < type2:
+ return -1
+ if type1 > type2:
+ return 1
+ lang1 = i1.get('xml:lang', '')
+ lang2 = i2.get('xml:lang', '')
+ if lang1 < lang2:
+ return -1
+ if lang1 > lang2:
+ return 1
+ return 0
+
+def sort_dataforms_func(d1, d2):
+ f1 = d1.getField('FORM_TYPE')
+ f2 = d2.getField('FORM_TYPE')
+ if f1 and f2 and (f1.getValue() < f2.getValue()):
+ return -1
+ return 1
+
+def compute_caps_hash(identities, features, dataforms=[], hash_method='sha-1'):
+ '''Compute caps hash according to XEP-0115, V1.5
+
+ dataforms are xmpp.DataForms objects as common.dataforms don't allow several
+ values without a field type list-multi'''
+ S = ''
+ identities.sort(cmp=sort_identities_func)
+ for i in identities:
+ c = i['category']
+ type_ = i.get('type', '')
+ lang = i.get('xml:lang', '')
+ name = i.get('name', '')
+ S += '%s/%s/%s/%s<' % (c, type_, lang, name)
+ features.sort()
+ for f in features:
+ S += '%s<' % f
+ dataforms.sort(cmp=sort_dataforms_func)
+ for dataform in dataforms:
+ # fields indexed by var
+ fields = {}
+ for f in dataform.getChildren():
+ fields[f.getVar()] = f
+ form_type = fields.get('FORM_TYPE')
+ if form_type:
+ S += form_type.getValue() + '<'
+ del fields['FORM_TYPE']
+ for var in sorted(fields.keys()):
+ S += '%s<' % var
+ values = sorted(fields[var].getValues())
+ for value in values:
+ S += '%s<' % value
+
+ if hash_method == 'sha-1':
+ hash_ = hashlib.sha1(S)
+ elif hash_method == 'md5':
+ hash_ = hashlib.md5(S)
+ else:
+ return ''
+ return base64.b64encode(hash_.digest())
+
+# import gajim only when needed (after decode_string is defined) see #4764
+
+import gajim
+
+try:
+ from osx import nsapp
+except ImportError:
+ pass
+
+
+def convert_bytes(string):
+ suffix = ''
+ # IEC standard says KiB = 1024 bytes KB = 1000 bytes
+ # but do we use the standard?
+ use_kib_mib = gajim.config.get('use_kib_mib')
+ align = 1024.
+ bytes = float(string)
+ if bytes >= align:
+ bytes = round(bytes/align, 1)
+ if bytes >= align:
+ bytes = round(bytes/align, 1)
+ if bytes >= align:
+ bytes = round(bytes/align, 1)
+ if use_kib_mib:
+ #GiB means gibibyte
+ suffix = _('%s GiB')
+ else:
+ #GB means gigabyte
+ suffix = _('%s GB')
+ else:
+ if use_kib_mib:
+ #MiB means mibibyte
+ suffix = _('%s MiB')
+ else:
+ #MB means megabyte
+ suffix = _('%s MB')
+ else:
+ if use_kib_mib:
+ #KiB means kibibyte
+ suffix = _('%s KiB')
+ else:
+ #KB means kilo bytes
+ suffix = _('%s KB')
+ else:
+ #B means bytes
+ suffix = _('%s B')
+ return suffix % unicode(bytes)
+
+def get_contact_dict_for_account(account):
+ ''' create a dict of jid, nick -> contact with all contacts of account.
+ Can be used for completion lists'''
+ contacts_dict = {}
+ for jid in gajim.contacts.get_jid_list(account):
+ contact = gajim.contacts.get_contact_with_highest_priority(account,
+ jid)
+ contacts_dict[jid] = contact
+ name = contact.name
+ if name in contacts_dict:
+ contact1 = contacts_dict[name]
+ del contacts_dict[name]
+ contacts_dict['%s (%s)' % (name, contact1.jid)] = contact1
+ contacts_dict['%s (%s)' % (name, jid)] = contact
+ else:
+ if contact.name == gajim.get_nick_from_jid(jid):
+ del contacts_dict[jid]
+ contacts_dict[name] = contact
+ return contacts_dict
+
def launch_browser_mailer(kind, uri):
#kind = 'url' or 'mail'
if os.name == 'nt':
@@ -517,49 +793,6 @@
command = build_command(player, path_to_soundfile)
exec_command(command)
-def get_file_path_from_dnd_dropped_uri(uri):
- path = urllib.unquote(uri) # escape special chars
- path = path.strip('\r\n\x00') # remove \r\n and NULL
- # get the path to file
- if re.match('^file:///[a-zA-Z]:/', path): # windows
- path = path[8:] # 8 is len('file:///')
- elif path.startswith('file://'): # nautilus, rox
- if sys.platform == 'darwin':
- # OS/X includes hostname in file:// URI
- path = re.sub('file://[^/]*', '', path)
- else:
- path = path[7:] # 7 is len('file://')
- elif path.startswith('file:'): # xffm
- path = path[5:] # 5 is len('file:')
- return path
-
-def from_xs_boolean_to_python_boolean(value):
- # this is xs:boolean so 'true', 'false', '1', '0'
- # convert those to True/False (python booleans)
- if value in ('1', 'true'):
- val = True
- else: # '0', 'false' or anything else
- val = False
-
- return val
-
-def get_xmpp_show(show):
- if show in ('online', 'offline'):
- return None
- return show
-
-def get_output_of_command(command):
- try:
- child_stdin, child_stdout = os.popen2(command)
- except ValueError:
- return None
-
- output = child_stdout.readlines()
- child_stdout.close()
- child_stdin.close()
-
- return output
-
def get_global_show():
maxi = 0
for account in gajim.connections:
@@ -625,91 +858,6 @@
return contact.show
return 'not in roster'
-def decode_string(string):
- '''try to decode (to make it Unicode instance) given string'''
- if isinstance(string, unicode):
- return string
- # by the time we go to iso15 it better be the one else we show bad characters
- encodings = (locale.getpreferredencoding(), 'utf-8', 'iso-8859-15')
- for encoding in encodings:
- try:
- string = string.decode(encoding)
- except UnicodeError:
- continue
- break
-
- return string
-
-def ensure_utf8_string(string):
- '''make sure string is in UTF-8'''
- try:
- string = decode_string(string).encode('utf-8')
- except Exception:
- pass
- return string
-
-def remove_invalid_xml_chars(string):
- if string:
- string = re.sub(gajim.interface.invalid_XML_chars_re, '', string)
- return string
-
-def get_windows_reg_env(varname, default=''):
- '''asks for paths commonly used but not exposed as ENVs
- in english Windows 2003 those are:
- 'AppData' = %USERPROFILE%\Application Data (also an ENV)
- 'Desktop' = %USERPROFILE%\Desktop
- 'Favorites' = %USERPROFILE%\Favorites
- 'NetHood' = %USERPROFILE%\NetHood
- 'Personal' = D:\My Documents (PATH TO MY DOCUMENTS)
- 'PrintHood' = %USERPROFILE%\PrintHood
- 'Programs' = %USERPROFILE%\Start Menu\Programs
- 'Recent' = %USERPROFILE%\Recent
- 'SendTo' = %USERPROFILE%\SendTo
- 'Start Menu' = %USERPROFILE%\Start Menu
- 'Startup' = %USERPROFILE%\Start Menu\Programs\Startup
- 'Templates' = %USERPROFILE%\Templates
- 'My Pictures' = D:\My Documents\My Pictures
- 'Local Settings' = %USERPROFILE%\Local Settings
- 'Local AppData' = %USERPROFILE%\Local Settings\Application Data
- 'Cache' = %USERPROFILE%\Local Settings\Temporary Internet Files
- 'Cookies' = %USERPROFILE%\Cookies
- 'History' = %USERPROFILE%\Local Settings\History
- '''
-
- if os.name != 'nt':
- return ''
-
- val = default
- try:
- rkey = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER,
-r'Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders')
- try:
- val = str(win32api.RegQueryValueEx(rkey, varname)[0])
- val = win32api.ExpandEnvironmentStrings(val) # expand using environ
- except Exception:
- pass
- finally:
- win32api.RegCloseKey(rkey)
- return val
-
-def get_my_pictures_path():
- '''windows-only atm. [Unix lives in the past]'''
- return get_windows_reg_env('My Pictures')
-
-def get_desktop_path():
- if os.name == 'nt':
- path = get_windows_reg_env('Desktop')
- else:
- path = os.path.join(os.path.expanduser('~'), 'Desktop')
- return path
-
-def get_documents_path():
- if os.name == 'nt':
- path = get_windows_reg_env('Personal')
- else:
- path = os.path.expanduser('~')
- return path
-
def get_full_jid_from_iq(iq_obj):
'''return the full jid (with resource) from an iq as unicode'''
return parse_jid(str(iq_obj.getFrom()))
@@ -723,6 +871,10 @@
''' return sha of sid + initiator + target used for proxy auth'''
return hashlib.sha1("%s%s%s" % (sid, initiator, target)).hexdigest()
+def remove_invalid_xml_chars(string):
+ if string:
+ string = re.sub(gajim.interface.invalid_XML_chars_re, '', string)
+ return string
distro_info = {
'Arch Linux': '/etc/arch-release',
@@ -832,23 +984,7 @@
gajim.os_info = os_info
return os_info
-def sanitize_filename(filename):
- '''makes sure the filename we will write does contain only acceptable and
- latin characters, and is not too long (in that case hash it)'''
- # 48 is the limit
- if len(filename) > 48:
- hash = hashlib.md5(filename)
- filename = base64.b64encode(hash.digest())
- filename = punycode_encode(filename) # make it latin chars only
- filename = filename.replace('/', '_')
- if os.name == 'nt':
- filename = filename.replace('?', '_').replace(':', '_')\
- .replace('\\', '_').replace('"', "'").replace('|', '_')\
- .replace('*', '_').replace('<', '_').replace('>', '_')
-
- return filename
-
def allow_showing_notification(account, type_ = 'notify_on_new_message',
advanced_notif_num = None, is_first_message = True):
'''is it allowed to show nofication?
@@ -919,39 +1055,6 @@
# unknown contact or offline message
return gajim.interface.msg_win_mgr.get_control(contact.jid, account)
-def reduce_chars_newlines(text, max_chars = 0, max_lines = 0):
- '''Cut the chars after 'max_chars' on each line
- and show only the first 'max_lines'.
- If any of the params is not present (None or 0) the action
- on it is not performed'''
-
- def _cut_if_long(string):
- if len(string) > max_chars:
- string = string[:max_chars - 3] + '...'
- return string
-
- if isinstance(text, str):
- text = text.decode('utf-8')
-
- if max_lines == 0:
- lines = text.split('\n')
- else:
- lines = text.split('\n', max_lines)[:max_lines]
- if max_chars > 0:
- if lines:
- lines = [_cut_if_long(e) for e in lines]
- if lines:
- reduced_text = '\n'.join(lines)
- if reduced_text != text:
- reduced_text += '...'
- else:
- reduced_text = ''
- return reduced_text
-
-def get_account_status(account):
- status = reduce_chars_newlines(account['status_line'], 100, 1)
- return status
-
def get_notification_icon_tooltip_dict():
'''returns a dict of the form {acct: {'show': show, 'message': message,
'event_lines': [list of text lines to show in tooltip]}'''
@@ -1086,38 +1189,7 @@
'show': status, 'message': message})
return accounts
-def get_avatar_path(prefix):
- '''Returns the filename of the avatar, distinguishes between user- and
- contact-provided one. Returns None if no avatar was found at all.
- prefix is the path to the requested avatar just before the ".png" or
- ".jpeg".'''
- # First, scan for a local, user-set avatar
- for type_ in ('jpeg', 'png'):
- file_ = prefix + '_local.' + type_
- if os.path.exists(file_):
- return file_
- # If none available, scan for a contact-provided avatar
- for type_ in ('jpeg', 'png'):
- file_ = prefix + '.' + type_
- if os.path.exists(file_):
- return file_
- return None
-def datetime_tuple(timestamp):
- '''Converts timestamp using strptime and the format: %Y%m%dT%H:%M:%S
- Because of various datetime formats are used the following exceptions
- are handled:
- - Optional milliseconds appened to the string are removed
- - Optional Z (that means UTC) appened to the string are removed
- - XEP-082 datetime strings have all '-' cahrs removed to meet
- the above format.'''
- timestamp = timestamp.split('.')[0]
- timestamp = timestamp.replace('-', '')
- timestamp = timestamp.replace('z', '')
- timestamp = timestamp.replace('Z', '')
- from time import strptime
- return strptime(timestamp, '%Y%m%dT%H:%M:%S')
-
def get_iconset_path(iconset):
if os.path.isdir(os.path.join(gajim.DATA_DIR, 'iconsets', iconset)):
return os.path.join(gajim.DATA_DIR, 'iconsets', iconset)
@@ -1179,74 +1251,6 @@
keyID = 'UNKNOWN'
return keyID
-def sort_identities_func(i1, i2):
- cat1 = i1['category']
- cat2 = i2['category']
- if cat1 < cat2:
- return -1
- if cat1 > cat2:
- return 1
- type1 = i1.get('type', '')
- type2 = i2.get('type', '')
- if type1 < type2:
- return -1
- if type1 > type2:
- return 1
- lang1 = i1.get('xml:lang', '')
- lang2 = i2.get('xml:lang', '')
- if lang1 < lang2:
- return -1
- if lang1 > lang2:
- return 1
- return 0
-
-def sort_dataforms_func(d1, d2):
- f1 = d1.getField('FORM_TYPE')
- f2 = d2.getField('FORM_TYPE')
- if f1 and f2 and (f1.getValue() < f2.getValue()):
- return -1
- return 1
-
-def compute_caps_hash(identities, features, dataforms=[], hash_method='sha-1'):
- '''Compute caps hash according to XEP-0115, V1.5
-
- dataforms are xmpp.DataForms objects as common.dataforms don't allow several
- values without a field type list-multi'''
- S = ''
- identities.sort(cmp=sort_identities_func)
- for i in identities:
- c = i['category']
- type_ = i.get('type', '')
- lang = i.get('xml:lang', '')
- name = i.get('name', '')
- S += '%s/%s/%s/%s<' % (c, type_, lang, name)
- features.sort()
- for f in features:
- S += '%s<' % f
- dataforms.sort(cmp=sort_dataforms_func)
- for dataform in dataforms:
- # fields indexed by var
- fields = {}
- for f in dataform.getChildren():
- fields[f.getVar()] = f
- form_type = fields.get('FORM_TYPE')
- if form_type:
- S += form_type.getValue() + '<'
- del fields['FORM_TYPE']
- for var in sorted(fields.keys()):
- S += '%s<' % var
- values = sorted(fields[var].getValues())
- for value in values:
- S += '%s<' % value
-
- if hash_method == 'sha-1':
- hash_ = hashlib.sha1(S)
- elif hash_method == 'md5':
- hash_ = hashlib.md5(S)
- else:
- return ''
- return base64.b64encode(hash_.digest())
-
def update_optional_features(account = None):
if account:
accounts = [account]