iTIP/iMIP improvements:
- Add Accept/Decline buttons to the summary display; both currently have the same effect: - Add the (someday: "accepted") item to the user's first calendar. Also, teach the timezone heuristics about Eastern Time. NPTOB. Not as cool as vlad.
This commit is contained in:
Родитель
2b6804c5fa
Коммит
da5cc1b648
|
@ -47,6 +47,7 @@ MODULE = lightning
|
|||
|
||||
EXTRA_COMPONENTS = \
|
||||
lightningTextCalendarConverter.js \
|
||||
calItipProtocolHandler.js \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
/* -*- Mode: javascript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
const CI = Components.interfaces;
|
||||
|
||||
const ITIP_HANDLER_MIMETYPE = "application/x-itip-internal";
|
||||
const ITIP_HANDLER_PROTOCOL = "moz-cal-handle-itip";
|
||||
|
||||
const CAL_ITIP_PROTO_HANDLER_CID =
|
||||
Components.ID("{6E957006-B4CE-11D9-B053-001124736B74}");
|
||||
const CAL_ITIP_PROTO_HANDLER_CONTRACTID =
|
||||
"@mozilla.org/network/protocol;1?name=" + ITIP_HANDLER_PROTOCOL;
|
||||
|
||||
const CALMGR_CONTRACTID = "@mozilla.org/calendar/manager;1";
|
||||
|
||||
const ItipProtocolHandlerFactory =
|
||||
{
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer != null)
|
||||
throw Components.results.NS_ERROR_NO_AGGREGATION;
|
||||
|
||||
return (new ItipProtocolHandler()).QueryInterface(iid);
|
||||
}
|
||||
};
|
||||
|
||||
const CAL_ITIP_CONTENT_HANDLER_CID =
|
||||
Components.ID("{47C31F2B-B4DE-11D9-BFE6-001124736B74}");
|
||||
const CAL_ITIP_CONTENT_HANDLER_CONTRACTID =
|
||||
"@mozilla.org/uriloader/content-handler;1?type=" +
|
||||
ITIP_HANDLER_MIMETYPE;
|
||||
|
||||
const ItipContentHandlerFactory =
|
||||
{
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer != null)
|
||||
throw Components.results.NS_ERROR_NO_AGGREGATION;
|
||||
|
||||
return (new ItipContentHandler()).QueryInterface(iid);
|
||||
}
|
||||
};
|
||||
|
||||
function NYI()
|
||||
{
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
function ItipChannel(URI)
|
||||
{
|
||||
this.URI = this.originalURI = URI;
|
||||
}
|
||||
|
||||
ItipChannel.prototype = {
|
||||
QueryInterface: function (aIID) {
|
||||
if (!aIID.equals(CI.nsISupports) &&
|
||||
!aIID.equals(CI.nsIChannel) &&
|
||||
!aIID.equals(CI.nsIRequest))
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
contentType: ITIP_HANDLER_MIMETYPE,
|
||||
loadAttributes: null,
|
||||
contentLength: 0,
|
||||
owner: null,
|
||||
loadGroup: null,
|
||||
notificationCallbacks: null,
|
||||
securityInfo: null,
|
||||
|
||||
open: NYI,
|
||||
asyncOpen: function (observer, ctxt) {
|
||||
observer.onStartRequest(this, ctxt);
|
||||
},
|
||||
asyncRead: function (listener, ctxt) {
|
||||
return listener.onStartRequest(this, ctxt);
|
||||
},
|
||||
|
||||
isPending: function () { return true; },
|
||||
status: Components.results.NS_OK,
|
||||
cancel: function (status) { this.status = status; },
|
||||
suspend: NYI,
|
||||
resume: NYI,
|
||||
};
|
||||
|
||||
function ItipProtocolHandler() { }
|
||||
|
||||
ItipProtocolHandler.prototype = {
|
||||
QueryInterface: function (aIID) {
|
||||
if (!aIID.equals(CI.nsISupports) &&
|
||||
!aIID.equals(CI.nsIProtocolHandler))
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
protocolFlags: CI.nsIProtocolHandler.URI_NORELATIVE,
|
||||
allowPort: function () { return false; },
|
||||
isSecure: false,
|
||||
newURI: function (spec, charSet, baseURI)
|
||||
{
|
||||
var cls = Components.classes["@mozilla.org/network/standard-url;1"];
|
||||
var url = cls.createInstance(CI.nsIStandardURL);
|
||||
url.init(CI.nsIStandardURL.URLTYPE_STANDARD, 0, spec, charSet, baseURI);
|
||||
dump("Creating new URI for " + spec + "\n");
|
||||
return url.QueryInterface(CI.nsIURI);
|
||||
},
|
||||
|
||||
newChannel: function (URI) {
|
||||
dump("Creating new ItipChannel for " + URI + "\n");
|
||||
return new ItipChannel(URI);
|
||||
},
|
||||
};
|
||||
|
||||
function ItipContentHandler() { }
|
||||
|
||||
ItipContentHandler.prototype = {
|
||||
QueryInterface: function (aIID) {
|
||||
if (!aIID.equals(CI.nsISupports) &&
|
||||
!aIID.equals(CI.nsIContentHandler))
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
handleContent: function (contentType, windowTarget, request)
|
||||
{
|
||||
dump("Handling some itip content, whee\n");
|
||||
var channel = request.QueryInterface(CI.nsIChannel);
|
||||
var uri = channel.URI.spec;
|
||||
if (uri.indexOf(ITIP_HANDLER_PROTOCOL + ":") != 0) {
|
||||
dump("unexpected uri " + uri + "\n");
|
||||
return Components.results.NS_ERROR_FAILURE;
|
||||
}
|
||||
// moz-cal-handle-itip:///?
|
||||
var paramString = uri.substring(ITIP_HANDLER_PROTOCOL.length + 4);
|
||||
var paramArray = paramString.split("&");
|
||||
var paramBlock = { };
|
||||
paramArray.forEach(function (v) {
|
||||
var parts = v.split("=");
|
||||
paramBlock[parts[0]] = unescape(unescape(parts[1]));
|
||||
});
|
||||
// dump("content-handler: have params " + paramBlock.toSource() + "\n");
|
||||
var event = Components.classes["@mozilla.org/calendar/event;1"].
|
||||
createInstance(CI.calIEvent);
|
||||
event.icalString = paramBlock.data;
|
||||
dump("Processing iTIP event '" + event.title + "' from " +
|
||||
event.organizer.id + " (" + event.id + ")\n");
|
||||
var calMgr = Components.classes[CALMGR_CONTRACTID].getService(CI.calICalendarManager);
|
||||
var cals = calMgr.getCalendars({});
|
||||
cals[0].addItem(event, null);
|
||||
}
|
||||
};
|
||||
|
||||
var myModule = {
|
||||
registerSelf: function (compMgr, fileSpec, location, type) {
|
||||
debug("*** Registering Lightning " + ITIP_HANDLER_PROTOCOL + ": handler\n");
|
||||
compMgr = compMgr.QueryInterface(CI.nsIComponentRegistrar);
|
||||
compMgr.registerFactoryLocation(CAL_ITIP_PROTO_HANDLER_CID,
|
||||
"Lightning " + ITIP_HANDLER_PROTOCOL + ": handler",
|
||||
CAL_ITIP_PROTO_HANDLER_CONTRACTID,
|
||||
fileSpec, location, type);
|
||||
debug("*** Registering Lightning " + ITIP_HANDLER_MIMETYPE + " handler\n");
|
||||
compMgr.registerFactoryLocation(CAL_ITIP_CONTENT_HANDLER_CID,
|
||||
"Lightning " + ITIP_HANDLER_MIMETYPE + " handler",
|
||||
CAL_ITIP_CONTENT_HANDLER_CONTRACTID,
|
||||
fileSpec, location, type);
|
||||
},
|
||||
|
||||
getClassObject: function (compMgr, cid, iid) {
|
||||
if (!iid.equals(Components.interfaces.nsIFactory))
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
if (cid.equals(CAL_ITIP_PROTO_HANDLER_CID))
|
||||
return ItipProtocolHandlerFactory;
|
||||
|
||||
if (cid.equals(CAL_ITIP_CONTENT_HANDLER_CID))
|
||||
return ItipContentHandlerFactory;
|
||||
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
|
||||
canUnload: function(compMgr) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
function NSGetModule(compMgr, fileSpec) {
|
||||
return myModule;
|
||||
}
|
|
@ -2,6 +2,22 @@
|
|||
|
||||
const CI = Components.interfaces;
|
||||
|
||||
function makeTableRow(val) {
|
||||
return "<tr><td>" + val[0] + "</td><td>" + val[1] + "</td></tr>\n";
|
||||
}
|
||||
|
||||
function makeButton(type, content) {
|
||||
return "<button type='submit' value='" + type + "'>" + content + "</button>";
|
||||
}
|
||||
|
||||
function startForm(calendarData) {
|
||||
var form = "<form method='GET' action='moz-cal-handle-itip:'>\n";
|
||||
form += "<input type='hidden' name='preferredCalendar' value=''>\n";
|
||||
// We use escape instead of encodeURI*, because we need to deal with single quotes, sigh
|
||||
form += "<input type='hidden' name='data' value='" + escape(calendarData) + "'>\n";
|
||||
return form;
|
||||
}
|
||||
|
||||
function ltnMimeConverter() { }
|
||||
|
||||
ltnMimeConverter.prototype = {
|
||||
|
@ -14,21 +30,33 @@ ltnMimeConverter.prototype = {
|
|||
},
|
||||
|
||||
convertToHTML: function(contentType, data) {
|
||||
dump("converting " + contentType + " to HTML\n");
|
||||
// dump("converting " + contentType + " to HTML\n");
|
||||
|
||||
var event = Components.classes["@mozilla.org/calendar/event;1"]
|
||||
.createInstance(CI.calIEvent);
|
||||
event.icalString = data;
|
||||
|
||||
var html = "<center><table bgcolor='#CCFFFF'>\n";
|
||||
var html = "<script src='chrome://lightning/content/text-calendar-handler.js'></script>\n";
|
||||
html += "<center>\n";
|
||||
html += startForm(data);
|
||||
html += "<table bgcolor='#CCFFFF'>\n";
|
||||
var organizer = event.organizer;
|
||||
html += "<tr><td>Invitation from</td><td><a href='" +
|
||||
organizer.id + "'>" + organizer.commonName + "</a></td></tr>\n";
|
||||
html += "<tr><td>Subject:</td><td>" + event.title + "</td></tr>\n";
|
||||
html += "</table></center>";
|
||||
var rows = [["Invitation from", "<a href='" + organizer.id + "'>" +
|
||||
organizer.commonName + "</a>"],
|
||||
["Topic:", event.title],
|
||||
["Start:", event.startDate.jsDate.toLocaleTimeString()],
|
||||
["End:", event.endDate.jsDate.toLocaleTimeString()]];
|
||||
html += rows.map(makeTableRow).join("\n");
|
||||
html += "<tr><td colspan='2'>";
|
||||
html += makeButton("accept", "Accept meeting") + " ";
|
||||
html += makeButton("decline", "Decline meeting") + " ";
|
||||
html += "</td></tr>\n";
|
||||
html += "</table>\n</form>\n</center>";
|
||||
|
||||
// dump("Generated HTML:\n\n" + html + "\n\n");
|
||||
return html;
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
var myModule = {
|
||||
|
@ -61,17 +89,9 @@ var myModule = {
|
|||
|
||||
myCID: Components.ID("{c70acb08-464e-4e55-899d-b2c84c5409fa}"),
|
||||
|
||||
/* ProgID for this class */
|
||||
myContractID: "@mozilla.org/lightning/mime-converter;1",
|
||||
|
||||
/* factory object */
|
||||
myFactory: {
|
||||
/*
|
||||
* Construct an instance of the interface specified by iid, possibly
|
||||
* aggregating it with the provided outer. (If you don't know what
|
||||
* aggregation is all about, you don't need to. It reduces even the
|
||||
* mightiest of XPCOM warriors to snivelling cowards.)
|
||||
*/
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer != null)
|
||||
throw Components.results.NS_ERROR_NO_AGGREGATION;
|
||||
|
@ -80,17 +100,6 @@ var myModule = {
|
|||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* The canUnload method signals that the component is about to be unloaded.
|
||||
* C++ components can return false to indicate that they don't wish to be
|
||||
* unloaded, but the return value from JS components' canUnload is ignored:
|
||||
* mark-and-sweep will keep everything around until it's no longer in use,
|
||||
* making unconditional ``unload'' safe.
|
||||
*
|
||||
* You still need to provide a (likely useless) canUnload method, though:
|
||||
* it's part of the nsIModule interface contract, and the JS loader _will_
|
||||
* call it.
|
||||
*/
|
||||
canUnload: function(compMgr) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -147,7 +147,9 @@ function calendarDefaultTimezone() {
|
|||
// XXX this table needs a _lot_ more stuff in it.
|
||||
const tzTable = {
|
||||
"GMT-0700 Pacific Daylight Time" : "/mozilla.org/20050126_1/America/Los_Angeles",
|
||||
"GMT-0800 Pacific Standard Time" : "/mozilla.org/20050126_1/America/Los_Angeles"
|
||||
"GMT-0800 Pacific Standard Time" : "/mozilla.org/20050126_1/America/Los_Angeles",
|
||||
"GMT-0400 EDT" : "/mozilla.org/20050126_1/America/New_York",
|
||||
"GMT-0500 EST" : "/mozilla.org/20050126_1/America/New_York",
|
||||
};
|
||||
|
||||
// returns a ICS timezone string
|
||||
|
|
Загрузка…
Ссылка в новой задаче