Merge branch 'master' into fix-some-warnings
Conflicts: SoObjects/Appointments/SOGoAppointmentObject.mpull/199/head
commit
a5cc2bb5d5
|
@ -40,6 +40,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "NSDate+ActiveSync.h"
|
||||
#include "NSString+ActiveSync.h"
|
||||
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
|
||||
@implementation NGVCard (ActiveSync)
|
||||
|
||||
//
|
||||
|
@ -227,7 +232,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
// Other, less important fields
|
||||
if ((o = [self birthday]))
|
||||
[s appendFormat: @"<Birthday xmlns=\"Contacts:\">%@</Birthday>", [o activeSyncRepresentationInContext: context]];
|
||||
{
|
||||
NSTimeZone *userTimeZone;
|
||||
userTimeZone = [[[context activeUser] userDefaults] timeZone];
|
||||
|
||||
[s appendFormat: @"<Birthday xmlns=\"Contacts:\">%@</Birthday>",
|
||||
[[o dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: ([userTimeZone secondsFromGMTForDate: o])*-1]
|
||||
activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
if ((o = [self note]))
|
||||
{
|
||||
|
|
|
@ -1081,17 +1081,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
SOGoMailAccounts *accountsFolder;
|
||||
SOGoUserFolder *userFolder;
|
||||
SOGoMailObject *mailObject;
|
||||
NSMutableArray *a;
|
||||
NSArray *partKeys;
|
||||
int p;
|
||||
|
||||
NSRange r1, r2;
|
||||
|
||||
r1 = [realCollectionId rangeOfString: @"/"];
|
||||
r2 = [realCollectionId rangeOfString: @"/" options: 0 range: NSMakeRange(NSMaxRange(r1)+1, [realCollectionId length]-NSMaxRange(r1)-1)];
|
||||
|
||||
folderName = [realCollectionId substringToIndex: r1.location];
|
||||
messageName = [realCollectionId substringWithRange: NSMakeRange(NSMaxRange(r1), r2.location-r1.location-1)];
|
||||
pathToPart = [realCollectionId substringFromIndex: r2.location+1];
|
||||
a = [[realCollectionId componentsSeparatedByString: @"/"] mutableCopy];
|
||||
[a autorelease];
|
||||
pathToPart = [a lastObject];
|
||||
[a removeLastObject];
|
||||
messageName = [a lastObject];
|
||||
[a removeLastObject];
|
||||
folderName = [a componentsJoinedByString: @"/"];
|
||||
|
||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||
|
@ -1288,20 +1289,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
SOGoMailAccounts *accountsFolder;
|
||||
SOGoUserFolder *userFolder;
|
||||
SOGoMailObject *mailObject;
|
||||
NSMutableArray *a;
|
||||
|
||||
if ([fileReference length])
|
||||
{
|
||||
// fetch attachment
|
||||
NSRange r1, r2;
|
||||
NSArray *partKeys;
|
||||
int p;
|
||||
|
||||
r1 = [realCollectionId rangeOfString: @"/"];
|
||||
r2 = [realCollectionId rangeOfString: @"/" options: 0 range: NSMakeRange(NSMaxRange(r1)+1, [realCollectionId length]-NSMaxRange(r1)-1)];
|
||||
|
||||
folderName = [realCollectionId substringToIndex: r1.location];
|
||||
messageName = [realCollectionId substringWithRange: NSMakeRange(NSMaxRange(r1), r2.location-r1.location-1)];
|
||||
pathToPart = [realCollectionId substringFromIndex: r2.location+1];
|
||||
a = [[realCollectionId componentsSeparatedByString: @"/"] mutableCopy];
|
||||
[a autorelease];
|
||||
pathToPart = [a lastObject];
|
||||
[a removeLastObject];
|
||||
messageName = [a lastObject];
|
||||
[a removeLastObject];
|
||||
folderName = [a componentsJoinedByString: @"/"];
|
||||
|
||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||
|
@ -1317,13 +1319,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
currentBodyPart = [mailObject lookupImap4BodyPartKey: [partKeys objectAtIndex:0] inContext: context];
|
||||
for (p = 1; p < [partKeys count]; p++)
|
||||
{
|
||||
currentBodyPart = [currentBodyPart lookupImap4BodyPartKey: [partKeys objectAtIndex:p] inContext: context];
|
||||
}
|
||||
{
|
||||
currentBodyPart = [currentBodyPart lookupImap4BodyPartKey: [partKeys objectAtIndex:p] inContext: context];
|
||||
}
|
||||
|
||||
[s appendString: @"<Fetch>"];
|
||||
[s appendString: @"<Status>1</Status>"];
|
||||
[s appendFormat: @"<FileReference xmlns=\"AirSyncBase:\">%@</FileReference>", fileReference];
|
||||
[s appendFormat: @"<FileReference xmlns=\"AirSyncBase:\">mail/%@/%@/%@</FileReference>", [folderName stringByEscapingURL], messageName, pathToPart];
|
||||
[s appendString: @"<Properties>"];
|
||||
|
||||
[s appendFormat: @"<ContentType xmlns=\"AirSyncBase:\">%@/%@</ContentType>", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]];
|
||||
|
@ -1946,6 +1948,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
}
|
||||
else
|
||||
{
|
||||
if (heartbeatInterval < internalInterval)
|
||||
heartbeatInterval = internalInterval;
|
||||
|
||||
status = 1;
|
||||
}
|
||||
|
||||
|
|
12
NEWS
12
NEWS
|
@ -1,6 +1,11 @@
|
|||
3.0.2 (2016-02-DD)
|
||||
------------------
|
||||
|
||||
New features
|
||||
- [web] show all/only this calendar
|
||||
- [web] convert a message to an appointment or a task (#1722)
|
||||
- [web] customizable base font size for HTML messages
|
||||
|
||||
Enhancements
|
||||
- [web] added Junk handling feature from v2
|
||||
- [web] updated Material Icons font to version 2.1.3
|
||||
|
@ -8,6 +13,8 @@ Enhancements
|
|||
- [web] mail filters are now sortable
|
||||
- [web] now supports RFC6154 and NoInferiors IMAP flag
|
||||
- [web] improved confirm dialogs for deletions
|
||||
- [web] allow resources to prevent invitations (#3410)
|
||||
- [web] warn when double-booking attendees and offer force save option
|
||||
|
||||
Bug fixes
|
||||
- [web] handle birthday dates before 1970
|
||||
|
@ -16,6 +23,11 @@ Bug fixes
|
|||
- [web] fixed virtual repeater when moving up in messages list
|
||||
- [web] really delete mailboxes being deleted from the Trash folder (#595, #1189, #641)
|
||||
- [web] fixed address autocompletion of mail editor affecting cards list of active addressbook
|
||||
- [web] fixed batched delete of components (#3516)
|
||||
- [web] fixed mail draft autosave in preferences (#3519)
|
||||
- [web] fixed password change (#3496)
|
||||
- [eas] allow EAS attachments get on 2nd-level mailboxes (#3505)
|
||||
- [eas] fix EAS bday shift (#3518)
|
||||
|
||||
3.0.1 (2016-02-05)
|
||||
------------------
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (C) 2007-2014 Inverse inc.
|
||||
Copyright (C) 2007-2016 Inverse inc.
|
||||
|
||||
This file is part of SOGo
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/*
|
||||
Copyright (C) 2007-2015 Inverse inc.
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
Copyright (C) 2007-2016 Inverse inc.
|
||||
|
||||
This file is part of SOGo
|
||||
|
||||
|
@ -46,6 +45,7 @@
|
|||
#import <SOGo/NSDictionary+Utilities.h>
|
||||
#import <SOGo/NSObject+DAV.h>
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <SOGo/SOGoDateFormatter.h>
|
||||
#import <SOGo/SOGoGroup.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserSettings.h>
|
||||
|
@ -402,8 +402,8 @@
|
|||
}
|
||||
|
||||
// This method scans the list of attendees.
|
||||
- (NSException *) _handleAttendeeAvailability: (NSArray *) theAttendees
|
||||
forEvent: (iCalEvent *) theEvent
|
||||
- (NSException *) _handleAttendeesAvailability: (NSArray *) theAttendees
|
||||
forEvent: (iCalEvent *) theEvent
|
||||
{
|
||||
iCalPerson *currentAttendee;
|
||||
SOGoUser *user;
|
||||
|
@ -420,7 +420,7 @@
|
|||
|
||||
i = count = 0;
|
||||
|
||||
// Build list of the attendees uids without ressources
|
||||
// Build list of the attendees uids
|
||||
unavailableAttendees = [[NSMutableArray alloc] init];
|
||||
enumerator = [theAttendees objectEnumerator];
|
||||
ownerUID = [[[self context] activeUser] login];
|
||||
|
@ -436,7 +436,7 @@
|
|||
moduleSettings = [us objectForKey:@"Calendar"];
|
||||
|
||||
// Check if the user prevented their account from beeing invited to events
|
||||
if (![user isResource] && [[moduleSettings objectForKey:@"PreventInvitations"] boolValue])
|
||||
if ([[moduleSettings objectForKey:@"PreventInvitations"] boolValue])
|
||||
{
|
||||
// Check if the user have a whiteList
|
||||
whiteList = [moduleSettings objectForKey:@"PreventInvitationsWhitelist"];
|
||||
|
@ -483,20 +483,20 @@
|
|||
//
|
||||
// This methods scans the list of attendees. If they are
|
||||
// considered as resource, it checks for conflicting
|
||||
// dates for the event.
|
||||
// dates for the event and potentially auto-accept/decline
|
||||
// the invitation.
|
||||
//
|
||||
// For normal attendees, it'll return an exception with
|
||||
// conflicting dates, unless we force the save.//
|
||||
// We check for between startDate + 1 second and
|
||||
// endDate - 1 second
|
||||
//
|
||||
//
|
||||
// It also CHANGES the participation status of resources
|
||||
// depending on constraints defined on them.
|
||||
//
|
||||
// Note that it doesn't matter if it changes the participation
|
||||
// status since in case of an error, nothing will get saved.
|
||||
//
|
||||
- (NSException *) _handleResourcesConflicts: (NSArray *) theAttendees
|
||||
- (NSException *) _handleAttendeesConflicts: (NSArray *) theAttendees
|
||||
forEvent: (iCalEvent *) theEvent
|
||||
force: (BOOL) forceSave
|
||||
{
|
||||
iCalPerson *currentAttendee;
|
||||
NSMutableArray *attendees;
|
||||
|
@ -527,110 +527,115 @@
|
|||
enumerator = [attendees objectEnumerator];
|
||||
while ((currentUID = [enumerator nextObject]))
|
||||
{
|
||||
NSCalendarDate *start, *end, *rangeStartDate, *rangeEndDate;
|
||||
SOGoAppointmentFolder *folder;
|
||||
NGCalendarDateRange *range;
|
||||
NSMutableArray *fbInfo;
|
||||
NSArray *allOccurences;
|
||||
|
||||
BOOL must_delete;
|
||||
int i, j, delta;
|
||||
|
||||
user = [SOGoUser userWithLogin: currentUID];
|
||||
|
||||
if ([user isResource])
|
||||
|
||||
// We get the start/end date for our conflict range. If the event to be added is recurring, we
|
||||
// check for at least a year to start with.
|
||||
start = [[theEvent startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: 1];
|
||||
end = [[theEvent endDate] dateByAddingYears: ([theEvent isRecurrent] ? 1 : 0) months: 0 days: 0 hours: 0 minutes: 0 seconds: -1];
|
||||
|
||||
folder = [user personalCalendarFolderInContext: context];
|
||||
|
||||
// Deny access to the resource if the ACLs don't allow the user
|
||||
if ([user isResource] && ![folder aclSQLListingFilter])
|
||||
{
|
||||
NSCalendarDate *start, *end, *rangeStartDate, *rangeEndDate;
|
||||
SOGoAppointmentFolder *folder;
|
||||
NGCalendarDateRange *range;
|
||||
NSMutableArray *fbInfo;
|
||||
NSArray *allOccurences;
|
||||
|
||||
BOOL must_delete;
|
||||
int i, j, delta;
|
||||
|
||||
// We get the start/end date for our conflict range. If the event to be added is recurring, we
|
||||
// check for at least a year to start with.
|
||||
start = [[theEvent startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: 1];
|
||||
end = [[theEvent endDate] dateByAddingYears: ([theEvent isRecurrent] ? 1 : 0) months: 0 days: 0 hours: 0 minutes: 0 seconds: -1];
|
||||
|
||||
folder = [user personalCalendarFolderInContext: context];
|
||||
|
||||
// Deny access to the resource if the ACLs don't allow the user
|
||||
if (![folder aclSQLListingFilter])
|
||||
{
|
||||
NSDictionary *values;
|
||||
NSString *reason;
|
||||
NSDictionary *values;
|
||||
NSString *reason;
|
||||
|
||||
values = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[user cn], @"Cn",
|
||||
[user systemEmail], @"SystemEmail"];
|
||||
reason = [values keysWithFormat: [self labelForKey: @"Cannot access resource: \"%{Cn} %{SystemEmail}\""]];
|
||||
return [NSException exceptionWithHTTPStatus:403 reason: reason];
|
||||
}
|
||||
values = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[user cn], @"Cn",
|
||||
[user systemEmail], @"SystemEmail"];
|
||||
reason = [values keysWithFormat: [self labelForKey: @"Cannot access resource: \"%{Cn} %{SystemEmail}\""]];
|
||||
return [NSException exceptionWithHTTPStatus:403 reason: reason];
|
||||
}
|
||||
|
||||
fbInfo = [NSMutableArray arrayWithArray: [folder fetchFreeBusyInfosFrom: start
|
||||
fbInfo = [NSMutableArray arrayWithArray: [folder fetchFreeBusyInfosFrom: start
|
||||
to: end]];
|
||||
|
||||
// We first remove any occurences in the freebusy that corresponds to the
|
||||
// current event. We do this to avoid raising a conflict if we move a 1 hour
|
||||
// meeting from 12:00-13:00 to 12:15-13:15. We would overlap on ourself otherwise.
|
||||
//
|
||||
// We must also check here for repetitive events that don't overlap our event.
|
||||
// We remove all events that don't overlap. The events here are already
|
||||
// decomposed.
|
||||
//
|
||||
if ([theEvent isRecurrent])
|
||||
allOccurences = [theEvent recurrenceRangesWithinCalendarDateRange: [NGCalendarDateRange calendarDateRangeWithStartDate: start
|
||||
endDate: end]
|
||||
firstInstanceCalendarDateRange: [NGCalendarDateRange calendarDateRangeWithStartDate: [theEvent startDate]
|
||||
endDate: [theEvent endDate]]];
|
||||
else
|
||||
allOccurences = nil;
|
||||
|
||||
for (i = [fbInfo count]-1; i >= 0; i--)
|
||||
// We first remove any occurences in the freebusy that corresponds to the
|
||||
// current event. We do this to avoid raising a conflict if we move a 1 hour
|
||||
// meeting from 12:00-13:00 to 12:15-13:15. We would overlap on ourself otherwise.
|
||||
//
|
||||
// We must also check here for repetitive events that don't overlap our event.
|
||||
// We remove all events that don't overlap. The events here are already
|
||||
// decomposed.
|
||||
//
|
||||
if ([theEvent isRecurrent])
|
||||
allOccurences = [theEvent recurrenceRangesWithinCalendarDateRange: [NGCalendarDateRange calendarDateRangeWithStartDate: start
|
||||
endDate: end]
|
||||
firstInstanceCalendarDateRange: [NGCalendarDateRange calendarDateRangeWithStartDate: [theEvent startDate]
|
||||
endDate: [theEvent endDate]]];
|
||||
else
|
||||
allOccurences = nil;
|
||||
|
||||
for (i = [fbInfo count]-1; i >= 0; i--)
|
||||
{
|
||||
// We MUST use the -uniqueChildWithTag method here because the event has been flattened, so its timezone has been
|
||||
// modified in SOGoAppointmentFolder: -fixupCycleRecord: ....
|
||||
rangeStartDate = [[fbInfo objectAtIndex: i] objectForKey: @"startDate"];
|
||||
delta = [[rangeStartDate timeZoneDetail] timeZoneSecondsFromGMT] - [[[(iCalDateTime *)[theEvent uniqueChildWithTag: @"dtstart"] timeZone] periodForDate: [theEvent startDate]] secondsOffsetFromGMT];
|
||||
rangeStartDate = [rangeStartDate dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: delta];
|
||||
|
||||
rangeEndDate = [[fbInfo objectAtIndex: i] objectForKey: @"endDate"];
|
||||
delta = [[rangeEndDate timeZoneDetail] timeZoneSecondsFromGMT] - [[[(iCalDateTime *)[theEvent uniqueChildWithTag: @"dtend"] timeZone] periodForDate: [theEvent endDate]] secondsOffsetFromGMT];
|
||||
rangeEndDate = [rangeEndDate dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: delta];
|
||||
|
||||
range = [NGCalendarDateRange calendarDateRangeWithStartDate: rangeStartDate
|
||||
endDate: rangeEndDate];
|
||||
|
||||
// We remove the freebusy entries corresponding to the actual event being modified
|
||||
if ([[[fbInfo objectAtIndex: i] objectForKey: @"c_uid"] compare: [theEvent uid]] == NSOrderedSame)
|
||||
{
|
||||
// We MUST use the -uniqueChildWithTag method here because the event has been flattened, so its timezone has been
|
||||
// modified in SOGoAppointmentFolder: -fixupCycleRecord: ....
|
||||
rangeStartDate = [[fbInfo objectAtIndex: i] objectForKey: @"startDate"];
|
||||
delta = [[rangeStartDate timeZoneDetail] timeZoneSecondsFromGMT] - [[[(iCalDateTime *)[theEvent uniqueChildWithTag: @"dtstart"] timeZone] periodForDate: [theEvent startDate]] secondsOffsetFromGMT];
|
||||
rangeStartDate = [rangeStartDate dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: delta];
|
||||
|
||||
rangeEndDate = [[fbInfo objectAtIndex: i] objectForKey: @"endDate"];
|
||||
delta = [[rangeEndDate timeZoneDetail] timeZoneSecondsFromGMT] - [[[(iCalDateTime *)[theEvent uniqueChildWithTag: @"dtend"] timeZone] periodForDate: [theEvent endDate]] secondsOffsetFromGMT];
|
||||
rangeEndDate = [rangeEndDate dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: delta];
|
||||
|
||||
range = [NGCalendarDateRange calendarDateRangeWithStartDate: rangeStartDate
|
||||
endDate: rangeEndDate];
|
||||
[fbInfo removeObjectAtIndex: i];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([[[fbInfo objectAtIndex: i] objectForKey: @"c_uid"] compare: [theEvent uid]] == NSOrderedSame)
|
||||
// No need to check if the event isn't recurrent here as it's handled correctly
|
||||
// when we compute the "end" date.
|
||||
if ([allOccurences count])
|
||||
{
|
||||
must_delete = YES;
|
||||
|
||||
for (j = 0; j < [allOccurences count]; j++)
|
||||
{
|
||||
[fbInfo removeObjectAtIndex: i];
|
||||
continue;
|
||||
}
|
||||
|
||||
// No need to check if the event isn't recurrent here as it's handled correctly
|
||||
// when we compute the "end" date.
|
||||
if ([allOccurences count])
|
||||
{
|
||||
must_delete = YES;
|
||||
|
||||
for (j = 0; j < [allOccurences count]; j++)
|
||||
if ([range doesIntersectWithDateRange: [allOccurences objectAtIndex: j]])
|
||||
{
|
||||
if ([range doesIntersectWithDateRange: [allOccurences objectAtIndex: j]])
|
||||
{
|
||||
must_delete = NO;
|
||||
break;
|
||||
}
|
||||
must_delete = NO;
|
||||
break;
|
||||
}
|
||||
|
||||
if (must_delete)
|
||||
[fbInfo removeObjectAtIndex: i];
|
||||
}
|
||||
|
||||
if (must_delete)
|
||||
[fbInfo removeObjectAtIndex: i];
|
||||
}
|
||||
}
|
||||
|
||||
// Find the attendee associated to the current UID
|
||||
for (i = 0; i < [theAttendees count]; i++)
|
||||
{
|
||||
currentAttendee = [theAttendees objectAtIndex: i];
|
||||
if ([[currentAttendee uidInContext: context] isEqualToString: currentUID])
|
||||
break;
|
||||
else
|
||||
currentAttendee = nil;
|
||||
}
|
||||
|
||||
if ([fbInfo count])
|
||||
{
|
||||
SOGoDateFormatter *formatter;
|
||||
|
||||
formatter = [[context activeUser] dateFormatterInContext: context];
|
||||
|
||||
// Find the attendee associated to the current UID
|
||||
for (i = 0; i < [theAttendees count]; i++)
|
||||
{
|
||||
currentAttendee = [theAttendees objectAtIndex: i];
|
||||
if ([[currentAttendee uidInContext: context] isEqualToString: currentUID])
|
||||
break;
|
||||
else
|
||||
currentAttendee = nil;
|
||||
}
|
||||
|
||||
if ([fbInfo count])
|
||||
if ([user isResource])
|
||||
{
|
||||
// If we always force the auto-accept if numberOfSimultaneousBookings <= 0 (ie., no limit
|
||||
// is imposed) or if numberOfSimultaneousBookings is greater than the number of
|
||||
|
@ -650,35 +655,63 @@
|
|||
NSDictionary *values;
|
||||
NSString *reason;
|
||||
iCalEvent *event;
|
||||
|
||||
|
||||
calendar = [iCalCalendar parseSingleFromSource: [[fbInfo objectAtIndex: 0] objectForKey: @"c_content"]];
|
||||
event = [[calendar events] lastObject];
|
||||
|
||||
|
||||
values = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSString stringWithFormat: @"%d", [user numberOfSimultaneousBookings]], @"NumberOfSimultaneousBookings",
|
||||
[user cn], @"Cn",
|
||||
[user systemEmail], @"SystemEmail",
|
||||
([event summary] ? [event summary] : @""), @"EventTitle",
|
||||
[[fbInfo objectAtIndex: 0] objectForKey: @"startDate"], @"StartDate",
|
||||
nil];
|
||||
|
||||
[NSString stringWithFormat: @"%d", [user numberOfSimultaneousBookings]], @"NumberOfSimultaneousBookings",
|
||||
[user cn], @"Cn",
|
||||
[user systemEmail], @"SystemEmail",
|
||||
([event summary] ? [event summary] : @""), @"EventTitle",
|
||||
[formatter formattedDateAndTime: [[fbInfo objectAtIndex: 0] objectForKey: @"startDate"]], @"StartDate",
|
||||
nil];
|
||||
|
||||
reason = [values keysWithFormat: [self labelForKey: @"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}."]];
|
||||
|
||||
|
||||
return [NSException exceptionWithHTTPStatus: 403
|
||||
reason: reason];
|
||||
}
|
||||
}
|
||||
else if (currentAttendee)
|
||||
//
|
||||
// We are dealing with a normal attendee. Lets check if we have conflicts, unless
|
||||
// we are being asked to force the save anyway
|
||||
//
|
||||
else if (!forceSave)
|
||||
{
|
||||
// No conflict, we auto-accept. We do this for resources automatically if no
|
||||
// double-booking is observed. If it's not the desired behavior, just don't
|
||||
// set the resource as one!
|
||||
[[currentAttendee attributes] removeObjectForKey: @"RSVP"];
|
||||
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
|
||||
NSMutableDictionary *info;
|
||||
NSMutableArray *conflicts;
|
||||
id o;
|
||||
|
||||
info = [NSMutableDictionary dictionary];
|
||||
conflicts = [NSMutableArray array];
|
||||
|
||||
[info setObject: [currentAttendee cn] forKey: @"attendee_name"];
|
||||
[info setObject: [currentAttendee rfc822Email] forKey: @"attendee_email"];
|
||||
|
||||
for (i = 0; i < [fbInfo count]; i++)
|
||||
{
|
||||
o = [fbInfo objectAtIndex: i];
|
||||
[conflicts addObject: [NSDictionary dictionaryWithObjectsAndKeys: [formatter formattedDateAndTime: [o objectForKey: @"startDate"]], @"startDate",
|
||||
[formatter formattedDateAndTime: [o objectForKey: @"endDate"]], @"endDate", nil]];
|
||||
}
|
||||
|
||||
[info setObject: conflicts forKey: @"conflicts"];
|
||||
|
||||
return [NSException exceptionWithHTTPStatus: 403
|
||||
reason: [info jsonRepresentation]];
|
||||
}
|
||||
} // if ([fbInfo count]) ...
|
||||
else if (currentAttendee && [user isResource])
|
||||
{
|
||||
// No conflict, we auto-accept. We do this for resources automatically if no
|
||||
// double-booking is observed. If it's not the desired behavior, just don't
|
||||
// set the resource as one!
|
||||
[[currentAttendee attributes] removeObjectForKey: @"RSVP"];
|
||||
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
|
||||
}
|
||||
}
|
||||
|
||||
} // if ([user isResource]) ...
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
@ -687,6 +720,7 @@
|
|||
//
|
||||
- (NSException *) _handleAddedUsers: (NSArray *) attendees
|
||||
fromEvent: (iCalEvent *) newEvent
|
||||
force: (BOOL) forceSave
|
||||
{
|
||||
iCalPerson *currentAttendee;
|
||||
NSEnumerator *enumerator;
|
||||
|
@ -694,9 +728,9 @@
|
|||
NSException *e;
|
||||
|
||||
// We check for conflicts
|
||||
if ((e = [self _handleResourcesConflicts: attendees forEvent: newEvent]))
|
||||
if ((e = [self _handleAttendeesConflicts: attendees forEvent: newEvent force: forceSave]))
|
||||
return e;
|
||||
if ((e = [self _handleAttendeeAvailability: attendees forEvent: newEvent]))
|
||||
if ((e = [self _handleAttendeesAvailability: attendees forEvent: newEvent]))
|
||||
return e;
|
||||
|
||||
enumerator = [attendees objectEnumerator];
|
||||
|
@ -752,6 +786,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
|||
//
|
||||
- (NSException *) _handleUpdatedEvent: (iCalEvent *) newEvent
|
||||
fromOldEvent: (iCalEvent *) oldEvent
|
||||
force: (BOOL) forceSave
|
||||
{
|
||||
NSArray *addedAttendees, *deletedAttendees, *updatedAttendees;
|
||||
iCalEventChanges *changes;
|
||||
|
@ -791,9 +826,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
|||
withType: @"calendar:cancellation"];
|
||||
}
|
||||
|
||||
if ((ex = [self _handleResourcesConflicts: [newEvent attendees] forEvent: newEvent]))
|
||||
if ((ex = [self _handleAttendeesConflicts: [newEvent attendees] forEvent: newEvent force: forceSave]))
|
||||
return ex;
|
||||
if ((ex = [self _handleAttendeeAvailability: [newEvent attendees] forEvent: newEvent]))
|
||||
if ((ex = [self _handleAttendeesAvailability: [newEvent attendees] forEvent: newEvent]))
|
||||
return ex;
|
||||
|
||||
addedAttendees = [changes insertedAttendees];
|
||||
|
@ -842,7 +877,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
|||
if ([addedAttendees count])
|
||||
{
|
||||
// Send an invitation to new attendees
|
||||
if ((ex = [self _handleAddedUsers: addedAttendees fromEvent: newEvent]))
|
||||
if ((ex = [self _handleAddedUsers: addedAttendees fromEvent: newEvent force: forceSave]))
|
||||
return ex;
|
||||
|
||||
[self sendEMailUsingTemplateNamed: @"Invitation"
|
||||
|
@ -887,6 +922,12 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
|||
//
|
||||
//
|
||||
- (NSException *) saveComponent: (iCalEvent *) newEvent
|
||||
{
|
||||
return [self saveComponent: newEvent force: NO];
|
||||
}
|
||||
|
||||
- (NSException *) saveComponent: (iCalEvent *) newEvent
|
||||
force: (BOOL) forceSave
|
||||
{
|
||||
iCalEvent *oldEvent, *oldMasterEvent;
|
||||
NSCalendarDate *recurrenceId;
|
||||
|
@ -911,7 +952,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
|||
|
||||
// We catch conflicts and abort the save process immediately
|
||||
// in case of one with resources
|
||||
if ((ex = [self _handleAddedUsers: attendees fromEvent: newEvent]))
|
||||
if ((ex = [self _handleAddedUsers: attendees fromEvent: newEvent force: forceSave]))
|
||||
return ex;
|
||||
|
||||
if ([attendees count])
|
||||
|
@ -954,7 +995,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
|||
if (!hasOrganizer || [oldMasterEvent userIsOrganizer: ownerUser])
|
||||
// The owner is the organizer of the event; handle the modifications. We aslo
|
||||
// catch conflicts just like when the events are created
|
||||
if ((ex = [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent]))
|
||||
if ((ex = [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent force: forceSave]))
|
||||
return ex;
|
||||
}
|
||||
|
||||
|
@ -1591,7 +1632,6 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
|||
currentUser = [context activeUser];
|
||||
attendees = [occurence attendeesWithoutUser: currentUser];
|
||||
|
||||
#warning Make sure this is correct ..
|
||||
if (![attendees count] && event != occurence)
|
||||
attendees = [event attendeesWithoutUser: currentUser];
|
||||
|
||||
|
@ -1994,7 +2034,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
|||
attendees = [event attendeesWithoutUser: ownerUser];
|
||||
if ([attendees count])
|
||||
{
|
||||
if ((ex = [self _handleAddedUsers: attendees fromEvent: event]))
|
||||
if ((ex = [self _handleAddedUsers: attendees fromEvent: event force: YES]))
|
||||
return ex;
|
||||
else
|
||||
{
|
||||
|
@ -2146,7 +2186,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
|||
if (!newEvent && oldEvent)
|
||||
[self prepareDeleteOccurence: oldEvent];
|
||||
// The master event was changed, A RECCURENCE-ID was added or modified
|
||||
else if ((ex = [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent]))
|
||||
else if ((ex = [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent force: YES]))
|
||||
return ex;
|
||||
}
|
||||
//
|
||||
|
|
|
@ -61,7 +61,8 @@
|
|||
- (void) updateComponent: (iCalRepeatableEntityObject *) newObject;
|
||||
- (NSException *) saveCalendar: (iCalCalendar *) newCalendar;
|
||||
- (NSException *) saveComponent: (iCalRepeatableEntityObject *) newObject;
|
||||
|
||||
- (NSException *) saveComponent: (iCalRepeatableEntityObject *) newEvent
|
||||
force: (BOOL) forceSave;
|
||||
/* mail notifications */
|
||||
- (void) sendEMailUsingTemplateNamed: (NSString *) pageName
|
||||
forObject: (iCalRepeatableEntityObject *) object
|
||||
|
|
|
@ -675,6 +675,12 @@
|
|||
return [self saveCalendar: [newObject parent]];
|
||||
}
|
||||
|
||||
- (NSException *) saveComponent: (iCalRepeatableEntityObject *) newEvent
|
||||
force: (BOOL) forceSave
|
||||
{
|
||||
return [self saveComponent: newEvent];
|
||||
}
|
||||
|
||||
/* raw saving */
|
||||
|
||||
/* EMail Notifications */
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
/*
|
||||
|
||||
Copyright (C) 2006-2014 Inverse inc.
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
Copyright (C) 2006-2014-2016 Inverse inc.
|
||||
|
||||
This file is part of SOGo.
|
||||
|
||||
|
@ -26,24 +24,6 @@
|
|||
|
||||
#import "SOGoCalendarComponent.h"
|
||||
|
||||
/*
|
||||
SOGoTaskObject
|
||||
|
||||
Represents a single task. This SOPE controller object manages all the
|
||||
attendee storages (that is, it might store into multiple folders for meeting
|
||||
tasks!).
|
||||
|
||||
Note: SOGoTaskObject do not need to exist yet. They can also be "new"
|
||||
tasks with an externally generated unique key.
|
||||
*/
|
||||
|
||||
@class NSArray;
|
||||
@class NSException;
|
||||
@class NSString;
|
||||
|
||||
@class iCalToDo;
|
||||
@class iCalCalendar;
|
||||
|
||||
@interface SOGoTaskObject : SOGoCalendarComponent
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/*
|
||||
Copyright (C) 2006-2014 Inverse inc.
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
Copyright (C) 2006-2014-2016 Inverse inc.
|
||||
|
||||
This file is part of SOGo.
|
||||
|
||||
|
|
|
@ -164,8 +164,7 @@
|
|||
NSMutableArray *keys;
|
||||
NSArray *acceptedTypes;
|
||||
|
||||
acceptedTypes
|
||||
= [NSArray arrayWithObjects: @"text/plain", @"text/html", nil];
|
||||
acceptedTypes = [NSArray arrayWithObjects: @"text/plain", @"text/html", nil];
|
||||
keys = [NSMutableArray array];
|
||||
[self addRequiredKeysOfStructure: [self bodyStructure]
|
||||
path: @"" toArray: keys acceptedTypes: acceptedTypes
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
/*
|
||||
Copyright (C) 2004 SKYRIX Software AG
|
||||
Copyright (C) 2005-2016 Inverse inc.
|
||||
|
||||
This file is part of OpenGroupware.org.
|
||||
This file is part of SOGo.
|
||||
|
||||
OGo is free software; you can redistribute it and/or modify it under
|
||||
SOGo is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
SOGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
@ -48,21 +48,6 @@
|
|||
|
||||
- (NSString *) stringForObjectValue: (id) date;
|
||||
|
||||
// - (void) setFullWeekdayNameAndDetails;
|
||||
|
||||
// - (NSString *) date: (NSCalendarDate *) date
|
||||
// withFormat: (unsigned int) format;
|
||||
// - (NSString *) date: (NSCalendarDate *) date
|
||||
// withNSFormat: (NSNumber *) format;
|
||||
|
||||
|
||||
// - (NSString *) shortDayOfWeek: (int)_day;
|
||||
// - (NSString *) fullDayOfWeek: (int)_day;
|
||||
// - (NSString *) shortMonthOfYear: (int)_month;
|
||||
// - (NSString *) fullMonthOfYear: (int)_month;
|
||||
|
||||
// - (NSString *) fullWeekdayNameAndDetailsForDate: (NSCalendarDate *)_date;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __SOGoDateFormatter_H_ */
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
/*
|
||||
Copyright (C) 2004 SKYRIX Software AG
|
||||
Copyright (C) 2005-2016 Inverse inc.
|
||||
|
||||
This file is part of OpenGroupware.org.
|
||||
This file is part of SOGo.
|
||||
|
||||
OGo is free software; you can redistribute it and/or modify it under
|
||||
SOGo is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
SOGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
@ -21,7 +21,7 @@
|
|||
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSUserDefaults.h> /* for NSXXXFormatString, ... */
|
||||
#import <Foundation/NSUserDefaults.h>
|
||||
|
||||
#import "SOGoDateFormatter.h"
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
SOGoTrashFolderName = "Trash";
|
||||
SOGoJunkFolderName = "Junk";
|
||||
SOGoMailComposeMessageType = "html";
|
||||
SOGoMailComposeFontSize = 0;
|
||||
SOGoMailDisplayRemoteInlineImages = "never";
|
||||
|
||||
SOGoMailAutoSave = "5";
|
||||
|
|
|
@ -132,6 +132,9 @@ extern NSString *SOGoWeekStartFirstFullWeek;
|
|||
- (void) setMailComposeMessageType: (NSString *) newValue;
|
||||
- (NSString *) mailComposeMessageType;
|
||||
|
||||
- (void) setMailComposeFontSize: (NSString *) newValue;
|
||||
- (NSString *) mailComposeFontSize;
|
||||
|
||||
- (void) setMailDisplayRemoteInlineImages: (NSString *) newValue;
|
||||
- (NSString *) mailDisplayRemoteInlineImages;
|
||||
|
||||
|
|
|
@ -534,6 +534,16 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
|
|||
return [self stringForKey: @"SOGoMailComposeMessageType"];
|
||||
}
|
||||
|
||||
- (void) setMailComposeFontSize: (NSString *) newValue
|
||||
{
|
||||
[self setObject: newValue forKey: @"SOGoMailComposeFontSize"];
|
||||
}
|
||||
|
||||
- (NSString *) mailComposeFontSize
|
||||
{
|
||||
return [self stringForKey: @"SOGoMailComposeFontSize"];
|
||||
}
|
||||
|
||||
- (void) setMailDisplayRemoteInlineImages: (NSString *) newValue
|
||||
{
|
||||
[self setObject: newValue forKey: @"SOGoMailDisplayRemoteInlineImages"];
|
||||
|
|
|
@ -187,6 +187,13 @@
|
|||
"Save As..." = "Save As...";
|
||||
"Print Preview" = "Print Preview";
|
||||
"View Message Source" = "View Message Source";
|
||||
|
||||
/* Message view "more" menu: create an event from message */
|
||||
"Convert To Event" = "Convert To Event";
|
||||
|
||||
/* Message view "more" menu: create a task from message */
|
||||
"Convert To Task" = "Convert To Task";
|
||||
|
||||
"Print..." = "Print...";
|
||||
"Delete Message" = "Delete Message";
|
||||
"Delete Selected Messages" = "Delete Selected Messages";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* UIxMailActions.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2014 Inverse inc.
|
||||
* Copyright (C) 2007-2016 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -24,10 +24,13 @@
|
|||
#import <NGObjWeb/WORequest.h>
|
||||
#import <NGObjWeb/NSException+HTTP.h>
|
||||
|
||||
#import <SoObjects/Mailer/NSString+Mail.h>
|
||||
#import <SoObjects/Mailer/SOGoDraftObject.h>
|
||||
#import <SoObjects/Mailer/SOGoDraftsFolder.h>
|
||||
#import <SoObjects/Mailer/SOGoMailAccount.h>
|
||||
#import <SoObjects/Mailer/SOGoMailObject.h>
|
||||
#import <SoObjects/Mailer/SOGoMailObject+Draft.h>
|
||||
#import <SoObjects/SOGo/NSArray+Utilities.h>
|
||||
#import <SoObjects/SOGo/NSDictionary+Utilities.h>
|
||||
#import <SoObjects/SOGo/NSString+Utilities.h>
|
||||
#import <SoObjects/SOGo/SOGoUser.h>
|
||||
|
@ -116,6 +119,61 @@
|
|||
andString: [data jsonRepresentation]];
|
||||
}
|
||||
|
||||
- (WOResponse *) viewPlainAction
|
||||
{
|
||||
BOOL htmlContent;
|
||||
NSArray *acceptedTypes, *types;
|
||||
NSDictionary *parts;
|
||||
NSMutableArray *keys;
|
||||
NSMutableDictionary *data;
|
||||
NSString *rawPart, *contentKey, *subject, *content;
|
||||
NSUInteger index;
|
||||
SOGoMailObject *co;
|
||||
|
||||
co = [self clientObject];
|
||||
subject = [co decodedSubject];
|
||||
htmlContent = NO;
|
||||
data = [NSMutableDictionary dictionary];
|
||||
|
||||
if (subject)
|
||||
[data setObject: subject
|
||||
forKey: @"subject"];
|
||||
|
||||
// Fetch the text parts of the message body structure
|
||||
acceptedTypes = [NSArray arrayWithObjects: @"text/plain", @"text/html", nil];
|
||||
keys = [NSMutableArray array];
|
||||
[co addRequiredKeysOfStructure: [co bodyStructure]
|
||||
path: @"" toArray: keys acceptedTypes: acceptedTypes
|
||||
withPeek: NO];
|
||||
|
||||
// Use plain part if available, otherwise use the HTML part
|
||||
types = [keys objectsForKey: @"mimeType" notFoundMarker: @""];
|
||||
index = [types indexOfObject: @"text/plain"];
|
||||
if (index == NSNotFound)
|
||||
{
|
||||
index = [types indexOfObject: @"text/html"];
|
||||
htmlContent = YES;
|
||||
}
|
||||
|
||||
// Fetch part and convert HTML if necessary
|
||||
contentKey = [keys objectAtIndex: index];
|
||||
parts = [co fetchPlainTextStrings: [NSArray arrayWithObject: contentKey]];
|
||||
if ([parts count] > 0)
|
||||
{
|
||||
rawPart = [[parts allValues] objectAtIndex: 0];
|
||||
if (htmlContent)
|
||||
content = [rawPart htmlToText];
|
||||
else
|
||||
content = rawPart;
|
||||
if (content)
|
||||
[data setObject: [content stringByTrimmingSpaces]
|
||||
forKey: @"content"];
|
||||
}
|
||||
|
||||
return [self responseWithStatus: 201
|
||||
andString: [data jsonRepresentation]];
|
||||
}
|
||||
|
||||
/* active message */
|
||||
|
||||
- (id) markMessageUnflaggedAction
|
||||
|
|
|
@ -612,9 +612,11 @@ static NSArray *infoKeys = nil;
|
|||
{
|
||||
NSDictionary *info;
|
||||
NSException *error;
|
||||
NSString *fontSize, *content;
|
||||
NGMimeType *mimeType;
|
||||
WORequest *request;
|
||||
SOGoDraftObject *co;
|
||||
SOGoUserDefaults *ud;
|
||||
|
||||
error = nil;
|
||||
request = [context request];
|
||||
|
@ -632,7 +634,22 @@ static NSArray *infoKeys = nil;
|
|||
info = [self infoFromRequest];
|
||||
[co setHeaders: info];
|
||||
[co setIsHTML: isHTML];
|
||||
[co setText: (isHTML ? [NSString stringWithFormat: @"<html>%@</html>", text] : text)];;
|
||||
if (isHTML)
|
||||
{
|
||||
// Set a base font size if mail is HTML and user has set a default font-size
|
||||
ud = [[context activeUser] userDefaults];
|
||||
fontSize = [ud mailComposeFontSize];
|
||||
if ([fontSize intValue] > 0)
|
||||
content = [NSString stringWithFormat: @"<html><span style=\"font-size: %@px;\">%@</span></html>",
|
||||
fontSize, text];
|
||||
else
|
||||
content = [NSString stringWithFormat: @"<html>%@</html>", text];
|
||||
}
|
||||
else
|
||||
{
|
||||
content = text;
|
||||
}
|
||||
[co setText: content];
|
||||
error = [co storeInfo];
|
||||
}
|
||||
|
||||
|
|
|
@ -278,6 +278,11 @@
|
|||
actionClass = "UIxMailActions";
|
||||
actionName = "forward";
|
||||
};
|
||||
viewplain = {
|
||||
protectedBy = "View";
|
||||
actionClass = "UIxMailActions";
|
||||
actionName = "viewPlain";
|
||||
};
|
||||
markMessageUncollapse = {
|
||||
protectedBy = "View";
|
||||
actionClass = "UIxMailActions";
|
||||
|
|
|
@ -142,6 +142,10 @@
|
|||
"Compose messages in" = "Compose messages in";
|
||||
"composemessagestype_html" = "HTML";
|
||||
"composemessagestype_text" = "Plain text";
|
||||
|
||||
/* Base font size for messages composed in HTML */
|
||||
"Default font size" = "Default font size";
|
||||
|
||||
"Display remote inline images" = "Display remote inline images";
|
||||
"displayremoteinlineimages_never" = "Never";
|
||||
"displayremoteinlineimages_always" = "Always";
|
||||
|
|
|
@ -171,6 +171,9 @@ static SoProduct *preferencesProduct = nil;
|
|||
if (![[defaults source] objectForKey: @"SOGoMailComposeMessageType"])
|
||||
[[defaults source] setObject: [defaults mailComposeMessageType] forKey: @"SOGoMailComposeMessageType"];
|
||||
|
||||
if (![[defaults source] objectForKey: @"SOGoMailComposeFontSize"])
|
||||
[[defaults source] setObject: [defaults mailComposeFontSize] forKey: @"SOGoMailComposeFontSize"];
|
||||
|
||||
if (![[defaults source] objectForKey: @"SOGoMailDisplayRemoteInlineImages"])
|
||||
[[defaults source] setObject: [defaults mailDisplayRemoteInlineImages] forKey: @"SOGoMailDisplayRemoteInlineImages"];
|
||||
|
||||
|
|
|
@ -1163,6 +1163,27 @@ static NSArray *reminderValues = nil;
|
|||
: [self _defaultEmailAddresses]);
|
||||
}
|
||||
|
||||
//
|
||||
// Used by templates
|
||||
//
|
||||
- (NSArray *) fontSizesList
|
||||
{
|
||||
static NSArray *fontSizes = nil;
|
||||
|
||||
if (!fontSizes)
|
||||
{
|
||||
fontSizes = [NSArray arrayWithObjects: @"8", @"9", @"10", @"11", @"12", @"13", @"14", @"16", @"18",
|
||||
@"20", @"22", @"24", @"26", @"28",
|
||||
@"36",
|
||||
@"48",
|
||||
@"72",
|
||||
nil];
|
||||
[fontSizes retain];
|
||||
}
|
||||
|
||||
return fontSizes;
|
||||
}
|
||||
|
||||
//
|
||||
// Used by templates
|
||||
//
|
||||
|
|
|
@ -437,6 +437,13 @@ vtodo_class2 = "(Confidential task)";
|
|||
"When I modify my calendar, send a mail to" = "When I modify my calendar, send a mail to";
|
||||
"Email Address" = "Email Address";
|
||||
"Export" = "Export";
|
||||
|
||||
/* Show only the calendar for which the menu is displayed */
|
||||
"Show Only This Calendar" = "Show Only This Calendar";
|
||||
|
||||
/* Show all calendar (personal, subscriptions and web) */
|
||||
"Show All Calendars" = "Show All Calendars";
|
||||
|
||||
"Links to this Calendar" = "Links to this Calendar";
|
||||
"Authenticated User Access" = "Authenticated User Access";
|
||||
"CalDAV URL" = "CalDAV URL ";
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
/* UIxAppointmentActions.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2010 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2010-2016 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
/* UIxAppointmentActions.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2011-2014 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2011-2016 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -65,6 +63,7 @@
|
|||
NSException *ex;
|
||||
SOGoAppointmentFolder *targetCalendar, *sourceCalendar;
|
||||
SOGoAppointmentFolders *folders;
|
||||
BOOL forceSave;
|
||||
|
||||
rq = [context request];
|
||||
params = [[rq contentAsString] objectFromJSONString];
|
||||
|
@ -73,6 +72,7 @@
|
|||
startDelta = [params objectForKey: @"start"];
|
||||
durationDelta = [params objectForKey: @"duration"];
|
||||
destionationCalendar = [params objectForKey: @"destination"];
|
||||
forceSave = NO;
|
||||
|
||||
if (daysDelta || startDelta || durationDelta)
|
||||
{
|
||||
|
@ -116,7 +116,8 @@
|
|||
[event updateRecurrenceRulesUntilDate: end];
|
||||
|
||||
[event setLastModified: [NSCalendarDate calendarDate]];
|
||||
ex = [co saveComponent: event];
|
||||
ex = [co saveComponent: event force: forceSave];
|
||||
|
||||
// This condition will be executed only if the event is moved from a calendar to another. If destionationCalendar == 0; there is no calendar change
|
||||
if ([destionationCalendar length] > 0)
|
||||
{
|
||||
|
@ -135,12 +136,21 @@
|
|||
ex = [co moveToFolder: targetCalendar];
|
||||
}
|
||||
}
|
||||
|
||||
if (ex)
|
||||
{
|
||||
unsigned int httpStatus;
|
||||
|
||||
httpStatus = 500;
|
||||
|
||||
if ([ex respondsToSelector: @selector(httpStatus)])
|
||||
httpStatus = [ex httpStatus];
|
||||
|
||||
jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[ex reason], @"message",
|
||||
[ex reason], @"message",
|
||||
nil];
|
||||
response = [self responseWithStatus: 403
|
||||
|
||||
response = [self responseWithStatus: httpStatus
|
||||
andJSONRepresentation: jsonResponse];
|
||||
}
|
||||
else
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* UIxAppointmentEditor.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2015 Inverse inc.
|
||||
* Copyright (C) 2007-2016 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* UIxAppointmentEditor.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2015 Inverse inc.
|
||||
* Copyright (C) 2007-2016 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -454,7 +454,9 @@
|
|||
SOGoAppointmentObject *co;
|
||||
SoSecurityManager *sm;
|
||||
WORequest *request;
|
||||
|
||||
unsigned int httpStatus;
|
||||
BOOL forceSave;
|
||||
|
||||
event = [self event];
|
||||
co = [self clientObject];
|
||||
|
@ -475,6 +477,7 @@
|
|||
else
|
||||
{
|
||||
[self setAttributes: params];
|
||||
forceSave = NO;
|
||||
|
||||
if ([event hasRecurrenceRules])
|
||||
[self _adjustRecurrentRules];
|
||||
|
@ -498,12 +501,12 @@
|
|||
}
|
||||
|
||||
// Save the event.
|
||||
ex = [co saveComponent: event];
|
||||
ex = [co saveComponent: event force: forceSave];
|
||||
}
|
||||
else
|
||||
{
|
||||
// The event was modified -- save it.
|
||||
ex = [co saveComponent: event];
|
||||
ex = [co saveComponent: event force: forceSave];
|
||||
|
||||
if (componentCalendar
|
||||
&& ![[componentCalendar ocsPath]
|
||||
|
@ -526,9 +529,12 @@
|
|||
if (ex)
|
||||
{
|
||||
httpStatus = 500;
|
||||
|
||||
if ([ex respondsToSelector: @selector(httpStatus)])
|
||||
httpStatus = [ex httpStatus];
|
||||
|
||||
jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"failure", @"status",
|
||||
[ex reason], @"message",
|
||||
[ex reason], @"message",
|
||||
nil];
|
||||
}
|
||||
else
|
||||
|
|
|
@ -101,7 +101,8 @@
|
|||
<a ui-sref="app.addressbook.card.view({addressbookId: editor.currentFolder.id, cardId: ref.reference})">{{ ref.$fullname() }}</a>
|
||||
</h3>
|
||||
<h4 ng-show="ref.email">
|
||||
<a ui-sref="mailto:{{ref.email}}" ng-class="ng-scope">{{ ref.email }}</a>
|
||||
<a href="#" ng-bind="ref.email"
|
||||
ng-click="addressbook.newMessageWithRecipient($event, ref.email, ref.$fullname())"><!-- contact email --></a>
|
||||
</h4>
|
||||
</div>
|
||||
</md-list-item>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
xmlns:label="OGo:label"
|
||||
className="UIxPageFrame"
|
||||
title="title"
|
||||
const:jsFiles="Common.js, Preferences.services.js, Contacts.services.js, Mailer.js, Mailer.services.js, vendor/ckeditor/ckeditor.js, vendor/ckeditor/ck.js, vendor/angular-file-upload.min.js">
|
||||
const:jsFiles="Common.js, Preferences.services.js, Contacts.services.js, Scheduler.services.js, Mailer.js, Mailer.services.js, vendor/ckeditor/ckeditor.js, vendor/ckeditor/ck.js, vendor/angular-file-upload.min.js">
|
||||
<script type="text/javascript">
|
||||
var mailAccounts = <var:string value="mailAccounts" const:escapeHTML="NO" />;
|
||||
var userNames = <var:string value="userNames" const:escapeHTML="NO" />;
|
||||
|
@ -116,7 +116,8 @@
|
|||
md-menu-origin="md-menu-origin">more_vert</md-icon>
|
||||
<md-menu-content width="3">
|
||||
<md-menu-item>
|
||||
<md-button type="button" md-menu-align-target="md-menu-align-target" ng-click="app.markFolderRead(folder)">
|
||||
<md-button type="button" md-menu-align-target="md-menu-align-target"
|
||||
ng-click="app.markFolderRead(folder)">
|
||||
<var:string label:value="Mark Folder Read"/>
|
||||
</md-button>
|
||||
</md-menu-item>
|
||||
|
|
|
@ -87,6 +87,19 @@
|
|||
<var:string label:value="View Message Source"/>
|
||||
</md-button>
|
||||
</md-menu-item>
|
||||
<md-menu-divider><!-- divider --></md-menu-divider>
|
||||
<md-menu-item>
|
||||
<md-button label:aria-label="Convert To Event"
|
||||
ng-click="viewer.convertToEvent($event)">
|
||||
<var:string label:value="Convert To Event"/>
|
||||
</md-button>
|
||||
</md-menu-item>
|
||||
<md-menu-item>
|
||||
<md-button label:aria-label="Convert To Task"
|
||||
ng-click="viewer.convertToTask($event)">
|
||||
<var:string label:value="Convert To Task"/>
|
||||
</md-button>
|
||||
</md-menu-item>
|
||||
</md-menu-content>
|
||||
</md-menu>
|
||||
</md-card-actions>
|
||||
|
|
|
@ -14,7 +14,11 @@
|
|||
<md-icon class="material-icons sg-icon-toolbar-bg">filter_list</md-icon>
|
||||
<md-input-container class="md-block md-flex">
|
||||
<label><var:string label:value="Filter name"/></label>
|
||||
<input class="md-title" type="text" ng-model="filterEditor.filter.name" required="required"/>
|
||||
<input class="md-title"
|
||||
type="text"
|
||||
md-autofocus="true"
|
||||
ng-model="filterEditor.filter.name"
|
||||
required="required"/>
|
||||
</md-input-container>
|
||||
<md-button type="button" class="sg-icon-button " ng-click="filterEditor.cancel()">
|
||||
<md-icon>close</md-icon>
|
||||
|
|
|
@ -511,19 +511,18 @@
|
|||
</md-checkbox>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div layout="row" layout-align="start center" flex="50" flex-xs="100">
|
||||
<md-checkbox
|
||||
class="md-align-top-left" ng-model="app.preferences.defaults.SOGoMailAddOutgoingAddresses"
|
||||
ng-true-value="1"
|
||||
ng-false-value="0"
|
||||
label:arial-label="When sending mail, add unknown recipients to my">
|
||||
<var:string label:value="When sending mail, add unknown recipients to my"/>
|
||||
</md-checkbox>
|
||||
|
||||
<md-input-container>
|
||||
<label><var:string label:aria-label="Address Book"/></label>
|
||||
<md-input-container class="md-block md-flex">
|
||||
<label><var:string label:value="When sending mail, add unknown recipients to my"/></label>
|
||||
<md-select
|
||||
ng-disabled="app.preferences.defaults.SOGoMailAddOutgoingAddresses == 0"
|
||||
ng-disabled="app.preferences.defaults.SOGoMailAddOutgoingAddresses != 1"
|
||||
ng-model="app.preferences.defaults.SOGoSelectedAddressBook">
|
||||
<var:foreach list="addressBookList" item="item">
|
||||
<md-option var:value="item.id">
|
||||
|
@ -534,7 +533,7 @@
|
|||
</md-input-container>
|
||||
</div>
|
||||
|
||||
<md-input-container class="md-block">
|
||||
<md-input-container class="md-block" flex="50" flex-xs="100">
|
||||
<label><var:string label:value="Forward messages"/></label>
|
||||
<md-select ng-model="app.preferences.defaults.SOGoMailMessageForwarding">
|
||||
<var:foreach list="messageForwardingList" item="item">
|
||||
|
@ -569,18 +568,42 @@
|
|||
</md-input-container>
|
||||
</div>
|
||||
|
||||
<md-input-container class="md-block">
|
||||
<label><var:string label:value="Compose messages in"/></label>
|
||||
<md-select ng-model="app.preferences.defaults.SOGoMailComposeMessageType">
|
||||
<var:foreach list="composeMessagesType" item="item">
|
||||
<md-option var:value="item">
|
||||
<var:string value="itemComposeMessagesText"/>
|
||||
</md-option>
|
||||
</var:foreach>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
<div layout="row" layout-wrap="layout-wrap">
|
||||
<md-input-container class="md-block" flex="50">
|
||||
<label><var:string label:value="Compose messages in"/></label>
|
||||
<md-select ng-model="app.preferences.defaults.SOGoMailComposeMessageType">
|
||||
<var:foreach list="composeMessagesType" item="item">
|
||||
<md-option var:value="item">
|
||||
<var:string value="itemComposeMessagesText"/>
|
||||
</md-option>
|
||||
</var:foreach>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container class="md-block">
|
||||
<div layout="row" flex="50"
|
||||
ng-show="app.preferences.defaults.SOGoMailComposeMessageType == 'html'">
|
||||
<md-checkbox
|
||||
ng-model="app.preferences.defaults.SOGoMailComposeFontSizeEnabled"
|
||||
label:aria-label="Default font size">
|
||||
</md-checkbox>
|
||||
<md-input-container class="md-block md-flex">
|
||||
<label><var:string label:value="Default font size"/></label>
|
||||
<md-select label:aria-label="Default font size"
|
||||
ng-disabled="!app.preferences.defaults.SOGoMailComposeFontSizeEnabled"
|
||||
ng-required="app.preferences.defaults.SOGoMailComposeFontSizeEnabled"
|
||||
ng-model="app.preferences.defaults.SOGoMailComposeFontSize">
|
||||
<var:foreach list="fontSizesList" item="item">
|
||||
<md-option var:value="item">
|
||||
<var:string value="item"/>
|
||||
</md-option>
|
||||
</var:foreach>
|
||||
</md-select>
|
||||
<div class="md-char-counter">px</div>
|
||||
</md-input-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<md-input-container class="md-block" flex="50" flex-xs="100">
|
||||
<label><var:string label:value="Display remote inline images"/></label>
|
||||
<md-select ng-model="app.preferences.defaults.SOGoMailDisplayRemoteInlineImages">
|
||||
<var:foreach list="displayRemoteInlineImages" item="item">
|
||||
|
@ -594,7 +617,8 @@
|
|||
<div layout="row" layout-align="start center">
|
||||
<p><var:string label:value="Auto save every"/></p>
|
||||
<md-input-container class="md-input-number" md-no-float="md-no-float">
|
||||
<input type="number" min="0" label:aria-label="minutes" ng-model="preferences.defaults.SOGoMailAutoSave"/>
|
||||
<input type="number" min="0" label:aria-label="minutes"
|
||||
ng-model="app.preferences.defaults.SOGoMailAutoSave"/>
|
||||
</md-input-container>
|
||||
<var:string label:value="minutes"/>
|
||||
</div>
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<form name="eventForm" ng-submit="editor.save(eventForm)">
|
||||
<md-toolbar ng-class="editor.component.getClassName('bg')">
|
||||
<div class="md-toolbar-tools">
|
||||
<md-icon class="material-icons sg-icon-toolbar-bg">event</md-icon>
|
||||
<!-- summary -->
|
||||
<md-icon ng-if="editor.component.classification == 'confidential'">visibility_off</md-icon>
|
||||
<md-icon ng-if="editor.component.classification == 'private'">vpn_key</md-icon>
|
||||
|
|
|
@ -188,6 +188,17 @@
|
|||
</md-button>
|
||||
</md-menu-item>
|
||||
<md-menu-divider><!-- divider --></md-menu-divider>
|
||||
<md-menu-item>
|
||||
<md-button ng-click="app.showOnly(calendar)">
|
||||
<var:string label:value="Show Only This Calendar"/>
|
||||
</md-button>
|
||||
</md-menu-item>
|
||||
<md-menu-item>
|
||||
<md-button ng-click="app.showAll()">
|
||||
<var:string label:value="Show All Calendars"/>
|
||||
</md-button>
|
||||
</md-menu-item>
|
||||
<md-menu-divider><!-- divider --></md-menu-divider>
|
||||
<md-menu-item>
|
||||
<md-button ng-click="app.showLinks(calendar)">
|
||||
<var:string label:value="Links to this Calendar"/>
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<form name="eventForm" ng-submit="editor.save(eventForm)">
|
||||
<md-toolbar ng-class="editor.component.getClassName('bg')">
|
||||
<div class="md-toolbar-tools">
|
||||
<md-icon class="material-icons sg-icon-toolbar-bg">assignment_turned_in</md-icon>
|
||||
<!-- summary -->
|
||||
<md-icon ng-if="editor.component.classification == 'confidential'">visibility_off</md-icon>
|
||||
<md-icon ng-if="editor.component.classification == 'private'">vpn_key</md-icon>
|
||||
|
|
|
@ -4,7 +4,7 @@ include ../common.make
|
|||
|
||||
WEBSERVER_RESOURCE_DIRS = css fonts img js
|
||||
JS_FILES = js/Administration.* js/Common.* js/Contacts.* js/Mailer.* js/Main.* js/Preferences.* js/Scheduler.* css/styles.*
|
||||
JS_LIB_FILES = js/vendor/angular-animate.* js/vendor/angular-aria.* js/vendor/angular-file-upload.min.js js/vendor/angular-material.* js/vendor/angular-sanitize.* js/vendor/angular-ui-router.* js/vendor/angular.* js/vendor/lodash.*
|
||||
JS_LIB_FILES = js/vendor/angular-animate.* js/vendor/angular-aria.* js/vendor/angular-file-upload.min.js js/vendor/ng-sortable.* js/vendor/angular-material.* js/vendor/angular-sanitize.* js/vendor/angular-ui-router.* js/vendor/angular.* js/vendor/lodash.*
|
||||
CSS_FILES = css/styles.css css/styles.css.map
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
|
@ -26,7 +26,11 @@ prod:
|
|||
grunt --stack build
|
||||
git update-index --no-assume-unchanged $(CSS_FILES) $(JS_FILES) $(JS_LIB_FILES)
|
||||
git add -f $(CSS_FILES) $(JS_FILES) $(JS_LIB_FILES)
|
||||
git commit -m "(js/css) Update generated files"
|
||||
@if ! git diff --quiet --exit-code; then \
|
||||
git commit -m "(js/css) Update generated files"; \
|
||||
else \
|
||||
echo "Nothing to commit; skipping git-commit"; \
|
||||
fi
|
||||
git update-index --assume-unchanged $(CSS_FILES) $(JS_FILES) $(JS_LIB_FILES)
|
||||
|
||||
all:
|
||||
|
|
|
@ -2,8 +2,8 @@ module.exports = function(grunt) {
|
|||
var js_files = {
|
||||
'js/Common.js': ['js/Common/*.app.js', 'js/Common/*.filter.js', 'js/Common/*Controller.js', 'js/Common/*.service.js', 'js/Common/*.directive.js', 'js/Common/utils.js'],
|
||||
'js/Main.js': ['js/Main/Main.app.js'],
|
||||
'js/Scheduler.services.js': ['js/Scheduler/*.service.js'],
|
||||
'js/Scheduler.js': ['js/Scheduler/Scheduler.app.js', 'js/Scheduler/*Controller.js', 'js/Scheduler/*.directive.js'],
|
||||
'js/Scheduler.services.js': ['js/Scheduler/*.service.js', 'js/Scheduler/*Controller.js', 'js/Scheduler/*.directive.js'],
|
||||
'js/Scheduler.js': ['js/Scheduler/Scheduler.app.js'],
|
||||
'js/Contacts.services.js': ['js/Contacts/*.service.js'],
|
||||
'js/Contacts.js': ['js/Contacts/Contacts.app.js', 'js/Contacts/*Controller.js', 'js/Contacts/*.directive.js'],
|
||||
'js/Mailer.services.js': ['js/Mailer/*.service.js', 'js/Mailer/*Controller.js', 'js/Mailer/*.directive.js'],
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -93,7 +93,7 @@
|
|||
*/
|
||||
getService.$inject = ['$q', '$http', 'passwordPolicyConfig'];
|
||||
function getService($q, $http, passwordPolicyConfig) {
|
||||
var _this = this, service;
|
||||
var service;
|
||||
|
||||
service = {
|
||||
login: function(data) {
|
||||
|
@ -171,7 +171,7 @@
|
|||
|
||||
changePassword: function(newPassword) {
|
||||
var d = $q.defer(),
|
||||
loginCookie = _this.readLoginCookie();
|
||||
loginCookie = readLoginCookie();
|
||||
|
||||
$http({
|
||||
method: 'POST',
|
||||
|
|
|
@ -328,7 +328,7 @@ Date.prototype.beginOfDay = function() {
|
|||
return beginOfDay;
|
||||
};
|
||||
|
||||
Date.prototype.beginOfWeek = function() {
|
||||
Date.prototype.beginOfWeek = function(firstDayOfWeek) {
|
||||
var offset = firstDayOfWeek - this.getDay();
|
||||
if (offset > 0)
|
||||
offset -= 7;
|
||||
|
@ -340,8 +340,8 @@ Date.prototype.beginOfWeek = function() {
|
|||
return beginOfWeek;
|
||||
};
|
||||
|
||||
Date.prototype.endOfWeek = function() {
|
||||
var endOfWeek = this.beginOfWeek();
|
||||
Date.prototype.endOfWeek = function(firstDayOfWeek) {
|
||||
var endOfWeek = this.beginOfWeek(firstDayOfWeek);
|
||||
endOfWeek.addDays(6);
|
||||
|
||||
endOfWeek.setHours(23);
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,2 +1,2 @@
|
|||
!function(){"use strict";function a(a,l){a.state("mail",{url:"/Mail",views:{mailboxes:{templateUrl:"UIxMailMainFrame",controller:"MailboxesController",controllerAs:"app"}},resolve:{stateAccounts:b}}).state("mail.account",{url:"/:accountId","abstract":!0,views:{mailbox:{template:"<ui-view/>"}},resolve:{stateAccount:c}}).state("mail.account.virtualMailbox",{url:"/virtual",views:{"mailbox@mail":{templateUrl:"UIxMailFolderTemplate",controller:"MailboxController",controllerAs:"mailbox"}},resolve:{stateMailbox:g}}).state("mail.account.virtualMailbox.message",{url:"/:mailboxId/:messageId",views:{message:{templateUrl:"UIxMailViewTemplate",controller:"MessageController",controllerAs:"viewer"}},resolve:{stateMailbox:h,stateMessages:f,stateMessage:i}}).state("mail.account.inbox",{url:"/inbox",views:{"mailbox@mail":{templateUrl:"UIxMailFolderTemplate",controller:"MailboxController",controllerAs:"mailbox"}},resolve:{stateMailbox:e,stateMessages:f}}).state("mail.account.mailbox",{url:"/:mailboxId",views:{"mailbox@mail":{templateUrl:"UIxMailFolderTemplate",controller:"MailboxController",controllerAs:"mailbox"}},resolve:{stateMailbox:d,stateMessages:f}}).state("mail.account.mailbox.message",{url:"/:messageId",views:{message:{templateUrl:"UIxMailViewTemplate",controller:"MessageController",controllerAs:"viewer"}},onEnter:j,onExit:k,resolve:{stateMessage:i}}),l.otherwise("/Mail/0/inbox")}function b(a,b){var c=b.$findAll(window.mailAccounts),d=[];return angular.forEach(c,function(a,b){var c=a.$getMailboxes();d.push(c.then(function(b){return a}))}),a.all(d)}function c(a,b){return _.find(b,function(b){return b.id==a.accountId})}function d(a,b,c,d,e,f){var g,h,i=e(c.mailboxId);return h=function(a){var b=_.find(a,function(a){return a.path==i});return b||angular.forEach(a,function(a){!b&&a.children&&a.children.length>0&&(b=h(a.children))}),b},f.selectedFolder&&(f.selectedFolder.$isLoading=!0),g=h(d.$mailboxes),g?(g.$topIndex=0,g):b.go("mail.account.inbox")}function e(a,b){return b.selectedFolder&&(b.selectedFolder.$isLoading=!0),a.$mailboxes[0]}function f(a,b){return a.$virtualMode?[]:b.$filter()}function g(a,b){return b.$virtualMode?b.selectedFolder:a.reject("No virtual mailbox defined")}function h(a,b,c,d){var e=c(d.mailboxId);return b.$virtualMode?(b.selectedFolder.resetSelectedMessage(),_.find(b.selectedFolder.$mailboxes,function(a){return a.path==e})):a.reject("No virtual mailbox defined for message")}function i(a,b,c,d,e,f){var g;return(g=_.find(e.$messages,function(a){return a.uid==parseInt(c.messageId)}))?g.$reload():void d.go("mail.account.mailbox",{accountId:e.$account.id,mailboxId:b(e.path)})}function j(a,b){b.selectedMessage=parseInt(a.messageId)}function k(a){a.selectedMessage=-1}function l(a,b,c){a.$on("$stateChangeError",function(a,d,e,f,g,h){b.error(h),a.preventDefault(),"mail.account.inbox"!=d.name?c.go("mail.account.inbox"):c.go("mail")}),a.$on("$routeChangeError",function(a,c,d,e){b.error(a,c,d,e)})}angular.module("SOGo.MailerUI",["ui.router","ck","angularFileUpload","SOGo.Common","SOGo.ContactsUI","ngAnimate","SOGo.PreferencesUI"]).config(a).run(l),a.$inject=["$stateProvider","$urlRouterProvider"],b.$inject=["$q","Account"],c.$inject=["$stateParams","stateAccounts"],d.$inject=["$q","$state","$stateParams","stateAccount","decodeUriFilter","Mailbox"],e.$inject=["stateAccount","Mailbox"],f.$inject=["Mailbox","stateMailbox"],g.$inject=["$q","Mailbox"],h.$inject=["$q","Mailbox","decodeUriFilter","$stateParams"],i.$inject=["Mailbox","encodeUriFilter","$stateParams","$state","stateMailbox","stateMessages"],j.$inject=["$stateParams","stateMailbox"],k.$inject=["stateMailbox"],l.$inject=["$rootScope","$log","$state"]}();
|
||||
!function(){"use strict";function a(a,l){a.state("mail",{url:"/Mail",views:{mailboxes:{templateUrl:"UIxMailMainFrame",controller:"MailboxesController",controllerAs:"app"}},resolve:{stateAccounts:b}}).state("mail.account",{url:"/:accountId","abstract":!0,views:{mailbox:{template:"<ui-view/>"}},resolve:{stateAccount:c}}).state("mail.account.virtualMailbox",{url:"/virtual",views:{"mailbox@mail":{templateUrl:"UIxMailFolderTemplate",controller:"MailboxController",controllerAs:"mailbox"}},resolve:{stateMailbox:g}}).state("mail.account.virtualMailbox.message",{url:"/:mailboxId/:messageId",views:{message:{templateUrl:"UIxMailViewTemplate",controller:"MessageController",controllerAs:"viewer"}},resolve:{stateMailbox:h,stateMessages:f,stateMessage:i}}).state("mail.account.inbox",{url:"/inbox",views:{"mailbox@mail":{templateUrl:"UIxMailFolderTemplate",controller:"MailboxController",controllerAs:"mailbox"}},resolve:{stateMailbox:e,stateMessages:f}}).state("mail.account.mailbox",{url:"/:mailboxId",views:{"mailbox@mail":{templateUrl:"UIxMailFolderTemplate",controller:"MailboxController",controllerAs:"mailbox"}},resolve:{stateMailbox:d,stateMessages:f}}).state("mail.account.mailbox.message",{url:"/:messageId",views:{message:{templateUrl:"UIxMailViewTemplate",controller:"MessageController",controllerAs:"viewer"}},onEnter:j,onExit:k,resolve:{stateMessage:i}}),l.otherwise("/Mail/0/inbox")}function b(a,b){var c=b.$findAll(window.mailAccounts),d=[];return angular.forEach(c,function(a,b){var c=a.$getMailboxes();d.push(c.then(function(b){return a}))}),a.all(d)}function c(a,b){return _.find(b,function(b){return b.id==a.accountId})}function d(a,b,c,d,e,f){var g,h,i=e(c.mailboxId);return h=function(a){var b=_.find(a,function(a){return a.path==i});return b||angular.forEach(a,function(a){!b&&a.children&&a.children.length>0&&(b=h(a.children))}),b},f.selectedFolder&&(f.selectedFolder.$isLoading=!0),g=h(d.$mailboxes),g?(g.$topIndex=0,g):b.go("mail.account.inbox")}function e(a,b){return b.selectedFolder&&(b.selectedFolder.$isLoading=!0),a.$mailboxes[0]}function f(a,b){return a.$virtualMode?[]:b.$filter()}function g(a,b){return b.$virtualMode?b.selectedFolder:a.reject("No virtual mailbox defined")}function h(a,b,c,d){var e=c(d.mailboxId);return b.$virtualMode?(b.selectedFolder.resetSelectedMessage(),_.find(b.selectedFolder.$mailboxes,function(a){return a.path==e})):a.reject("No virtual mailbox defined for message")}function i(a,b,c,d,e,f){var g;return(g=_.find(e.$messages,function(a){return a.uid==parseInt(c.messageId)}))?g.$reload():void d.go("mail.account.mailbox",{accountId:e.$account.id,mailboxId:b(e.path)})}function j(a,b){b.selectedMessage=parseInt(a.messageId)}function k(a){a.selectedMessage=-1}function l(a,b,c){a.$on("$stateChangeError",function(a,d,e,f,g,h){b.error(h),a.preventDefault(),"mail.account.inbox"!=d.name?c.go("mail.account.inbox"):c.go("mail")}),a.$on("$routeChangeError",function(a,c,d,e){b.error(a,c,d,e)})}angular.module("SOGo.MailerUI",["ui.router","ck","angularFileUpload","SOGo.Common","SOGo.ContactsUI","SOGo.SchedulerUI","ngAnimate","SOGo.PreferencesUI"]).config(a).run(l),a.$inject=["$stateProvider","$urlRouterProvider"],b.$inject=["$q","Account"],c.$inject=["$stateParams","stateAccounts"],d.$inject=["$q","$state","$stateParams","stateAccount","decodeUriFilter","Mailbox"],e.$inject=["stateAccount","Mailbox"],f.$inject=["Mailbox","stateMailbox"],g.$inject=["$q","Mailbox"],h.$inject=["$q","Mailbox","decodeUriFilter","$stateParams"],i.$inject=["Mailbox","encodeUriFilter","$stateParams","$state","stateMailbox","stateMessages"],j.$inject=["$stateParams","stateMailbox"],k.$inject=["stateMailbox"],l.$inject=["$rootScope","$log","$state"]}();
|
||||
//# sourceMappingURL=Mailer.js.map
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -4,7 +4,7 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular.module('SOGo.MailerUI', ['ui.router', 'ck', 'angularFileUpload', 'SOGo.Common', 'SOGo.ContactsUI', 'ngAnimate', 'SOGo.PreferencesUI'])
|
||||
angular.module('SOGo.MailerUI', ['ui.router', 'ck', 'angularFileUpload', 'SOGo.Common', 'SOGo.ContactsUI', 'SOGo.SchedulerUI', 'ngAnimate', 'SOGo.PreferencesUI'])
|
||||
.config(configure)
|
||||
.run(runBlock);
|
||||
|
||||
|
|
|
@ -362,6 +362,15 @@
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $plainContent
|
||||
* @memberof Message.prototype
|
||||
* @returns the a plain text representation of the subject and body
|
||||
*/
|
||||
Message.prototype.$plainContent = function() {
|
||||
return Message.$$resource.fetch(this.$absolutePath(), 'viewplain');
|
||||
};
|
||||
|
||||
/**
|
||||
* @function addTag
|
||||
* @memberof Message.prototype
|
||||
|
@ -576,27 +585,24 @@
|
|||
*/
|
||||
Message.prototype.$send = function() {
|
||||
var _this = this,
|
||||
data = angular.copy(this.editable),
|
||||
deferred = Message.$q.defer();
|
||||
data = angular.copy(this.editable);
|
||||
|
||||
Message.$log.debug('send = ' + JSON.stringify(data, undefined, 2));
|
||||
|
||||
Message.$$resource.post(this.$absolutePath({asDraft: true}), 'send', data).then(function(data) {
|
||||
return Message.$$resource.post(this.$absolutePath({asDraft: true}), 'send', data).then(function(data) {
|
||||
if (data.status == 'success') {
|
||||
deferred.resolve(data);
|
||||
if (angular.isDefined(_this.origin)) {
|
||||
if (_this.origin.action.startsWith('reply'))
|
||||
_this.origin.message.isanswered = true;
|
||||
else if (_this.origin.action == 'forward')
|
||||
_this.origin.message.isforwarded = true;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
else {
|
||||
deferred.reject(data);
|
||||
return Message.$q.reject(data);
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
/**
|
||||
* @ngInject
|
||||
*/
|
||||
MessageController.$inject = ['$window', '$scope', '$state', '$mdDialog', 'stateAccounts', 'stateAccount', 'stateMailbox', 'stateMessage', 'encodeUriFilter', 'sgSettings', 'sgFocus', 'Dialog', 'Account', 'Mailbox', 'Message'];
|
||||
function MessageController($window, $scope, $state, $mdDialog, stateAccounts, stateAccount, stateMailbox, stateMessage, encodeUriFilter, sgSettings, focus, Dialog, Account, Mailbox, Message) {
|
||||
MessageController.$inject = ['$window', '$scope', '$state', '$mdDialog', 'stateAccounts', 'stateAccount', 'stateMailbox', 'stateMessage', 'encodeUriFilter', 'sgSettings', 'sgFocus', 'Dialog', 'Calendar', 'Component', 'Account', 'Mailbox', 'Message'];
|
||||
function MessageController($window, $scope, $state, $mdDialog, stateAccounts, stateAccount, stateMailbox, stateMessage, encodeUriFilter, sgSettings, focus, Dialog, Calendar, Component, Account, Mailbox, Message) {
|
||||
var vm = this, messageDialog = null, popupWindow = null;
|
||||
|
||||
// Expose controller
|
||||
|
@ -35,6 +35,8 @@
|
|||
vm.saveMessage = saveMessage;
|
||||
vm.toggleRawSource = toggleRawSource;
|
||||
vm.showRawSource = false;
|
||||
vm.convertToEvent = convertToEvent;
|
||||
vm.convertToTask = convertToTask;
|
||||
|
||||
// One-way refresh of the parent window when modifying the message from a popup window.
|
||||
if ($window.opener) {
|
||||
|
@ -254,6 +256,45 @@
|
|||
vm.showRawSource = !vm.showRawSource;
|
||||
}
|
||||
}
|
||||
|
||||
function convertToEvent($event) {
|
||||
return convertToComponent($event, 'appointment');
|
||||
}
|
||||
|
||||
function convertToTask($event) {
|
||||
return convertToComponent($event, 'task');
|
||||
}
|
||||
|
||||
function convertToComponent($event, type) {
|
||||
vm.message.$plainContent().then(function(data) {
|
||||
var componentData = {
|
||||
pid: Calendar.$defaultCalendar(),
|
||||
type: type,
|
||||
summary: data.subject,
|
||||
comment: data.content
|
||||
};
|
||||
var component = new Component(componentData);
|
||||
// UI/Templates/SchedulerUI/UIxAppointmentEditorTemplate.wox or
|
||||
// UI/Templates/SchedulerUI/UIxTaskEditorTemplate.wox
|
||||
var templateUrl = [
|
||||
sgSettings.activeUser('folderURL'),
|
||||
'Calendar',
|
||||
'UIx' + type.capitalize() + 'EditorTemplate'
|
||||
].join('/');
|
||||
return $mdDialog.show({
|
||||
parent: angular.element(document.body),
|
||||
targetEvent: $event,
|
||||
clickOutsideToClose: true,
|
||||
escapeToClose: true,
|
||||
templateUrl: templateUrl,
|
||||
controller: 'ComponentEditorController',
|
||||
controllerAs: 'editor',
|
||||
locals: {
|
||||
stateComponent: component
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
!function(){"use strict";function a(){var b=this;this.defaults={},this.settings={},this.defaultsPromise=a.$$resource.fetch("jsonDefaults").then(function(c){var d=_.object(_.map(c.SOGoMailLabelsColors,function(a,b){return"$"==b.charAt(0)?["_"+b,a]:[b,a]}));return c.SOGoMailLabelsColors=d,c.Vacation?(c.Vacation.endDate?c.Vacation.endDate=new Date(1e3*parseInt(c.Vacation.endDate)):(c.Vacation.endDateEnabled=0,c.Vacation.endDate=new Date),c.Vacation.autoReplyEmailAddresses&&c.Vacation.autoReplyEmailAddresses.length?c.Vacation.autoReplyEmailAddresses=c.Vacation.autoReplyEmailAddresses.join(","):delete c.Vacation.autoReplyEmailAddresses):c.Vacation={},angular.isUndefined(c.Vacation.autoReplyEmailAddresses)&&angular.isDefined(window.defaultEmailAddresses)&&(c.Vacation.autoReplyEmailAddresses=window.defaultEmailAddresses),angular.isUndefined(c.Vacation.daysBetweenResponse)&&(c.Vacation.daysBetweenResponse=7),angular.isUndefined(c.Vacation.endDate)&&(c.Vacation.endDateEnabled=0,c.Vacation.endDate=new Date),c.Forward&&c.Forward.forwardAddress&&(c.Forward.forwardAddress=c.Forward.forwardAddress.join(",")),angular.isUndefined(c.SOGoCalendarCategoriesColors)&&(c.SOGoCalendarCategoriesColors={},c.SOGoCalendarCategories=[]),angular.isUndefined(c.SOGoContactsCategories)&&(c.SOGoContactsCategories=[]),angular.extend(b.defaults,c),angular.extend(a.$mdDateLocaleProvider,c.locale),a.$mdDateLocaleProvider.firstDayOfWeek=parseInt(c.SOGoFirstDayOfWeek),a.$mdDateLocaleProvider.weekNumberFormatter=function(a){return l("Week %d",a)},a.$mdDateLocaleProvider.msgCalendar=l("Calender"),a.$mdDateLocaleProvider.msgOpenCalendar=l("Open Calendar"),a.$mdDateLocaleProvider.parseDate=function(b){return b?b.parseDate(a.$mdDateLocaleProvider,c.SOGoShortDateFormat):new Date(NaN)},a.$mdDateLocaleProvider.formatDate=function(b){return b?b.format(a.$mdDateLocaleProvider,c.SOGoShortDateFormat):""},a.$mdDateLocaleProvider.formatTime=function(b){return b?b.format(a.$mdDateLocaleProvider,c.SOGoTimeFormat):""},b.defaults}),this.settingsPromise=a.$$resource.fetch("jsonSettings").then(function(c){return c.Calendar&&(c.Calendar.PreventInvitationsWhitelist?c.Calendar.PreventInvitationsWhitelist=_.map(c.Calendar.PreventInvitationsWhitelist,function(b,c){var d=/^(.+)\s<(\S+)>$/.exec(b);return new a.$User({uid:c,cn:d[1],c_email:d[2]})}):c.Calendar.PreventInvitationsWhitelist=[]),angular.extend(b.settings,c),b.settings})}a.$factory=["$q","$timeout","$log","$mdDateLocale","sgSettings","Resource","User",function(b,c,d,e,f,g,h){return angular.extend(a,{$q:b,$timeout:c,$log:d,$mdDateLocaleProvider:e,$$resource:new g(f.activeUser("folderURL"),f.activeUser()),activeUser:f.activeUser(),$User:h}),new a}];try{angular.module("SOGo.PreferencesUI")}catch(b){angular.module("SOGo.PreferencesUI",["SOGo.Common"])}angular.module("SOGo.PreferencesUI").factory("Preferences",a.$factory),a.prototype.ready=function(){return a.$q.all([this.defaultsPromise,this.settingsPromise])},a.prototype.$save=function(){return a.$$resource.save("Preferences",this.$omit(!0)).then(function(a){return a})},a.prototype.$omit=function(a){var b,c,d;return b={},d={},angular.forEach(this,function(c,d){"constructor"!=d&&"$"!=d[0]&&(a?b[d]=angular.copy(c):b[d]=c)}),c=_.object(_.map(b.defaults.SOGoMailLabelsColors,function(a,b){return"_"==b.charAt(0)&&"$"==b.charAt(1)?b.length>2&&"$"==b.charAt(2)?[a[0].toLowerCase().replace(/[ \(\)\/\{%\*<>\\\"]/g,"_"),a]:[b.substring(1),a]:[b,a]})),b.defaults.SOGoMailLabelsColors=c,b.defaults.Vacation&&(b.defaults.Vacation.endDateEnabled?b.defaults.Vacation.endDate=b.defaults.Vacation.endDate.getTime()/1e3:b.defaults.Vacation.endDate=0,b.defaults.Vacation.autoReplyEmailAddresses?b.defaults.Vacation.autoReplyEmailAddresses=_.filter(b.defaults.Vacation.autoReplyEmailAddresses.split(","),function(a){return a.length}):b.defaults.Vacation.autoReplyEmailAddresses=[]),b.defaults.Forward&&b.defaults.Forward.forwardAddress&&(b.defaults.Forward.forwardAddress=b.defaults.Forward.forwardAddress.split(",")),b.settings.Calendar&&b.settings.Calendar.PreventInvitationsWhitelist&&(_.each(b.settings.Calendar.PreventInvitationsWhitelist,function(a){d[a.uid]=a.$shortFormat()}),b.settings.Calendar.PreventInvitationsWhitelist=d),b}}();
|
||||
!function(){"use strict";function a(){var b=this;this.defaults={},this.settings={},this.defaultsPromise=a.$$resource.fetch("jsonDefaults").then(function(c){var d=_.object(_.map(c.SOGoMailLabelsColors,function(a,b){return"$"==b.charAt(0)?["_"+b,a]:[b,a]}));return c.SOGoMailLabelsColors=d,c.SOGoMailAutoSave=parseInt(c.SOGoMailAutoSave)||0,c.SOGoMailComposeFontSizeEnabled=parseInt(c.SOGoMailComposeFontSize)>0,window.CKEDITOR&&c.SOGoMailComposeFontSizeEnabled&&(window.CKEDITOR.config.fontSize_defaultLabel=c.SOGoMailComposeFontSize,window.CKEDITOR.addCss(".cke_editable { font-size: "+c.SOGoMailComposeFontSize+"px; }")),c.Vacation?(c.Vacation.endDate?c.Vacation.endDate=new Date(1e3*parseInt(c.Vacation.endDate)):(c.Vacation.endDateEnabled=0,c.Vacation.endDate=new Date),c.Vacation.autoReplyEmailAddresses&&c.Vacation.autoReplyEmailAddresses.length?c.Vacation.autoReplyEmailAddresses=c.Vacation.autoReplyEmailAddresses.join(","):delete c.Vacation.autoReplyEmailAddresses):c.Vacation={},angular.isUndefined(c.Vacation.autoReplyEmailAddresses)&&angular.isDefined(window.defaultEmailAddresses)&&(c.Vacation.autoReplyEmailAddresses=window.defaultEmailAddresses),angular.isUndefined(c.Vacation.daysBetweenResponse)&&(c.Vacation.daysBetweenResponse=7),angular.isUndefined(c.Vacation.endDate)&&(c.Vacation.endDateEnabled=0,c.Vacation.endDate=new Date),c.Forward&&c.Forward.forwardAddress&&(c.Forward.forwardAddress=c.Forward.forwardAddress.join(",")),angular.isUndefined(c.SOGoCalendarCategoriesColors)&&(c.SOGoCalendarCategoriesColors={},c.SOGoCalendarCategories=[]),angular.isUndefined(c.SOGoContactsCategories)&&(c.SOGoContactsCategories=[]),angular.extend(b.defaults,c),b.$mdDateLocaleProvider=a.$mdDateLocaleProvider,angular.extend(b.$mdDateLocaleProvider,c.locale),b.$mdDateLocaleProvider.firstDayOfWeek=parseInt(c.SOGoFirstDayOfWeek),b.$mdDateLocaleProvider.weekNumberFormatter=function(a){return l("Week %d",a)},b.$mdDateLocaleProvider.msgCalendar=l("Calender"),b.$mdDateLocaleProvider.msgOpenCalendar=l("Open Calendar"),b.$mdDateLocaleProvider.parseDate=function(a){return a?a.parseDate(b.$mdDateLocaleProvider,c.SOGoShortDateFormat):new Date(NaN)},b.$mdDateLocaleProvider.formatDate=function(a){return a?a.format(b.$mdDateLocaleProvider,c.SOGoShortDateFormat):""},b.$mdDateLocaleProvider.formatTime=function(a){return a?a.format(b.$mdDateLocaleProvider,c.SOGoTimeFormat):""},b.defaults}),this.settingsPromise=a.$$resource.fetch("jsonSettings").then(function(c){return c.Calendar&&(c.Calendar.PreventInvitationsWhitelist?c.Calendar.PreventInvitationsWhitelist=_.map(c.Calendar.PreventInvitationsWhitelist,function(b,c){var d=/^(.+)\s<(\S+)>$/.exec(b);return new a.$User({uid:c,cn:d[1],c_email:d[2]})}):c.Calendar.PreventInvitationsWhitelist=[]),angular.extend(b.settings,c),b.settings})}a.$factory=["$q","$timeout","$log","$mdDateLocale","sgSettings","Resource","User",function(b,c,d,e,f,g,h){return angular.extend(a,{$q:b,$timeout:c,$log:d,$mdDateLocaleProvider:e,$$resource:new g(f.activeUser("folderURL"),f.activeUser()),activeUser:f.activeUser(),$User:h}),new a}];try{angular.module("SOGo.PreferencesUI")}catch(b){angular.module("SOGo.PreferencesUI",["SOGo.Common"])}angular.module("SOGo.PreferencesUI").factory("Preferences",a.$factory),a.prototype.ready=function(){return a.$q.all([this.defaultsPromise,this.settingsPromise])},a.prototype.$save=function(){return a.$$resource.save("Preferences",this.$omit(!0)).then(function(a){return a})},a.prototype.$omit=function(a){var b,c,d;return b={},d={},angular.forEach(this,function(c,d){"constructor"!=d&&"$"!=d[0]&&(a?b[d]=angular.copy(c):b[d]=c)}),c=_.object(_.map(b.defaults.SOGoMailLabelsColors,function(a,b){return"_"==b.charAt(0)&&"$"==b.charAt(1)?b.length>2&&"$"==b.charAt(2)?[a[0].toLowerCase().replace(/[ \(\)\/\{%\*<>\\\"]/g,"_"),a]:[b.substring(1),a]:[b,a]})),b.defaults.SOGoMailLabelsColors=c,b.defaults.SOGoMailComposeFontSizeEnabled||(b.defaults.SOGoMailComposeFontSize=0),delete b.defaults.SOGoMailComposeFontSizeEnabled,b.defaults.Vacation&&(b.defaults.Vacation.endDateEnabled?b.defaults.Vacation.endDate=b.defaults.Vacation.endDate.getTime()/1e3:b.defaults.Vacation.endDate=0,b.defaults.Vacation.autoReplyEmailAddresses?b.defaults.Vacation.autoReplyEmailAddresses=_.filter(b.defaults.Vacation.autoReplyEmailAddresses.split(","),function(a){return a.length}):b.defaults.Vacation.autoReplyEmailAddresses=[]),b.defaults.Forward&&b.defaults.Forward.forwardAddress&&(b.defaults.Forward.forwardAddress=b.defaults.Forward.forwardAddress.split(",")),b.settings.Calendar&&b.settings.Calendar.PreventInvitationsWhitelist&&(_.each(b.settings.Calendar.PreventInvitationsWhitelist,function(a){d[a.uid]=a.$shortFormat()}),b.settings.Calendar.PreventInvitationsWhitelist=d),b}}();
|
||||
//# sourceMappingURL=Preferences.services.js.map
|
File diff suppressed because one or more lines are too long
|
@ -23,6 +23,18 @@
|
|||
|
||||
data.SOGoMailLabelsColors = labels;
|
||||
|
||||
// Mail editor autosave is a number of minutes or 0 if disabled
|
||||
data.SOGoMailAutoSave = parseInt(data.SOGoMailAutoSave) || 0;
|
||||
|
||||
// Specify a base font size for HTML messages when SOGoMailComposeFontSize is not zero
|
||||
data.SOGoMailComposeFontSizeEnabled = parseInt(data.SOGoMailComposeFontSize) > 0;
|
||||
|
||||
if (window.CKEDITOR && data.SOGoMailComposeFontSizeEnabled) {
|
||||
// HTML editor is enabled; set user's preferred font size
|
||||
window.CKEDITOR.config.fontSize_defaultLabel = data.SOGoMailComposeFontSize;
|
||||
window.CKEDITOR.addCss('.cke_editable { font-size: ' + data.SOGoMailComposeFontSize + 'px; }');
|
||||
}
|
||||
|
||||
// We convert our list of autoReplyEmailAddresses/forwardAddress into a string.
|
||||
// We also convert our date objects into real date, otherwise we'll have strings
|
||||
// or undefined values and the md-datepicker does NOT like this.
|
||||
|
@ -66,21 +78,22 @@
|
|||
angular.extend(_this.defaults, data);
|
||||
|
||||
// Configure date locale
|
||||
angular.extend(Preferences.$mdDateLocaleProvider, data.locale);
|
||||
Preferences.$mdDateLocaleProvider.firstDayOfWeek = parseInt(data.SOGoFirstDayOfWeek);
|
||||
Preferences.$mdDateLocaleProvider.weekNumberFormatter = function(weekNumber) {
|
||||
_this.$mdDateLocaleProvider = Preferences.$mdDateLocaleProvider;
|
||||
angular.extend(_this.$mdDateLocaleProvider, data.locale);
|
||||
_this.$mdDateLocaleProvider.firstDayOfWeek = parseInt(data.SOGoFirstDayOfWeek);
|
||||
_this.$mdDateLocaleProvider.weekNumberFormatter = function(weekNumber) {
|
||||
return l('Week %d', weekNumber);
|
||||
};
|
||||
Preferences.$mdDateLocaleProvider.msgCalendar = l('Calender');
|
||||
Preferences.$mdDateLocaleProvider.msgOpenCalendar = l('Open Calendar');
|
||||
Preferences.$mdDateLocaleProvider.parseDate = function(dateString) {
|
||||
return dateString? dateString.parseDate(Preferences.$mdDateLocaleProvider, data.SOGoShortDateFormat) : new Date(NaN);
|
||||
_this.$mdDateLocaleProvider.msgCalendar = l('Calender');
|
||||
_this.$mdDateLocaleProvider.msgOpenCalendar = l('Open Calendar');
|
||||
_this.$mdDateLocaleProvider.parseDate = function(dateString) {
|
||||
return dateString? dateString.parseDate(_this.$mdDateLocaleProvider, data.SOGoShortDateFormat) : new Date(NaN);
|
||||
};
|
||||
Preferences.$mdDateLocaleProvider.formatDate = function(date) {
|
||||
return date? date.format(Preferences.$mdDateLocaleProvider, data.SOGoShortDateFormat) : '';
|
||||
_this.$mdDateLocaleProvider.formatDate = function(date) {
|
||||
return date? date.format(_this.$mdDateLocaleProvider, data.SOGoShortDateFormat) : '';
|
||||
};
|
||||
Preferences.$mdDateLocaleProvider.formatTime = function(date) {
|
||||
return date? date.format(Preferences.$mdDateLocaleProvider, data.SOGoTimeFormat) : '';
|
||||
_this.$mdDateLocaleProvider.formatTime = function(date) {
|
||||
return date? date.format(_this.$mdDateLocaleProvider, data.SOGoTimeFormat) : '';
|
||||
};
|
||||
|
||||
return _this.defaults;
|
||||
|
@ -197,6 +210,10 @@
|
|||
|
||||
preferences.defaults.SOGoMailLabelsColors = labels;
|
||||
|
||||
if (!preferences.defaults.SOGoMailComposeFontSizeEnabled)
|
||||
preferences.defaults.SOGoMailComposeFontSize = 0;
|
||||
delete preferences.defaults.SOGoMailComposeFontSizeEnabled;
|
||||
|
||||
if (preferences.defaults.Vacation) {
|
||||
if (preferences.defaults.Vacation.endDateEnabled)
|
||||
preferences.defaults.Vacation.endDate = preferences.defaults.Vacation.endDate.getTime()/1000;
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -124,6 +124,14 @@
|
|||
_this.$calendars.push(calendar);
|
||||
});
|
||||
}
|
||||
else if (angular.isUndefined(this.$calendars)) {
|
||||
this.$calendars = [];
|
||||
this.$subscriptions = [];
|
||||
this.$webcalendars = [];
|
||||
Calendar.$$resource.fetch('calendarslist').then(function(data) {
|
||||
Calendar.$findAll(data.calendars, writable);
|
||||
});
|
||||
}
|
||||
|
||||
if (writable) {
|
||||
return _.union(this.$calendars, _.filter(this.$subscriptions, function(calendar) { return calendar.acls.objectCreator; }));
|
||||
|
@ -235,25 +243,19 @@
|
|||
* @return a promise of the HTTP operation
|
||||
*/
|
||||
Calendar.$deleteComponents = function(components) {
|
||||
|
||||
// We create a c_folder -> event hash
|
||||
var calendars = {}, _this = this;
|
||||
var _this = this, calendars = {}, promises = [];
|
||||
|
||||
_.forEach(components, function(component) {
|
||||
if (!angular.isDefined(calendars[component.c_folder]))
|
||||
calendars[component.c_folder] = [];
|
||||
|
||||
calendars[component.c_folder].push(component.c_name);
|
||||
if (!angular.isDefined(calendars[component.pid]))
|
||||
calendars[component.pid] = [];
|
||||
calendars[component.pid].push(component.id);
|
||||
});
|
||||
|
||||
_.forEach(calendars, function(uids, c_folder) {
|
||||
Calendar.$$resource.post(c_folder, 'batchDelete', {uids: uids});
|
||||
_.forEach(calendars, function(uids, pid) {
|
||||
promises.push(Calendar.$$resource.post(pid, 'batchDelete', {uids: uids}));
|
||||
});
|
||||
|
||||
// We slice both arrays - might be useful if in the future, we can delete
|
||||
// events and tasks at the same time.
|
||||
_this.$Component.$events = _.difference(_this.$Component.$events, components);
|
||||
_this.$Component.$tasks = _.difference(_this.$Component.$tasks, components);
|
||||
return Calendar.$q.all(promises);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -80,8 +80,12 @@
|
|||
{ ok: l('Delete') })
|
||||
.then(function() {
|
||||
// User confirmed the deletion
|
||||
var components = _.filter(Component['$' + vm.componentType], function(component) { return component.selected; });
|
||||
Calendar.$deleteComponents(components);
|
||||
var components = _.filter(Component['$' + vm.componentType], function(component) {
|
||||
return component.selected;
|
||||
});
|
||||
Calendar.$deleteComponents(components).then(function() {
|
||||
$rootScope.$emit('calendars:list');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
vm.share = share;
|
||||
vm.importCalendar = importCalendar;
|
||||
vm.exportCalendar = exportCalendar;
|
||||
vm.showOnly = showOnly;
|
||||
vm.showAll = showAll;
|
||||
vm.showLinks = showLinks;
|
||||
vm.showProperties = showProperties;
|
||||
vm.subscribeToFolder = subscribeToFolder;
|
||||
|
@ -202,6 +204,19 @@
|
|||
window.location.href = ApplicationBaseURL + '/' + calendar.id + '.ics' + '/export';
|
||||
}
|
||||
|
||||
function showOnly(calendar) {
|
||||
_.forEach(Calendar.$findAll(), function(o) {
|
||||
if (calendar.id == o.id)
|
||||
o.active = 1;
|
||||
else
|
||||
o.active = 0;
|
||||
});
|
||||
}
|
||||
|
||||
function showAll() {
|
||||
_.forEach(Calendar.$findAll(), function(o) { o.active = 1; });
|
||||
}
|
||||
|
||||
function showLinks(calendar) {
|
||||
$mdDialog.show({
|
||||
parent: angular.element(document.body),
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
$Preferences: Preferences,
|
||||
$Card: Card,
|
||||
$gravatar: Gravatar,
|
||||
$$resource: new Resource(Settings.baseURL(), Settings.activeUser()),
|
||||
$$resource: new Resource(Settings.activeUser('folderURL') + 'Calendar', Settings.activeUser()),
|
||||
timeFormat: "%H:%M",
|
||||
// Filter parameters common to events and tasks
|
||||
$query: { value: '', search: 'title_Category_Location' },
|
||||
|
@ -236,35 +236,40 @@
|
|||
* @returns a promise of a collection of objects describing the events blocks
|
||||
*/
|
||||
Component.$eventsBlocksForView = function(view, date) {
|
||||
var viewAction, startDate, endDate, params;
|
||||
var _this = this;
|
||||
|
||||
if (view == 'day') {
|
||||
viewAction = 'dayView';
|
||||
startDate = endDate = date;
|
||||
}
|
||||
else if (view == 'multicolumnday') {
|
||||
viewAction = 'multicolumndayView';
|
||||
startDate = endDate = date;
|
||||
}
|
||||
else if (view == 'week') {
|
||||
viewAction = 'weekView';
|
||||
startDate = date.beginOfWeek();
|
||||
endDate = new Date();
|
||||
endDate.setTime(startDate.getTime());
|
||||
endDate.addDays(6);
|
||||
}
|
||||
else if (view == 'month') {
|
||||
viewAction = 'monthView';
|
||||
startDate = date;
|
||||
startDate.setDate(1);
|
||||
startDate = startDate.beginOfWeek();
|
||||
endDate = new Date();
|
||||
endDate.setTime(startDate.getTime());
|
||||
endDate.setMonth(endDate.getMonth() + 1);
|
||||
endDate.addDays(-1);
|
||||
endDate = endDate.endOfWeek();
|
||||
}
|
||||
return this.$eventsBlocks(viewAction, startDate, endDate);
|
||||
return Component.$Preferences.ready().then(function(data) {
|
||||
var firstDayOfWeek, viewAction, startDate, endDate, params;
|
||||
firstDayOfWeek = Component.$Preferences.defaults.SOGoFirstDayOfWeek;
|
||||
|
||||
if (view == 'day') {
|
||||
viewAction = 'dayView';
|
||||
startDate = endDate = date;
|
||||
}
|
||||
else if (view == 'multicolumnday') {
|
||||
viewAction = 'multicolumndayView';
|
||||
startDate = endDate = date;
|
||||
}
|
||||
else if (view == 'week') {
|
||||
viewAction = 'weekView';
|
||||
startDate = date.beginOfWeek(firstDayOfWeek);
|
||||
endDate = new Date();
|
||||
endDate.setTime(startDate.getTime());
|
||||
endDate.addDays(6);
|
||||
}
|
||||
else if (view == 'month') {
|
||||
viewAction = 'monthView';
|
||||
startDate = date;
|
||||
startDate.setDate(1);
|
||||
startDate = startDate.beginOfWeek(firstDayOfWeek);
|
||||
endDate = new Date();
|
||||
endDate.setTime(startDate.getTime());
|
||||
endDate.setMonth(endDate.getMonth() + 1);
|
||||
endDate.addDays(-1);
|
||||
endDate = endDate.endOfWeek(firstDayOfWeek);
|
||||
}
|
||||
return _this.$eventsBlocks(viewAction, startDate, endDate);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,17 +23,6 @@ CKEDITOR.editorConfig = function( config ) {
|
|||
config.tabSpaces = 4;
|
||||
config.allowedContent = true; // don't filter tags
|
||||
|
||||
// The list of fonts size to be displayed in the Font Size combo in the toolbar.
|
||||
config.fontSize_sizes = '8/8px;9/9px;10/10px;11/11px;12/12px;13/13px;14/14px;16/16px;18/18px;20/20px;22/22px;24/24px;26/26px;28/28px;36/36px;48/48px;72/72px';
|
||||
|
||||
// Explicitly show the default site font size to the end user (as defined in contents.css)
|
||||
config.fontSize_defaultLabel = '13px';
|
||||
|
||||
// The CSS file(s) to be used to apply style to editor content.
|
||||
// For example, the following ck.css could overwrite the font-size of .cke_editable
|
||||
//config.contentsCss = ['/SOGo.woa/WebServerResources/js/vendor/ckeditor/contents.css', // default CSS
|
||||
// '/css/ck.css']; // custom CSS
|
||||
|
||||
// Disables the built-in words spell checker if browser provides one. Defaults to true.
|
||||
// http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-disableNativeSpellChecker
|
||||
//config.disableNativeSpellChecker = false;
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue