Bug 463402 - Remove E-Mail notification dialogs - Part 1: processing;r=philipp

- adding support for processing in different response modes
- removing compatibility mode for OL 2002 and earlier

--HG--
extra : rebase_source : 20b8933d72ed6220a03d881a93c2dfdb8061a7fc
This commit is contained in:
makemyday@gmx-topmail.de 2018-03-11 13:59:42 +01:00
Родитель 03cd32f4cf
Коммит f65c0a39f9
8 изменённых файлов: 216 добавлений и 169 удалений

Просмотреть файл

@ -227,14 +227,14 @@ function setDefaultItemValues(aItem, aCalendar=null, aStartDate=null, aEndDate=n
* allday event. * allday event.
*/ */
function createEventWithDialog(calendar, startDate, endDate, summary, event, aForceAllday) { function createEventWithDialog(calendar, startDate, endDate, summary, event, aForceAllday) {
let onNewEvent = function(item, opcalendar, originalItem, listener) { let onNewEvent = function(item, opcalendar, originalItem, listener, extresponse=null) {
if (item.id) { if (item.id) {
// If the item already has an id, then this is the result of // If the item already has an id, then this is the result of
// saving the item without closing, and then saving again. // saving the item without closing, and then saving again.
doTransaction("modify", item, opcalendar, originalItem, listener); doTransaction("modify", item, opcalendar, originalItem, listener, extresponse);
} else { } else {
// Otherwise, this is an addition // Otherwise, this is an addition
doTransaction("add", item, opcalendar, null, listener); doTransaction("add", item, opcalendar, null, listener, extresponse);
} }
}; };
@ -282,14 +282,14 @@ function createEventWithDialog(calendar, startDate, endDate, summary, event, aFo
* @param initialDate (optional) The initial date for new task datepickers * @param initialDate (optional) The initial date for new task datepickers
*/ */
function createTodoWithDialog(calendar, dueDate, summary, todo, initialDate) { function createTodoWithDialog(calendar, dueDate, summary, todo, initialDate) {
let onNewItem = function(item, opcalendar, originalItem, listener) { let onNewItem = function(item, opcalendar, originalItem, listener, extresponse=null) {
if (item.id) { if (item.id) {
// If the item already has an id, then this is the result of // If the item already has an id, then this is the result of
// saving the item without closing, and then saving again. // saving the item without closing, and then saving again.
doTransaction("modify", item, opcalendar, originalItem, listener); doTransaction("modify", item, opcalendar, originalItem, listener, extresponse);
} else { } else {
// Otherwise, this is an addition // Otherwise, this is an addition
doTransaction("add", item, opcalendar, null, listener); doTransaction("add", item, opcalendar, null, listener, extresponse);
} }
}; };
@ -348,8 +348,8 @@ function modifyEventWithDialog(aItem, job=null, aPromptOccurrence, initialDate=n
return; return;
} }
let onModifyItem = function(item, calendar, originalItem, listener) { let onModifyItem = function(item, calendar, originalItem, listener, extresponse=null) {
doTransaction("modify", item, calendar, originalItem, listener); doTransaction("modify", item, calendar, originalItem, listener, extresponse);
}; };
let item = aItem; let item = aItem;
@ -629,8 +629,10 @@ function getTransactionMgr() {
* @param aCalendar The calendar to do the transaction on * @param aCalendar The calendar to do the transaction on
* @param aOldItem (optional) some actions require an old item * @param aOldItem (optional) some actions require an old item
* @param aListener (optional) the listener to call when complete. * @param aListener (optional) the listener to call when complete.
* @param aExtResponse (optional) JS object with additional parameters for sending itip messages
* (see also description of checkAndSend in calItipUtils.jsm)
*/ */
function doTransaction(aAction, aItem, aCalendar, aOldItem, aListener) { function doTransaction(aAction, aItem, aCalendar, aOldItem, aListener, aExtResponse=null) {
// This is usually a user-initiated transaction, so make sure the calendar // This is usually a user-initiated transaction, so make sure the calendar
// this transaction is happening on is visible. // this transaction is happening on is visible.
ensureCalendarVisible(aCalendar); ensureCalendarVisible(aCalendar);
@ -640,7 +642,8 @@ function doTransaction(aAction, aItem, aCalendar, aOldItem, aListener) {
aItem, aItem,
aCalendar, aCalendar,
aOldItem, aOldItem,
aListener ? aListener : null); aListener ? aListener : null,
aExtResponse);
updateUndoRedoMenu(); updateUndoRedoMenu();
} }
@ -705,15 +708,27 @@ function updateUndoRedoMenu() {
goUpdateCommand("cmd_redo"); goUpdateCommand("cmd_redo");
} }
function setContextPartstat(value, scope, items) { /**
* Updates the partstat of the calendar owner for specified items triggered by a
* ontext menu operation
*
* @param {String} aPartStat a valid partstat string as per RfC 5545
* @param {String} aScope indicates the scope of anrecurring event - must
* be 'this-occurrence' || 'all-occurences'
* @param {Array} aItems an array of calEvent or calIToDo items
* @param {Object} aExtResponse [optional] an object to provide additional
* parameters for sending itip messages as response
* response
*/
function setContextPartstat(aPartStat, aScope, aItems, aExtResponse=null) {
startBatchTransaction(); startBatchTransaction();
try { try {
for (let oldItem of items) { for (let oldItem of aItems) {
// Skip this item if its calendar is read only. // Skip this item if its calendar is read only.
if (oldItem.calendar.readOnly) { if (oldItem.calendar.readOnly) {
continue; continue;
} }
if (scope == "all-occurrences") { if (aScope == "all-occurrences") {
oldItem = oldItem.parentItem; oldItem = oldItem.parentItem;
} }
let attendee = null; let attendee = null;
@ -732,7 +747,7 @@ function setContextPartstat(value, scope, items) {
let newItem = oldItem.clone(); let newItem = oldItem.clone();
let newAttendee = attendee.clone(); let newAttendee = attendee.clone();
newAttendee.participationStatus = value; newAttendee.participationStatus = aPartStat;
if (newAttendee.isOrganizer) { if (newAttendee.isOrganizer) {
newItem.organizer = newAttendee; newItem.organizer = newAttendee;
} else { } else {
@ -740,7 +755,7 @@ function setContextPartstat(value, scope, items) {
newItem.addAttendee(newAttendee); newItem.addAttendee(newAttendee);
} }
doTransaction("modify", newItem, newItem.calendar, oldItem, null); doTransaction("modify", newItem, newItem.calendar, oldItem, null, aExtResponse);
} }
} }
} catch (e) { } catch (e) {

Просмотреть файл

@ -634,8 +634,19 @@ cal.itip = {
* *
* Checks to see if e.g. attendees were added/removed or an item has been * Checks to see if e.g. attendees were added/removed or an item has been
* deleted and sends out appropriate iTIP messages. * deleted and sends out appropriate iTIP messages.
* @param {Number} aOpType Type of operation - (e.g. ADD, MODIFY or DELETE)
* @param {calIEvent|calITodo} aItem The updated item
* @param {calIEvent|calITodo} aOriginalItem The original item
* @param {Object} aExtResponse [optional] An object to provide additional
* parameters for sending itip messages as response
* mode, comments or a subset of recipients. Currently
* implemented attributes are:
* * responseMode Response mode (long) as defined for autoResponse
* of calIItipItem. The default mode is USER (which
* will trigger displaying the previously known popup
* to ask the user whether to send)
*/ */
checkAndSend: function(aOpType, aItem, aOriginalItem) { checkAndSend: function(aOpType, aItem, aOriginalItem, aExtResponse=null) {
// balance out parts of the modification vs delete confusion, deletion of occurrences // balance out parts of the modification vs delete confusion, deletion of occurrences
// are notified as parent modifications and modifications of occurrences are notified // are notified as parent modifications and modifications of occurrences are notified
// as mixed new-occurrence, old-parent (IIRC). // as mixed new-occurrence, old-parent (IIRC).
@ -679,8 +690,30 @@ cal.itip = {
} }
} }
} }
// for backward compatibility, we assume USER mode if not set otherwise
let autoResponse = { value: false }; // controls confirm to send email only once let autoResponse = { mode: Ci.calIItipItem.USER };
if (aExtResponse && aExtResponse.hasOwnProperty("responseMode")) {
switch (aExtResponse.responseMode) {
case Ci.calIItipItem.AUTO:
case Ci.calIItipItem.NONE:
case Ci.calIItipItem.USER:
autoResponse.mode = aExtResponse.responseMode;
break;
default:
cal.ERROR("cal.itip.checkAndSend(): Invalid value " + aExtResponse.responseMode +
" provided for responseMode attribute in argument aExtResponse." +
" Falling back to USER mode.\r\n" + cal.STACK(20));
}
} else {
// let's log something useful to notify addon developers or find any missing pieces in
// the conversions
cal.LOG("cal.itip.checkAndSend: no response mode provided, " +
"falling back to USER mode.\r\n" + cal.STACK(20));
}
if (autoResponse.mode == Ci.calIItipItem.NONE) {
// we stop here and don't send anything if the user opted out before
return;
}
let invitedAttendee = cal.isInvitation(aItem) && cal.getInvitedAttendee(aItem); let invitedAttendee = cal.isInvitation(aItem) && cal.getInvitedAttendee(aItem);
if (invitedAttendee) { // actually is an invitation copy, fix attendee list to send REPLY if (invitedAttendee) { // actually is an invitation copy, fix attendee list to send REPLY
@ -954,10 +987,10 @@ cal.itip = {
/** /**
* A shortcut to send DECLINECOUNTER messages - for everything else use cal.itip.checkAndSend * A shortcut to send DECLINECOUNTER messages - for everything else use cal.itip.checkAndSend
* *
* @param aItem iTIP item to be sent * @param {calIItipItem} aItem item to be sent
* @param aMethod iTIP method * @param {String} aMethod iTIP method
* @param aRecipientsList an array of calIAttendee objects the message should be sent to * @param {Array} aRecipientsList array of calIAttendee objects the message should be sent to
* @param aAutoResponse an inout object whether the transport should ask before sending * @param {Object} aAutoResponse JS object whether the transport should ask before sending
*/ */
sendDeclineCounterMessage: function(aItem, aMethod, aRecipientsList, aAutoResponse) { sendDeclineCounterMessage: function(aItem, aMethod, aRecipientsList, aAutoResponse) {
if (aMethod == "DECLINECOUNTER") { if (aMethod == "DECLINECOUNTER") {
@ -1142,17 +1175,15 @@ function sendMessage(aItem, aMethod, aRecipientsList, autoResponse) {
transport = transport.QueryInterface(Components.interfaces.calIItipTransport); transport = transport.QueryInterface(Components.interfaces.calIItipTransport);
let _sendItem = function(aSendToList, aSendItem) { let _sendItem = function(aSendToList, aSendItem) {
let cIII = Components.interfaces.calIItipItem;
let itipItem = Components.classes["@mozilla.org/calendar/itip-item;1"] let itipItem = Components.classes["@mozilla.org/calendar/itip-item;1"]
.createInstance(cIII); .createInstance(Ci.calIItipItem);
itipItem.init(cal.item.serialize(aSendItem)); itipItem.init(cal.item.serialize(aSendItem));
itipItem.responseMethod = aMethod; itipItem.responseMethod = aMethod;
itipItem.targetCalendar = aSendItem.calendar; itipItem.targetCalendar = aSendItem.calendar;
itipItem.autoResponse = autoResponse && autoResponse.value ? cIII.AUTO : cIII.USER; itipItem.autoResponse = autoResponse.mode;
if (autoResponse) { // we switch to AUTO for each subsequent call of _sendItem()
autoResponse.value = true; // auto every following autoResponse.mode = Ci.calIItipItem.AUTO;
} // XXX I don't know whether the below is used at all, since we don't use the itip processor
// XXX I don't know whether the below are used at all, since we don't use the itip processor
itipItem.isSend = true; itipItem.isSend = true;
return transport.sendItems(aSendToList.length, aSendToList, itipItem); return transport.sendItems(aSendToList.length, aSendToList, itipItem);
@ -1189,16 +1220,22 @@ function sendMessage(aItem, aMethod, aRecipientsList, autoResponse) {
* @param opListener operation listener to forward * @param opListener operation listener to forward
* @param oldItem the previous item before modification (if any) * @param oldItem the previous item before modification (if any)
*/ */
function ItipOpListener(opListener, oldItem) { function ItipOpListener(aOpListener, aOldItem, aExtResponse=null) {
this.mOpListener = opListener; this.mOpListener = aOpListener;
this.mOldItem = oldItem; this.mOldItem = aOldItem;
this.mExtResponse = aExtResponse;
} }
ItipOpListener.prototype = { ItipOpListener.prototype = {
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.calIOperationListener]), QueryInterface: XPCOMUtils.generateQI([Components.interfaces.calIOperationListener]),
mOpListener: null,
mOldItem: null,
mExtResponse: null,
onOperationComplete: function(aCalendar, aStatus, aOperationType, aId, aDetail) { onOperationComplete: function(aCalendar, aStatus, aOperationType, aId, aDetail) {
cal.ASSERT(Components.isSuccessCode(aStatus), "error on iTIP processing"); cal.ASSERT(Components.isSuccessCode(aStatus), "error on iTIP processing");
if (Components.isSuccessCode(aStatus)) { if (Components.isSuccessCode(aStatus)) {
cal.itip.checkAndSend(aOperationType, aDetail, this.mOldItem); cal.itip.checkAndSend(aOperationType, aDetail, this.mOldItem, this.mExtResponse);
} }
if (this.mOpListener) { if (this.mOpListener) {
this.mOpListener.onOperationComplete(aCalendar, this.mOpListener.onOperationComplete(aCalendar,
@ -1407,7 +1444,7 @@ ItipItemFinder.prototype = {
cal.ASSERT(attendees.length == 1, cal.ASSERT(attendees.length == 1,
"invalid number of attendees in REFRESH!"); "invalid number of attendees in REFRESH!");
if (attendees.length > 0) { if (attendees.length > 0) {
let action = function(opListener) { let action = function(opListener, partStat, extResponse) {
if (!item.organizer) { if (!item.organizer) {
let org = createOrganizer(item.calendar); let org = createOrganizer(item.calendar);
if (org) { if (org) {
@ -1415,7 +1452,12 @@ ItipItemFinder.prototype = {
item.organizer = org; item.organizer = org;
} }
} }
sendMessage(item, "REQUEST", attendees, true /* don't ask */); sendMessage(
item,
"REQUEST",
attendees,
{ responseMode: Ci.calIItipItem.AUTO } /* don't ask */
);
}; };
operations.push(action); operations.push(action);
} }
@ -1427,7 +1469,7 @@ ItipItemFinder.prototype = {
if (item.calendar.getProperty("itip.disableRevisionChecks") || if (item.calendar.getProperty("itip.disableRevisionChecks") ||
cal.itip.compare(itipItemItem, item) > 0) { cal.itip.compare(itipItemItem, item) > 0) {
let newItem = updateItem(item, itipItemItem); let newItem = updateItem(item, itipItemItem);
let action = function(opListener) { let action = function(opListener, partStat, extResponse) {
return newItem.calendar.modifyItem(newItem, item, opListener); return newItem.calendar.modifyItem(newItem, item, opListener);
}; };
actionMethod = method + ":UPDATE"; actionMethod = method + ":UPDATE";
@ -1457,7 +1499,7 @@ ItipItemFinder.prototype = {
(item.calendar.getProperty("itip.disableRevisionChecks") || (item.calendar.getProperty("itip.disableRevisionChecks") ||
cal.itip.compare(itipItemItem, item) == 0)) { cal.itip.compare(itipItemItem, item) == 0)) {
actionMethod = "REQUEST:NEEDS-ACTION"; actionMethod = "REQUEST:NEEDS-ACTION";
operations.push((opListener, partStat) => { operations.push((opListener, partStat, extResponse) => {
let changedItem = firstFoundItem.clone(); let changedItem = firstFoundItem.clone();
changedItem.removeAttendee(foundAttendee); changedItem.removeAttendee(foundAttendee);
foundAttendee = foundAttendee.clone(); foundAttendee = foundAttendee.clone();
@ -1467,7 +1509,7 @@ ItipItemFinder.prototype = {
changedItem.addAttendee(foundAttendee); changedItem.addAttendee(foundAttendee);
return changedItem.calendar.modifyItem( return changedItem.calendar.modifyItem(
changedItem, firstFoundItem, new ItipOpListener(opListener, firstFoundItem)); changedItem, firstFoundItem, new ItipOpListener(opListener, firstFoundItem, extResponse));
}); });
} else if (item.calendar.getProperty("itip.disableRevisionChecks") || } else if (item.calendar.getProperty("itip.disableRevisionChecks") ||
cal.itip.compare(itipItemItem, item) > 0) { cal.itip.compare(itipItemItem, item) > 0) {
@ -1477,7 +1519,7 @@ ItipItemFinder.prototype = {
cal.itip.getSequence(item); cal.itip.getSequence(item);
actionMethod = (isMinorUpdate ? method + ":UPDATE-MINOR" actionMethod = (isMinorUpdate ? method + ":UPDATE-MINOR"
: method + ":UPDATE"); : method + ":UPDATE");
operations.push((opListener, partStat) => { operations.push((opListener, partStat, extResponse) => {
if (!partStat) { // keep PARTSTAT if (!partStat) { // keep PARTSTAT
let att_ = cal.getInvitedAttendee(item); let att_ = cal.getInvitedAttendee(item);
partStat = att_ ? att_.participationStatus : "NEEDS-ACTION"; partStat = att_ ? att_.participationStatus : "NEEDS-ACTION";
@ -1487,7 +1529,7 @@ ItipItemFinder.prototype = {
att.participationStatus = partStat; att.participationStatus = partStat;
newItem.addAttendee(att); newItem.addAttendee(att);
return newItem.calendar.modifyItem( return newItem.calendar.modifyItem(
newItem, item, new ItipOpListener(opListener, item)); newItem, item, new ItipOpListener(opListener, item, extResponse));
}); });
} }
} }
@ -1546,7 +1588,7 @@ ItipItemFinder.prototype = {
// Make sure the provider-specified properties are copied over // Make sure the provider-specified properties are copied over
copyProviderProperties(this.mItipItem, itipItemItem, newItem); copyProviderProperties(this.mItipItem, itipItemItem, newItem);
let action = function(opListener) { let action = function(opListener, partStat, extResponse) {
// n.b.: this will only be processed in case of reply or // n.b.: this will only be processed in case of reply or
// declining the counter request - of sending the // declining the counter request - of sending the
// appropriate reply will be taken care within the // appropriate reply will be taken care within the
@ -1555,7 +1597,7 @@ ItipItemFinder.prototype = {
return newItem.calendar.modifyItem( return newItem.calendar.modifyItem(
newItem, item, newItem, item,
newItem.calendar.getProperty("itip.notify-replies") newItem.calendar.getProperty("itip.notify-replies")
? new ItipOpListener(opListener, item) ? new ItipOpListener(opListener, item, extResponse)
: opListener); : opListener);
}; };
operations.push(action); operations.push(action);
@ -1582,15 +1624,21 @@ ItipItemFinder.prototype = {
// Make sure the provider-specified properties are copied over // Make sure the provider-specified properties are copied over
copyProviderProperties(this.mItipItem, itipItemItem, newItem); copyProviderProperties(this.mItipItem, itipItemItem, newItem);
operations.push(opListener => newItem.calendar.modifyItem(newItem, item, opListener)); operations.push((opListener, partStat, extResponse) =>
newItem.calendar.modifyItem(newItem, item, opListener)
);
} }
newItem.recurrenceInfo.removeOccurrenceAt(rid); newItem.recurrenceInfo.removeOccurrenceAt(rid);
} else if (item.recurrenceId && (item.recurrenceId.compare(rid) == 0)) { } else if (item.recurrenceId && (item.recurrenceId.compare(rid) == 0)) {
// parentless occurrence to be deleted (future) // parentless occurrence to be deleted (future)
operations.push(opListener => item.calendar.deleteItem(item, opListener)); operations.push((opListener, partStat, extResponse) =>
item.calendar.deleteItem(item, opListener)
);
} }
} else { } else {
operations.push(opListener => item.calendar.deleteItem(item, opListener)); operations.push((opListener, partStat, extResponse) =>
item.calendar.deleteItem(item, opListener)
);
} }
} }
} }
@ -1612,7 +1660,7 @@ ItipItemFinder.prototype = {
switch (method) { switch (method) {
case "REQUEST": case "REQUEST":
case "PUBLISH": { case "PUBLISH": {
let action = (opListener, partStat) => { let action = (opListener, partStat, extResponse) => {
let newItem = itipItemItem.clone(); let newItem = itipItemItem.clone();
setReceivedInfo(newItem, itipItemItem); setReceivedInfo(newItem, itipItemItem);
newItem.parentItem.calendar = this.mItipItem.targetCalendar; newItem.parentItem.calendar = this.mItipItem.targetCalendar;
@ -1641,7 +1689,7 @@ ItipItemFinder.prototype = {
} }
return newItem.calendar.addItem(newItem, return newItem.calendar.addItem(newItem,
method == "REQUEST" method == "REQUEST"
? new ItipOpListener(opListener, null) ? new ItipOpListener(opListener, null, extResponse)
: opListener); : opListener);
}; };
operations.push(action); operations.push(action);
@ -1661,10 +1709,10 @@ ItipItemFinder.prototype = {
cal.LOG("iTIP operations: " + operations.length); cal.LOG("iTIP operations: " + operations.length);
let actionFunc = null; let actionFunc = null;
if (operations.length > 0) { if (operations.length > 0) {
actionFunc = function(opListener, partStat) { actionFunc = function(opListener, partStat=null, extResponse=null) {
for (let operation of operations) { for (let operation of operations) {
try { try {
operation(opListener, partStat); operation(opListener, partStat, extResponse);
} catch (exc) { } catch (exc) {
cal.ERROR(exc); cal.ERROR(exc);
} }

Просмотреть файл

@ -14,15 +14,15 @@ interface calIOperationListener;
* regarding the calendar. It is here as a service so that we can keep the * regarding the calendar. It is here as a service so that we can keep the
* transactions around without holding onto the whole global js scope+window. * transactions around without holding onto the whole global js scope+window.
*/ */
[scriptable, uuid(40a1ccf4-5f54-4815-b842-abf06f84dbfd)] [scriptable, uuid(1d529847-d292-4222-b066-b8b17a794d62)]
interface calITransactionManager : nsISupports interface calITransactionManager : nsISupports
{ {
/** /**
* @param aAction The Action to execute. This can be one of: * @param aAction The Action to execute. This can be one of:
* add Adds an item * * add Adds an item
* modify Modfifies an item * * modify Modfifies an item
* delete Deletes an item * * delete Deletes an item
* move Move an item from one calendar to the * * move Move an item from one calendar to the
* next. With this operation, aCalendar is * next. With this operation, aCalendar is
* the calendar that the event should be * the calendar that the event should be
* moved to. * moved to.
@ -33,12 +33,16 @@ interface calITransactionManager : nsISupports
* modify. * modify.
* @param aListener The listener to call when the transaction has * @param aListener The listener to call when the transaction has
* completed. This parameter can be null. * completed. This parameter can be null.
* @param aExtResponse JS object to provide additional parameters to prepare an itip message.
Valid attributes are:
* * responseMode A value as defined for calIItipItem.autoResponse
*/ */
void createAndCommitTxn(in AUTF8String aAction, void createAndCommitTxn(in AUTF8String aAction,
in calIItemBase aItem, in calIItemBase aItem,
in calICalendar aCalendar, in calICalendar aCalendar,
in calIItemBase aOldItem, in calIItemBase aOldItem,
in calIOperationListener aListener); in calIOperationListener aListener,
in jsval aExtResponse);
/** /**
* Signals the transaction manager that a series of transactions are going * Signals the transaction manager that a series of transactions are going

Просмотреть файл

@ -65,8 +65,8 @@ category profile-after-change calendar-startup-service @mozilla.org/calendar/sta
component {fcb54c82-2fb9-42cb-bf44-1e197a55e520} calItemModule.js component {fcb54c82-2fb9-42cb-bf44-1e197a55e520} calItemModule.js
contract @mozilla.org/calendar/transaction;1 {fcb54c82-2fb9-42cb-bf44-1e197a55e520} contract @mozilla.org/calendar/transaction;1 {fcb54c82-2fb9-42cb-bf44-1e197a55e520}
component {40a1ccf4-5f54-4815-b842-abf06f84dbfd} calItemModule.js component {1d529847-d292-4222-b066-b8b17a794d62} calItemModule.js
contract @mozilla.org/calendar/transactionmanager;1 {40a1ccf4-5f54-4815-b842-abf06f84dbfd} contract @mozilla.org/calendar/transactionmanager;1 {1d529847-d292-4222-b066-b8b17a794d62}
component {7af51168-6abe-4a31-984d-6f8a3989212d} calItemModule.js component {7af51168-6abe-4a31-984d-6f8a3989212d} calItemModule.js
contract @mozilla.org/calendar/todo;1 {7af51168-6abe-4a31-984d-6f8a3989212d} contract @mozilla.org/calendar/todo;1 {7af51168-6abe-4a31-984d-6f8a3989212d}

Просмотреть файл

@ -15,7 +15,7 @@ function calTransactionManager() {
} }
} }
var calTransactionManagerClassID = Components.ID("{40a1ccf4-5f54-4815-b842-abf06f84dbfd}"); var calTransactionManagerClassID = Components.ID("{1d529847-d292-4222-b066-b8b17a794d62}");
var calTransactionManagerInterfaces = [Components.interfaces.calITransactionManager]; var calTransactionManagerInterfaces = [Components.interfaces.calITransactionManager];
calTransactionManager.prototype = { calTransactionManager.prototype = {
@ -30,12 +30,13 @@ calTransactionManager.prototype = {
}), }),
transactionManager: null, transactionManager: null,
createAndCommitTxn: function(aAction, aItem, aCalendar, aOldItem, aListener) { createAndCommitTxn: function(aAction, aItem, aCalendar, aOldItem, aListener, aExtResponse) {
let txn = new calTransaction(aAction, let txn = new calTransaction(aAction,
aItem, aItem,
aCalendar, aCalendar,
aOldItem, aOldItem,
aListener); aListener,
aExtResponse);
this.transactionManager.doTransaction(txn); this.transactionManager.doTransaction(txn);
}, },
@ -77,13 +78,14 @@ calTransactionManager.prototype = {
} }
}; };
function calTransaction(aAction, aItem, aCalendar, aOldItem, aListener) { function calTransaction(aAction, aItem, aCalendar, aOldItem, aListener, aExtResponse) {
this.wrappedJSObject = this; this.wrappedJSObject = this;
this.mAction = aAction; this.mAction = aAction;
this.mItem = aItem; this.mItem = aItem;
this.mCalendar = aCalendar; this.mCalendar = aCalendar;
this.mOldItem = aOldItem; this.mOldItem = aOldItem;
this.mListener = aListener; this.mListener = aListener;
this.mExtResponse = aExtResponse;
} }
var calTransactionClassID = Components.ID("{fcb54c82-2fb9-42cb-bf44-1e197a55e520}"); var calTransactionClassID = Components.ID("{fcb54c82-2fb9-42cb-bf44-1e197a55e520}");
@ -108,12 +110,14 @@ calTransaction.prototype = {
mOldCalendar: null, mOldCalendar: null,
mListener: null, mListener: null,
mIsDoTransaction: false, mIsDoTransaction: false,
mExtResponse: null,
onOperationComplete: function(aCalendar, aStatus, aOperationType, aId, aDetail) { onOperationComplete: function(aCalendar, aStatus, aOperationType, aId, aDetail) {
if (Components.isSuccessCode(aStatus)) { if (Components.isSuccessCode(aStatus)) {
cal.itip.checkAndSend(aOperationType, cal.itip.checkAndSend(aOperationType,
aDetail, aDetail,
this.mIsDoTransaction ? this.mOldItem : this.mItem); this.mIsDoTransaction ? this.mOldItem : this.mItem,
this.mExtResponse);
if (aOperationType == Components.interfaces.calIOperationListener.ADD || if (aOperationType == Components.interfaces.calIOperationListener.ADD ||
aOperationType == Components.interfaces.calIOperationListener.MODIFY) { aOperationType == Components.interfaces.calIOperationListener.MODIFY) {

Просмотреть файл

@ -17,7 +17,7 @@ function calItipEmailTransport() {
this._initEmailTransport(); this._initEmailTransport();
} }
var calItipEmailTransportClassID = Components.ID("{d4d7b59e-c9e0-4a7a-b5e8-5958f85515f0}"); var calItipEmailTransportClassID = Components.ID("{d4d7b59e-c9e0-4a7a-b5e8-5958f85515f0}");
var calItipEmailTransportInterfaces = [Components.interfaces.calIItipTransport]; var calItipEmailTransportInterfaces = [Ci.calIItipTransport];
calItipEmailTransport.prototype = { calItipEmailTransport.prototype = {
classID: calItipEmailTransportClassID, classID: calItipEmailTransportClassID,
QueryInterface: XPCOMUtils.generateQI(calItipEmailTransportInterfaces), QueryInterface: XPCOMUtils.generateQI(calItipEmailTransportInterfaces),
@ -55,7 +55,7 @@ calItipEmailTransport.prototype = {
return this._sendXpcomMail(aRecipients, items.subject, items.body, aItipItem); return this._sendXpcomMail(aRecipients, items.subject, items.body, aItipItem);
} else { } else {
// sending xpcom mail is not available if no identity has been set // sending xpcom mail is not available if no identity has been set
throw Components.results.NS_ERROR_NOT_AVAILABLE; throw Cr.NS_ERROR_NOT_AVAILABLE;
} }
}, },
@ -198,7 +198,7 @@ calItipEmailTransport.prototype = {
// identity. // identity.
let allIdentities = MailServices.accounts.allIdentities; let allIdentities = MailServices.accounts.allIdentities;
if (allIdentities.length > 0) { if (allIdentities.length > 0) {
this.mDefaultIdentity = allIdentities.queryElementAt(0, Components.interfaces.nsIMsgIdentity); this.mDefaultIdentity = allIdentities.queryElementAt(0, Ci.nsIMsgIdentity);
} else { } else {
// If there are no identities, then we are in the same // If there are no identities, then we are in the same
// situation as if we didn't have Xpcom Mail. // situation as if we didn't have Xpcom Mail.
@ -212,17 +212,19 @@ calItipEmailTransport.prototype = {
} }
}, },
_sendXpcomMail: function(aToList, aSubject, aBody, aItem) { _sendXpcomMail: function(aToList, aSubject, aBody, aItipItem) {
let identity = null; let identity = null;
let account; let account;
if (aItem.targetCalendar) { if (aItipItem.targetCalendar) {
identity = aItem.targetCalendar.getProperty("imip.identity"); identity = aItipItem.targetCalendar.getProperty("imip.identity");
if (identity) { if (identity) {
identity = identity.QueryInterface(Components.interfaces.nsIMsgIdentity); identity = identity.QueryInterface(Ci.nsIMsgIdentity);
account = aItem.targetCalendar.getProperty("imip.account") account = aItipItem.targetCalendar
.QueryInterface(Components.interfaces.nsIMsgAccount); .getProperty("imip.account")
.QueryInterface(Ci.nsIMsgAccount);
} else { } else {
cal.WARN("No email identity configured for calendar " + aItem.targetCalendar.name); cal.WARN("No email identity configured for calendar " +
aItipItem.targetCalendar.name);
} }
} }
if (!identity) { // use some default identity/account: if (!identity) { // use some default identity/account:
@ -230,41 +232,34 @@ calItipEmailTransport.prototype = {
account = this.mDefaultAccount; account = this.mDefaultAccount;
} }
let compatMode = 0; switch (aItipItem.autoResponse) {
switch (aItem.autoResponse) { case Ci.calIItipItem.USER: {
case Components.interfaces.calIItipItem.USER: { cal.LOG("sendXpcomMail: Found USER autoResponse type.");
cal.LOG("sendXpcomMail: Found USER autoResponse type.\n" + // We still need this as a last resort if a user just deletes or
"This type is currently unsupported, the compose API will always enter a text/plain\n" + // drags an invitation related event
"or text/html part as first part of the message.\n" +
"This will disable OL (up to 2003) to consume the mail as an iTIP invitation showing\n" +
"the usual calendar buttons.");
// To somehow have a last resort before sending spam, the user can choose to send the mail.
let prefCompatMode = Preferences.get("calendar.itip.compatSendMode", 0);
let inoutCheck = { value: prefCompatMode == 1 };
let parent = Services.wm.getMostRecentWindow(null); let parent = Services.wm.getMostRecentWindow(null);
if (parent.closed) { if (parent.closed) {
parent = cal.window.getCalendarWindow(); parent = cal.window.getCalendarWindow();
} }
if (Services.prompt.confirmEx(parent, let confirmed = Services.prompt.confirmEx(
parent,
cal.calGetString("lightning", "imipSendMail.title", null, "lightning"), cal.calGetString("lightning", "imipSendMail.title", null, "lightning"),
cal.calGetString("lightning", "imipSendMail.text", null, "lightning"), cal.calGetString("lightning", "imipSendMail.text", null, "lightning"),
Services.prompt.STD_YES_NO_BUTTONS, Services.prompt.STD_YES_NO_BUTTONS,
null, null,
null, null,
null, null,
cal.calGetString("lightning", "imipSendMail.Outlook2000CompatMode.text", null, "lightning"), null,
inoutCheck)) { {}
);
if (!confirmed) {
break; break;
} // else go on with auto sending for now } // else go on with auto sending for now
compatMode = (inoutCheck.value ? 1 : 0);
if (compatMode != prefCompatMode) {
Preferences.set("calendar.itip.compatSendMode", compatMode);
} }
} // falls through intended
// falls through, based on prompting above case Ci.calIItipItem.AUTO: {
case Components.interfaces.calIItipItem.AUTO: {
// don't show log message in case of falling through // don't show log message in case of falling through
if (aItem.autoResponse == Components.interfaces.calIItipItem.AUTO) { if (aItipItem.autoResponse == Ci.calIItipItem.AUTO) {
cal.LOG("sendXpcomMail: Found AUTO autoResponse type."); cal.LOG("sendXpcomMail: Found AUTO autoResponse type.");
} }
let cbEmail = function(aVal, aInd, aArr) { let cbEmail = function(aVal, aInd, aArr) {
@ -280,14 +275,14 @@ calItipEmailTransport.prototype = {
return false; return false;
} }
let toList = toMap.join(", "); let toList = toMap.join(", ");
let composeUtils = Components.classes["@mozilla.org/messengercompose/computils;1"] let composeUtils = Cc["@mozilla.org/messengercompose/computils;1"]
.createInstance(Components.interfaces.nsIMsgCompUtils); .createInstance(Ci.nsIMsgCompUtils);
let messageId = composeUtils.msgGenerateMessageId(identity); let messageId = composeUtils.msgGenerateMessageId(identity);
let mailFile = this._createTempImipFile(compatMode, toList, aSubject, aBody, aItem, identity, messageId); let mailFile = this._createTempImipFile(toList, aSubject, aBody, aItipItem, identity, messageId);
if (mailFile) { if (mailFile) {
// compose fields for message: from/to etc need to be specified both here and in the file // compose fields for message: from/to etc need to be specified both here and in the file
let composeFields = Components.classes["@mozilla.org/messengercompose/composefields;1"] let composeFields = Cc["@mozilla.org/messengercompose/composefields;1"]
.createInstance(Components.interfaces.nsIMsgCompFields); .createInstance(Ci.nsIMsgCompFields);
composeFields.characterSet = "UTF-8"; composeFields.characterSet = "UTF-8";
composeFields.to = toList; composeFields.to = toList;
let mailfrom = (identity.fullName.length ? identity.fullName + " <" + identity.email + ">" : identity.email); let mailfrom = (identity.fullName.length ? identity.fullName + " <" + identity.email + ">" : identity.email);
@ -314,16 +309,16 @@ calItipEmailTransport.prototype = {
// "@mozilla.org/messengercompose/composesendlistener;1" // "@mozilla.org/messengercompose/composesendlistener;1"
// and/or "chrome://messenger/content/messengercompose/sendProgress.xul" // and/or "chrome://messenger/content/messengercompose/sendProgress.xul"
// i.e. bug 432662 // i.e. bug 432662
let msgSend = Components.classes["@mozilla.org/messengercompose/send;1"] let msgSend = Cc["@mozilla.org/messengercompose/send;1"]
.createInstance(Components.interfaces.nsIMsgSend); .createInstance(Ci.nsIMsgSend);
msgSend.sendMessageFile(identity, msgSend.sendMessageFile(identity,
account.key, account.key,
composeFields, composeFields,
mailFile, mailFile,
true, // deleteSendFileOnCompletion true, // deleteSendFileOnCompletion
false, // digest_p false, // digest_p
(Services.io.offline ? Components.interfaces.nsIMsgSend.nsMsgQueueForLater (Services.io.offline ? Ci.nsIMsgSend.nsMsgQueueForLater
: Components.interfaces.nsIMsgSend.nsMsgDeliverNow), : Ci.nsIMsgSend.nsMsgDeliverNow),
null, // nsIMsgDBHdr msgToReplace null, // nsIMsgDBHdr msgToReplace
null, // nsIMsgSendListener aListener null, // nsIMsgSendListener aListener
null, // nsIMsgStatusFeedback aStatusFeedback null, // nsIMsgStatusFeedback aStatusFeedback
@ -332,30 +327,30 @@ calItipEmailTransport.prototype = {
} }
break; break;
} }
case Components.interfaces.calIItipItem.NONE: { case Ci.calIItipItem.NONE: {
// we shouldn't get here, as we stoppped processing in this case
// earlier in checkAndSend in calItipUtils.jsm
cal.LOG("sendXpcomMail: Found NONE autoResponse type."); cal.LOG("sendXpcomMail: Found NONE autoResponse type.");
// No response
break; break;
} }
default: { default: {
// Unknown autoResponse type // Also of this case should have been taken care at the same place
throw new Error("sendXpcomMail: " + throw new Error("sendXpcomMail: " +
"Unknown autoResponse type: " + "Unknown autoResponse type: " +
aItem.autoResponse); aItipItem.autoResponse);
} }
} }
return false; return false;
}, },
_createTempImipFile: function(compatMode, aToList, aSubject, aBody, aItem, aIdentity, aMessageId) { _createTempImipFile: function(aToList, aSubject, aBody, aItipItem, aIdentity, aMessageId) {
try { try {
let itemList = aItem.getItemList({}); let itemList = aItipItem.getItemList({});
let serializer = Components.classes["@mozilla.org/calendar/ics-serializer;1"] let serializer = Cc["@mozilla.org/calendar/ics-serializer;1"]
.createInstance(Components.interfaces.calIIcsSerializer); .createInstance(Ci.calIIcsSerializer);
serializer.addItems(itemList, itemList.length); serializer.addItems(itemList, itemList.length);
let methodProp = cal.getIcsService().createIcalProperty("METHOD"); let methodProp = cal.getIcsService().createIcalProperty("METHOD");
methodProp.value = aItem.responseMethod; methodProp.value = aItipItem.responseMethod;
serializer.addProperty(methodProp); serializer.addProperty(methodProp);
let calText = serializer.serializeToString(); let calText = serializer.serializeToString();
let utf8CalText = ltn.invitation.encodeUTF8(calText); let utf8CalText = ltn.invitation.encodeUTF8(calText);
@ -364,16 +359,6 @@ calItipEmailTransport.prototype = {
// it can cope with nested attachments, // it can cope with nested attachments,
// like multipart/alternative with enclosed text/calendar and text/plain. // like multipart/alternative with enclosed text/calendar and text/plain.
let mailText = ltn.invitation.getHeaderSection(aMessageId, aIdentity, aToList, aSubject); let mailText = ltn.invitation.getHeaderSection(aMessageId, aIdentity, aToList, aSubject);
switch (compatMode) {
case 1:
mailText += "Content-class: urn:content-classes:calendarmessage\r\n" +
"Content-type: text/calendar; method=" + aItem.responseMethod + "; charset=UTF-8\r\n" +
"Content-transfer-encoding: 8BIT\r\n" +
"\r\n" +
utf8CalText +
"\r\n";
break;
default:
mailText += "Content-type: multipart/mixed; boundary=\"Boundary_(ID_qyG4ZdjoAsiZ+Jo19dCbWQ)\"\r\n" + mailText += "Content-type: multipart/mixed; boundary=\"Boundary_(ID_qyG4ZdjoAsiZ+Jo19dCbWQ)\"\r\n" +
"\r\n\r\n" + "\r\n\r\n" +
"--Boundary_(ID_qyG4ZdjoAsiZ+Jo19dCbWQ)\r\n" + "--Boundary_(ID_qyG4ZdjoAsiZ+Jo19dCbWQ)\r\n" +
@ -387,7 +372,7 @@ calItipEmailTransport.prototype = {
ltn.invitation.encodeUTF8(aBody) + ltn.invitation.encodeUTF8(aBody) +
"\r\n\r\n\r\n" + "\r\n\r\n\r\n" +
"--Boundary_(ID_ryU4ZdJoASiZ+Jo21dCbwA)\r\n" + "--Boundary_(ID_ryU4ZdJoASiZ+Jo21dCbwA)\r\n" +
"Content-type: text/calendar; method=" + aItem.responseMethod + "; charset=UTF-8\r\n" + "Content-type: text/calendar; method=" + aItipItem.responseMethod + "; charset=UTF-8\r\n" +
"Content-transfer-encoding: 8BIT\r\n" + "Content-transfer-encoding: 8BIT\r\n" +
"\r\n" + "\r\n" +
utf8CalText + utf8CalText +
@ -402,17 +387,15 @@ calItipEmailTransport.prototype = {
utf8CalText + utf8CalText +
"\r\n\r\n" + "\r\n\r\n" +
"--Boundary_(ID_qyG4ZdjoAsiZ+Jo19dCbWQ)--\r\n"; "--Boundary_(ID_qyG4ZdjoAsiZ+Jo19dCbWQ)--\r\n";
break;
}
cal.LOG("mail text:\n" + mailText); cal.LOG("mail text:\n" + mailText);
let tempFile = Services.dirsvc.get("TmpD", Components.interfaces.nsIFile); let tempFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
tempFile.append("itipTemp"); tempFile.append("itipTemp");
tempFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, tempFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE,
parseInt("0600", 8)); parseInt("0600", 8));
let outputStream = Components.classes["@mozilla.org/network/file-output-stream;1"] let outputStream = Cc["@mozilla.org/network/file-output-stream;1"]
.createInstance(Components.interfaces.nsIFileOutputStream); .createInstance(Ci.nsIFileOutputStream);
// Let's write the file - constants from file-utils.js // Let's write the file - constants from file-utils.js
const MODE_WRONLY = 0x02; const MODE_WRONLY = 0x02;
const MODE_CREATE = 0x08; const MODE_CREATE = 0x08;

Просмотреть файл

@ -57,12 +57,6 @@ pref("calendar.alarms.todoalarmunit", "minutes");
pref("calendar.invitations.autorefresh.enabled", true); pref("calendar.invitations.autorefresh.enabled", true);
pref("calendar.invitations.autorefresh.timeout", 3); pref("calendar.invitations.autorefresh.timeout", 3);
// iTIP compatibility send mode
// 0 -- Outlook 2003 and following with text/plain and application/ics (default)
// 1 -- all Outlook, but no text/plain nor application/ics
// We may extend the compat mode if necessary.
pref("calendar.itip.compatSendMode", 0);
// whether "notify" is checked by default when creating new events/todos with attendees // whether "notify" is checked by default when creating new events/todos with attendees
pref("calendar.itip.notify", true); pref("calendar.itip.notify", true);

Просмотреть файл

@ -141,7 +141,6 @@ imipBarProcessingFailed=Processing message failed. Status: %1$S.
imipBarNotWritable=No writable calendars are configured for invitations, please check the calendar properties. imipBarNotWritable=No writable calendars are configured for invitations, please check the calendar properties.
imipSendMail.title=E-Mail Notification imipSendMail.title=E-Mail Notification
imipSendMail.text=Would you like to send out notification E-Mail now? imipSendMail.text=Would you like to send out notification E-Mail now?
imipSendMail.Outlook2000CompatMode.text=Support Outlook 2000 and Outlook 2002/XP
imipNoIdentity=None imipNoIdentity=None
imipNoCalendarAvailable=There are no writable calendars available. imipNoCalendarAvailable=There are no writable calendars available.