diff --git a/trac/ticket/notification.py b/trac/ticket/notification.py
index df67904..ab5e343 100644
|
a
|
b
|
from trac.notification import NotifyEmail |
| 22 | 22 | from trac.ticket.api import TicketSystem |
| 23 | 23 | from trac.util import md5 |
| 24 | 24 | from trac.util.datefmt import to_utimestamp |
| 25 | | from trac.util.text import CRLF, wrap, obfuscate_email_address |
| | 25 | from trac.util.text import CRLF, wrap, obfuscate_email_address, to_unicode |
| 26 | 26 | from trac.util.translation import deactivate, reactivate |
| 27 | 27 | |
| 28 | 28 | from genshi.template.text import NewTextTemplate |
| | 29 | from unicodedata import east_asian_width |
| 29 | 30 | |
| 30 | 31 | class TicketNotificationSystem(Component): |
| 31 | 32 | |
| … |
… |
class TicketNotificationSystem(Component): |
| 51 | 52 | `$prefix` being the value of the `smtp_subject_prefix` option. |
| 52 | 53 | ''(since 0.11)''""") |
| 53 | 54 | |
| | 55 | ambiguous_char_width = Option('notification', 'ambiguous_char_width', 'single', |
| | 56 | """Which width of ambiguous characters (e.g. 'single' or 'double') |
| | 57 | should be used in the table of notification mail. |
| | 58 | |
| | 59 | If 'single', the same width as characters in US-ASCII. This is expected |
| | 60 | by most users. If 'double', twice the width of US-ASCII characters. |
| | 61 | This is expected by CJK users.""") |
| | 62 | |
| 54 | 63 | |
| 55 | 64 | class TicketNotifyEmail(NotifyEmail): |
| 56 | 65 | """Notification of ticket changes.""" |
| … |
… |
class TicketNotifyEmail(NotifyEmail): |
| 65 | 74 | def __init__(self, env): |
| 66 | 75 | NotifyEmail.__init__(self, env) |
| 67 | 76 | self.prev_cc = [] |
| | 77 | self.ambiguous_char_width = env.config.get('notification', 'ambiguous_char_width', 'single') |
| | 78 | self.text_widths = {} |
| 68 | 79 | |
| 69 | 80 | def notify(self, ticket, newticket=True, modtime=None): |
| 70 | 81 | """Send ticket change notification e-mail (untranslated)""" |
| … |
… |
class TicketNotifyEmail(NotifyEmail): |
| 180 | 191 | if fval.find('\n') != -1: |
| 181 | 192 | continue |
| 182 | 193 | idx = 2 * (i % 2) |
| 183 | | if len(f) > width[idx]: |
| 184 | | width[idx] = len(f) |
| 185 | | if len(fval) > width[idx + 1]: |
| 186 | | width[idx + 1] = len(fval) |
| | 194 | text_width = self.get_text_width(f) |
| | 195 | if text_width > width[idx]: |
| | 196 | width[idx] = text_width |
| | 197 | text_width = self.get_text_width(fval) |
| | 198 | if text_width > width[idx + 1]: |
| | 199 | width[idx + 1] = text_width |
| 187 | 200 | i += 1 |
| 188 | | format = (u'%%%is: %%-%is | ' % (width[0], width[1]), |
| 189 | | u' %%%is: %%-%is%s' % (width[2], width[3], CRLF)) |
| 190 | 201 | l = (width[0] + width[1] + 5) |
| 191 | 202 | sep = l * '-' + '+' + (self.COLS - l) * '-' |
| 192 | 203 | txt = sep + CRLF |
| … |
… |
class TicketNotifyEmail(NotifyEmail): |
| 204 | 215 | else: |
| 205 | 216 | # Note: f['label'] is a Babel's LazyObject, make sure its |
| 206 | 217 | # __str__ method won't be called. |
| 207 | | txt += format[i % 2] % (f['label'], unicode(fval)) |
| | 218 | flabel = f['label'] |
| | 219 | if i % 2 == 0: |
| | 220 | format = u'%%%is: %%-%is | ' % ( |
| | 221 | width[0] - (self.get_text_width(flabel) - len(flabel)), |
| | 222 | width[1] - (self.get_text_width(fval) - len(fval))) |
| | 223 | else: |
| | 224 | format = u' %%%is: %%-%is%s' % ( |
| | 225 | width[2] - (self.get_text_width(flabel) - len(flabel)), |
| | 226 | width[3] - (self.get_text_width(fval) - len(fval)), |
| | 227 | CRLF) |
| | 228 | txt += format % (flabel, unicode(fval)) |
| 208 | 229 | i += 1 |
| 209 | 230 | if i % 2: |
| 210 | 231 | txt += CRLF |
| … |
… |
class TicketNotifyEmail(NotifyEmail): |
| 327 | 348 | hdrs['References'] = msgid |
| 328 | 349 | NotifyEmail.send(self, torcpts, ccrcpts, hdrs) |
| 329 | 350 | |
| | 351 | def get_text_width(self, text): |
| | 352 | if self.ambiguous_char_width == 'double': |
| | 353 | ambiwidth = 2 |
| | 354 | else: |
| | 355 | ambiwidth = 1 |
| | 356 | |
| | 357 | if not isinstance(text, unicode): |
| | 358 | text = to_unicode(text) |
| | 359 | |
| | 360 | if text in self.text_widths: |
| | 361 | return self.text_widths[text] |
| | 362 | |
| | 363 | width = 0 |
| | 364 | for ch in text: |
| | 365 | eaw = east_asian_width(ch) |
| | 366 | if eaw == 'W' or eaw == 'F': |
| | 367 | width += 2 |
| | 368 | elif eaw == 'A': |
| | 369 | width += ambiwidth |
| | 370 | else: |
| | 371 | width += 1 |
| | 372 | self.text_widths[text] = width |
| | 373 | return width |
| | 374 | |