summaryrefslogtreecommitdiffstats
path: root/comm/calendar/base/content/calendar-extract.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/calendar/base/content/calendar-extract.js')
-rw-r--r--comm/calendar/base/content/calendar-extract.js266
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"
+ );
+ },
+};