forked from openlp/openlp
Head1145
This commit is contained in:
commit
64a819045e
38
openlp/core/lib/mailto/LICENSE
Normal file
38
openlp/core/lib/mailto/LICENSE
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
PSF LICENSE AGREEMENT FOR PYTHON 2.7.1
|
||||||
|
|
||||||
|
1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"),
|
||||||
|
and the Individual or Organization ("Licensee") accessing and otherwise
|
||||||
|
using Python 2.7.1 software in source or binary form and its associated
|
||||||
|
documentation.
|
||||||
|
2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
||||||
|
grants Licensee a nonexclusive, royalty-free, world-wide license to
|
||||||
|
reproduce, analyze, test, perform and/or display publicly, prepare
|
||||||
|
derivative works, distribute, and otherwise use Python 2.7.1 alone or in any
|
||||||
|
derivative version, provided, however, that PSF's License Agreement and
|
||||||
|
PSF's notice of copyright, i.e., "Copyright (c) 2001-2010 Python Software
|
||||||
|
Foundation; All Rights Reserved" are retained in Python 2.7.1 alone or in
|
||||||
|
any derivative version prepared by Licensee.
|
||||||
|
3. In the event Licensee prepares a derivative work that is based on or
|
||||||
|
incorporates Python 2.7.1 or any part thereof, and wants to make the
|
||||||
|
derivative work available to others as provided herein, then Licensee hereby
|
||||||
|
agrees to include in any such work a brief summary of the changes made to
|
||||||
|
Python 2.7.1.
|
||||||
|
4. PSF is making Python 2.7.1 available to Licensee on an "AS IS" basis. PSF
|
||||||
|
MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF
|
||||||
|
EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION
|
||||||
|
OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT
|
||||||
|
THE USE OF PYTHON 2.7.1 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
|
||||||
|
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 2.7.1 FOR
|
||||||
|
ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF
|
||||||
|
MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.7.1, OR ANY DERIVATIVE
|
||||||
|
THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||||
|
6. This License Agreement will automatically terminate upon a material breach
|
||||||
|
of its terms and conditions.
|
||||||
|
7. Nothing in this License Agreement shall be deemed to create any relationship
|
||||||
|
of agency, partnership, or joint venture between PSF and Licensee. This
|
||||||
|
License Agreement does not grant permission to use PSF trademarks or trade
|
||||||
|
name in a trademark sense to endorse or promote products or services of
|
||||||
|
Licensee, or any third party.
|
||||||
|
8. By copying, installing or otherwise using Python 2.7.1, Licensee agrees to
|
||||||
|
be bound by the terms and conditions of this License Agreement.
|
||||||
|
|
321
openlp/core/lib/mailto/__init__.py
Normal file
321
openlp/core/lib/mailto/__init__.py
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Utilities for opening files or URLs in the registered default application #
|
||||||
|
# and for sending e-mail using the user's preferred composer. #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2007 Antonio Valentino #
|
||||||
|
# All rights reserved. #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program offered under the PSF License as published by the Python #
|
||||||
|
# Software Foundation. #
|
||||||
|
# #
|
||||||
|
# The license text can be found at http://docs.python.org/license.html #
|
||||||
|
# #
|
||||||
|
# This code is taken from: http://code.activestate.com/recipes/511443 #
|
||||||
|
# Modified for use in OpenLP #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
__version__ = u'1.1'
|
||||||
|
__all__ = [u'open', u'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] == u'win':
|
||||||
|
closefds = False
|
||||||
|
startupinfo = subprocess.STARTUPINFO()
|
||||||
|
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||||
|
else:
|
||||||
|
closefds = True
|
||||||
|
startupinfo = None
|
||||||
|
|
||||||
|
if (os.environ.get(u'DISPLAY') or sys.platform[:3] == u'win' or \
|
||||||
|
sys.platform == u'darwin'):
|
||||||
|
inout = file(os.devnull, u'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, u'setsid', None)
|
||||||
|
if not setsid:
|
||||||
|
setsid = getattr(os, u'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, u'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] == u'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[u'windows-default'] = Start(u'start')
|
||||||
|
_open = _controllers[u'windows-default'].open
|
||||||
|
|
||||||
|
|
||||||
|
# Platform support for MacOS
|
||||||
|
elif sys.platform == u'darwin':
|
||||||
|
_controllers[u'open']= Controller(u'open')
|
||||||
|
_open = _controllers[u'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=u'kfmclient'):
|
||||||
|
super(KfmClient, self).__init__(kfmclient, u'exec')
|
||||||
|
self.kde_version = self.detect_kde_version()
|
||||||
|
|
||||||
|
def detect_kde_version(self):
|
||||||
|
kde_version = None
|
||||||
|
try:
|
||||||
|
info = commands.getoutput(u'kfmclient --version')
|
||||||
|
|
||||||
|
for line in info.splitlines():
|
||||||
|
if line.startswith(u'KDE'):
|
||||||
|
kde_version = line.split(u':')[-1].strip()
|
||||||
|
break
|
||||||
|
except (OSError, RuntimeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
return kde_version
|
||||||
|
|
||||||
|
def fixreturncode(self, returncode):
|
||||||
|
if returncode is not None and self.kde_version > u'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 = u'generic'
|
||||||
|
|
||||||
|
if os.environ.get(u'KDE_FULL_SESSION') == u'true':
|
||||||
|
desktop_environment = u'kde'
|
||||||
|
elif os.environ.get(u'GNOME_DESKTOP_SESSION_ID'):
|
||||||
|
desktop_environment = u'gnome'
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
info = commands.getoutput(u'xprop -root _DT_SAVE_MODE')
|
||||||
|
if u' = "xfce4"' in info:
|
||||||
|
desktop_environment = u'xfce'
|
||||||
|
except (OSError, RuntimeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
return desktop_environment
|
||||||
|
|
||||||
|
|
||||||
|
def register_X_controllers():
|
||||||
|
if _iscommand(u'kfmclient'):
|
||||||
|
_controllers[u'kde-open'] = KfmClient()
|
||||||
|
|
||||||
|
for command in (u'gnome-open', u'exo-open', u'xdg-open'):
|
||||||
|
if _iscommand(command):
|
||||||
|
_controllers[command] = Controller(command)
|
||||||
|
|
||||||
|
|
||||||
|
def get():
|
||||||
|
controllers_map = {
|
||||||
|
u'gnome': u'gnome-open',
|
||||||
|
u'kde': u'kde-open',
|
||||||
|
u'xfce': u'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(u'xdg-open'):
|
||||||
|
return _controllers[u'xdg-open'].open
|
||||||
|
else:
|
||||||
|
return webbrowser.open
|
||||||
|
|
||||||
|
if os.environ.get(u'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 (u'address', u'to', u'cc', u'bcc'):
|
||||||
|
try:
|
||||||
|
headervalue = kwargs[headername]
|
||||||
|
if not headervalue:
|
||||||
|
del kwargs[headername]
|
||||||
|
continue
|
||||||
|
elif not isinstance(headervalue, basestring):
|
||||||
|
# assume it is a sequence
|
||||||
|
headervalue = u','.join(headervalue)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
except TypeError:
|
||||||
|
raise TypeError(u'string or sequence expected for "%s", %s '
|
||||||
|
u'found' % (headername, type(headervalue).__name__))
|
||||||
|
else:
|
||||||
|
translation_map = {u'%': u'%25', u'&': u'%26', u'?': u'%3F'}
|
||||||
|
for char, replacement in translation_map.items():
|
||||||
|
headervalue = headervalue.replace(char, replacement)
|
||||||
|
kwargs[headername] = headervalue
|
||||||
|
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
|
def mailto_format(**kwargs):
|
||||||
|
"""
|
||||||
|
Compile mailto string from call parameters
|
||||||
|
"""
|
||||||
|
# @TODO: implement utf8 option
|
||||||
|
|
||||||
|
kwargs = _fix_addersses(**kwargs)
|
||||||
|
parts = []
|
||||||
|
for headername in (u'to', u'cc', u'bcc', u'subject', u'body', u'attach'):
|
||||||
|
if kwargs.has_key(headername):
|
||||||
|
headervalue = kwargs[headername]
|
||||||
|
if not headervalue:
|
||||||
|
continue
|
||||||
|
if headername in (u'address', u'to', u'cc', u'bcc'):
|
||||||
|
parts.append(u'%s=%s' % (headername, headervalue))
|
||||||
|
else:
|
||||||
|
headervalue = encode_rfc2231(headervalue) # @TODO: check
|
||||||
|
parts.append(u'%s=%s' % (headername, headervalue))
|
||||||
|
|
||||||
|
mailto_string = u'mailto:%s' % kwargs.get(u'address', '')
|
||||||
|
if parts:
|
||||||
|
mailto_string = u'%s?%s' % (mailto_string, u'&'.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)
|
||||||
|
|
@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.lib import translate
|
from openlp.core.lib import translate, build_icon
|
||||||
|
|
||||||
class Ui_ExceptionDialog(object):
|
class Ui_ExceptionDialog(object):
|
||||||
def setupUi(self, exceptionDialog):
|
def setupUi(self, exceptionDialog):
|
||||||
@ -63,12 +63,27 @@ class Ui_ExceptionDialog(object):
|
|||||||
self.exceptionButtonBox.setStandardButtons(QtGui.QDialogButtonBox.Close)
|
self.exceptionButtonBox.setStandardButtons(QtGui.QDialogButtonBox.Close)
|
||||||
self.exceptionButtonBox.setObjectName(u'exceptionButtonBox')
|
self.exceptionButtonBox.setObjectName(u'exceptionButtonBox')
|
||||||
self.exceptionLayout.addWidget(self.exceptionButtonBox)
|
self.exceptionLayout.addWidget(self.exceptionButtonBox)
|
||||||
|
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.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.retranslateUi(exceptionDialog)
|
self.retranslateUi(exceptionDialog)
|
||||||
QtCore.QObject.connect(self.exceptionButtonBox,
|
QtCore.QObject.connect(self.exceptionButtonBox,
|
||||||
QtCore.SIGNAL(u'accepted()'), exceptionDialog.accept)
|
QtCore.SIGNAL(u'accepted()'), exceptionDialog.accept)
|
||||||
QtCore.QObject.connect(self.exceptionButtonBox,
|
QtCore.QObject.connect(self.exceptionButtonBox,
|
||||||
QtCore.SIGNAL(u'rejected()'), exceptionDialog.reject)
|
QtCore.SIGNAL(u'rejected()'), exceptionDialog.reject)
|
||||||
|
QtCore.QObject.connect(self.sendReportButton,
|
||||||
|
QtCore.SIGNAL(u'pressed()'), self.onSendReportButtonPressed)
|
||||||
|
QtCore.QObject.connect(self.saveReportButton,
|
||||||
|
QtCore.SIGNAL(u'pressed()'), self.onSaveReportButtonPressed)
|
||||||
QtCore.QMetaObject.connectSlotsByName(exceptionDialog)
|
QtCore.QMetaObject.connectSlotsByName(exceptionDialog)
|
||||||
|
|
||||||
def retranslateUi(self, exceptionDialog):
|
def retranslateUi(self, exceptionDialog):
|
||||||
@ -80,3 +95,7 @@ class Ui_ExceptionDialog(object):
|
|||||||
'developers, so please e-mail it to bugs@openlp.org, along with a '
|
'developers, so please e-mail it to bugs@openlp.org, along with a '
|
||||||
'detailed description of what you were doing when the problem '
|
'detailed description of what you were doing when the problem '
|
||||||
'occurred.'))
|
'occurred.'))
|
||||||
|
self.sendReportButton.setText(translate('OpenLP.ExceptionDialog',
|
||||||
|
'Send E-Mail'))
|
||||||
|
self.saveReportButton.setText(translate('OpenLP.ExceptionDialog',
|
||||||
|
'Save to File'))
|
||||||
|
@ -24,7 +24,38 @@
|
|||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
from PyQt4 import QtGui
|
import re
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
|
||||||
|
import sqlalchemy
|
||||||
|
import BeautifulSoup
|
||||||
|
from lxml import etree
|
||||||
|
from PyQt4 import Qt, QtCore, QtGui
|
||||||
|
|
||||||
|
try:
|
||||||
|
from PyQt4.phonon import Phonon
|
||||||
|
phonon_version = Phonon.phononVersion()
|
||||||
|
except ImportError:
|
||||||
|
phonon_version = u'-'
|
||||||
|
try:
|
||||||
|
import chardet
|
||||||
|
chardet_version = chardet.__version__
|
||||||
|
except ImportError:
|
||||||
|
chardet_version = u'-'
|
||||||
|
try:
|
||||||
|
import enchant
|
||||||
|
enchant_version = enchant.__version__
|
||||||
|
except ImportError:
|
||||||
|
enchant_version = u'-'
|
||||||
|
try:
|
||||||
|
import sqlite
|
||||||
|
sqlite_version = sqlite.version
|
||||||
|
except ImportError:
|
||||||
|
sqlite_version = u'-'
|
||||||
|
|
||||||
|
from openlp.core.lib import translate, SettingsManager
|
||||||
|
from openlp.core.lib.mailto import mailto
|
||||||
|
|
||||||
from exceptiondialog import Ui_ExceptionDialog
|
from exceptiondialog import Ui_ExceptionDialog
|
||||||
|
|
||||||
@ -35,3 +66,78 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog):
|
|||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
QtGui.QDialog.__init__(self, parent)
|
QtGui.QDialog.__init__(self, parent)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
self.settingsSection = u'crashreport'
|
||||||
|
|
||||||
|
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 = u'Python: %s\n' % platform.python_version() + \
|
||||||
|
u'Qt4: %s\n' % Qt.qVersion() + \
|
||||||
|
u'Phonon: %s\n' % phonon_version + \
|
||||||
|
u'PyQt4: %s\n' % Qt.PYQT_VERSION_STR + \
|
||||||
|
u'SQLAlchemy: %s\n' % sqlalchemy.__version__ + \
|
||||||
|
u'BeautifulSoup: %s\n' % BeautifulSoup.__version__ + \
|
||||||
|
u'lxml: %s\n' % etree.__version__ + \
|
||||||
|
u'Chardet: %s\n' % chardet_version + \
|
||||||
|
u'PyEnchant: %s\n' % enchant_version + \
|
||||||
|
u'PySQLite: %s\n' % sqlite_version
|
||||||
|
if platform.system() == u'Linux':
|
||||||
|
if os.environ.get(u'KDE_FULL_SESSION') == u'true':
|
||||||
|
system = system + u'Desktop: KDE SC\n'
|
||||||
|
elif os.environ.get(u'GNOME_DESKTOP_SESSION_ID'):
|
||||||
|
system = system + u'Desktop: GNOME\n'
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
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'))
|
||||||
|
content = self._createReport()
|
||||||
|
for line in content[1].split(u'\n'):
|
||||||
|
if re.search(r'[/\\]openlp[/\\]', line):
|
||||||
|
source = re.sub(r'.*[/\\]openlp[/\\](.*)".*', r'\1', line)
|
||||||
|
if u':' in line:
|
||||||
|
exception = line.split(u'\n')[-1].split(u':')[0]
|
||||||
|
subject = u'Bug report: %s in %s' % (exception, source)
|
||||||
|
mailto(address=u'bugs@openlp.org', subject=subject, body=body % content)
|
||||||
|
@ -62,6 +62,9 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
|||||||
QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSingleClick)
|
QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSingleClick)
|
||||||
|
|
||||||
def loadList(self):
|
def loadList(self):
|
||||||
|
"""
|
||||||
|
Loads the list with alerts.
|
||||||
|
"""
|
||||||
self.AlertListWidget.clear()
|
self.AlertListWidget.clear()
|
||||||
alerts = self.manager.get_all_objects(AlertItem,
|
alerts = self.manager.get_all_objects(AlertItem,
|
||||||
order_by_ref=AlertItem.text)
|
order_by_ref=AlertItem.text)
|
||||||
@ -81,12 +84,16 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
|||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def onDeleteClick(self):
|
def onDeleteClick(self):
|
||||||
|
"""
|
||||||
|
Deletes the selected item.
|
||||||
|
"""
|
||||||
item = self.AlertListWidget.currentItem()
|
item = self.AlertListWidget.currentItem()
|
||||||
if item:
|
if item:
|
||||||
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||||
self.manager.delete_object(AlertItem, item_id)
|
self.manager.delete_object(AlertItem, item_id)
|
||||||
row = self.AlertListWidget.row(item)
|
row = self.AlertListWidget.row(item)
|
||||||
self.AlertListWidget.takeItem(row)
|
self.AlertListWidget.takeItem(row)
|
||||||
|
self.item_id = None
|
||||||
self.AlertTextEdit.setText(u'')
|
self.AlertTextEdit.setText(u'')
|
||||||
self.SaveButton.setEnabled(False)
|
self.SaveButton.setEnabled(False)
|
||||||
self.DeleteButton.setEnabled(False)
|
self.DeleteButton.setEnabled(False)
|
||||||
@ -107,7 +114,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
|||||||
|
|
||||||
def onSaveClick(self):
|
def onSaveClick(self):
|
||||||
"""
|
"""
|
||||||
Save an alert
|
Save the alert, we are editing.
|
||||||
"""
|
"""
|
||||||
if self.item_id:
|
if self.item_id:
|
||||||
alert = self.manager.get_object(AlertItem, self.item_id)
|
alert = self.manager.get_object(AlertItem, self.item_id)
|
||||||
@ -115,13 +122,13 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
|||||||
self.manager.save_object(alert)
|
self.manager.save_object(alert)
|
||||||
self.item_id = None
|
self.item_id = None
|
||||||
self.loadList()
|
self.loadList()
|
||||||
else:
|
|
||||||
self.onNewClick()
|
|
||||||
|
|
||||||
def onTextChanged(self):
|
def onTextChanged(self):
|
||||||
"""
|
"""
|
||||||
Enable save button when data has been changed by editing the form
|
Enable save button when data has been changed by editing the form
|
||||||
"""
|
"""
|
||||||
|
# Only enable the button, if we are editing an item.
|
||||||
|
if self.item_id:
|
||||||
self.SaveButton.setEnabled(True)
|
self.SaveButton.setEnabled(True)
|
||||||
|
|
||||||
def onDoubleClick(self):
|
def onDoubleClick(self):
|
||||||
@ -131,8 +138,8 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
|||||||
items = self.AlertListWidget.selectedIndexes()
|
items = self.AlertListWidget.selectedIndexes()
|
||||||
for item in items:
|
for item in items:
|
||||||
bitem = self.AlertListWidget.item(item.row())
|
bitem = self.AlertListWidget.item(item.row())
|
||||||
self.triggerAlert(bitem.text())
|
self.triggerAlert(unicode(bitem.text()))
|
||||||
self.AlertTextEdit.setText(bitem.text())
|
self.AlertTextEdit.setText(unicode(bitem.text()))
|
||||||
self.item_id = (bitem.data(QtCore.Qt.UserRole)).toInt()[0]
|
self.item_id = (bitem.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||||
self.SaveButton.setEnabled(False)
|
self.SaveButton.setEnabled(False)
|
||||||
self.DeleteButton.setEnabled(True)
|
self.DeleteButton.setEnabled(True)
|
||||||
@ -145,13 +152,45 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
|||||||
items = self.AlertListWidget.selectedIndexes()
|
items = self.AlertListWidget.selectedIndexes()
|
||||||
for item in items:
|
for item in items:
|
||||||
bitem = self.AlertListWidget.item(item.row())
|
bitem = self.AlertListWidget.item(item.row())
|
||||||
self.AlertTextEdit.setText(bitem.text())
|
self.AlertTextEdit.setText(unicode(bitem.text()))
|
||||||
self.item_id = (bitem.data(QtCore.Qt.UserRole)).toInt()[0]
|
self.item_id = (bitem.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||||
|
# If the alert does not contain '<>' we clear the ParameterEdit field.
|
||||||
|
if unicode(self.AlertTextEdit.text()).find(u'<>') == -1:
|
||||||
|
self.ParameterEdit.setText(u'')
|
||||||
self.SaveButton.setEnabled(False)
|
self.SaveButton.setEnabled(False)
|
||||||
self.DeleteButton.setEnabled(True)
|
self.DeleteButton.setEnabled(True)
|
||||||
|
|
||||||
def triggerAlert(self, text):
|
def triggerAlert(self, text):
|
||||||
|
"""
|
||||||
|
Prepares the alert text for displaying.
|
||||||
|
|
||||||
|
``text``
|
||||||
|
The alert text (unicode).
|
||||||
|
"""
|
||||||
if text:
|
if text:
|
||||||
|
# We found '<>' in the alert text, but the ParameterEdit field is
|
||||||
|
# empty.
|
||||||
|
if text.find(u'<>') != -1 and not self.ParameterEdit.text() and \
|
||||||
|
QtGui.QMessageBox.question(self, translate(
|
||||||
|
'AlertPlugin.AlertForm', 'No Parameter found'),
|
||||||
|
translate('AlertPlugin.AlertForm', 'You have not entered a '
|
||||||
|
'parameter to be replaced.\nDo you want to continue '
|
||||||
|
'anyway?'),
|
||||||
|
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
|
||||||
|
QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
|
||||||
|
self.ParameterEdit.setFocus()
|
||||||
|
return False
|
||||||
|
# The ParameterEdit field is not empty, but we have not found '<>'
|
||||||
|
# in the alert text.
|
||||||
|
elif text.find(u'<>') == -1 and self.ParameterEdit.text() and \
|
||||||
|
QtGui.QMessageBox.question(self, translate(
|
||||||
|
'AlertPlugin.AlertForm', 'No Placeholder found'),
|
||||||
|
translate('AlertPlugin.AlertForm', 'The alert text does not'
|
||||||
|
' contain \'<>\'.\nDo want to continue anyway?'),
|
||||||
|
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
|
||||||
|
QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
|
||||||
|
self.ParameterEdit.setFocus()
|
||||||
|
return False
|
||||||
text = text.replace(u'<>', unicode(self.ParameterEdit.text()))
|
text = text.replace(u'<>', unicode(self.ParameterEdit.text()))
|
||||||
self.parent.alertsmanager.displayAlert(text)
|
self.parent.alertsmanager.displayAlert(text)
|
||||||
return True
|
return True
|
||||||
|
@ -86,7 +86,7 @@ class AlertsManager(QtCore.QObject):
|
|||||||
text = self.alertList.pop(0)
|
text = self.alertList.pop(0)
|
||||||
alertTab = self.parent.alertsTab
|
alertTab = self.parent.alertsTab
|
||||||
self.parent.liveController.display.alert(text)
|
self.parent.liveController.display.alert(text)
|
||||||
# check to see if we have a timer running
|
# Check to see if we have a timer running.
|
||||||
if self.timer_id == 0:
|
if self.timer_id == 0:
|
||||||
self.timer_id = self.startTimer(int(alertTab.timeout) * 1000)
|
self.timer_id = self.startTimer(int(alertTab.timeout) * 1000)
|
||||||
|
|
||||||
@ -94,9 +94,9 @@ class AlertsManager(QtCore.QObject):
|
|||||||
"""
|
"""
|
||||||
Time has finished so if our time then request the next Alert
|
Time has finished so if our time then request the next Alert
|
||||||
if there is one and reset the timer.
|
if there is one and reset the timer.
|
||||||
|
|
||||||
``event``
|
``event``
|
||||||
the QT event that has been triggered.
|
the QT event that has been triggered.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
log.debug(u'timer event')
|
log.debug(u'timer event')
|
||||||
if event.timerId() == self.timer_id:
|
if event.timerId() == self.timer_id:
|
||||||
|
BIN
resources/images/general_email.png
Normal file
BIN
resources/images/general_email.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 833 B |
@ -39,6 +39,7 @@
|
|||||||
<file>general_new.png</file>
|
<file>general_new.png</file>
|
||||||
<file>general_open.png</file>
|
<file>general_open.png</file>
|
||||||
<file>general_save.png</file>
|
<file>general_save.png</file>
|
||||||
|
<file>general_email.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="slides">
|
<qresource prefix="slides">
|
||||||
<file>slide_close.png</file>
|
<file>slide_close.png</file>
|
||||||
|
Loading…
Reference in New Issue
Block a user