mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-05-22 03:45:25 +00:00
Monotone-Parent: 6303f532fa4bb3c36fe2f298f62f659c4fc71b18
Monotone-Revision: c111ae7ec8c39efb8d0cc56ff4627bb958ff0cc8 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-04-18T20:03:07
This commit is contained in:
@@ -1,5 +1,28 @@
|
||||
2012-04-18 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* UI/WebServerResources/generic.js (showAuthenticationDialog): new
|
||||
dialog providing an interface for requesting a username and a
|
||||
password to acallback passed as parameter.
|
||||
(accessToSubscribedFolder): fixed method to return the proper url
|
||||
for folders owned by the active user.
|
||||
|
||||
* UI/WebServerResources/SchedulerUI.js (reloadWebCalendars): now
|
||||
invokes "reloadAction" on every web calendar foldar, in a chain of
|
||||
simultaneous requests.
|
||||
(reloadWebCalendar): new function invoking "reloadAction" on a
|
||||
single cal folder.
|
||||
(reloadWebCalendarCallback): callback for the above invocation
|
||||
that takes care of refreshing the views at the end of a refresh
|
||||
chain, if present, and when useful.
|
||||
|
||||
* UI/Scheduler/UIxCalFolderActions.m (reloadAction): new web
|
||||
method that applies only to SOGoWebAppointmentFolder instances and
|
||||
which returns a JSON representation of the result returned by
|
||||
-[SOGoWebAppointmentFolder loadWebCalendar]. This method replaces
|
||||
-[UIxCalMainActions -reloadWebCalendars].
|
||||
(setCredentialsAction): new web method acting as a proxy for
|
||||
-[SOGoWebAppointmentFolder setUsername:andPassword:].
|
||||
|
||||
* UI/WebServerResources/generic.js (clickEventWrapper): new
|
||||
function that returns a wrapper function for click callbacks which
|
||||
invokes "preventDefault" on the "event" parameter before it is
|
||||
@@ -7,6 +30,7 @@
|
||||
|
||||
* UI/Scheduler/UIxCalMainActions.m
|
||||
(-reloadWebCalendarsAndRedirectAction): removed obsolete method.
|
||||
(-reloadWebCalendars): removed obsolete method.
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentFolders.m
|
||||
(-newWebCalendarWithName:atURL:): perform sanity checks on the url
|
||||
|
||||
@@ -20,12 +20,13 @@
|
||||
*/
|
||||
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import <SoObjects/SOGo/NSDictionary+Utilities.h>
|
||||
|
||||
#import <NGObjWeb/NSException+HTTP.h>
|
||||
#import <NGObjWeb/WORequest.h>
|
||||
|
||||
#import <SOGo/NSDictionary+Utilities.h>
|
||||
|
||||
#import <Appointments/SOGoAppointmentFolder.h>
|
||||
#import <Appointments/SOGoWebAppointmentFolder.h>
|
||||
#import <Appointments/SOGoAppointmentFolderICS.h>
|
||||
|
||||
#import "UIxCalFolderActions.h"
|
||||
@@ -95,4 +96,43 @@
|
||||
return response;
|
||||
}
|
||||
|
||||
/* These methods are only available on instance of SOGoWebAppointmentFolder. */
|
||||
- (WOResponse *) reloadAction
|
||||
{
|
||||
WOResponse *response;
|
||||
NSDictionary *results;
|
||||
|
||||
response = [self responseWithStatus: 200];
|
||||
[response setHeader: @"application/json" forKey: @"content-type"];
|
||||
results = [[self clientObject] loadWebCalendar];
|
||||
[response appendContentString: [results jsonRepresentation]];
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
- (WOResponse *) setCredentialsAction
|
||||
{
|
||||
WORequest *request;
|
||||
WOResponse *response;
|
||||
NSString *username, *password;
|
||||
|
||||
request = [context request];
|
||||
|
||||
username = [[request formValueForKey: @"username"] stringByTrimmingSpaces];
|
||||
password = [[request formValueForKey: @"password"] stringByTrimmingSpaces];
|
||||
if ([username length] > 0 && [password length] > 0)
|
||||
{
|
||||
[[self clientObject] setUsername: username
|
||||
andPassword: password];
|
||||
response = [self responseWith204];
|
||||
}
|
||||
else
|
||||
response
|
||||
= (WOResponse *) [NSException exceptionWithHTTPStatus: 400
|
||||
reason: @"missing 'username' and/or"
|
||||
@" 'password' parameters"];
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@end /* UIxCalFolderActions */
|
||||
|
||||
@@ -60,51 +60,41 @@
|
||||
WORequest *r;
|
||||
WOResponse *response;
|
||||
SOGoWebAppointmentFolder *folder;
|
||||
NSURL *url;
|
||||
NSString *urlString, *displayName;
|
||||
NSMutableDictionary *rc;
|
||||
SOGoAppointmentFolders *folders;
|
||||
int imported = 0;
|
||||
|
||||
r = [context request];
|
||||
rc = [NSMutableDictionary dictionary];
|
||||
|
||||
// Just a check
|
||||
urlString = [r formValueForKey: @"url"];
|
||||
url = [NSURL URLWithString: urlString];
|
||||
if (url)
|
||||
urlString = [[r formValueForKey: @"url"] stringByTrimmingSpaces];
|
||||
if ([urlString length] > 0)
|
||||
{
|
||||
folders = [self clientObject];
|
||||
displayName = [self displayNameForUrl: [r formValueForKey: @"url"]];
|
||||
displayName = [self displayNameForUrl: urlString];
|
||||
folder = [folders newWebCalendarWithName: displayName
|
||||
atURL: urlString];
|
||||
if (folder)
|
||||
{
|
||||
imported = [folder loadWebCalendar];
|
||||
if (imported >= 0)
|
||||
{
|
||||
[rc setObject: displayName forKey: @"displayname"];
|
||||
[rc setObject: [folder nameInContainer] forKey: @"name"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[folder delete];
|
||||
}
|
||||
[rc setObject: [NSNumber numberWithInt: imported]
|
||||
forKey: @"imported"];
|
||||
response = [self responseWithStatus: 200];
|
||||
[response setHeader: @"application/json" forKey: @"content-type"];
|
||||
|
||||
rc = [NSMutableDictionary dictionary];
|
||||
[rc setObject: [folder displayName] forKey: @"name"];
|
||||
[rc setObject: [folder folderReference] forKey: @"folderID"];
|
||||
[response appendContentString: [rc jsonRepresentation]];
|
||||
}
|
||||
else
|
||||
response = (WOResponse *)
|
||||
[NSException exceptionWithHTTPStatus: 400
|
||||
reason: @"folder was not created"];
|
||||
}
|
||||
else
|
||||
response = (WOResponse *)
|
||||
[NSException exceptionWithHTTPStatus: 400
|
||||
reason: @"missing 'url' parameter"];
|
||||
|
||||
response = [self responseWithStatus: 200];
|
||||
[response appendContentString: [rc jsonRepresentation]];
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
- (WOResponse *) reloadWebCalendarsAction
|
||||
{
|
||||
[[self clientObject] reloadWebCalendars: YES];
|
||||
|
||||
return [self responseWith204];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -45,11 +45,6 @@
|
||||
actionClass = "UIxCalMainActions";
|
||||
actionName = "addWebCalendar";
|
||||
};
|
||||
reloadWebCalendars = {
|
||||
protectedBy = "View";
|
||||
actionClass = "UIxCalMainActions";
|
||||
actionName = "reloadWebCalendars";
|
||||
};
|
||||
saveDragHandleState = {
|
||||
protectedBy = "View";
|
||||
pageName = "UIxCalMainView";
|
||||
@@ -176,6 +171,21 @@
|
||||
};
|
||||
};
|
||||
|
||||
SOGoWebAppointmentFolder = {
|
||||
methods = {
|
||||
reload = {
|
||||
protectedBy = "View";
|
||||
actionClass = "UIxCalFolderActions";
|
||||
actionName = "reload";
|
||||
};
|
||||
"set-credentials" = {
|
||||
protectedBy = "View";
|
||||
actionClass = "UIxCalFolderActions";
|
||||
actionName = "setCredentials";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
SOGoAppointmentFolderICS = {
|
||||
methods = {
|
||||
export = {
|
||||
|
||||
@@ -1203,25 +1203,180 @@ function onMonthOverview() {
|
||||
return _ensureView("monthview");
|
||||
}
|
||||
|
||||
function onCalendarReload() {
|
||||
function refreshEventsAndTasks() {
|
||||
refreshEvents();
|
||||
refreshTasks();
|
||||
reloadWebCalendars();
|
||||
}
|
||||
|
||||
function onCalendarReload() {
|
||||
if (!reloadWebCalendars()) {
|
||||
refreshEventsAndTasks();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function reloadWebCalendars() {
|
||||
var url = ApplicationBaseURL + "reloadWebCalendars";
|
||||
if (document.reloadWebCalAjaxRequest) {
|
||||
document.reloadWebCalAjaxRequest.aborted = true;
|
||||
document.reloadWebCalAjaxRequest.abort();
|
||||
log("* reloadWebCalendars");
|
||||
var hasWebCalendars = false;
|
||||
|
||||
var remaining = [];
|
||||
var refreshOperations = { "remaining": remaining };
|
||||
if (UserSettings['Calendar']
|
||||
&& UserSettings['Calendar']['WebCalendars']) {
|
||||
var webCalendars = UserSettings['Calendar']['WebCalendars'];
|
||||
|
||||
var folders = $("calendarList");
|
||||
var calendarNodes = folders.childNodesWithTag("li");
|
||||
for (var i = 0; i < calendarNodes.length; i++) {
|
||||
var current = calendarNodes[i];
|
||||
var calendarID = current.getAttribute("id");
|
||||
var owner = current.getAttribute("owner");
|
||||
var realID = owner + ":Calendar/" + calendarID.substr(1);
|
||||
if (webCalendars[realID]) { /* is web calendar ? */
|
||||
remaining.push(realID);
|
||||
reloadWebCalendar(realID, refreshOperations);
|
||||
}
|
||||
}
|
||||
}
|
||||
document.reloadWebCalAjaxRequest
|
||||
= triggerAjaxRequest(url, reloadWebCalendarsCallback);
|
||||
|
||||
return (remaining.length > 0);
|
||||
}
|
||||
|
||||
function reloadWebCalendarsCallback (http) {
|
||||
changeCalendarDisplay(null, currentView);
|
||||
var calendarReloadErrors = { "invalid-calendar-content":
|
||||
_("the returned content was not valid calendar"
|
||||
+ " data"),
|
||||
"http-error": _("an unknown http error occurred"
|
||||
+ " during the load operation"),
|
||||
"bad-url": _("the url in use is invalid or the"
|
||||
+ " host is currently unreachable"),
|
||||
"invalid-url": _("the url being used is invalid"
|
||||
+ " or not handled") };
|
||||
|
||||
function reloadWebCalendar(folderID, refreshOperations) {
|
||||
var url = URLForFolderID(folderID) + "/reload";
|
||||
var cbData = { "folderID": folderID };
|
||||
if (refreshOperations) {
|
||||
cbData["refreshOperations"] = refreshOperations;
|
||||
}
|
||||
triggerAjaxRequest(url, reloadWebCalendarCallback, cbData);
|
||||
}
|
||||
|
||||
function reloadWebCalendarCallback(http) {
|
||||
var cbData = http.callbackData;
|
||||
if (http.status == 200) {
|
||||
var result = http.responseText.evalJSON(true);
|
||||
var requireAuth = false;
|
||||
var success = false;
|
||||
if (result.status) {
|
||||
if (result.status == 401) {
|
||||
requireAuth = true;
|
||||
}
|
||||
else {
|
||||
if (result.status == 200) {
|
||||
success = true;
|
||||
}
|
||||
else {
|
||||
var errorMessage = _("An error occurred while importing calendar.");
|
||||
if (result["error"]) {
|
||||
var message = calendarReloadErrors[result["error"]];
|
||||
errorMessage = (_("An error occurred while loading remote"
|
||||
+ " calendar: %{0}.").formatted(message));
|
||||
}
|
||||
showAlertDialog (errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
var errorMessage = _("An error occurred while importing calendar.");
|
||||
if (result["error"]) {
|
||||
var message = calendarReloadErrors[result["error"]];
|
||||
errorMessage = (_("An error occurred while loading remote"
|
||||
+ " calendar: %{0}.").formatted(message));
|
||||
}
|
||||
showAlertDialog (errorMessage);
|
||||
}
|
||||
|
||||
if (requireAuth) {
|
||||
reauthenticateWebCalendar(cbData["folderID"], cbData);
|
||||
}
|
||||
else {
|
||||
var refreshOperations = cbData["refreshOperations"];
|
||||
if (refreshOperations) {
|
||||
var remaining = refreshOperations["remaining"];
|
||||
var calIdx = remaining.indexOf(cbData["folderID"]);
|
||||
remaining.splice(calIdx, 1);
|
||||
if (remaining.length == 0) {
|
||||
refreshEventsAndTasks();
|
||||
changeCalendarDisplay(null, currentView);
|
||||
}
|
||||
else {
|
||||
var newFolderID = remaining[0];
|
||||
reloadWebCalendar(newFolderID, refreshOperations);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (success) {
|
||||
refreshEventsAndTasks();
|
||||
changeCalendarDisplay(null, currentView);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
showAlertDialog(_("An error occurred while importing calendar."));
|
||||
var refreshOperations = cbData["refreshOperations"];
|
||||
if (refreshOperations) {
|
||||
var remaining = refreshOperations["remaining"];
|
||||
var calIdx = remaining.indexOf(cbData["folderID"]);
|
||||
remaining.splice(calIdx, 1);
|
||||
if (remaining.length > 0) {
|
||||
var newFolderID = remaining[0];
|
||||
reloadWebCalendar(newFolderID, refreshOperations);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function reauthenticateWebCalendar(folderID, refreshCBData) {
|
||||
var remoteURL = null;
|
||||
if (UserSettings['Calendar'] && UserSettings['Calendar']['WebCalendars']) {
|
||||
var webCalendars = UserSettings['Calendar']['WebCalendars'];
|
||||
remoteURL = webCalendars[folderID];
|
||||
}
|
||||
var parts = remoteURL.split("/");
|
||||
var hostname = parts[2];
|
||||
function authenticate(username, password) {
|
||||
disposeDialog();
|
||||
var url = URLForFolderID(folderID) + "/set-credentials";
|
||||
var parameters = ("username=" + encodeURIComponent(username)
|
||||
+ "&password=" + encodeURIComponent(password));
|
||||
triggerAjaxRequest(url, authenticateWebCalendarCallback, refreshCBData, parameters,
|
||||
{ "Content-type": "application/x-www-form-urlencoded" });
|
||||
}
|
||||
showAuthenticationDialog(_("Please identify yourself to \"%{0}\"...")
|
||||
.formatted(hostname),
|
||||
authenticate);
|
||||
}
|
||||
|
||||
function authenticateWebCalendarCallback(http) {
|
||||
var cbData = http.callbackData;
|
||||
var folderID = cbData["folderID"];
|
||||
var refreshOperations = cbData["refreshOperations"];
|
||||
if (isHttpStatus204(http.status)) {
|
||||
reloadWebCalendar(folderID, refreshOperations);
|
||||
}
|
||||
else {
|
||||
if (refreshOperations) {
|
||||
var remaining = refreshOperations["remaining"];
|
||||
var calIdx = remaining.indexOf(folderID);
|
||||
remaining.splice(calIdx, 1);
|
||||
if (remaining.length > 0) {
|
||||
var newFolderID = remaining[0];
|
||||
reloadWebCalendar(newFolderID, refreshOperations);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scrollDayView(scrollEvent) {
|
||||
@@ -2567,29 +2722,51 @@ function onCalendarWebAdd(event) {
|
||||
}
|
||||
|
||||
function onCalendarWebAddConfirm() {
|
||||
disposeDialog();
|
||||
var calendarUrl = this.value;
|
||||
if (calendarUrl) {
|
||||
if (document.addWebCalendarRequest) {
|
||||
document.addWebCalendarRequest.aborted = true;
|
||||
document.addWebCalendarRequest.abort ();
|
||||
}
|
||||
var url = ApplicationBaseURL + "/addWebCalendar?url=" + escape (calendarUrl);
|
||||
document.addWebCalendarRequest =
|
||||
triggerAjaxRequest (url, addWebCalendarCallback);
|
||||
var url = ApplicationBaseURL + "/addWebCalendar"
|
||||
var parameters = "url=" + calendarUrl;
|
||||
triggerAjaxRequest(url, addWebCalendarCallback, calendarUrl, parameters,
|
||||
{ "Content-type": "application/x-www-form-urlencoded" });
|
||||
}
|
||||
disposeDialog();
|
||||
}
|
||||
function addWebCalendarCallback (http) {
|
||||
var data = http.responseText.evalJSON(true);
|
||||
if (data.imported >= 0) {
|
||||
appendCalendar(data.displayname, "/" + data.name);
|
||||
refreshEvents();
|
||||
refreshTasks();
|
||||
changeCalendarDisplay();
|
||||
|
||||
function addWebCalendarCallback(http) {
|
||||
if (http.status == 200) {
|
||||
var data = http.responseText.evalJSON(true);
|
||||
if (!data || data["error"] || !data["name"] || !data["folderID"]) {
|
||||
showAlertDialog (_("An error occurred while importing calendar."));
|
||||
}
|
||||
else {
|
||||
if (UserSettings['Calendar']) {
|
||||
var webCalendars = UserSettings['Calendar']['WebCalendars'];
|
||||
if (!webCalendars) {
|
||||
webCalendars = {};
|
||||
UserSettings['Calendar']['WebCalendars'] = webCalendars;
|
||||
}
|
||||
webCalendars[data["folderID"]] = http.callbackData;
|
||||
}
|
||||
|
||||
appendCalendar(data["name"], data["folderID"]);
|
||||
reloadWebCalendar(data["folderID"]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
showAlertDialog (_("An error occurred while importing calendar."));
|
||||
}
|
||||
|
||||
// if (data.imported) {
|
||||
// appendCalendar(data.displayname, "/" + data.name);
|
||||
// refreshEvents();
|
||||
// refreshTasks();
|
||||
// changeCalendarDisplay();
|
||||
// }
|
||||
// else if (data.status && data.status == 401) {
|
||||
// reauthenticateWebCalendar(data.name, data.url);
|
||||
// }
|
||||
// else {
|
||||
// }
|
||||
}
|
||||
|
||||
function onCalendarExport(event) {
|
||||
|
||||
@@ -1266,11 +1266,18 @@ function accessToSubscribedFolder(serverFolder) {
|
||||
|
||||
var parts = serverFolder.split(":");
|
||||
if (parts.length > 1) {
|
||||
var username = parts[0];
|
||||
var paths = parts[1].split("/");
|
||||
folder = "/" + parts[0].asCSSIdentifier() + "_" + paths[2];
|
||||
if (username == UserLogin) {
|
||||
folder = paths[1];
|
||||
}
|
||||
else {
|
||||
folder = "/" + username.asCSSIdentifier() + "_" + paths[1];
|
||||
}
|
||||
}
|
||||
else
|
||||
else {
|
||||
folder = serverFolder;
|
||||
}
|
||||
|
||||
return folder;
|
||||
}
|
||||
@@ -2095,6 +2102,53 @@ function _showSelectDialog(title, label, options, button, callbackFcn, callbackA
|
||||
dialog.appear({ duration: 0.2 });
|
||||
}
|
||||
|
||||
function showAuthenticationDialog(label, callback) {
|
||||
log("* showAuthenticationDialog");
|
||||
log(backtrace());
|
||||
|
||||
var div = $("bgDialogDiv");
|
||||
if (div && div.visible() && div.getOpacity() > 0) {
|
||||
log("push");
|
||||
dialogsStack.push(_showAuthenticationDialog.bind(this, label, callback));
|
||||
}
|
||||
else {
|
||||
log("show");
|
||||
_showAuthenticationDialog(label, callback);
|
||||
}
|
||||
}
|
||||
|
||||
function _showAuthenticationDialog(label, callback) {
|
||||
var dialog = dialogs[label];
|
||||
if (dialog) {
|
||||
$("bgDialogDiv").show();
|
||||
var inputs = dialog.getElementsByTagName("input");
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
inputs[i].value = "";
|
||||
}
|
||||
}
|
||||
else {
|
||||
var fields = createElement("p", null, ["prompt"]);
|
||||
fields.appendChild(document.createTextNode(_("Username:")));
|
||||
var un_input = createElement("input", null, "textField",
|
||||
{ type: "text", "value": "" });
|
||||
fields.appendChild(un_input);
|
||||
fields.appendChild(document.createTextNode(_("Password:")));
|
||||
var pw_input = createElement("input", null, "textField",
|
||||
{ type: "password", "value": "" });
|
||||
fields.appendChild(pw_input);
|
||||
function callbackWrapper() {
|
||||
callback(un_input.value, pw_input.value);
|
||||
}
|
||||
fields.appendChild(createButton(null, _("OK"), callbackWrapper));
|
||||
fields.appendChild(createButton(null, _("Cancel"), disposeDialog));
|
||||
dialog = createDialog(null, label, null, fields, "none");
|
||||
document.body.appendChild(dialog);
|
||||
dialogs[label] = dialog;
|
||||
}
|
||||
dialog.appear({ duration: 0.2,
|
||||
afterFinish: function () { dialog.down("input").focus(); } });
|
||||
}
|
||||
|
||||
function disposeDialog() {
|
||||
$$("DIV.dialog").each(function(div) {
|
||||
if (div.visible() && div.getOpacity() == 1)
|
||||
|
||||
Reference in New Issue
Block a user