diff --git a/trac/ticket/notification.py b/trac/ticket/notification.py
index 7602393..5e1eca0 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', |
| | 56 | 'single', |
| | 57 | """Which width of ambiguous characters (e.g. 'single' or 'double') |
| | 58 | should be used in the table of notification mail. |
| | 59 | |
| | 60 | If 'single', the same width as characters in US-ASCII. This is expected |
| | 61 | by most users. If 'double', twice the width of US-ASCII characters. |
| | 62 | This is expected by CJK users.""") |
| | 63 | |
| 54 | 64 | |
| 55 | 65 | class TicketNotifyEmail(NotifyEmail): |
| 56 | 66 | """Notification of ticket changes.""" |
| … |
… |
class TicketNotifyEmail(NotifyEmail): |
| 65 | 75 | def __init__(self, env): |
| 66 | 76 | NotifyEmail.__init__(self, env) |
| 67 | 77 | self.prev_cc = [] |
| | 78 | self.ambiguous_char_width = env.config.get('notification', |
| | 79 | 'ambiguous_char_width', |
| | 80 | 'single') |
| | 81 | self.text_widths = {} |
| 68 | 82 | |
| 69 | 83 | def notify(self, ticket, newticket=True, modtime=None): |
| 70 | 84 | """Send ticket change notification e-mail (untranslated)""" |
| … |
… |
class TicketNotifyEmail(NotifyEmail): |
| 192 | 206 | fval = tkt[fname] or '' |
| 193 | 207 | if fval.find('\n') != -1: |
| 194 | 208 | continue |
| | 209 | if fname in ['owner', 'reporter']: |
| | 210 | fval = obfuscate_email_address(fval) |
| 195 | 211 | idx = 2 * (i % 2) |
| 196 | | width[idx] = max(len(f['label']), width[idx]) |
| 197 | | width[idx + 1] = max(len(fval), width[idx + 1]) |
| | 212 | width[idx] = max(self.get_text_width(f['label']), width[idx]) |
| | 213 | width[idx + 1] = max(self.get_text_width(fval), width[idx + 1]) |
| 198 | 214 | i += 1 |
| 199 | 215 | width_l = width[0] + width[1] + 5 |
| 200 | 216 | width_r = width[2] + width[3] + 5 |
| … |
… |
class TicketNotifyEmail(NotifyEmail): |
| 231 | 247 | str_tmp = u'%s: %s' % (f['label'], unicode(fval)) |
| 232 | 248 | idx = i % 2 |
| 233 | 249 | cell_tmp[idx] += wrap(str_tmp, width_lr[idx] - 2 + 2 * idx, |
| 234 | | (width[2 * idx] - len(f['label']) |
| | 250 | (width[2 * idx] |
| | 251 | - self.get_text_width(f['label']) |
| 235 | 252 | + 2 * idx) * ' ', |
| 236 | 253 | 2 * ' ', CRLF) |
| 237 | 254 | cell_tmp[idx] += CRLF |
| 238 | 255 | i += 1 |
| 239 | 256 | cell_l = cell_tmp[0].splitlines() |
| 240 | 257 | cell_r = cell_tmp[1].splitlines() |
| 241 | | format = u'%%-%is|%%s%%s' % width_l |
| 242 | 258 | for i in range(max(len(cell_l), len(cell_r))): |
| 243 | 259 | if i >= len(cell_l): |
| 244 | 260 | cell_l.append(width_l * ' ') |
| 245 | 261 | elif i >= len(cell_r): |
| 246 | 262 | cell_r.append('') |
| 247 | | txt += format % (cell_l[i], cell_r[i], CRLF) |
| | 263 | fmt_width = width_l - self.get_text_width(cell_l[i]) \ |
| | 264 | + len(cell_l[i]) |
| | 265 | txt += u'%-*s|%s%s' % (fmt_width, cell_l[i], cell_r[i], CRLF) |
| 248 | 266 | if big: |
| 249 | 267 | txt += sep |
| 250 | 268 | for name, value in big: |
| … |
… |
class TicketNotifyEmail(NotifyEmail): |
| 364 | 382 | hdrs['References'] = msgid |
| 365 | 383 | NotifyEmail.send(self, torcpts, ccrcpts, hdrs) |
| 366 | 384 | |
| | 385 | def get_text_width(self, text): |
| | 386 | if self.ambiguous_char_width == 'double': |
| | 387 | ambiwidth = 2 |
| | 388 | else: |
| | 389 | ambiwidth = 1 |
| | 390 | |
| | 391 | if not isinstance(text, unicode): |
| | 392 | text = to_unicode(text) |
| | 393 | |
| | 394 | if text in self.text_widths: |
| | 395 | return self.text_widths[text] |
| | 396 | |
| | 397 | width = 0 |
| | 398 | for ch in text: |
| | 399 | eaw = east_asian_width(ch) |
| | 400 | if eaw == 'W' or eaw == 'F': |
| | 401 | width += 2 |
| | 402 | elif eaw == 'A': |
| | 403 | width += ambiwidth |
| | 404 | else: |
| | 405 | width += 1 |
| | 406 | self.text_widths[text] = width |
| | 407 | return width |
| | 408 | |