mailSvc.py (56126B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\services\mail\mailSvc.py 2 import copy 3 import os 4 import shelve 5 import sys 6 import zlib 7 import blue 8 import characterSettingsStorage.characterSettingsConsts as cSettings 9 import eve.common.script.util.notificationconst as notificationConst 10 import localization 11 import log 12 import uthread 13 import utillib 14 from carbon.common.script.sys.service import Service 15 from carbon.common.script.sys.serviceConst import SERVICE_RUNNING, SERVICE_START_PENDING 16 from carbon.common.script.util.format import FmtDate 17 from carbonui import uiconst 18 from carbonui.uicore import uicore 19 from carbonui.util.various_unsorted import SortListOfTuples 20 from characterSettingsStorage.characterSettingsObject import CharacterSettingsObject 21 from eve.client.script.ui.shared.neocom.evemail import MailAssignColorWnd, MailReadingWnd, MailWindow, NewNewMessage 22 from eve.client.script.ui.util import utilWindows 23 from eve.client.script.util import eveMisc 24 from eve.common.lib import appConst as const 25 from eve.common.script.sys import idCheckers 26 from eve.common.script.util.notificationconst import groupNamePaths as notificationGroupNamePaths 27 from eveexceptions import UserError 28 from eveexceptions.exceptionEater import ExceptionEater 29 from localization import GetByLabel 30 from notifications.common.formatters.mailsummary import MailSummaryFormatter 31 from notifications.common.formatters.newMail import NewMailFormatter 32 from player_messaging.client.ui_message import prompt_player_fsd_dialog 33 MAIL_PATH = blue.paths.ResolvePath('cache:/EveMail/') 34 BODIES_IN_CACHE = 30 35 MAIL_FILE_HEADER_VERSION = 5 36 MAIL_FILE_BODY_VERSION = 1 37 ALL_LABELS = 255 38 swatchColors = {0: ('ffffff', 0), 39 1: ('ffff01', 1), 40 2: ('ff6600', 2), 41 3: ('fe0000', 3), 42 4: ('9a0000', 4), 43 5: ('660066', 5), 44 6: ('0000fe', 6), 45 7: ('0099ff', 7), 46 8: ('01ffff', 8), 47 9: ('00ff33', 9), 48 10: ('349800', 10), 49 11: ('006634', 11), 50 12: ('666666', 12), 51 13: ('999999', 13), 52 14: ('e6e6e6', 14), 53 15: ('ffffcd', 15), 54 16: ('99ffff', 16), 55 17: ('ccff9a', 17)} 56 57 class mailSvc(Service): 58 __guid__ = 'svc.mailSvc' 59 __displayname__ = 'Mail service' 60 __exportedcalls__ = {'MoveMessagesToTrash': [], 61 'MoveMessagesFromTrash': [], 62 'MarkMessagesAsUnread': [], 63 'MarkMessagesAsRead': [], 64 'MoveAllToTrash': [], 65 'MoveToTrashByLabel': [], 66 'MoveToTrashByList': [], 67 'MoveAllFromTrash': [], 68 'MarkAllAsUnread': [], 69 'MarkAsUnreadByLabel': [], 70 'MarkAsUnreadByList': [], 71 'MarkAllAsRead': [], 72 'MarkAsReadByLabel': [], 73 'MarkAsReadByList': [], 74 'EmptyTrash': [], 75 'DeleteMails': [], 76 'GetBody': [], 77 'GetMailsByLabelOrListID': [], 78 'SyncMail': [], 79 'GetLabels': [], 80 'EditLabel': [], 81 'CreateLabel': [], 82 'DeleteLabel': [], 83 'AssignLabels': [], 84 'RemoveLabels': [], 85 'ReplaceLabels': [], 86 'SaveChangesToDisk': [], 87 'ClearCache': [], 88 'SendMail': [], 89 'IsFileCacheCorrupted': [], 90 'PrimeOwners': []} 91 __notifyevents__ = ['OnMailSent', 92 'OnMailDeleted', 93 'OnMailUndeleted', 94 'OnCharacterSessionChanged', 95 'OnLabelsCreatedByExternal', 96 'OnMailUpdatedByExternal', 97 'OnMailTrashed', 98 'OnMailRestored'] 99 __startupdependencies__ = ['settings', 'characterSettings'] 100 101 def __init__(self): 102 Service.__init__(self) 103 self.mailSynced = 0 104 self.cacheFileCorruption = False 105 106 def Run(self, ms = None): 107 self.state = SERVICE_START_PENDING 108 self.Initialize() 109 self.state = SERVICE_RUNNING 110 111 def Initialize(self): 112 self.mailMgr = sm.RemoteSvc('mailMgr') 113 self.mailHeaders = {} 114 self.mailBodies = {} 115 self.mailBodiesOrder = [] 116 self.labels = None 117 self.needToSaveHeaders = False 118 self.isSaving = False 119 self.blinkTab = False 120 self.blinkNeocom = False 121 self.donePrimingRecipients = False 122 self.mailSettingObject = None 123 if session.charid: 124 self.mailFileHeaders = MAIL_PATH + 'mailheaders_' + str(session.charid) 125 self.mailFileBodies = MAIL_PATH + 'mailbodies_' + str(session.charid) 126 try: 127 if not os.path.exists(MAIL_PATH): 128 os.mkdir(MAIL_PATH) 129 except: 130 self.LogError('Error creating mail cache folder. charid:', str(session.charid)) 131 self.cacheFileCorruption = True 132 self.TryCloseMailWindow() 133 raise UserError('MailCacheFileError') 134 135 def OnCharacterSessionChanged(self, _oldCharacterID, _newCharacterID): 136 if session.charid: 137 self.Initialize() 138 self.mailSynced = 0 139 140 def PrimeOwners(self, owners): 141 uthread.Lock(self, 'Prime') 142 try: 143 if len(owners) > 0: 144 toprime = filter(lambda x: x > 0, owners) 145 cfg.eveowners.Prime(toprime) 146 finally: 147 uthread.UnLock(self, 'Prime') 148 149 def TrySyncMail(self): 150 if not self.mailSynced: 151 try: 152 self.SyncMail() 153 except UserError as e: 154 raise 155 except Exception as e: 156 log.LogException('mail failed to load') 157 raise UserError('CustomInfo', {'info': localization.GetByLabel('UI/Mail/FailedToLoad')}) 158 159 def SyncMail(self): 160 self.LogInfo('Syncing mail') 161 uthread.Lock(self) 162 try: 163 if self.__ReadFromHeaderFile('version') != MAIL_FILE_HEADER_VERSION: 164 self.__ClearHeaderCache() 165 self.__WriteToHeaderFile('version', MAIL_FILE_HEADER_VERSION) 166 if self.__ReadFromBodyFile('version') != MAIL_FILE_BODY_VERSION: 167 self.__ClearBodyCache() 168 self.__WriteToBodyFile('version', MAIL_FILE_BODY_VERSION) 169 self.mailHeaders = self.__ReadFromHeaderFile('mail') 170 if self.mailHeaders is None: 171 self.mailHeaders = {} 172 self.__WriteToHeaderFile('mail', {}) 173 lastID = 0 174 firstID = sys.maxint 175 for messageID in self.mailHeaders: 176 if messageID > lastID: 177 lastID = messageID 178 if messageID < firstID: 179 firstID = messageID 180 181 if lastID == 0: 182 firstID = None 183 mailbox = self.mailMgr.SyncMail(firstID, lastID) 184 toPrime = set() 185 incoming = mailbox.newMail 186 if mailbox.oldMail is not None: 187 incoming.extend(mailbox.oldMail) 188 for mailRow in incoming: 189 toPrime.add(mailRow.senderID) 190 toListID = mailRow.toListID 191 toCharacterIDs = [] 192 if mailRow.toCharacterIDs: 193 toCharacterIDs = [ int(x) for x in mailRow.toCharacterIDs.split(',') ] 194 m = utillib.KeyVal(messageID=mailRow.messageID, senderID=mailRow.senderID, toCharacterIDs=toCharacterIDs, toListID=toListID, toCorpOrAllianceID=mailRow.toCorpOrAllianceID, subject=mailRow.title, sentDate=mailRow.sentDate) 195 self.mailHeaders[mailRow.messageID] = m 196 197 toDelete = self.mailHeaders.keys() 198 toFetch = {} 199 for statusRow in mailbox.mailStatus: 200 if statusRow.messageID not in self.mailHeaders: 201 m = utillib.KeyVal(messageID=statusRow.messageID, read=statusRow.statusMask & const.mailStatusMaskRead == const.mailStatusMaskRead, replied=statusRow.statusMask & const.mailStatusMaskReplied == const.mailStatusMaskReplied, forwarded=statusRow.statusMask & const.mailStatusMaskForwarded == const.mailStatusMaskForwarded, trashed=statusRow.statusMask & const.mailStatusMaskTrashed == const.mailStatusMaskTrashed, statusMask=statusRow.statusMask, labels=self.GetLabelMaskAsList(statusRow.labelMask), labelMask=statusRow.labelMask) 202 toFetch[statusRow.messageID] = m 203 else: 204 toDelete.remove(statusRow.messageID) 205 mail = self.mailHeaders[statusRow.messageID] 206 if getattr(mail, 'statusMask', None) != statusRow.statusMask: 207 mail.statusMask = statusRow.statusMask 208 mail.read = mail.statusMask & const.mailStatusMaskRead == const.mailStatusMaskRead 209 mail.replied = mail.statusMask & const.mailStatusMaskReplied == const.mailStatusMaskReplied 210 mail.forwarded = mail.statusMask & const.mailStatusMaskForwarded == const.mailStatusMaskForwarded 211 mail.trashed = mail.statusMask & const.mailStatusMaskTrashed == const.mailStatusMaskTrashed 212 if getattr(mail, 'labelMask', None) != statusRow.labelMask: 213 mail.labelMask = statusRow.labelMask 214 mail.labels = self.GetLabelMaskAsList(mail.labelMask) 215 if mail.statusMask & const.mailStatusMaskAutomated > 0 and mail.senderID == mail.toListID and mail.senderID in toPrime: 216 toPrime.remove(mail.senderID) 217 218 if len(toFetch) > 0: 219 revivedMail = self.mailMgr.GetMailHeaders(toFetch.keys()) 220 for mailRow in revivedMail: 221 toListID = mailRow.toListID 222 toCharacterIDs = [] 223 if mailRow.toCharacterIDs: 224 toCharacterIDs = [ int(x) for x in mailRow.toCharacterIDs.split(',') ] 225 mail = toFetch[mailRow.messageID] 226 mail.senderID = mailRow.senderID 227 mail.toCharacterIDs = toCharacterIDs 228 mail.toListID = toListID 229 mail.toCorpOrAllianceID = mailRow.toCorpOrAllianceID 230 mail.subject = mailRow.title 231 mail.sentDate = mailRow.sentDate 232 if mail.statusMask & const.mailStatusMaskAutomated == 0 or mail.senderID != mail.toListID: 233 toPrime.add(mail.senderID) 234 self.mailHeaders[mailRow.messageID] = mail 235 236 self.PrimeOwners(list(toPrime)) 237 for mail in self.mailHeaders.itervalues(): 238 if hasattr(mail, 'senderName'): 239 continue 240 if mail.senderID is None: 241 raise RuntimeError('Invalid mail item', mail) 242 if mail.statusMask & const.mailStatusMaskAutomated and mail.senderID == mail.toListID: 243 mail.senderName = sm.GetService('mailinglists').GetDisplayName(mail.senderID) 244 else: 245 try: 246 mail.senderName = cfg.eveowners.Get(mail.senderID).name 247 except IndexError: 248 mail.senderName = localization.GetByLabel('UI/Generic/Unknown') 249 250 for messageID in toDelete: 251 del self.mailHeaders[messageID] 252 self.__DeleteFromBodyFile(messageID) 253 254 self.__WriteToHeaderFile('mail', self.mailHeaders) 255 self.mailSynced = 1 256 finally: 257 uthread.UnLock(self) 258 259 self.LogInfo('Done syncing mail') 260 261 def GetLabelMaskAsList(self, mask): 262 if mask < 0: 263 raise RuntimeError('Invalid label mask', mask) 264 counter = 0 265 labels = [] 266 while mask != 0: 267 mask, bitSet = divmod(mask, 2) 268 if bitSet == 1: 269 labels.append(pow(2, counter)) 270 counter += 1 271 272 return labels 273 274 def GetUnreadCounts(self): 275 self.LogInfo('GetUnreadCounts') 276 unreadCounts = utillib.KeyVal(labels={}, lists={}) 277 totalUnread = 0 278 for messageID, message in self.mailHeaders.iteritems(): 279 if not message.read and not message.trashed: 280 totalUnread += 1 281 mask = message.labelMask 282 counter = 0 283 while mask != 0: 284 mask, bitSet = divmod(mask, 2) 285 if bitSet == 1: 286 labelID = pow(2, counter) 287 if labelID in unreadCounts.labels: 288 unreadCounts.labels[labelID] += 1 289 else: 290 unreadCounts.labels[labelID] = 1 291 counter += 1 292 293 if message.toListID is not None: 294 if message.toListID in unreadCounts.lists: 295 unreadCounts.lists[message.toListID] += 1 296 else: 297 unreadCounts.lists[message.toListID] = 1 298 299 unreadCounts.labels[None] = totalUnread 300 self.LogInfo('unreadCounts:', unreadCounts) 301 return unreadCounts 302 303 def CheckShouldStopBlinking(self, *args): 304 allUnreadGroups = self.GetUnreadCounts() 305 allUnread = allUnreadGroups.labels.get(None, 0) 306 if allUnread == 0: 307 self.StopMailBlinking() 308 309 def StopMailBlinking(self, *args): 310 self.SetBlinkTabState(False) 311 sm.ScatterEvent('OnMailStartStopBlinkingTab', 'mail', 0) 312 self.SetBlinkNeocomState(False) 313 sm.GetService('neocom').BlinkOff('mail') 314 315 def MoveMessagesToTrash(self, messageIDs): 316 self.LogInfo('Move to trash', messageIDs) 317 self.mailMgr.MoveToTrash(messageIDs) 318 self._RefreshTrashedStatusWhenMovingToTrash(messageIDs) 319 320 def _RefreshTrashedStatusWhenMovingToTrash(self, messageIDs): 321 self._RefreshTrashedStatus(messageIDs, True, lambda mailHeader: mailHeader.statusMask | const.mailStatusMaskTrashed) 322 self.CheckShouldStopBlinking() 323 324 def MoveMessagesFromTrash(self, messageIDs): 325 self.LogInfo('Move from trash', messageIDs) 326 self.mailMgr.MoveFromTrash(messageIDs) 327 self._RefreshTrashedStatusWhenMovingFromTrash(messageIDs) 328 329 def _RefreshTrashedStatusWhenMovingFromTrash(self, messageIDs): 330 self._RefreshTrashedStatus(messageIDs, False, lambda mailHeader: mailHeader.statusMask & ALL_LABELS - const.mailStatusMaskRead) 331 sm.ScatterEvent('OnMailCountersUpdate') 332 333 def _RefreshTrashedStatus(self, messageIDs, isTrashed, statusMaskFunc): 334 for messageID in messageIDs: 335 mail = self.mailHeaders[messageID] 336 mail.trashed = isTrashed 337 mail.statusMask = statusMaskFunc(mail) 338 339 self.needToSaveHeaders = True 340 341 def MarkMessagesAsUnread(self, messageIDs, notifyServer = True): 342 self.LogInfo('Mark as unread', messageIDs) 343 if notifyServer: 344 self.mailMgr.MarkAsUnread(messageIDs) 345 for messageID in messageIDs: 346 mail = self.mailHeaders[messageID] 347 mail.read = False 348 mail.statusMask = mail.statusMask & ALL_LABELS - const.mailStatusMaskRead 349 350 self.needToSaveHeaders = True 351 sm.ScatterEvent('OnMailCountersUpdate') 352 353 def MarkMessagesAsRead(self, messageIDs, notifyServer = True): 354 self.LogInfo('Mark as read', messageIDs, notifyServer) 355 if notifyServer: 356 self.mailMgr.MarkAsRead(messageIDs) 357 for messageID in messageIDs: 358 mail = self.mailHeaders[messageID] 359 mail.read = True 360 mail.statusMask = mail.statusMask | const.mailStatusMaskRead 361 362 self.needToSaveHeaders = True 363 sm.ScatterEvent('OnMailCountersUpdate') 364 self.CheckShouldStopBlinking() 365 366 def MoveAllToTrash(self): 367 self.LogInfo('Move all to trash') 368 self.mailMgr.MoveAllToTrash() 369 for mail in self.mailHeaders.itervalues(): 370 mail.trashed = True 371 mail.statusMask = mail.statusMask | const.mailStatusMaskTrashed 372 373 self.needToSaveHeaders = True 374 375 def MoveToTrashByLabel(self, labelID): 376 self.LogInfo('Move label to trash', labelID) 377 self.mailMgr.MoveToTrashByLabel(labelID) 378 for mail in self.mailHeaders.itervalues(): 379 if mail.labelMask & labelID == labelID: 380 mail.trashed = True 381 mail.statusMask = mail.statusMask | const.mailStatusMaskTrashed 382 383 self.needToSaveHeaders = True 384 385 def MoveToTrashByList(self, listID): 386 self.LogInfo('Move list to trash', listID) 387 self.mailMgr.MoveToTrashByList(listID) 388 for mail in self.mailHeaders.itervalues(): 389 if mail.toListID is not None and listID == mail.toListID: 390 mail.trashed = True 391 mail.statusMask = mail.statusMask | const.mailStatusMaskTrashed 392 393 self.needToSaveHeaders = True 394 395 def MarkAllAsUnread(self): 396 self.LogInfo('Mark all as unread') 397 self.mailMgr.MarkAllAsUnread() 398 for mail in self.mailHeaders.itervalues(): 399 if not mail.trashed: 400 mail.read = False 401 mail.statusMask = mail.statusMask & ALL_LABELS - const.mailStatusMaskRead 402 403 self.needToSaveHeaders = True 404 405 def MarkAsUnreadByLabel(self, labelID): 406 self.LogInfo('Mark label unread', labelID) 407 self.mailMgr.MarkAsUnreadByLabel(labelID) 408 for mail in self.mailHeaders.itervalues(): 409 if not mail.trashed and mail.labelMask & labelID == labelID: 410 mail.read = False 411 mail.statusMask = mail.statusMask & ALL_LABELS - const.mailStatusMaskRead 412 413 self.needToSaveHeaders = True 414 415 def MarkAsUnreadByList(self, listID): 416 self.LogInfo('Mark list unread', listID) 417 self.mailMgr.MarkAsUnreadByList(listID) 418 for mail in self.mailHeaders.itervalues(): 419 if not mail.trashed and mail.toListID is not None and listID == mail.toListID: 420 mail.read = False 421 mail.statusMask = mail.statusMask & ALL_LABELS - const.mailStatusMaskRead 422 423 self.needToSaveHeaders = True 424 425 def MarkAllAsRead(self): 426 self.LogInfo('Mark all read') 427 self.mailMgr.MarkAllAsRead() 428 for mail in self.mailHeaders.itervalues(): 429 if not mail.trashed: 430 mail.read = True 431 mail.statusMask = mail.statusMask | const.mailStatusMaskRead 432 433 self.needToSaveHeaders = True 434 435 def MarkAsReadByLabel(self, labelID): 436 self.LogInfo('Mark label read', labelID) 437 self.mailMgr.MarkAsReadByLabel(labelID) 438 for mail in self.mailHeaders.itervalues(): 439 if not mail.trashed and mail.labelMask & labelID == labelID: 440 mail.read = True 441 mail.statusMask = mail.statusMask | const.mailStatusMaskRead 442 443 self.needToSaveHeaders = True 444 445 def MarkAsReadByList(self, listID): 446 self.LogInfo('Mark list read', listID) 447 self.mailMgr.MarkAsReadByList(listID) 448 for mail in self.mailHeaders.itervalues(): 449 if not mail.trashed and mail.toListID is not None and listID == mail.toListID: 450 mail.read = True 451 mail.statusMask = mail.statusMask | const.mailStatusMaskRead 452 453 self.needToSaveHeaders = True 454 455 def MoveAllFromTrash(self): 456 self.LogInfo('Move all from trash') 457 self.mailMgr.MoveAllFromTrash() 458 for mail in self.mailHeaders.itervalues(): 459 mail.trashed = False 460 mail.statusMask = mail.statusMask & ALL_LABELS - const.mailStatusMaskTrashed 461 462 self.needToSaveHeaders = True 463 464 def EmptyTrash(self): 465 self.LogInfo('Empty trash') 466 self.mailMgr.EmptyTrash() 467 deleted = [] 468 for messageID, mail in self.mailHeaders.iteritems(): 469 if mail.trashed: 470 deleted.append(messageID) 471 472 self.LogInfo('Deleted', len(deleted), 'messages') 473 for messageID in deleted: 474 del self.mailHeaders[messageID] 475 if messageID in self.mailBodies: 476 del self.mailBodies[messageID] 477 if messageID in self.mailBodiesOrder: 478 self.mailBodiesOrder.remove(messageID) 479 self.__DeleteFromBodyFile(messageID) 480 481 self.needToSaveHeaders = True 482 483 def DeleteMails(self, messageIDs): 484 self.LogInfo('Delete', messageIDs) 485 self.mailMgr.DeleteMail(messageIDs) 486 for messageID in messageIDs: 487 if messageID in self.mailHeaders: 488 del self.mailHeaders[messageID] 489 if messageID in self.mailBodies: 490 del self.mailBodies[messageID] 491 if messageID in self.mailBodiesOrder: 492 self.mailBodiesOrder.remove(messageID) 493 self.__DeleteFromBodyFile(messageID) 494 495 self.needToSaveHeaders = True 496 497 def GetBody(self, messageID): 498 self.LogInfo('GetMailBody', messageID) 499 if messageID in self.mailBodies: 500 self.LogInfo('Cached') 501 if not self.mailHeaders[messageID].read: 502 self.MarkMessagesAsRead([messageID]) 503 return self.mailBodies[messageID] 504 if len(self.mailBodiesOrder) > BODIES_IN_CACHE: 505 try: 506 self.LogInfo('Must make room in cache') 507 del self.mailBodies[self.mailBodiesOrder.pop(0)] 508 except KeyError: 509 pass 510 511 if messageID in self.mailHeaders: 512 body = self.__ReadFromBodyFile(messageID) 513 shouldMarkAsRead = not self.mailHeaders[messageID].read 514 if body is None: 515 compressedBody = self.mailMgr.GetBody(messageID, shouldMarkAsRead) 516 if compressedBody is None: 517 return '' 518 shouldMarkAsRead = False 519 body = zlib.decompress(compressedBody).decode('utf-8') 520 self.__WriteToBodyFile(messageID, body) 521 self.MarkMessagesAsRead([messageID], shouldMarkAsRead) 522 self.mailBodies[messageID] = body 523 self.mailBodiesOrder.append(messageID) 524 return body 525 self.LogError("Asking me to get a body, but I can't find the messageID asked for in self.mailHeaders. messageID:", messageID) 526 527 def GetMailsByLabelOrListID(self, labelID = None, orderBy = None, ascending = False, pos = 0, count = 20, listID = None): 528 if orderBy is None: 529 orderBy = localization.GetByLabel('UI/Mail/Received') 530 self.LogInfo('Get messages', labelID, orderBy, ascending, pos, count, listID) 531 tmpList = [] 532 isSentitems = labelID == const.mailLabelSent 533 if isSentitems and orderBy == localization.GetByLabel('UI/Mail/Sender'): 534 mails = [] 535 for message in self.mailHeaders.itervalues(): 536 if labelID in message.labels: 537 mails.append(message) 538 539 self.TryPrimeRecipients(mails, setDone=True) 540 for message in self.mailHeaders.itervalues(): 541 if message.trashed: 542 continue 543 if listID is not None: 544 if message.toListID is not None and listID == message.toListID: 545 tmpList.append(self.PrepareOrder(message, orderBy, ascending, isSentitems)) 546 continue 547 if labelID is None or message.labelMask & labelID == labelID: 548 tmpList.append(self.PrepareOrder(message, orderBy, ascending, isSentitems)) 549 550 ret = utillib.KeyVal() 551 ret.totalNum = len(tmpList) 552 ret.sorted = self.DoSort(tmpList, ascending=ascending, pos=pos, count=count) 553 if isSentitems and orderBy != localization.GetByLabel('UI/Mail/Sender'): 554 self.TryPrimeRecipients(ret.sorted) 555 return ret 556 557 def GetTrashedMails(self, orderBy = None, ascending = False, pos = 0, count = 20): 558 if orderBy is None: 559 orderBy = localization.GetByLabel('UI/Mail/Received') 560 self.LogInfo('Get trash', orderBy, ascending, pos, count) 561 tmpList = [] 562 for message in self.mailHeaders.itervalues(): 563 if not message.trashed: 564 continue 565 tmpList.append(self.PrepareOrder(message, orderBy, ascending)) 566 567 ret = utillib.KeyVal() 568 ret.totalNum = len(tmpList) 569 ret.sorted = self.DoSort(tmpList, ascending=ascending, pos=pos, count=count) 570 return ret 571 572 def PrepareOrder(self, message, orderBy = None, ascending = False, sentItems = 0): 573 if orderBy is None: 574 orderBy = localization.GetByLabel('UI/Mail/Received') 575 if ascending: 576 secondarySortID = message.messageID 577 else: 578 secondarySortID = -message.messageID 579 if orderBy == localization.GetByLabel('UI/Mail/Received'): 580 return (message.messageID, secondarySortID) 581 if orderBy == localization.GetByLabel('UI/Mail/Subject'): 582 return (message.subject.lower(), secondarySortID) 583 if orderBy == localization.GetByLabel('UI/Mail/Sender'): 584 if sentItems: 585 name = self.GetRecipient(message, getName=1) 586 else: 587 name = message.senderName 588 return (name.lower(), secondarySortID) 589 if orderBy == localization.GetByLabel('UI/Mail/Status'): 590 order = 4 591 if not message.read: 592 order = 1 593 elif message.replied: 594 order = 2 595 elif message.forwarded: 596 order = 3 597 return (order, secondarySortID) 598 599 def DoSort(self, list, ascending = False, pos = 0, count = 20): 600 self.LogInfo('Sort', ascending, pos, count) 601 retMails = [] 602 list.sort(reverse=ascending) 603 for message in list[pos:pos + count]: 604 retMails.append(self.mailHeaders[abs(message[1])]) 605 606 return retMails 607 608 def GetMailsByIDs(self, messageIDs): 609 mailDict = {} 610 for messageID in messageIDs: 611 mailDict[messageID] = self.GetMailByID(messageID) 612 613 return mailDict 614 615 def GetMailByID(self, messageID): 616 try: 617 return self.mailHeaders[messageID] 618 except KeyError: 619 return None 620 621 def TryPrimeRecipients(self, messages, setDone = False): 622 if getattr(self, 'donePrimingRecipients', False): 623 return 624 idSet = set() 625 for msg in messages: 626 entityID = self.GetRecipient(msg, getName=0) 627 if entityID > 0: 628 idSet.add(entityID) 629 630 owners = list(idSet) 631 self.PrimeOwners(owners) 632 if setDone: 633 self.donePrimingRecipients = True 634 635 def GetRecipient(self, message, getName = 1): 636 toCharIDs = message.toCharacterIDs or [] 637 toListID = message.toListID 638 if message.toCorpOrAllianceID is not None: 639 toCorpIDs = [message.toCorpOrAllianceID] 640 else: 641 toCorpIDs = [] 642 if toListID is not None: 643 numSentTo = 1 644 else: 645 numSentTo = 0 646 numSentTo += len(toCharIDs) + len(toCorpIDs) 647 if numSentTo > 1: 648 if getName: 649 return localization.GetByLabel('UI/Mail/Multiple') 650 else: 651 return -1 652 for each in toCharIDs + toCorpIDs: 653 if getName: 654 return cfg.eveowners.Get(each).ownerName 655 return each 656 657 if toListID is not None: 658 if getName: 659 return sm.GetService('mailinglists').GetDisplayName(toListID) 660 else: 661 return -1 662 self.LogWarn('In GetRecipient: message = ', message) 663 if getName: 664 return '' 665 else: 666 return -1 667 668 def GetLabels(self): 669 if self.labels is None: 670 self.labels = self.mailMgr.GetLabels() 671 return self.labels 672 673 def GetAllLabels(self, assignable = 0): 674 self.LogInfo('GetAllLabels') 675 allLabels = copy.copy(self.GetLabels()) 676 static = [(localization.GetByLabel('UI/Mail/LabelAlliance'), const.mailLabelAlliance), 677 (localization.GetByLabel('UI/Mail/LabelCorp'), const.mailLabelCorporation), 678 (localization.GetByLabel('UI/Mail/LabelInbox'), const.mailLabelInbox), 679 (localization.GetByLabel('UI/Mail/LabelSent'), const.mailLabelSent)] 680 for name, id in static: 681 keyVal = allLabels.get(id, None) 682 if keyVal is not None: 683 keyVal.static = 1 684 keyVal.name = name 685 else: 686 keyVal = utillib.KeyVal() 687 keyVal.name = name 688 keyVal.labelID = id 689 keyVal.static = 1 690 keyVal.color = None 691 allLabels[id] = keyVal 692 693 self.labels = allLabels.copy() 694 if assignable: 695 allLabels.pop(const.mailLabelSent, None) 696 return allLabels 697 698 def EditLabel(self, labelID, name = None, color = None): 699 self.LogInfo('EditLabel', labelID, name, color) 700 if name is None and color is None or name == '': 701 raise UserError('MailLabelMustProvideName') 702 self.mailMgr.EditLabel(labelID, name, color) 703 staticLabels = [const.mailLabelSent, 704 const.mailLabelInbox, 705 const.mailLabelCorporation, 706 const.mailLabelAlliance] 707 self.GetLabels() 708 if labelID not in self.labels and labelID not in staticLabels: 709 raise RuntimeError("Invalid label cache, can't update", labelID, name) 710 if name is not None: 711 self.labels[labelID].name = name 712 if color is not None: 713 self.labels[labelID].color = color 714 sm.ScatterEvent('OnMyLabelsChanged', 'mail_labels', None) 715 716 def AddLabel(self, labelID, name, color): 717 self.GetLabels() 718 self.labels[labelID] = utillib.KeyVal(labelID=labelID, name=name, color=color) 719 720 def CreateLabel(self, name, color = None): 721 self.LogInfo('CreateLabel', name, color) 722 labelID = self.mailMgr.CreateLabel(name, color) 723 self.AddLabel(labelID, name, color) 724 sm.ScatterEvent('OnMyLabelsChanged', 'mail_labels', labelID) 725 return labelID 726 727 def DeleteLabel(self, labelID): 728 self.LogInfo('DeleteLabel', labelID) 729 self.mailMgr.DeleteLabel(labelID) 730 self.GetLabels() 731 try: 732 del self.labels[labelID] 733 except KeyError: 734 raise RuntimeError("Invalid label cache, can't remove", labelID) 735 736 for message in self.mailHeaders.itervalues(): 737 if labelID in message.labels: 738 message.labelMask = message.labelMask & const.maxInt - labelID 739 message.labels = self.GetLabelMaskAsList(message.labelMask) 740 741 sm.ScatterEvent('OnMyLabelsChanged', 'mail_labels', None) 742 743 def AssignLabels(self, messageIDs, labelID, notifyServer = True): 744 self.LogInfo('AssignLabels', messageIDs, labelID) 745 mailsToGetLabel = {} 746 for messageID in messageIDs: 747 mail = self.mailHeaders[messageID] 748 if labelID & mail.labelMask != labelID: 749 mailsToGetLabel[messageID] = mail 750 751 if len(mailsToGetLabel) > 0 and notifyServer: 752 self.mailMgr.AssignLabels(mailsToGetLabel.keys(), labelID) 753 for mail in mailsToGetLabel.itervalues(): 754 mail.labelMask = mail.labelMask | labelID 755 mail.labels = self.GetLabelMaskAsList(mail.labelMask) 756 757 self.needToSaveHeaders = True 758 sm.ScatterEvent('OnMailCountersUpdate') 759 760 def RemoveLabels(self, messageIDs, labelID, notifyServer = True): 761 self.LogInfo('RemoveLabels', messageIDs, labelID) 762 mailsToRemoveLabelFrom = {} 763 for messageID in messageIDs: 764 mail = self.mailHeaders[messageID] 765 if labelID & mail.labelMask > 0: 766 mailsToRemoveLabelFrom[messageID] = mail 767 768 if len(mailsToRemoveLabelFrom) > 0 and notifyServer: 769 self.mailMgr.RemoveLabels(mailsToRemoveLabelFrom.keys(), labelID) 770 for mail in mailsToRemoveLabelFrom.itervalues(): 771 mail.labelMask = mail.labelMask & const.maxInt - labelID 772 mail.labels = self.GetLabelMaskAsList(mail.labelMask) 773 774 self.needToSaveHeaders = True 775 sm.ScatterEvent('OnMailCountersUpdate') 776 777 def ReplaceLabels(self, messageIDs, labelID, notifyServer = True): 778 mailsToReplaceLabel = {} 779 for messageID in messageIDs: 780 mail = self.mailHeaders[messageID] 781 if labelID != mail.labelMask: 782 mailsToReplaceLabel[messageID] = mail 783 784 if len(mailsToReplaceLabel) > 0 and notifyServer: 785 self.mailMgr.ReplaceLabels(mailsToReplaceLabel.keys(), labelID) 786 for mail in mailsToReplaceLabel.itervalues(): 787 mail.labelMask = labelID 788 mail.labels = self.GetLabelMaskAsList(mail.labelMask) 789 790 self.needToSaveHeaders = True 791 sm.ScatterEvent('OnMailCountersUpdate') 792 793 def SaveChangesToDisk(self): 794 if not self.needToSaveHeaders or self.isSaving: 795 self.LogInfo('SaveChangesToDisk - nothing to do') 796 return 797 self.isSaving = True 798 try: 799 uthread.Lock(self) 800 try: 801 self.LogInfo('SaveChangesToDisk - saving') 802 self.__WriteToHeaderFile('mail', self.mailHeaders) 803 self.needToSaveHeaders = False 804 finally: 805 uthread.UnLock(self) 806 807 finally: 808 self.isSaving = False 809 810 def TryCloseMailWindow(self): 811 MailWindow.CloseIfOpen() 812 813 def ClearMailCache(self): 814 if prompt_player_fsd_dialog('AskClearMailCache', {}, uiconst.YESNO) == uiconst.ID_YES: 815 self.TryCloseMailWindow() 816 self.ClearCache() 817 818 def ClearCache(self): 819 uthread.Lock(self) 820 try: 821 self.__ClearHeaderCache() 822 self.__ClearBodyCache() 823 finally: 824 uthread.UnLock(self) 825 826 def __ClearHeaderCache(self): 827 try: 828 if os.path.exists(self.mailFileHeaders + '.dir'): 829 os.remove(self.mailFileHeaders + '.dir') 830 if os.path.exists(self.mailFileHeaders + '.dat'): 831 os.remove(self.mailFileHeaders + '.dat') 832 if os.path.exists(self.mailFileHeaders + '.bak'): 833 os.remove(self.mailFileHeaders + '.bak') 834 except: 835 self.cacheFileCorruption = True 836 self.LogError('Error deleting header mail cache files. charid:', str(session.charid)) 837 raise UserError('MailCacheFileError') 838 839 self.mailHeaders = {} 840 self.labels = None 841 self.needToSaveHeaders = False 842 self.cacheFileCorruption = False 843 self.mailSynced = 0 844 845 def __ClearBodyCache(self): 846 try: 847 if os.path.exists(self.mailFileBodies + '.dir'): 848 os.remove(self.mailFileBodies + '.dir') 849 if os.path.exists(self.mailFileBodies + '.dat'): 850 os.remove(self.mailFileBodies + '.dat') 851 if os.path.exists(self.mailFileBodies + '.bak'): 852 os.remove(self.mailFileBodies + '.bak') 853 except: 854 self.cacheFileCorruption = True 855 self.LogError('Error deleting body mail cache files. charid:', str(session.charid)) 856 raise UserError('MailCacheFileError') 857 858 self.mailBodies = {} 859 self.mailBodiesOrder = [] 860 self.cacheFileCorruption = False 861 self.mailSynced = 0 862 863 def __WriteToShelveFile(self, fileName, key, value): 864 self.LogInfo('WriteToShelve', fileName, key) 865 s = blue.os.GetWallclockTimeNow() 866 key = str(key) 867 try: 868 fileHandle = shelve.open(fileName) 869 fileHandle[key] = value 870 fileHandle.close() 871 except: 872 self.LogError('Error writing to shelve file (', fileName, ', ', key, ',', value, ')') 873 self.cacheFileCorruption = True 874 self.TryCloseMailWindow() 875 raise UserError('MailCacheFileError') 876 877 self.LogInfo('Writing', blue.os.TimeDiffInMs(s, blue.os.GetWallclockTimeNow())) 878 879 def __ReadFromShelveFile(self, fileName, key): 880 self.LogInfo('ReadingFromShelve', fileName, key) 881 s = blue.os.GetWallclockTimeNow() 882 key = str(key) 883 retValue = None 884 try: 885 fileHandle = shelve.open(fileName) 886 if key in fileHandle: 887 retValue = fileHandle[key] 888 fileHandle.close() 889 return retValue 890 except: 891 self.LogError('Error reading from shelve file (', fileName, ', ', key, ')') 892 self.cacheFileCorruption = True 893 self.TryCloseMailWindow() 894 raise UserError('MailCacheFileError') 895 896 self.LogInfo('Reading', blue.os.TimeDiffInMs(s, blue.os.GetWallclockTimeNow())) 897 898 def __WriteToHeaderFile(self, key, value): 899 self.__WriteToShelveFile(self.mailFileHeaders, key, value) 900 901 def __ReadFromHeaderFile(self, key): 902 return self.__ReadFromShelveFile(self.mailFileHeaders, key) 903 904 def __WriteToBodyFile(self, key, value): 905 self.__WriteToShelveFile(self.mailFileBodies, key, value) 906 907 def __ReadFromBodyFile(self, key): 908 return self.__ReadFromShelveFile(self.mailFileBodies, key) 909 910 def __DeleteFromBodyFile(self, key): 911 self.LogInfo('DeletingFromShelve', key) 912 s = blue.os.GetWallclockTimeNow() 913 key = str(key) 914 try: 915 fileHandle = shelve.open(self.mailFileBodies) 916 if key in fileHandle: 917 del fileHandle[key] 918 fileHandle.close() 919 except: 920 self.LogError('Error deleting from body shelve file (', key, ')') 921 self.cacheFileCorruption = True 922 self.TryCloseMailWindow() 923 raise UserError('MailCacheFileError') 924 925 self.LogInfo('Deleting', blue.os.TimeDiffInMs(s, blue.os.GetWallclockTimeNow())) 926 927 def IsFileCacheCorrupted(self): 928 return self.cacheFileCorruption 929 930 def SendMail(self, toCharacterIDs = [], toListID = None, toCorpOrAllianceID = None, title = '', body = '', isReplyTo = 0, isForwardedFrom = 0): 931 self.LogInfo('SendMail', toCharacterIDs, toListID, toCorpOrAllianceID, isReplyTo, isForwardedFrom) 932 if toListID is not None: 933 myLists = sm.GetService('mailinglists').GetMyMailingLists() 934 if toListID not in myLists: 935 raise UserError('EveMailCanOnlySendToOwnMailinglists') 936 messageID = eveMisc.CSPAChargedAction('CSPAMailCheck', self.mailMgr, 'SendMail', toCharacterIDs, toListID, toCorpOrAllianceID, title, body, isReplyTo, isForwardedFrom) 937 if messageID is None: 938 return 939 if len(self.mailBodiesOrder) > BODIES_IN_CACHE: 940 try: 941 del self.mailBodies[self.mailBodiesOrder.pop(0)] 942 except KeyError: 943 pass 944 945 self.__WriteToBodyFile(messageID, body) 946 self.mailBodies[messageID] = body 947 self.mailBodiesOrder.append(messageID) 948 if isReplyTo > 0: 949 if isReplyTo in self.mailHeaders: 950 mail = self.mailHeaders[isReplyTo] 951 mail.replied = 1 952 mail.statusMask = mail.statusMask | const.mailStatusMaskReplied 953 self.needToSaveHeaders = True 954 if isForwardedFrom > 0: 955 if isForwardedFrom in self.mailHeaders: 956 mail = self.mailHeaders[isForwardedFrom] 957 mail.forwarded = 1 958 mail.statusMask = mail.statusMask | const.mailStatusMaskForwarded 959 self.needToSaveHeaders = True 960 self.OnMailSent(messageID, session.charid, blue.os.GetWallclockTime() / const.MIN * const.MIN, toCharacterIDs, toListID, toCorpOrAllianceID, title, 0) 961 sm.ScatterEvent('OnMailStatusUpdate', isReplyTo, isForwardedFrom) 962 return messageID 963 964 def SendMsgDlg(self, toCharacterIDs = [], toListID = None, toCorpOrAllianceID = [], isForwardedFrom = 0, isReplyTo = 0, subject = None, body = None): 965 if session.inDetention: 966 raise UserError('NotAllowedInDetention') 967 sendPage = NewNewMessage.Open(windowID='NewMessageWindow', windowInstanceID=blue.os.GetWallclockTime(), toCharacterIDs=toCharacterIDs, toListID=toListID, toCorpOrAllianceID=toCorpOrAllianceID, isForwardedFrom=isForwardedFrom, isReplyTo=isReplyTo, subject=subject, body=body) 968 return sendPage 969 970 def GetReplyWnd(self, msg, all = 0, *args): 971 if msg is None: 972 return 973 toListID = None 974 toCorpOrAllianceID = [] 975 toCharacterIDs = [] 976 senders = [] 977 if all: 978 if msg.toCharacterIDs is not None: 979 toCharacterIDs = msg.toCharacterIDs[:] 980 if session.charid in toCharacterIDs: 981 toCharacterIDs.remove(session.charid) 982 toCharacterIDs.append(msg.senderID) 983 if msg.toCorpOrAllianceID is not None: 984 toCorpOrAllianceID = [msg.toCorpOrAllianceID] 985 toListID = msg.toListID 986 else: 987 toCharacterIDs = [msg.senderID] 988 receiversText = self.GetReceiverText(msg) 989 newmsg = self.SendMsgDlg(toCharacterIDs=toCharacterIDs, toListID=toListID, toCorpOrAllianceID=toCorpOrAllianceID, isReplyTo=msg.messageID) 990 newmsgText = self.GetReplyMessage(msg) 991 if msg.subject.startswith(localization.GetByLabel('UI/Mail/GenericInboxRe')): 992 newSubjectText = msg.subject 993 else: 994 newSubjectText = '%s %s' % (localization.GetByLabel('UI/Mail/GenericInboxRe'), msg.subject) 995 newmsg.sr.subjecField.SetValue(newSubjectText) 996 newmsg.messageedit.SetValue(newmsgText, scrolltotop=1) 997 998 def GetReceiverText(self, mail, format = 0): 999 receiversChar = mail.toCharacterIDs 1000 receiversMaillistID = mail.toListID 1001 receiversCorp = mail.toCorpOrAllianceID or '' 1002 receiversText = '' 1003 if receiversMaillistID is not None: 1004 name = sm.GetService('mailinglists').GetDisplayName(receiversMaillistID) 1005 if format: 1006 name = localization.GetByLabel('UI/Map/StarMap/lblBoldName', name=name) 1007 receiversText += '%s, ' % name 1008 if receiversCorp: 1009 name = cfg.eveowners.Get(receiversCorp).ownerName 1010 toAdd = '%s' % name 1011 if format: 1012 toAdd = localization.GetByLabel('UI/Map/StarMap/lblBoldName', name=toAdd) 1013 receiversText += '%s, ' % toAdd 1014 charsIDs = [ charID for charID in receiversChar if charID not in (-1, None) ] 1015 self.PrimeOwners(charsIDs) 1016 charNameList = [] 1017 for each in charsIDs: 1018 name = cfg.eveowners.Get(each).ownerName 1019 text = '<a href="showinfo:1377//%s">%s</a>, ' % (each, name) 1020 charNameList.append((name.lower(), text)) 1021 1022 charNameList = SortListOfTuples(charNameList) 1023 for each in charNameList: 1024 receiversText += each 1025 1026 return receiversText 1027 1028 def GetMailText(self, node): 1029 msg = node 1030 body = self.GetBody(msg.messageID) 1031 subject = msg.subject 1032 senderID = msg.senderID 1033 senderName = msg.senderName 1034 date = msg.sentDate 1035 receiversText = self.GetReceiverText(msg, format=1) 1036 if msg.statusMask & const.mailStatusMaskAutomated == const.mailStatusMaskAutomated: 1037 senderText = localization.GetByLabel('UI/Map/StarMap/lblBoldName', name=senderName) 1038 else: 1039 senderText = '<a href="showinfo:1377//%s">%s</a>' % (senderID, senderName) 1040 txt = localization.GetByLabel('UI/Mail/MailText', subject=subject, sender=senderText, date=FmtDate(date, 'ls'), receivers=receiversText, body=body) 1041 return txt 1042 1043 def GetReplyMessage(self, msg): 1044 if msg is None: 1045 return '' 1046 receiversText = self.GetReceiverText(msg) 1047 if msg.statusMask & const.mailStatusMaskAutomated == const.mailStatusMaskAutomated: 1048 senderText = localization.GetByLabel('UI/Map/StarMap/lblBoldName', name=msg.senderName) 1049 else: 1050 senderText = '<a href="showinfo:1377//%s">%s</a>' % (msg.senderID, cfg.eveowners.Get(msg.senderID).ownerName) 1051 body = self.GetBody(msg.messageID) 1052 newmsgText = '<br><br>%(line)s<br>%(subject)s<br>%(from)s: %(senders)s<br>%(sent)s: %(date)s<br>%(to)s: %(receivers)s<br><br>%(body)s' % {'line': '--------------------------------', 1053 'subject': msg.subject, 1054 'from': localization.GetByLabel('UI/Mail/From'), 1055 'senders': senderText, 1056 'sent': localization.GetByLabel('UI/Mail/Sent'), 1057 'date': FmtDate(msg.sentDate, 'ls'), 1058 'to': localization.GetByLabel('UI/Mail/To'), 1059 'receivers': receiversText, 1060 'body': body} 1061 maxLen = const.mailMaxBodySize * 0.9 1062 if len(newmsgText) > maxLen: 1063 while len(newmsgText) > maxLen: 1064 try: 1065 brIndex = newmsgText.rindex('<br>', int(maxLen * 0.7)) 1066 newmsgText = newmsgText[:brIndex] 1067 except ValueError: 1068 lastIndex = int(len(newmsgText) * 0.9) 1069 newmsgText = newmsgText[:lastIndex] 1070 1071 newmsgText += '<br>...' 1072 return newmsgText 1073 1074 def GetForwardWnd(self, msg): 1075 if msg is None: 1076 return 1077 newmsg = self.SendMsgDlg(isForwardedFrom=msg.messageID) 1078 newmsgText = self.GetReplyMessage(msg) 1079 newmsg.sr.subjecField.SetValue('%s %s' % ('FW:', msg.subject)) 1080 newmsg.messageedit.SetValue(newmsgText, scrolltotop=1) 1081 1082 def OnMailSent(self, messageID, senderID, sentDate, toCharacterIDs, toListID, toCorpOrAllianceID, title, statusMask): 1083 uthread.Lock(self) 1084 try: 1085 if messageID in self.mailHeaders: 1086 return 1087 labelMask = 0 1088 read = False 1089 if session.charid in toCharacterIDs: 1090 labelMask = const.mailLabelInbox 1091 if senderID == session.charid: 1092 if labelMask == 0 and toCorpOrAllianceID is None and toListID is None: 1093 read = True 1094 statusMask = statusMask | 1 1095 labelMask = labelMask | const.mailLabelSent 1096 if toCorpOrAllianceID is not None: 1097 if idCheckers.IsCorporation(toCorpOrAllianceID): 1098 labelMask = labelMask | const.mailLabelCorporation 1099 else: 1100 labelMask = labelMask | const.mailLabelAlliance 1101 self.mailHeaders[messageID] = utillib.KeyVal(messageID=messageID, senderID=senderID, toCharacterIDs=toCharacterIDs, toListID=toListID, toCorpOrAllianceID=toCorpOrAllianceID, subject=title, sentDate=sentDate, read=read, replied=False, forwarded=False, trashed=False, statusMask=statusMask, labelMask=labelMask, labels=self.GetLabelMaskAsList(labelMask)) 1102 if statusMask & const.mailStatusMaskAutomated > 0 and senderID == toListID: 1103 self.mailHeaders[messageID].senderName = sm.GetService('mailinglists').GetDisplayName(senderID) 1104 else: 1105 try: 1106 self.mailHeaders[messageID].senderName = cfg.eveowners.Get(senderID).name 1107 except IndexError: 1108 self.mailHeaders[messageID].senderName = localization.GetByLabel('UI/Generic/Unknown') 1109 1110 self.needToSaveHeaders = True 1111 if not read: 1112 self.OnNewMailReceived(self.mailHeaders[messageID]) 1113 finally: 1114 uthread.UnLock(self) 1115 1116 def OnMailDeleted(self, messageIDs): 1117 uthread.Lock(self) 1118 try: 1119 for messageID in messageIDs: 1120 if messageID in self.mailHeaders: 1121 del self.mailHeaders[messageID] 1122 self.needToSaveHeaders = True 1123 if messageID in self.mailBodies: 1124 del self.mailBodies[messageID] 1125 if messageID in self.mailBodiesOrder: 1126 self.mailBodiesOrder.remove(messageID) 1127 self.__DeleteFromBodyFile(messageID) 1128 1129 sm.ScatterEvent('OnMailTrashedDeleted', None, 1) 1130 finally: 1131 uthread.UnLock(self) 1132 1133 def OnMailUpdatedByExternal(self, messageIDs, is_read, new_label): 1134 if is_read is not None: 1135 if is_read: 1136 self.MarkMessagesAsRead(messageIDs, notifyServer=False) 1137 else: 1138 self.MarkMessagesAsUnread(messageIDs, notifyServer=False) 1139 if new_label is not None: 1140 self.ReplaceLabels(messageIDs, new_label, notifyServer=False) 1141 1142 def OnMailUndeleted(self, messageIDs): 1143 self.SyncMail() 1144 sm.ScatterEvent('OnNewMailReceived') 1145 1146 def OnMailTrashed(self, messageIDs): 1147 self._RefreshTrashedStatusWhenMovingToTrash(messageIDs) 1148 1149 def OnMailRestored(self, messageIDs): 1150 self._RefreshTrashedStatusWhenMovingFromTrash(messageIDs) 1151 1152 def OnNewMailReceived(self, msg, *args): 1153 self.SetBlinkTabState(True) 1154 self.SetBlinkNeocomState(True) 1155 sm.ScatterEvent('OnNewMailReceived') 1156 mailSettings = self.GetMailSettings() 1157 if mailSettings.GetSingleValue(cSettings.MAIL_BLINK_NEOCOM, True): 1158 sm.GetService('neocom').Blink('mail', GetByLabel('UI/Neocom/Blink/NewMailReceived')) 1159 if mailSettings.GetSingleValue(cSettings.MAIL_SHOW_POPUP, True): 1160 self.GetMailNotification(msg) 1161 1162 def OnLabelsCreatedByExternal(self, label): 1163 self.AddLabel(label.labelID, label.name, label.color) 1164 sm.ScatterEvent('OnMyLabelsChanged', 'mail_labels', label.labelID) 1165 1166 def GetMailNotification(self, msg): 1167 notificationData = NewMailFormatter.MakeData(msg) 1168 senderIDForNotification = msg.senderID 1169 if msg.toListID and msg.toListID == msg.senderID: 1170 senderIDForNotification = None 1171 sm.ScatterEvent('OnNotificationReceived', 123, notificationConst.notificationTypeNewMailFrom, senderIDForNotification, blue.os.GetWallclockTime(), data=notificationData) 1172 1173 def OnClickingMailPopup(self, popup, msg, *args): 1174 self.OnOpenPopupMail(msg) 1175 if popup and not popup.destroyed: 1176 popup.CloseNotification() 1177 1178 def GetMailPopupOnCharacterLogin(self, mailCount): 1179 blue.pyos.synchro.SleepWallclock(7000) 1180 if mailCount < 1: 1181 return 1182 notificationData = MailSummaryFormatter.MakeData(mailCount) 1183 sm.GetService('notificationSvc').MakeAndScatterNotification(type=notificationConst.notificationTypeMailSummary, data=notificationData) 1184 1185 def OnClickingPopup(self, popup, *args): 1186 sm.GetService('cmd').OpenMail() 1187 popup.CloseNotification() 1188 1189 def OnOpenPopupMail(self, msg, *args): 1190 wndName = 'mail_readingWnd_%s' % msg.messageID 1191 wnd = MailReadingWnd.Open(windowInstanceID=wndName, mail=msg, msgID=msg.messageID, txt='', toolbar=1, trashed=msg.trashed, type=const.mailTypeMail) 1192 if not msg.read: 1193 sm.ScatterEvent('OnMailStatusUpdate', None, None, [msg.messageID]) 1194 if wnd is not None: 1195 wnd.Maximize() 1196 blue.pyos.synchro.SleepWallclock(1) 1197 wnd.SetText(self.GetMailText(msg)) 1198 1199 def CheckNewMessages_thread(self, mailCount): 1200 blue.pyos.synchro.SleepSim(3000) 1201 endTime = blue.os.GetSimTime() + 10 * const.SEC 1202 while blue.os.GetSimTime() < endTime: 1203 if getattr(session, 'charid', None): 1204 break 1205 blue.pyos.synchro.SleepSim(1000) 1206 1207 if getattr(session, 'charid', None) is None: 1208 log.LogWarn('Mail: Waited for 13 seconds to display new messages, gave up') 1209 return 1210 self.BlinkNeocomIfNeeded(mailCount) 1211 1212 def BlinkNeocomIfNeeded(self, mailCount): 1213 shouldBlink = 0 1214 mailSettings = self.GetMailSettings() 1215 if mailCount: 1216 self.SetBlinkNeocomState(True) 1217 if mailSettings.GetSingleValue(cSettings.MAIL_BLINK_NEOCOM, True): 1218 self.SetBlinkTabState(True) 1219 shouldBlink = 1 1220 if mailSettings.GetSingleValue(cSettings.MAIL_SHOW_POPUP, True): 1221 self.GetMailPopupOnCharacterLogin(mailCount) 1222 if mailSettings.GetSingleValue(cSettings.MAIL_BLINK_TAB, True): 1223 self.SetBlinkTabState(True) 1224 if shouldBlink: 1225 sm.GetService('neocom').Blink('mail', GetByLabel('UI/Neocom/Blink/NewMailReceived')) 1226 1227 def CheckLabelName(self, name, *args): 1228 name = name.strip() 1229 myLabelNames = [ label.name for label in self.GetAllLabels(assignable=0).values() ] 1230 if name in myLabelNames: 1231 return localization.GetByLabel('UI/Mail/LabelNameTaken') 1232 1233 def RenameLabelFromUI(self, labelID): 1234 ret = utilWindows.NamePopup(localization.GetByLabel('UI/Mail/LabelName'), localization.GetByLabel('UI/Mail/LabelTypeNew'), maxLength=const.mailMaxLabelSize, validator=self.CheckLabelName) 1235 if ret is None: 1236 return 1237 name = ret 1238 name = name.strip() 1239 if name: 1240 self.EditLabel(labelID, name=name) 1241 1242 def ChangeLabelColorFromUI(self, color, labelID): 1243 allLabels = self.GetAllLabels() 1244 label = allLabels.get(labelID, None) 1245 if label is None: 1246 return 1247 if color != label.color: 1248 self.EditLabel(labelID, color=color) 1249 1250 def GetSwatchColors(self, *args): 1251 return swatchColors 1252 1253 def DeleteLabelFromUI(self, labelID, labelName): 1254 if prompt_player_fsd_dialog('DeleteMailLabel', {'labelName': labelName}, uiconst.YESNO) == uiconst.ID_YES: 1255 self.DeleteLabel(labelID) 1256 1257 def ShouldTabBlink(self, *args): 1258 return self.blinkTab 1259 1260 def SetBlinkTabState(self, state = False): 1261 self.blinkTab = state 1262 1263 def ShouldNeocomBlink(self, *args): 1264 return self.blinkNeocom 1265 1266 def SetBlinkNeocomState(self, state = False): 1267 self.blinkNeocom = state 1268 1269 def GetAssignColorWnd(self, labelID, doneCallBack = None, doneArgs = (), width = 104, height = 74, *args): 1270 colorpar = MailAssignColorWnd(name='colorpar', parent=uicore.layer.menu, idx=0, align=uiconst.TOPLEFT, pos=(0, 1271 0, 1272 width, 1273 height)) 1274 colorpar.Startup(labelID, doneCallBack, doneArgs) 1275 1276 def GetMailSettings(self): 1277 self.SetMailSettingsIfNeeded() 1278 return self.mailSettingObject 1279 1280 def SetMailSettingsIfNeeded(self): 1281 if self.mailSettingObject is None: 1282 self.mailSettingObject = '' 1283 yamlString = self.FetchMailSettingsToServer() 1284 self.mailSettingObject = CharacterSettingsObject(yamlString) 1285 with ExceptionEater('Failed to migrate settings'): 1286 self.TryMigrateSettings(self.mailSettingObject) 1287 1288 def FetchMailSettingsToServer(self): 1289 yamlString = self.characterSettings.Get('mailSettings') 1290 return yamlString 1291 1292 def SaveMailSettingsOnServer(self, newSettingsObject): 1293 isSaveNeeded = self.mailSettingObject.IsSaveNeeded(newSettingsObject.allSettings) 1294 if not isSaveNeeded: 1295 return 1296 self.mailSettingObject.UpdateSettings(newSettingsObject.allSettings) 1297 newYamlString = self.mailSettingObject.GetYamlStringForServer() 1298 self.characterSettings.Save('mailSettings', newYamlString) 1299 1300 def TryMigrateSettings(self, settingsObject): 1301 if settingsObject.GetSingleValue('settingMigrationDone1', False): 1302 return 1303 for settingKey in (cSettings.MAIL_BLINK_NEOCOM, 1304 cSettings.MAIL_SHOW_POPUP, 1305 cSettings.MAIL_BLINK_TAB, 1306 cSettings.MAIL_GET_SEARCH_WND): 1307 oldSetting = settings.user.ui.Get(settingKey, True) 1308 settingsObject.SetSingleValue(settingKey, oldSetting) 1309 1310 oldMailPerPage = settings.user.ui.Get(cSettings.MAILS_PER_PAGE, 30) 1311 settingsObject.SetSingleValue(cSettings.MAILS_PER_PAGE, oldMailPerPage) 1312 allNotificationGroups = notificationGroupNamePaths.keys() 1313 for newSettingKey, oldSettingKey in ((cSettings.NOTIFICATIONS_GROUPS_DONT_BLINK_NEOCOM, 'notification_blinkNecom'), (cSettings.NOTIFICATIONS_GROUPS_DONT_BLINK_TAB, 'notification_blinkTab')): 1314 oldSetting = settings.user.ui.Get(oldSettingKey, True) 1315 if oldSetting: 1316 newValue = [] 1317 else: 1318 newValue = allNotificationGroups 1319 settingsObject.UpdateSettingListWithNewValues(newSettingKey, newValue) 1320 1321 settingsObject.SetSingleValue('settingMigrationDone1', True) 1322 newYamlString = settingsObject.GetYamlStringForServer() 1323 self.characterSettings.Save('mailSettings', newYamlString)