openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #04778
[Merge] lp:~m2j/openlp/work into lp:openlp
m2j has proposed merging lp:~m2j/openlp/work into lp:openlp.
Requested reviews:
OpenLP Core (openlp-core)
Additional buttons in the exception dialog for sending a bug report per email or saving it to a file.
The report contains the traceback along with library versions.
ATM the revision number is not necessary correct. Maybe you want additional informations to be reported (language environment, phonon backend, etc).
--
https://code.launchpad.net/~m2j/openlp/work/+merge/43411
Your team OpenLP Core is requested to review the proposed merge of lp:~m2j/openlp/work into lp:openlp.
=== added directory 'openlp/core/lib/mailto'
=== added file 'openlp/core/lib/mailto/__init__.py'
--- openlp/core/lib/mailto/__init__.py 1970-01-01 00:00:00 +0000
+++ openlp/core/lib/mailto/__init__.py 2010-12-11 00:46:10 +0000
@@ -0,0 +1,338 @@
+#!/usr/bin/env python
+
+'''Utilities for opening files or URLs in the registered default application
+and for sending e-mail using the user's preferred composer.
+
+'''
+
+__version__ = '1.1'
+__all__ = ['open', 'mailto']
+
+import os
+import sys
+import webbrowser
+import subprocess
+
+from email.Utils import encode_rfc2231
+
+_controllers = {}
+_open = None
+
+
+class BaseController(object):
+ '''Base class for open program controllers.'''
+
+ def __init__(self, name):
+ self.name = name
+
+ def open(self, filename):
+ raise NotImplementedError
+
+
+class Controller(BaseController):
+ '''Controller for a generic open program.'''
+
+ def __init__(self, *args):
+ super(Controller, self).__init__(os.path.basename(args[0]))
+ self.args = list(args)
+
+ def _invoke(self, cmdline):
+ if sys.platform[:3] == 'win':
+ closefds = False
+ startupinfo = subprocess.STARTUPINFO()
+ startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
+ else:
+ closefds = True
+ startupinfo = None
+
+ if (os.environ.get('DISPLAY') or sys.platform[:3] == 'win' or
+ sys.platform == 'darwin'):
+ inout = file(os.devnull, 'r+')
+ else:
+ # for TTY programs, we need stdin/out
+ inout = None
+
+ # if possible, put the child precess in separate process group,
+ # so keyboard interrupts don't affect child precess as well as
+ # Python
+ setsid = getattr(os, 'setsid', None)
+ if not setsid:
+ setsid = getattr(os, 'setpgrp', None)
+
+ pipe = subprocess.Popen(cmdline, stdin=inout, stdout=inout,
+ stderr=inout, close_fds=closefds,
+ preexec_fn=setsid, startupinfo=startupinfo)
+
+ # It is assumed that this kind of tools (gnome-open, kfmclient,
+ # exo-open, xdg-open and open for OSX) immediately exit after lauching
+ # the specific application
+ returncode = pipe.wait()
+ if hasattr(self, 'fixreturncode'):
+ returncode = self.fixreturncode(returncode)
+ return not returncode
+
+ def open(self, filename):
+ if isinstance(filename, basestring):
+ cmdline = self.args + [filename]
+ else:
+ # assume it is a sequence
+ cmdline = self.args + filename
+ try:
+ return self._invoke(cmdline)
+ except OSError:
+ return False
+
+
+# Platform support for Windows
+if sys.platform[:3] == 'win':
+
+ class Start(BaseController):
+ '''Controller for the win32 start progam through os.startfile.'''
+
+ def open(self, filename):
+ try:
+ os.startfile(filename)
+ except WindowsError:
+ # [Error 22] No application is associated with the specified
+ # file for this operation: '<URL>'
+ return False
+ else:
+ return True
+
+ _controllers['windows-default'] = Start('start')
+ _open = _controllers['windows-default'].open
+
+
+# Platform support for MacOS
+elif sys.platform == 'darwin':
+ _controllers['open']= Controller('open')
+ _open = _controllers['open'].open
+
+
+# Platform support for Unix
+else:
+
+ import commands
+
+ # @WARNING: use the private API of the webbrowser module
+ from webbrowser import _iscommand
+
+ class KfmClient(Controller):
+ '''Controller for the KDE kfmclient program.'''
+
+ def __init__(self, kfmclient='kfmclient'):
+ super(KfmClient, self).__init__(kfmclient, 'exec')
+ self.kde_version = self.detect_kde_version()
+
+ def detect_kde_version(self):
+ kde_version = None
+ try:
+ info = commands.getoutput('kde-config --version')
+
+ for line in info.splitlines():
+ if line.startswith('KDE'):
+ kde_version = line.split(':')[-1].strip()
+ break
+ except (OSError, RuntimeError):
+ pass
+
+ return kde_version
+
+ def fixreturncode(self, returncode):
+ if returncode is not None and self.kde_version > '3.5.4':
+ return returncode
+ else:
+ return os.EX_OK
+
+ def detect_desktop_environment():
+ '''Checks for known desktop environments
+
+ Return the desktop environments name, lowercase (kde, gnome, xfce)
+ or "generic"
+
+ '''
+
+ desktop_environment = 'generic'
+
+ if os.environ.get('KDE_FULL_SESSION') == 'true':
+ desktop_environment = 'kde'
+ elif os.environ.get('GNOME_DESKTOP_SESSION_ID'):
+ desktop_environment = 'gnome'
+ else:
+ try:
+ info = commands.getoutput('xprop -root _DT_SAVE_MODE')
+ if ' = "xfce4"' in info:
+ desktop_environment = 'xfce'
+ except (OSError, RuntimeError):
+ pass
+
+ return desktop_environment
+
+
+ def register_X_controllers():
+ if _iscommand('kfmclient'):
+ _controllers['kde-open'] = KfmClient()
+
+ for command in ('gnome-open', 'exo-open', 'xdg-open'):
+ if _iscommand(command):
+ _controllers[command] = Controller(command)
+
+ def get():
+ controllers_map = {
+ 'gnome': 'gnome-open',
+ 'kde': 'kde-open',
+ 'xfce': 'exo-open',
+ }
+
+ desktop_environment = detect_desktop_environment()
+
+ try:
+ controller_name = controllers_map[desktop_environment]
+ return _controllers[controller_name].open
+
+ except KeyError:
+ if _controllers.has_key('xdg-open'):
+ return _controllers['xdg-open'].open
+ else:
+ return webbrowser.open
+
+
+ if os.environ.get("DISPLAY"):
+ register_X_controllers()
+ _open = get()
+
+
+def open(filename):
+ '''Open a file or an URL in the registered default application.'''
+
+ return _open(filename)
+
+
+def _fix_addersses(**kwargs):
+ for headername in ('address', 'to', 'cc', 'bcc'):
+ try:
+ headervalue = kwargs[headername]
+ if not headervalue:
+ del kwargs[headername]
+ continue
+ elif not isinstance(headervalue, basestring):
+ # assume it is a sequence
+ headervalue = ','.join(headervalue)
+
+ except KeyError:
+ pass
+ except TypeError:
+ raise TypeError('string or sequence expected for "%s", '
+ '%s found' % (headername,
+ type(headervalue).__name__))
+ else:
+ translation_map = {'%': '%25', '&': '%26', '?': '%3F'}
+ for char, replacement in translation_map.items():
+ headervalue = headervalue.replace(char, replacement)
+ kwargs[headername] = headervalue
+
+ return kwargs
+
+
+def mailto_format(**kwargs):
+ # @TODO: implement utf8 option
+
+ kwargs = _fix_addersses(**kwargs)
+ parts = []
+ for headername in ('to', 'cc', 'bcc', 'subject', 'body', 'attach'):
+ if kwargs.has_key(headername):
+ headervalue = kwargs[headername]
+ if not headervalue:
+ continue
+ if headername in ('address', 'to', 'cc', 'bcc'):
+ parts.append('%s=%s' % (headername, headervalue))
+ else:
+ headervalue = encode_rfc2231(headervalue) # @TODO: check
+ parts.append('%s=%s' % (headername, headervalue))
+
+ mailto_string = 'mailto:%s' % kwargs.get('address', '')
+ if parts:
+ mailto_string = '%s?%s' % (mailto_string, '&'.join(parts))
+
+ return mailto_string
+
+
+def mailto(address, to=None, cc=None, bcc=None, subject=None, body=None,
+ attach=None):
+ '''Send an e-mail using the user's preferred composer.
+
+ Open the user's preferred e-mail composer in order to send a mail to
+ address(es) that must follow the syntax of RFC822. Multiple addresses
+ may be provided (for address, cc and bcc parameters) as separate
+ arguments.
+
+ All parameters provided are used to prefill corresponding fields in
+ the user's e-mail composer. The user will have the opportunity to
+ change any of this information before actually sending the e-mail.
+
+ address - specify the destination recipient
+ cc - specify a recipient to be copied on the e-mail
+ bcc - specify a recipient to be blindly copied on the e-mail
+ subject - specify a subject for the e-mail
+ body - specify a body for the e-mail. Since the user will be able
+ to make changes before actually sending the e-mail, this
+ can be used to provide the user with a template for the
+ e-mail text may contain linebreaks
+ attach - specify an attachment for the e-mail. file must point to
+ an existing file
+
+ '''
+
+ mailto_string = mailto_format(**locals())
+ return open(mailto_string)
+
+
+if __name__ == '__main__':
+ from optparse import OptionParser
+
+ version = '%%prog %s' % __version__
+ usage = (
+ '\n\n%prog FILENAME [FILENAME(s)] -- for opening files'
+ '\n\n%prog -m [OPTIONS] ADDRESS [ADDRESS(es)] -- for sending e-mails'
+ )
+
+ parser = OptionParser(usage=usage, version=version, description=__doc__)
+ parser.add_option('-m', '--mailto', dest='mailto_mode', default=False,
+ action='store_true', help='set mailto mode. '
+ 'If not set any other option is ignored')
+ parser.add_option('--cc', dest='cc', help='specify a recipient to be '
+ 'copied on the e-mail')
+ parser.add_option('--bcc', dest='bcc', help='specify a recipient to be '
+ 'blindly copied on the e-mail')
+ parser.add_option('--subject', dest='subject',
+ help='specify a subject for the e-mail')
+ parser.add_option('--body', dest='body', help='specify a body for the '
+ 'e-mail. Since the user will be able to make changes '
+ 'before actually sending the e-mail, this can be used '
+ 'to provide the user with a template for the e-mail '
+ 'text may contain linebreaks')
+ parser.add_option('--attach', dest='attach', help='specify an attachment '
+ 'for the e-mail. file must point to an existing file')
+
+ (options, args) = parser.parse_args()
+
+ if not args:
+ parser.print_usage()
+ parser.exit(1)
+
+ if options.mailto_mode:
+ if not mailto(args, None, options.cc, options.bcc, options.subject,
+ options.body, options.attach):
+ sys.exit('Unable to open the e-mail client')
+ else:
+ for name in ('cc', 'bcc', 'subject', 'body', 'attach'):
+ if getattr(options, name):
+ parser.error('The "cc", "bcc", "subject", "body" and "attach" '
+ 'options are only accepten in mailto mode')
+ success = False
+ for arg in args:
+ if not open(arg):
+ print 'Unable to open "%s"' % arg
+ else:
+ success = True
+ sys.exit(success)
=== added file 'openlp/core/lib/mailto/mailto.INFO'
--- openlp/core/lib/mailto/mailto.INFO 1970-01-01 00:00:00 +0000
+++ openlp/core/lib/mailto/mailto.INFO 2010-12-11 00:46:10 +0000
@@ -0,0 +1,4 @@
+Cross-platform startfile and mailto functions
+Author: Antonio Valentino
+License: PSF license (http://docs.python.org/license.html)
+Source: http://code.activestate.com/recipes/511443-cross-platform-startfile-and-mailto-functions/
=== modified file 'openlp/core/resources.py'
--- openlp/core/resources.py 2010-11-19 22:26:15 +0000
+++ openlp/core/resources.py 2010-12-11 00:46:10 +0000
@@ -59162,6 +59162,56 @@
\xea\x24\x0f\x93\xcc\x00\x6a\x00\x7e\x03\x38\x00\x50\x3d\x94\x6f\
\x8c\xf9\xf3\x17\xb1\x57\xd8\xfd\x23\x30\x24\x2d\x00\x00\x00\x00\
\x49\x45\x4e\x44\xae\x42\x60\x82\
+\x00\x00\x02\xf3\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
+\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
+\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x03\x76\x00\x00\x03\x76\
+\x01\x7d\xd5\x82\xcc\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
+\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
+\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x02\x70\x49\x44\
+\x41\x54\x78\xda\xc5\x93\x4f\x48\xd4\x41\x14\xc7\xbf\x33\xf3\x1b\
+\x65\x5d\x77\x5b\x93\x4c\xd7\xe4\xb7\x89\xfd\x73\x65\x4b\x5a\x09\
+\x14\x42\x4b\x44\x89\xa0\x5b\x97\x8e\x49\xd0\xd1\x0e\x66\x81\xd4\
+\x12\x5e\x2a\x08\xa1\x93\xa7\xa0\x4b\x88\x10\x21\x78\xf1\x18\x15\
+\x91\x88\x84\x88\x10\xb9\xbb\xb5\x89\xb4\xb4\xb9\xbb\xba\xfb\xdb\
+\xdf\xfe\xe6\x35\x8e\x74\xed\xe2\xa1\x0f\x3c\x18\xbe\xbc\xef\x7b\
+\x6f\xde\x30\x8c\x88\x70\x10\x38\x0e\x08\x6b\x68\x68\xe8\xb3\x6d\
+\xbb\x05\x9a\x10\x23\xf9\x9b\x98\x0b\x8d\xa8\xa9\xb7\x94\xbb\x63\
+\xce\x41\x22\x99\x67\xfb\x3a\x97\x7e\xf9\x57\x4f\xa5\x52\x9b\x98\
+\x98\x98\x98\xfa\x99\x2b\x52\x36\x57\xa0\x4c\x4f\x8c\x8a\xbb\x0e\
+\x95\x1d\x97\x86\x1f\xbd\x23\xa5\x14\x79\xe5\x12\xfd\xe8\x8b\x93\
+\xeb\xba\x26\x2e\x3d\x78\xab\x73\xca\x94\x2f\x96\x28\x91\x48\x3c\
+\xb1\x38\xe7\x70\x1c\x17\x96\xc5\x51\x73\x3f\x01\x80\xc0\x18\x70\
+\xf7\x5a\x07\x88\x08\xc4\x05\x02\x93\x09\x73\xd6\x05\x71\xe7\xca\
+\x71\x93\x5f\x55\xb4\xa7\x29\x71\xb2\xff\xc6\xcd\x97\x2b\xac\x6b\
+\x28\x76\x04\xc1\xce\x53\xba\x90\x80\xb4\x2c\x44\x9a\xea\x8c\x89\
+\x00\x88\x48\x3b\x0a\x3b\x25\xcc\xbe\xcf\x42\x11\x47\xd0\x07\xdc\
+\x9a\x59\x03\xf3\x4a\x19\x1e\xc6\xf7\xe4\x8b\xdb\x31\x34\x06\x6a\
+\x20\x04\x87\x25\x84\x99\xc0\x98\x09\x70\x5c\x0f\xa5\x8a\x87\x6a\
+\xb5\x8a\x1d\x57\x21\xe0\xb7\xe0\x97\x0c\x33\xa3\x9d\x08\xf3\xcd\
+\x34\x67\x9a\x5a\x29\xcc\x15\x7e\x65\xb3\xe0\x0c\x7a\x44\xc7\x18\
+\xca\xe5\x12\xb4\x0c\x06\xa5\x43\xa0\xa9\x9e\x43\x0a\x86\xaa\xa7\
+\x4c\x3e\x48\x29\x2b\xfa\x2d\x19\xd9\x1d\xbe\x88\xd0\xdc\x3c\x96\
+\x97\x97\x30\x37\xf7\xd5\x14\x88\xc7\xe3\x48\xa7\xd3\x90\x52\xe2\
+\xec\xb9\x6e\x1c\x6b\xb3\x31\x10\x3d\x0c\xb7\xea\x81\x72\x39\xd0\
+\xf5\x11\x9c\x39\xd1\x69\x5b\xeb\x76\x7b\xfa\xea\xb3\xe7\x90\xfe\
+\x3a\x0c\x0e\x0e\x9a\xd1\x35\xc6\x58\xa9\x54\x50\xd5\x86\x92\x53\
+\x31\x5d\xbd\xbd\x50\x04\xd9\xd8\x88\xda\xd7\x8b\x58\x9b\x7e\x9a\
+\xb4\x88\x31\x12\x3e\x1f\x04\x67\x78\xb3\xb0\x80\x42\xa1\x80\xe6\
+\xe6\x66\x6c\x6d\x6d\x81\x73\x0e\x4b\xd6\x22\xa7\x3b\xfa\x03\x87\
+\x30\x70\x79\x68\x7f\x4f\x3a\x84\xac\x81\xd2\x5e\x2b\x43\xad\x91\
+\x91\xa9\x0f\x98\x1d\xeb\x41\x5b\xac\x1f\xdd\x91\x20\xc8\xf3\x50\
+\xf9\xf4\x11\xe8\x8e\xc3\x71\x15\x96\x37\xb6\x71\xba\xa5\x4e\xeb\
+\x0a\xe2\xf3\x12\x0a\x5d\xe7\x31\x3a\xbd\x02\x5b\xb5\x46\x78\x9b\
+\xd8\x4c\x2d\x4e\xf6\x22\xa4\xb7\x3b\xf9\x6a\x1d\x7b\x50\xd5\x45\
+\x7e\x7c\x0c\x00\x33\x23\x3f\x9e\x4f\x9a\xe7\xf5\x49\x80\x1e\x8e\
+\xe3\x68\xc8\x87\x85\x7b\x17\xd0\x42\xe9\x0d\x16\x0e\x87\x7b\xa3\
+\xd1\xa8\x0d\x8d\xaa\x6f\x0d\xf2\x62\x26\x0f\x4d\x87\xe7\x1e\xfa\
+\x22\xe4\xf6\xbf\xf4\xd5\xd5\xd5\xd4\xff\xff\x8d\x7f\x00\xba\xc6\
+\x35\x77\x1f\xae\xe5\xaf\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\
+\x60\x82\
\x00\x00\x02\x74\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@@ -59835,6 +59885,11 @@
\x0e\x36\xec\xe7\
\x00\x67\
\x00\x65\x00\x6e\x00\x65\x00\x72\x00\x61\x00\x6c\x00\x5f\x00\x61\x00\x64\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x11\
+\x06\x44\xfc\x07\
+\x00\x67\
+\x00\x65\x00\x6e\x00\x65\x00\x72\x00\x61\x00\x6c\x00\x5f\x00\x65\x00\x6d\x00\x61\x00\x69\x00\x6c\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\
\x00\x12\
\x0d\x2c\x6d\x87\
\x00\x67\
@@ -59857,32 +59912,33 @@
qt_resource_struct = "\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x0f\x00\x00\x00\x01\
-\x00\x00\x00\x98\x00\x02\x00\x00\x00\x06\x00\x00\x00\x6b\
-\x00\x00\x00\x3a\x00\x02\x00\x00\x00\x04\x00\x00\x00\x67\
-\x00\x00\x00\xf4\x00\x02\x00\x00\x00\x03\x00\x00\x00\x64\
-\x00\x00\x00\x5e\x00\x02\x00\x00\x00\x10\x00\x00\x00\x54\
-\x00\x00\x00\x4e\x00\x02\x00\x00\x00\x02\x00\x00\x00\x52\
-\x00\x00\x00\x84\x00\x02\x00\x00\x00\x09\x00\x00\x00\x49\
-\x00\x00\x00\x14\x00\x02\x00\x00\x00\x0b\x00\x00\x00\x3e\
-\x00\x00\x00\xa6\x00\x02\x00\x00\x00\x03\x00\x00\x00\x3b\
-\x00\x00\x00\xe2\x00\x02\x00\x00\x00\x0c\x00\x00\x00\x2f\
-\x00\x00\x00\x6e\x00\x02\x00\x00\x00\x03\x00\x00\x00\x2c\
-\x00\x00\x01\x04\x00\x02\x00\x00\x00\x03\x00\x00\x00\x29\
-\x00\x00\x00\xcc\x00\x02\x00\x00\x00\x08\x00\x00\x00\x21\
-\x00\x00\x00\x26\x00\x02\x00\x00\x00\x04\x00\x00\x00\x1d\
-\x00\x00\x00\x00\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x13\
+\x00\x00\x00\x98\x00\x02\x00\x00\x00\x06\x00\x00\x00\x6c\
+\x00\x00\x00\x3a\x00\x02\x00\x00\x00\x04\x00\x00\x00\x68\
+\x00\x00\x00\xf4\x00\x02\x00\x00\x00\x03\x00\x00\x00\x65\
+\x00\x00\x00\x5e\x00\x02\x00\x00\x00\x10\x00\x00\x00\x55\
+\x00\x00\x00\x4e\x00\x02\x00\x00\x00\x02\x00\x00\x00\x53\
+\x00\x00\x00\x84\x00\x02\x00\x00\x00\x09\x00\x00\x00\x4a\
+\x00\x00\x00\x14\x00\x02\x00\x00\x00\x0b\x00\x00\x00\x3f\
+\x00\x00\x00\xa6\x00\x02\x00\x00\x00\x03\x00\x00\x00\x3c\
+\x00\x00\x00\xe2\x00\x02\x00\x00\x00\x0c\x00\x00\x00\x30\
+\x00\x00\x00\x6e\x00\x02\x00\x00\x00\x03\x00\x00\x00\x2d\
+\x00\x00\x01\x04\x00\x02\x00\x00\x00\x03\x00\x00\x00\x2a\
+\x00\x00\x00\xcc\x00\x02\x00\x00\x00\x08\x00\x00\x00\x22\
+\x00\x00\x00\x26\x00\x02\x00\x00\x00\x04\x00\x00\x00\x1e\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x0b\x00\x00\x00\x13\
\x00\x00\x00\xb8\x00\x02\x00\x00\x00\x03\x00\x00\x00\x10\
\x00\x00\x06\x26\x00\x00\x00\x00\x00\x01\x00\x08\x78\xb3\
\x00\x00\x05\xc2\x00\x00\x00\x00\x00\x01\x00\x03\x36\xef\
\x00\x00\x05\xf4\x00\x00\x00\x00\x00\x01\x00\x05\xd7\xd1\
\x00\x00\x10\x3e\x00\x00\x00\x00\x00\x01\x00\x0e\x5b\xe4\
\x00\x00\x0f\xee\x00\x00\x00\x00\x00\x01\x00\x0e\x56\x88\
-\x00\x00\x10\xd8\x00\x00\x00\x00\x00\x01\x00\x0e\x65\xa7\
+\x00\x00\x11\x00\x00\x00\x00\x00\x00\x01\x00\x0e\x68\x9e\
+\x00\x00\x10\xae\x00\x00\x00\x00\x00\x01\x00\x0e\x63\x2f\
\x00\x00\x0f\xc4\x00\x00\x00\x00\x00\x01\x00\x0e\x53\xea\
-\x00\x00\x11\x04\x00\x00\x00\x00\x00\x01\x00\x0e\x68\xda\
-\x00\x00\x10\xae\x00\x00\x00\x00\x00\x01\x00\x0e\x63\x2f\
+\x00\x00\x11\x2c\x00\x00\x00\x00\x00\x01\x00\x0e\x6b\xd1\
+\x00\x00\x10\xd6\x00\x00\x00\x00\x00\x01\x00\x0e\x66\x26\
\x00\x00\x10\x14\x00\x00\x00\x00\x00\x01\x00\x0e\x59\x62\
-\x00\x00\x11\x2a\x00\x00\x00\x00\x00\x01\x00\x0e\x6b\xae\
+\x00\x00\x11\x52\x00\x00\x00\x00\x00\x01\x00\x0e\x6e\xa5\
\x00\x00\x10\x8a\x00\x00\x00\x00\x00\x01\x00\x0e\x60\x32\
\x00\x00\x10\x64\x00\x00\x00\x00\x00\x01\x00\x0e\x5e\x1b\
\x00\x00\x0d\x50\x00\x00\x00\x00\x00\x01\x00\x0e\x2d\xb0\
=== modified file 'openlp/core/ui/exceptiondialog.py'
--- openlp/core/ui/exceptiondialog.py 2010-09-19 08:47:00 +0000
+++ openlp/core/ui/exceptiondialog.py 2010-12-11 00:46:10 +0000
@@ -26,7 +26,7 @@
from PyQt4 import QtCore, QtGui
-from openlp.core.lib import translate
+from openlp.core.lib import translate, build_icon
class Ui_ExceptionDialog(object):
def setupUi(self, exceptionDialog):
@@ -63,12 +63,26 @@
self.exceptionButtonBox.setStandardButtons(QtGui.QDialogButtonBox.Close)
self.exceptionButtonBox.setObjectName(u'exceptionButtonBox')
self.exceptionLayout.addWidget(self.exceptionButtonBox)
-
+ self.saveReportButton = QtGui.QPushButton(exceptionDialog)
+ self.saveReportButton.setIcon(build_icon(u':/general/general_save.png'))
+ self.saveReportButton.setObjectName(u'saveReportButton')
+ self.exceptionButtonBox.addButton(self.saveReportButton,
+ QtGui.QDialogButtonBox.ActionRole)
+ self.sendReportButton = QtGui.QPushButton(exceptionDialog)
+ self.sendReportButton.setIcon(build_icon(
+ u':/general/general_email.png'))
+ self.sendReportButton.setObjectName(u'sendReportButton')
+ self.exceptionButtonBox.addButton(self.sendReportButton,
+ QtGui.QDialogButtonBox.ActionRole)
self.retranslateUi(exceptionDialog)
QtCore.QObject.connect(self.exceptionButtonBox,
QtCore.SIGNAL(u'accepted()'), exceptionDialog.accept)
QtCore.QObject.connect(self.exceptionButtonBox,
QtCore.SIGNAL(u'rejected()'), exceptionDialog.reject)
+ QtCore.QObject.connect(self.saveReportButton,
+ QtCore.SIGNAL(u'pressed()'), self.onSaveReportButtonPressed)
+ QtCore.QObject.connect(self.sendReportButton,
+ QtCore.SIGNAL(u'pressed()'), self.onSendReportButtonPressed)
QtCore.QMetaObject.connectSlotsByName(exceptionDialog)
def retranslateUi(self, exceptionDialog):
@@ -80,3 +94,7 @@
'developers, so please e-mail it to bugs@xxxxxxxxxx, along with a '
'detailed description of what you were doing when the problem '
'occurred.'))
+ self.saveReportButton.setText(translate('OpenLP.ExceptionDialog',
+ 'Save Report to File'))
+ self.sendReportButton.setText(translate('OpenLP.ExceptionDialog',
+ 'Send Report Mail'))
=== modified file 'openlp/core/ui/exceptionform.py'
--- openlp/core/ui/exceptionform.py 2010-10-21 15:31:22 +0000
+++ openlp/core/ui/exceptionform.py 2010-12-11 00:46:10 +0000
@@ -24,7 +24,23 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
-from PyQt4 import QtGui
+import os
+import platform
+
+import sqlalchemy
+import BeautifulSoup
+import enchant
+import chardet
+try:
+ import sqlite
+ sqlite_version = sqlite.version
+except ImportError:
+ sqlite_version = u'-'
+
+from lxml import etree
+from PyQt4 import Qt, QtCore, QtGui
+
+from openlp.core.lib import translate, SettingsManager, mailto
from exceptiondialog import Ui_ExceptionDialog
@@ -35,3 +51,71 @@
def __init__(self, parent):
QtGui.QDialog.__init__(self, parent)
self.setupUi(self)
+ self.settingsSection = u'crashreport'
+ #TODO: Icons
+
+ def _createReport(self):
+ openlp_version = self.parent().applicationVersion[u'full']
+ traceback = unicode(self.exceptionTextEdit.toPlainText())
+ system = unicode(translate('OpenLP.ExceptionForm',
+ 'Platform: %s\n')) % (platform.platform())
+ libraries = unicode(translate('OpenLP.ExceptionForm',
+ 'Python: %s\n'
+ 'PyQt4: %s\n'
+ 'Qt4: %s\n'
+ 'SQLAlchemy: %s\n'
+ 'lxml: %s\n'
+ 'BeautifulSoup: %s\n'
+ 'PyEnchant: %s\n'
+ 'Chardet: %s\n'
+ 'PySQLite: %s\n')) % (platform.python_version(),
+ Qt.PYQT_VERSION_STR, Qt.qVersion(), sqlalchemy.__version__,
+ etree.__version__, BeautifulSoup.__version__ , enchant.__version__,
+ chardet.__version__, sqlite_version)
+ return (openlp_version, traceback, system, libraries)
+
+ def onSaveReportButtonPressed(self):
+ """
+ Saving exception log and system informations to a file.
+ """
+ report = unicode(translate('OpenLP.ExceptionForm',
+ '**OpenLP Bug Report**\n'
+ 'Version: %s\n\n'
+ '--- Exception Traceback ---\n%s\n'
+ '--- System information ---\n%s\n'
+ '--- Library Versions ---\n%s\n'))
+ filename = QtGui.QFileDialog.getSaveFileName(self,
+ translate('OpenLP.ExceptionForm', 'Save Crash Report'),
+ SettingsManager.get_last_dir(self.settingsSection),
+ translate('OpenLP.ExceptionForm', 'Text files (*.txt *.log *.text)'))
+ if filename:
+ filename = unicode(QtCore.QDir.toNativeSeparators(filename))
+ SettingsManager.set_last_dir(self.settingsSection, os.path.dirname(
+ filename))
+ report = report % self._createReport()
+ try:
+ file = open(filename, u'w')
+ try:
+ file.write(report)
+ except UnicodeError:
+ file.close()
+ file = open(filename, u'wb')
+ file.write(report.encode(u'utf-8'))
+ file.close()
+ except IOError:
+ log.exception(u'Failed to write crash report')
+
+ def onSendReportButtonPressed(self):
+ """
+ Opening systems default email client and inserting exception log and
+ system informations.
+ """
+ email_body = unicode(translate('OpenLP.ExceptionForm',
+ '*OpenLP Bug Report*\n'
+ 'Version: %s\n\n'
+ '--- Please enter the report below this line. ---\n\n\n'
+ '--- Exception Traceback ---\n%s\n'
+ '--- System information ---\n%s\n'
+ '--- Library Versions ---\n%s\n'))
+ mailto.mailto(address=u'bugs@xxxxxxxxxx', subject=u'OpenLP Bug Report',
+ body=email_body % self._createReport())
=== added file 'resources/images/general_email.png'
Binary files resources/images/general_email.png 1970-01-01 00:00:00 +0000 and resources/images/general_email.png 2010-12-11 00:46:10 +0000 differ
=== modified file 'resources/images/openlp-2.qrc'
--- resources/images/openlp-2.qrc 2010-11-19 22:26:15 +0000
+++ resources/images/openlp-2.qrc 2010-12-11 00:46:10 +0000
@@ -39,6 +39,7 @@
<file>general_new.png</file>
<file>general_open.png</file>
<file>general_save.png</file>
+ <file>general_email.png</file>
</qresource>
<qresource prefix="slides">
<file>slide_close.png</file>
Follow ups