mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Preferences widget for email sending
This commit is contained in:
parent
6b8d204f07
commit
a6bd77247d
@ -766,8 +766,17 @@ class Sending(PreferencesPlugin):
|
|||||||
name_order = 3
|
name_order = 3
|
||||||
config_widget = 'calibre.gui2.preferences.sending'
|
config_widget = 'calibre.gui2.preferences.sending'
|
||||||
|
|
||||||
|
class Email(PreferencesPlugin):
|
||||||
|
name = 'Email'
|
||||||
|
gui_name = _('Sending books by email')
|
||||||
|
category = 'Sharing'
|
||||||
|
gui_category = _('Sharing')
|
||||||
|
category_order = 4
|
||||||
|
name_order = 1
|
||||||
|
config_widget = 'calibre.gui2.preferences.emailp'
|
||||||
|
|
||||||
plugins += [LookAndFeel, Behavior, Columns, Toolbar, InputOptions,
|
plugins += [LookAndFeel, Behavior, Columns, Toolbar, InputOptions,
|
||||||
CommonOptions, OutputOptions, Adding, Saving, Sending]
|
CommonOptions, OutputOptions, Adding, Saving, Sending, Email]
|
||||||
|
|
||||||
#}}}
|
#}}}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ class AbortCommit(Exception):
|
|||||||
class ConfigWidgetInterface(object):
|
class ConfigWidgetInterface(object):
|
||||||
|
|
||||||
changed_signal = None
|
changed_signal = None
|
||||||
|
supports_restoring_to_defaults = True
|
||||||
|
|
||||||
def genesis(self, gui):
|
def genesis(self, gui):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
@ -165,6 +166,7 @@ class CommaSeparatedList(Setting):
|
|||||||
class ConfigWidgetBase(QWidget, ConfigWidgetInterface):
|
class ConfigWidgetBase(QWidget, ConfigWidgetInterface):
|
||||||
|
|
||||||
changed_signal = pyqtSignal()
|
changed_signal = pyqtSignal()
|
||||||
|
supports_restoring_to_defaults = True
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QWidget.__init__(self, parent)
|
QWidget.__init__(self, parent)
|
||||||
@ -243,6 +245,7 @@ def test_widget(category, name, gui=None):
|
|||||||
w = pl.create_widget(d)
|
w = pl.create_widget(d)
|
||||||
d.set_widget(w)
|
d.set_widget(w)
|
||||||
bb.button(bb.RestoreDefaults).clicked.connect(w.restore_defaults)
|
bb.button(bb.RestoreDefaults).clicked.connect(w.restore_defaults)
|
||||||
|
bb.button(bb.RestoreDefaults).setEnabled(w.supports_restoring_to_defaults)
|
||||||
bb.button(bb.Apply).setEnabled(False)
|
bb.button(bb.Apply).setEnabled(False)
|
||||||
bb.button(bb.Apply).clicked.connect(d.accept)
|
bb.button(bb.Apply).clicked.connect(d.accept)
|
||||||
w.changed_signal.connect(lambda : bb.button(bb.Apply).setEnabled(True))
|
w.changed_signal.connect(lambda : bb.button(bb.Apply).setEnabled(True))
|
||||||
|
112
src/calibre/gui2/preferences/email.ui
Normal file
112
src/calibre/gui2/preferences/email.ui
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Form</class>
|
||||||
|
<widget class="QWidget" name="Form">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>701</width>
|
||||||
|
<height>494</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_22">
|
||||||
|
<property name="text">
|
||||||
|
<string>calibre can send your books to you (or your reader) by email. Emails will be automatically sent for downloaded news to all email addresses that have Auto-send checked.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
||||||
|
<item>
|
||||||
|
<widget class="QTableView" name="email_view">
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::SingleSelection</enum>
|
||||||
|
</property>
|
||||||
|
<property name="selectionBehavior">
|
||||||
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="email_add">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Add an email address to which to send books</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Add email</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
|
<normaloff>:/images/plus.png</normaloff>:/images/plus.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>24</width>
|
||||||
|
<height>24</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolButtonStyle">
|
||||||
|
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="email_make_default">
|
||||||
|
<property name="text">
|
||||||
|
<string>Make &default</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="email_remove">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Remove email</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
|
<normaloff>:/images/minus.png</normaloff>:/images/minus.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>24</width>
|
||||||
|
<height>24</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolButtonStyle">
|
||||||
|
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="SendEmail" name="send_email_widget" native="true"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>SendEmail</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>calibre/gui2/wizard/send_email.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources>
|
||||||
|
<include location="../../../../resources/images.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
196
src/calibre/gui2/preferences/emailp.py
Normal file
196
src/calibre/gui2/preferences/emailp.py
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from PyQt4.Qt import QAbstractTableModel, QVariant, QFont, Qt
|
||||||
|
|
||||||
|
|
||||||
|
from calibre.gui2.preferences import ConfigWidgetBase, test_widget, \
|
||||||
|
AbortCommit
|
||||||
|
from calibre.gui2.preferences.email_ui import Ui_Form
|
||||||
|
from calibre.utils.config import ConfigProxy
|
||||||
|
from calibre.gui2 import NONE
|
||||||
|
from calibre.utils.smtp import config as smtp_prefs
|
||||||
|
|
||||||
|
class EmailAccounts(QAbstractTableModel): # {{{
|
||||||
|
|
||||||
|
def __init__(self, accounts):
|
||||||
|
QAbstractTableModel.__init__(self)
|
||||||
|
self.accounts = accounts
|
||||||
|
self.account_order = sorted(self.accounts.keys())
|
||||||
|
self.headers = map(QVariant, [_('Email'), _('Formats'), _('Auto send')])
|
||||||
|
self.default_font = QFont()
|
||||||
|
self.default_font.setBold(True)
|
||||||
|
self.default_font = QVariant(self.default_font)
|
||||||
|
self.tooltips =[NONE] + map(QVariant,
|
||||||
|
[_('Formats to email. The first matching format will be sent.'),
|
||||||
|
'<p>'+_('If checked, downloaded news will be automatically '
|
||||||
|
'mailed <br>to this email address '
|
||||||
|
'(provided it is in one of the listed formats).')])
|
||||||
|
|
||||||
|
def rowCount(self, *args):
|
||||||
|
return len(self.account_order)
|
||||||
|
|
||||||
|
def columnCount(self, *args):
|
||||||
|
return 3
|
||||||
|
|
||||||
|
def headerData(self, section, orientation, role):
|
||||||
|
if role == Qt.DisplayRole and orientation == Qt.Horizontal:
|
||||||
|
return self.headers[section]
|
||||||
|
return NONE
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
row, col = index.row(), index.column()
|
||||||
|
if row < 0 or row >= self.rowCount():
|
||||||
|
return NONE
|
||||||
|
account = self.account_order[row]
|
||||||
|
if role == Qt.UserRole:
|
||||||
|
return (account, self.accounts[account])
|
||||||
|
if role == Qt.ToolTipRole:
|
||||||
|
return self.tooltips[col]
|
||||||
|
if role in [Qt.DisplayRole, Qt.EditRole]:
|
||||||
|
if col == 0:
|
||||||
|
return QVariant(account)
|
||||||
|
if col == 1:
|
||||||
|
return QVariant(self.accounts[account][0])
|
||||||
|
if role == Qt.FontRole and self.accounts[account][2]:
|
||||||
|
return self.default_font
|
||||||
|
if role == Qt.CheckStateRole and col == 2:
|
||||||
|
return QVariant(Qt.Checked if self.accounts[account][1] else Qt.Unchecked)
|
||||||
|
return NONE
|
||||||
|
|
||||||
|
def flags(self, index):
|
||||||
|
if index.column() == 2:
|
||||||
|
return QAbstractTableModel.flags(self, index)|Qt.ItemIsUserCheckable
|
||||||
|
else:
|
||||||
|
return QAbstractTableModel.flags(self, index)|Qt.ItemIsEditable
|
||||||
|
|
||||||
|
def setData(self, index, value, role):
|
||||||
|
if not index.isValid():
|
||||||
|
return False
|
||||||
|
row, col = index.row(), index.column()
|
||||||
|
account = self.account_order[row]
|
||||||
|
if col == 2:
|
||||||
|
self.accounts[account][1] ^= True
|
||||||
|
elif col == 1:
|
||||||
|
self.accounts[account][0] = unicode(value.toString()).upper()
|
||||||
|
else:
|
||||||
|
na = unicode(value.toString())
|
||||||
|
from email.utils import parseaddr
|
||||||
|
addr = parseaddr(na)[-1]
|
||||||
|
if not addr:
|
||||||
|
return False
|
||||||
|
self.accounts[na] = self.accounts.pop(account)
|
||||||
|
self.account_order[row] = na
|
||||||
|
if '@kindle.com' in addr:
|
||||||
|
self.accounts[na][0] = 'AZW, MOBI, TPZ, PRC, AZW1'
|
||||||
|
|
||||||
|
self.dataChanged.emit(
|
||||||
|
self.index(index.row(), 0), self.index(index.row(), 2))
|
||||||
|
return True
|
||||||
|
|
||||||
|
def make_default(self, index):
|
||||||
|
if index.isValid():
|
||||||
|
row = index.row()
|
||||||
|
for x in self.accounts.values():
|
||||||
|
x[2] = False
|
||||||
|
self.accounts[self.account_order[row]][2] = True
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
def add(self):
|
||||||
|
x = _('new email address')
|
||||||
|
y = x
|
||||||
|
c = 0
|
||||||
|
while y in self.accounts:
|
||||||
|
c += 1
|
||||||
|
y = x + str(c)
|
||||||
|
auto_send = len(self.accounts) < 1
|
||||||
|
self.accounts[y] = ['MOBI, EPUB', auto_send,
|
||||||
|
len(self.account_order) == 0]
|
||||||
|
self.account_order = sorted(self.accounts.keys())
|
||||||
|
self.reset()
|
||||||
|
return self.index(self.account_order.index(y), 0)
|
||||||
|
|
||||||
|
def remove(self, index):
|
||||||
|
if index.isValid():
|
||||||
|
row = index.row()
|
||||||
|
account = self.account_order[row]
|
||||||
|
self.accounts.pop(account)
|
||||||
|
self.account_order = sorted(self.accounts.keys())
|
||||||
|
has_default = False
|
||||||
|
for account in self.account_order:
|
||||||
|
if self.accounts[account][2]:
|
||||||
|
has_default = True
|
||||||
|
break
|
||||||
|
if not has_default and self.account_order:
|
||||||
|
self.accounts[self.account_order[0]][2] = True
|
||||||
|
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||||
|
|
||||||
|
supports_restoring_to_defaults = False
|
||||||
|
|
||||||
|
def genesis(self, gui):
|
||||||
|
self.gui = gui
|
||||||
|
self.proxy = ConfigProxy(smtp_prefs())
|
||||||
|
|
||||||
|
self.send_email_widget.initialize(self.preferred_to_address)
|
||||||
|
self.send_email_widget.changed_signal.connect(self.changed_signal.emit)
|
||||||
|
opts = self.send_email_widget.smtp_opts
|
||||||
|
self._email_accounts = EmailAccounts(opts.accounts)
|
||||||
|
self._email_accounts.dataChanged.connect(lambda x,y:
|
||||||
|
self.changed_signal.emit())
|
||||||
|
self.email_view.setModel(self._email_accounts)
|
||||||
|
|
||||||
|
self.email_add.clicked.connect(self.add_email_account)
|
||||||
|
self.email_make_default.clicked.connect(self.make_default)
|
||||||
|
self.email_view.resizeColumnsToContents()
|
||||||
|
self.email_remove.clicked.connect(self.remove_email_account)
|
||||||
|
|
||||||
|
def preferred_to_address(self):
|
||||||
|
if self._email_accounts.account_order:
|
||||||
|
return self._email_accounts.account_order[0]
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
ConfigWidgetBase.initialize(self)
|
||||||
|
# Initializing all done in genesis
|
||||||
|
|
||||||
|
def restore_defaults(self):
|
||||||
|
ConfigWidgetBase.restore_defaults(self)
|
||||||
|
# No defaults to restore to
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
to_set = bool(self._email_accounts.accounts)
|
||||||
|
if not self.send_email_widget.set_email_settings(to_set):
|
||||||
|
raise AbortCommit('abort')
|
||||||
|
self.proxy['accounts'] = self._email_accounts.accounts
|
||||||
|
return ConfigWidgetBase.commit(self)
|
||||||
|
|
||||||
|
def make_default(self, *args):
|
||||||
|
self._email_accounts.make_default(self.email_view.currentIndex())
|
||||||
|
self.changed_signal.emit()
|
||||||
|
|
||||||
|
def add_email_account(self, *args):
|
||||||
|
index = self._email_accounts.add()
|
||||||
|
self.email_view.setCurrentIndex(index)
|
||||||
|
self.email_view.resizeColumnsToContents()
|
||||||
|
self.email_view.edit(index)
|
||||||
|
self.changed_signal.emit()
|
||||||
|
|
||||||
|
def remove_email_account(self, *args):
|
||||||
|
idx = self.email_view.currentIndex()
|
||||||
|
self._email_accounts.remove(idx)
|
||||||
|
self.changed_signal.emit()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from PyQt4.Qt import QApplication
|
||||||
|
app = QApplication([])
|
||||||
|
test_widget('Sharing', 'Email')
|
||||||
|
|
@ -9,7 +9,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
import cStringIO, sys
|
import cStringIO, sys
|
||||||
from binascii import hexlify, unhexlify
|
from binascii import hexlify, unhexlify
|
||||||
|
|
||||||
from PyQt4.Qt import QWidget, SIGNAL, QDialog, Qt
|
from PyQt4.Qt import QWidget, pyqtSignal, QDialog, Qt
|
||||||
|
|
||||||
from calibre.gui2.wizard.send_email_ui import Ui_Form
|
from calibre.gui2.wizard.send_email_ui import Ui_Form
|
||||||
from calibre.utils.smtp import config as smtp_prefs
|
from calibre.utils.smtp import config as smtp_prefs
|
||||||
@ -24,7 +24,7 @@ class TestEmail(QDialog, TE_Dialog):
|
|||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
opts = smtp_prefs().parse()
|
opts = smtp_prefs().parse()
|
||||||
self.test_func = parent.test_email_settings
|
self.test_func = parent.test_email_settings
|
||||||
self.connect(self.test_button, SIGNAL('clicked(bool)'), self.test)
|
self.test_button.clicked.connect(self.test)
|
||||||
self.from_.setText(unicode(self.from_.text())%opts.from_)
|
self.from_.setText(unicode(self.from_.text())%opts.from_)
|
||||||
if pa:
|
if pa:
|
||||||
self.to.setText(pa)
|
self.to.setText(pa)
|
||||||
@ -33,7 +33,7 @@ class TestEmail(QDialog, TE_Dialog):
|
|||||||
(opts.relay_username, unhexlify(opts.relay_password),
|
(opts.relay_username, unhexlify(opts.relay_password),
|
||||||
opts.relay_host, opts.relay_port, opts.encryption))
|
opts.relay_host, opts.relay_port, opts.encryption))
|
||||||
|
|
||||||
def test(self):
|
def test(self, *args):
|
||||||
self.log.setPlainText(_('Sending...'))
|
self.log.setPlainText(_('Sending...'))
|
||||||
self.test_button.setEnabled(False)
|
self.test_button.setEnabled(False)
|
||||||
try:
|
try:
|
||||||
@ -47,6 +47,8 @@ class TestEmail(QDialog, TE_Dialog):
|
|||||||
|
|
||||||
class SendEmail(QWidget, Ui_Form):
|
class SendEmail(QWidget, Ui_Form):
|
||||||
|
|
||||||
|
changed_signal = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QWidget.__init__(self, parent)
|
QWidget.__init__(self, parent)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
@ -57,23 +59,31 @@ class SendEmail(QWidget, Ui_Form):
|
|||||||
self.smtp_opts = opts
|
self.smtp_opts = opts
|
||||||
if opts.from_:
|
if opts.from_:
|
||||||
self.email_from.setText(opts.from_)
|
self.email_from.setText(opts.from_)
|
||||||
|
self.email_from.textChanged.connect(self.changed)
|
||||||
if opts.relay_host:
|
if opts.relay_host:
|
||||||
self.relay_host.setText(opts.relay_host)
|
self.relay_host.setText(opts.relay_host)
|
||||||
|
self.relay_host.textChanged.connect(self.changed)
|
||||||
self.relay_port.setValue(opts.relay_port)
|
self.relay_port.setValue(opts.relay_port)
|
||||||
|
self.relay_port.valueChanged.connect(self.changed)
|
||||||
if opts.relay_username:
|
if opts.relay_username:
|
||||||
self.relay_username.setText(opts.relay_username)
|
self.relay_username.setText(opts.relay_username)
|
||||||
|
self.relay_username.textChanged.connect(self.changed)
|
||||||
if opts.relay_password:
|
if opts.relay_password:
|
||||||
self.relay_password.setText(unhexlify(opts.relay_password))
|
self.relay_password.setText(unhexlify(opts.relay_password))
|
||||||
|
self.relay_password.textChanged.connect(self.changed)
|
||||||
(self.relay_tls if opts.encryption == 'TLS' else self.relay_ssl).setChecked(True)
|
(self.relay_tls if opts.encryption == 'TLS' else self.relay_ssl).setChecked(True)
|
||||||
self.connect(self.relay_use_gmail, SIGNAL('clicked(bool)'),
|
self.relay_tls.toggled.connect(self.changed)
|
||||||
self.create_gmail_relay)
|
|
||||||
self.connect(self.relay_show_password, SIGNAL('stateChanged(int)'),
|
|
||||||
lambda
|
|
||||||
state:self.relay_password.setEchoMode(self.relay_password.Password if
|
|
||||||
state == 0 else self.relay_password.Normal))
|
|
||||||
self.connect(self.test_email_button, SIGNAL('clicked(bool)'),
|
|
||||||
self.test_email)
|
|
||||||
|
|
||||||
|
self.relay_use_gmail.clicked.connect(
|
||||||
|
self.create_gmail_relay)
|
||||||
|
self.relay_show_password.stateChanged.connect(
|
||||||
|
lambda state : self.relay_password.setEchoMode(
|
||||||
|
self.relay_password.Password if
|
||||||
|
state == 0 else self.relay_password.Normal))
|
||||||
|
self.test_email_button.clicked.connect(self.test_email)
|
||||||
|
|
||||||
|
def changed(self, *args):
|
||||||
|
self.changed_signal.emit()
|
||||||
|
|
||||||
def test_email(self, *args):
|
def test_email(self, *args):
|
||||||
pa = self.preferred_to_address()
|
pa = self.preferred_to_address()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user