Email delivery: You can now specify a subject that calibre will use when sending emails per email account, configured in Preferences->Sending by email. The subject is a template of the same kind used in Save to Disk, etc. So youcan specift the title/authors/series/whatever in the template. Fixes #743535 (Sending to Kindle Needs "Convert" Subject Line)

This commit is contained in:
Kovid Goyal 2011-03-30 12:20:36 -06:00
commit 671022e3c2
6 changed files with 47 additions and 19 deletions

View File

@ -51,7 +51,7 @@ class ConvertAction(InterfaceAction):
self.queue_convert_jobs(jobs, changed, bad, rows, previous, self.queue_convert_jobs(jobs, changed, bad, rows, previous,
self.book_auto_converted, extra_job_args=[on_card]) self.book_auto_converted, extra_job_args=[on_card])
def auto_convert_mail(self, to, fmts, delete_from_library, book_ids, format): def auto_convert_mail(self, to, fmts, delete_from_library, book_ids, format, subject):
previous = self.gui.library_view.currentIndex() previous = self.gui.library_view.currentIndex()
rows = [x.row() for x in \ rows = [x.row() for x in \
self.gui.library_view.selectionModel().selectedRows()] self.gui.library_view.selectionModel().selectedRows()]
@ -59,7 +59,7 @@ class ConvertAction(InterfaceAction):
if jobs == []: return if jobs == []: return
self.queue_convert_jobs(jobs, changed, bad, rows, previous, self.queue_convert_jobs(jobs, changed, bad, rows, previous,
self.book_auto_converted_mail, self.book_auto_converted_mail,
extra_job_args=[delete_from_library, to, fmts]) extra_job_args=[delete_from_library, to, fmts, subject])
def auto_convert_news(self, book_ids, format): def auto_convert_news(self, book_ids, format):
previous = self.gui.library_view.currentIndex() previous = self.gui.library_view.currentIndex()
@ -145,9 +145,10 @@ class ConvertAction(InterfaceAction):
self.gui.sync_to_device(on_card, False, specific_format=fmt, send_ids=[book_id], do_auto_convert=False) self.gui.sync_to_device(on_card, False, specific_format=fmt, send_ids=[book_id], do_auto_convert=False)
def book_auto_converted_mail(self, job): def book_auto_converted_mail(self, job):
temp_files, fmt, book_id, delete_from_library, to, fmts = self.conversion_jobs[job] temp_files, fmt, book_id, delete_from_library, to, fmts, subject = self.conversion_jobs[job]
self.book_converted(job) self.book_converted(job)
self.gui.send_by_mail(to, fmts, delete_from_library, specific_format=fmt, send_ids=[book_id], do_auto_convert=False) self.gui.send_by_mail(to, fmts, delete_from_library, subject=subject,
specific_format=fmt, send_ids=[book_id], do_auto_convert=False)
def book_auto_converted_news(self, job): def book_auto_converted_news(self, job):
temp_files, fmt, book_id = self.conversion_jobs[job] temp_files, fmt, book_id = self.conversion_jobs[job]

View File

@ -82,7 +82,8 @@ class ShareConnMenu(QMenu): # {{{
keys = sorted(opts.accounts.keys()) keys = sorted(opts.accounts.keys())
for account in keys: for account in keys:
formats, auto, default = opts.accounts[account] formats, auto, default = opts.accounts[account]
dest = 'mail:'+account+';'+formats subject = opts.subjects.get(account, '')
dest = 'mail:'+account+';'+formats+';'+subject
action1 = DeviceAction(dest, False, False, I('mail.png'), action1 = DeviceAction(dest, False, False, I('mail.png'),
account) account)
action2 = DeviceAction(dest, True, False, I('mail.png'), action2 = DeviceAction(dest, True, False, I('mail.png'),

View File

@ -887,9 +887,14 @@ class DeviceMixin(object): # {{{
on_card = dest on_card = dest
self.sync_to_device(on_card, delete, fmt) self.sync_to_device(on_card, delete, fmt)
elif dest == 'mail': elif dest == 'mail':
to, fmts = sub_dest.split(';') sub_dest_parts = sub_dest.split(';')
while len(sub_dest_parts) < 3:
sub_dest_parts.append('')
to = sub_dest_parts[0]
fmts = sub_dest_parts[1]
subject = ';'.join(sub_dest_parts[2:])
fmts = [x.strip().lower() for x in fmts.split(',')] fmts = [x.strip().lower() for x in fmts.split(',')]
self.send_by_mail(to, fmts, delete) self.send_by_mail(to, fmts, delete, subject=subject)
def cover_to_thumbnail(self, data): def cover_to_thumbnail(self, data):
if self.device_manager.device and \ if self.device_manager.device and \

View File

@ -22,6 +22,7 @@ from calibre.customize.ui import available_input_formats, available_output_forma
from calibre.ebooks.metadata import authors_to_string from calibre.ebooks.metadata import authors_to_string
from calibre.constants import preferred_encoding from calibre.constants import preferred_encoding
from calibre.gui2 import config, Dispatcher, warning_dialog from calibre.gui2 import config, Dispatcher, warning_dialog
from calibre.library.save_to_disk import get_components
from calibre.utils.config import tweaks from calibre.utils.config import tweaks
class EmailJob(BaseJob): # {{{ class EmailJob(BaseJob): # {{{
@ -210,7 +211,7 @@ class EmailMixin(object): # {{{
def __init__(self): def __init__(self):
self.emailer = Emailer(self.job_manager) self.emailer = Emailer(self.job_manager)
def send_by_mail(self, to, fmts, delete_from_library, send_ids=None, def send_by_mail(self, to, fmts, delete_from_library, subject='', send_ids=None,
do_auto_convert=True, specific_format=None): do_auto_convert=True, specific_format=None):
ids = [self.library_view.model().id(r) for r in self.library_view.selectionModel().selectedRows()] if send_ids is None else send_ids ids = [self.library_view.model().id(r) for r in self.library_view.selectionModel().selectedRows()] if send_ids is None else send_ids
if not ids or len(ids) == 0: if not ids or len(ids) == 0:
@ -239,7 +240,14 @@ class EmailMixin(object): # {{{
remove_ids.append(id) remove_ids.append(id)
jobnames.append(t) jobnames.append(t)
attachments.append(f) attachments.append(f)
subjects.append(_('E-book:')+ ' '+t) if not subject:
subjects.append(_('E-book:')+ ' '+t)
else:
components = get_components(subject, mi, id)
if not components:
components = [mi.title]
subject = os.path.join(*components)
subjects.append(subject)
a = authors_to_string(mi.authors if mi.authors else \ a = authors_to_string(mi.authors if mi.authors else \
[_('Unknown')]) [_('Unknown')])
texts.append(_('Attached, you will find the e-book') + \ texts.append(_('Attached, you will find the e-book') + \
@ -292,7 +300,7 @@ class EmailMixin(object): # {{{
if self.auto_convert_question( if self.auto_convert_question(
_('Auto convert the following books before sending via ' _('Auto convert the following books before sending via '
'email?'), autos): 'email?'), autos):
self.iactions['Convert Books'].auto_convert_mail(to, fmts, delete_from_library, auto, format) self.iactions['Convert Books'].auto_convert_mail(to, fmts, delete_from_library, auto, format, subject)
if bad: if bad:
bad = '\n'.join('%s'%(i,) for i in bad) bad = '\n'.join('%s'%(i,) for i in bad)

View File

@ -5,6 +5,8 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import textwrap
from PyQt4.Qt import QAbstractTableModel, QVariant, QFont, Qt from PyQt4.Qt import QAbstractTableModel, QVariant, QFont, Qt
@ -17,25 +19,30 @@ from calibre.utils.smtp import config as smtp_prefs
class EmailAccounts(QAbstractTableModel): # {{{ class EmailAccounts(QAbstractTableModel): # {{{
def __init__(self, accounts): def __init__(self, accounts, subjects):
QAbstractTableModel.__init__(self) QAbstractTableModel.__init__(self)
self.accounts = accounts self.accounts = accounts
self.subjects = subjects
self.account_order = sorted(self.accounts.keys()) self.account_order = sorted(self.accounts.keys())
self.headers = map(QVariant, [_('Email'), _('Formats'), _('Auto send')]) self.headers = map(QVariant, [_('Email'), _('Formats'), _('Subject'), _('Auto send')])
self.default_font = QFont() self.default_font = QFont()
self.default_font.setBold(True) self.default_font.setBold(True)
self.default_font = QVariant(self.default_font) self.default_font = QVariant(self.default_font)
self.tooltips =[NONE] + map(QVariant, self.tooltips =[NONE] + list(map(QVariant, map(textwrap.fill,
[_('Formats to email. The first matching format will be sent.'), [_('Formats to email. The first matching format will be sent.'),
_('Subject of the email to use when sending. When left blank '
'the title will be used for the subject. Also, the same '
'templates used for "Save to disk" such as {title} and '
'{author_sort} can be used here.'),
'<p>'+_('If checked, downloaded news will be automatically ' '<p>'+_('If checked, downloaded news will be automatically '
'mailed <br>to this email address ' 'mailed <br>to this email address '
'(provided it is in one of the listed formats).')]) '(provided it is in one of the listed formats).')])))
def rowCount(self, *args): def rowCount(self, *args):
return len(self.account_order) return len(self.account_order)
def columnCount(self, *args): def columnCount(self, *args):
return 3 return len(self.headers)
def headerData(self, section, orientation, role): def headerData(self, section, orientation, role):
if role == Qt.DisplayRole and orientation == Qt.Horizontal: if role == Qt.DisplayRole and orientation == Qt.Horizontal:
@ -56,14 +63,16 @@ class EmailAccounts(QAbstractTableModel): # {{{
return QVariant(account) return QVariant(account)
if col == 1: if col == 1:
return QVariant(self.accounts[account][0]) return QVariant(self.accounts[account][0])
if col == 2:
return QVariant(self.subjects.get(account, ''))
if role == Qt.FontRole and self.accounts[account][2]: if role == Qt.FontRole and self.accounts[account][2]:
return self.default_font return self.default_font
if role == Qt.CheckStateRole and col == 2: if role == Qt.CheckStateRole and col == 3:
return QVariant(Qt.Checked if self.accounts[account][1] else Qt.Unchecked) return QVariant(Qt.Checked if self.accounts[account][1] else Qt.Unchecked)
return NONE return NONE
def flags(self, index): def flags(self, index):
if index.column() == 2: if index.column() == 3:
return QAbstractTableModel.flags(self, index)|Qt.ItemIsUserCheckable return QAbstractTableModel.flags(self, index)|Qt.ItemIsUserCheckable
else: else:
return QAbstractTableModel.flags(self, index)|Qt.ItemIsEditable return QAbstractTableModel.flags(self, index)|Qt.ItemIsEditable
@ -73,8 +82,10 @@ class EmailAccounts(QAbstractTableModel): # {{{
return False return False
row, col = index.row(), index.column() row, col = index.row(), index.column()
account = self.account_order[row] account = self.account_order[row]
if col == 2: if col == 3:
self.accounts[account][1] ^= True self.accounts[account][1] ^= True
if col == 2:
self.subjects[account] = unicode(value.toString())
elif col == 1: elif col == 1:
self.accounts[account][0] = unicode(value.toString()).upper() self.accounts[account][0] = unicode(value.toString()).upper()
else: else:
@ -143,7 +154,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.send_email_widget.initialize(self.preferred_to_address) self.send_email_widget.initialize(self.preferred_to_address)
self.send_email_widget.changed_signal.connect(self.changed_signal.emit) self.send_email_widget.changed_signal.connect(self.changed_signal.emit)
opts = self.send_email_widget.smtp_opts opts = self.send_email_widget.smtp_opts
self._email_accounts = EmailAccounts(opts.accounts) self._email_accounts = EmailAccounts(opts.accounts, opts.subjects)
self._email_accounts.dataChanged.connect(lambda x,y: self._email_accounts.dataChanged.connect(lambda x,y:
self.changed_signal.emit()) self.changed_signal.emit())
self.email_view.setModel(self._email_accounts) self.email_view.setModel(self._email_accounts)
@ -170,6 +181,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
if not self.send_email_widget.set_email_settings(to_set): if not self.send_email_widget.set_email_settings(to_set):
raise AbortCommit('abort') raise AbortCommit('abort')
self.proxy['accounts'] = self._email_accounts.accounts self.proxy['accounts'] = self._email_accounts.accounts
self.proxy['subjects'] = self._email_accounts.subjects
return ConfigWidgetBase.commit(self) return ConfigWidgetBase.commit(self)

View File

@ -250,6 +250,7 @@ def config(defaults=None):
c = Config('smtp',desc) if defaults is None else StringConfig(defaults,desc) c = Config('smtp',desc) if defaults is None else StringConfig(defaults,desc)
c.add_opt('from_') c.add_opt('from_')
c.add_opt('accounts', default={}) c.add_opt('accounts', default={})
c.add_opt('subjects', default={})
c.add_opt('relay_host') c.add_opt('relay_host')
c.add_opt('relay_port', default=25) c.add_opt('relay_port', default=25)
c.add_opt('relay_username') c.add_opt('relay_username')