diff options
Diffstat (limited to 'comm/calendar/base/content/calendar-extract.js')
-rw-r--r-- | comm/calendar/base/content/calendar-extract.js | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/comm/calendar/base/content/calendar-extract.js b/comm/calendar/base/content/calendar-extract.js new file mode 100644 index 0000000000..de021cb042 --- /dev/null +++ b/comm/calendar/base/content/calendar-extract.js @@ -0,0 +1,266 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* globals getMessagePaneBrowser, addMenuItem, getSelectedCalendar + createEventWithDialog*/ + +var { Extractor } = ChromeUtils.import("resource:///modules/calendar/calExtract.jsm"); +var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm"); +var { MailServices } = ChromeUtils.import("resource:///modules/MailServices.jsm"); +var { XPCOMUtils } = ChromeUtils.importESModule("resource://gre/modules/XPCOMUtils.sys.mjs"); + +XPCOMUtils.defineLazyModuleGetters(this, { + CalEvent: "resource:///modules/CalEvent.jsm", + CalTodo: "resource:///modules/CalTodo.jsm", +}); + +XPCOMUtils.defineLazyGetter(this, "extractService", () => { + const { CalExtractParserService } = ChromeUtils.import( + "resource:///modules/calendar/extract/CalExtractParserService.jsm" + ); + return new CalExtractParserService(); +}); + +var calendarExtract = { + onShowLocaleMenu(target) { + let localeList = document.getElementById(target.id); + let langs = []; + let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"] + .getService(Ci.nsIXULChromeRegistry) + .QueryInterface(Ci.nsIToolkitChromeRegistry); + let langRegex = /^(([^-]+)-*(.*))$/; + + for (let locale of chrome.getLocalesForPackage("calendar")) { + let localeParts = langRegex.exec(locale); + let langName = localeParts[2]; + + try { + langName = cal.l10n.getAnyString("global", "languageNames", langName); + } catch (ex) { + // If no language name is found that is ok, keep the technical term + } + + let label = cal.l10n.getCalString("extractUsing", [langName]); + if (localeParts[3] != "") { + label = cal.l10n.getCalString("extractUsingRegion", [langName, localeParts[3]]); + } + + langs.push([label, localeParts[1]]); + } + + // sort + let pref = "calendar.patterns.last.used.languages"; + let lastUsedLangs = Services.prefs.getStringPref(pref, ""); + + langs.sort((a, b) => { + let idx_a = lastUsedLangs.indexOf(a[1]); + let idx_b = lastUsedLangs.indexOf(b[1]); + + if (idx_a == -1 && idx_b == -1) { + return a[0].localeCompare(b[0]); + } else if (idx_a != -1 && idx_b != -1) { + return idx_a - idx_b; + } else if (idx_a == -1) { + return 1; + } + return -1; + }); + while (localeList.lastChild) { + localeList.lastChild.remove(); + } + + for (let lang of langs) { + addMenuItem(localeList, lang[0], lang[1], null); + } + }, + + extractWithLocale(event, isEvent) { + event.stopPropagation(); + let locale = event.target.value; + this.extractFromEmail(null, isEvent, true, locale); + }, + + async extractFromEmail(message, isEvent, fixedLang, fixedLocale) { + let folder = message.folder; + let title = message.mime2DecodedSubject; + + let content = ""; + await new Promise((resolve, reject) => { + let listener = { + QueryInterface: ChromeUtils.generateQI(["nsIStreamListener"]), + onDataAvailable(request, inputStream, offset, count) { + let text = folder.getMsgTextFromStream( + inputStream, + message.charset, + count, // bytesToRead + 32768, // maxOutputLen + false, // compressQuotes + true, // stripHTMLTags + {} // out contentType + ); + // If we ever got text, we're good. Ignore further chunks. + content ||= text; + }, + onStartRequest(request) {}, + onStopRequest(request, statusCode) { + if (!Components.isSuccessCode(statusCode)) { + reject(new Error(statusCode)); + } + resolve(); + }, + }; + let uri = message.folder.getUriForMsg(message); + MailServices.messageServiceFromURI(uri).streamMessage(uri, listener, null, null, false, ""); + }); + + cal.LOG("[calExtract] Original email content: \n" + title + "\r\n" + content); + let date = new Date(message.date / 1000); + let time = new Date().getTime(); + + let item = isEvent ? new CalEvent() : new CalTodo(); + item.title = message.mime2DecodedSubject; + item.calendar = getSelectedCalendar(); + item.setProperty("DESCRIPTION", content); + item.setProperty("URL", `mid:${message.messageId}`); + cal.dtz.setDefaultStartEndHour(item); + cal.alarms.setDefaultValues(item); + let tabmail = document.getElementById("tabmail"); + let messagePaneBrowser = + tabmail?.currentTabInfo.chromeBrowser.contentWindow.visibleMessagePaneBrowser?.() || + tabmail?.currentAboutMessage?.getMessagePaneBrowser() || + document.getElementById("messageBrowser")?.contentWindow?.getMessagePaneBrowser(); + let sel = messagePaneBrowser?.contentWindow?.getSelection(); + // Check if there's an iframe with a selection (e.g. Thunderbird Conversations) + if (sel && sel.type !== "Range") { + try { + sel = messagePaneBrowser?.contentDocument + .querySelector("iframe") + .contentDocument.getSelection(); + } catch (ex) { + // If Thunderbird Conversations is not installed that is fine, + // we will just have an empty or null selection. + } + } + + let guessed; + let endGuess; + let extractor; + let collected = []; + let useService = Services.prefs.getBoolPref("calendar.extract.service.enabled"); + if (useService) { + let result = extractService.extract(content, { now: date }); + if (!result) { + useService = false; + } else { + guessed = result.startTime; + endGuess = result.endTime; + } + } + + if (!useService) { + let locale = Services.locale.requestedLocale; + let dayStart = Services.prefs.getIntPref("calendar.view.daystarthour", 6); + if (fixedLang) { + extractor = new Extractor(fixedLocale, dayStart); + } else { + extractor = new Extractor(locale, dayStart, false); + } + collected = extractor.extract(title, content, date, sel); + } + + // if we only have email date then use default start and end + if (!useService && collected.length <= 1) { + cal.LOG("[calExtract] Date and time information was not found in email/selection."); + createEventWithDialog(null, null, null, null, item); + } else { + if (!useService) { + guessed = extractor.guessStart(!isEvent); + endGuess = extractor.guessEnd(guessed, !isEvent); + } + let allDay = (guessed.hour == null || guessed.minute == null) && isEvent; + + if (isEvent) { + if (guessed.year != null) { + item.startDate.year = guessed.year; + } + if (guessed.month != null) { + item.startDate.month = guessed.month - 1; + } + if (guessed.day != null) { + item.startDate.day = guessed.day; + } + if (guessed.hour != null) { + item.startDate.hour = guessed.hour; + } + if (guessed.minute != null) { + item.startDate.minute = guessed.minute; + } + + item.endDate = item.startDate.clone(); + item.endDate.minute += Services.prefs.getIntPref("calendar.event.defaultlength", 60); + + if (endGuess.year != null) { + item.endDate.year = endGuess.year; + } + if (endGuess.month != null) { + item.endDate.month = endGuess.month - 1; + } + if (endGuess.day != null) { + item.endDate.day = endGuess.day; + if (allDay) { + item.endDate.day++; + } + } + if (endGuess.hour != null) { + item.endDate.hour = endGuess.hour; + } + if (endGuess.minute != null) { + item.endDate.minute = endGuess.minute; + } + } else { + let dtz = cal.dtz.defaultTimezone; + let dueDate = new Date(); + // set default + dueDate.setHours(0); + dueDate.setMinutes(0); + dueDate.setSeconds(0); + + if (endGuess.year != null) { + dueDate.setYear(endGuess.year); + } + if (endGuess.month != null) { + dueDate.setMonth(endGuess.month - 1); + } + if (endGuess.day != null) { + dueDate.setDate(endGuess.day); + } + if (endGuess.hour != null) { + dueDate.setHours(endGuess.hour); + } + if (endGuess.minute != null) { + dueDate.setMinutes(endGuess.minute); + } + + cal.item.setItemProperty(item, "entryDate", cal.dtz.jsDateToDateTime(date, dtz)); + if (endGuess.year != null) { + cal.item.setItemProperty(item, "dueDate", cal.dtz.jsDateToDateTime(dueDate, dtz)); + } + } + + // if time not guessed set allday for events + if (allDay) { + createEventWithDialog(null, null, null, null, item, true); + } else { + createEventWithDialog(null, null, null, null, item); + } + } + + let timeSpent = new Date().getTime() - time; + cal.LOG( + "[calExtract] Total time spent for conversion (including loading of dictionaries): " + + timeSpent + + "ms" + ); + }, +}; |