merge of '3f8608c82d9c379cc9e0fea6ffe853cc1949a24b'
and '7634b9d503d61b6240548e75e7f872081debc6b4' Monotone-Parent: 3f8608c82d9c379cc9e0fea6ffe853cc1949a24b Monotone-Parent: 7634b9d503d61b6240548e75e7f872081debc6b4 Monotone-Revision: a4c7e35d642045e63467a131fae8049524f450e4 Monotone-Author: flachapelle@inverse.ca Monotone-Date: 2012-06-30T18:20:30 Monotone-Branch: ca.inverse.sogomaint-2.0.2
commit
09504ed1cb
32
ChangeLog
32
ChangeLog
|
@ -3,6 +3,38 @@
|
||||||
* SoObjects/SOGo/SOGoObject.m (-initWithName:inContainer:): make
|
* SoObjects/SOGo/SOGoObject.m (-initWithName:inContainer:): make
|
||||||
sure that "_name" is neither nil nor empty.
|
sure that "_name" is neither nil nor empty.
|
||||||
|
|
||||||
|
2012-06-27 Jean Raby <jraby@inverse.ca>
|
||||||
|
|
||||||
|
* SoObjects/Appointments/SOGoAppointmentObject.m
|
||||||
|
(PUTAction:): detect conflicting event UID and
|
||||||
|
deny the request accordingly.
|
||||||
|
|
||||||
|
2012-06-21 Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||||
|
|
||||||
|
* Added the SOGoSearchMinimumWordLength domain
|
||||||
|
default which controls the minimal length required
|
||||||
|
before trigging server-side search operations for
|
||||||
|
attendee completion, contact searches, etc. The
|
||||||
|
default value is 2, which means search operations
|
||||||
|
are trigged once the 3rd character is typed.
|
||||||
|
|
||||||
|
2012-06-20 Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||||
|
|
||||||
|
* SoObjects/Appointments/SOGoAppointmentObject.m
|
||||||
|
(-_handleResourcesConflicts:forEvent:): We now
|
||||||
|
handle correctly recurring events overlapping other
|
||||||
|
recurring events.
|
||||||
|
|
||||||
|
2012-06-19 Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||||
|
|
||||||
|
* SoObjects/Appointments/SOGoAppointmentFolder.m (-importCalendar:):
|
||||||
|
We now handle correctly floating events by forcing the use of
|
||||||
|
the user's timezone.
|
||||||
|
|
||||||
|
* SoObjects/Appointments/SOGoCalendarComponent.m (-expandGroupsInEvent:):
|
||||||
|
We now remove all attendees that are equal (email-based comparison) to
|
||||||
|
the event's organizer instead of only for decomposed groups.
|
||||||
|
|
||||||
2012-06-12 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
2012-06-12 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
|
|
||||||
* SoObjects/SOGo/SOGoGroup.m
|
* SoObjects/SOGo/SOGoGroup.m
|
||||||
|
|
Binary file not shown.
12
NEWS
12
NEWS
|
@ -1,3 +1,14 @@
|
||||||
|
1.3.17 (2012-MM-DD)
|
||||||
|
-------------------
|
||||||
|
New Features
|
||||||
|
-
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
- updated Czech translation
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
-
|
||||||
|
|
||||||
1.3.16 (2012-06-07)
|
1.3.16 (2012-06-07)
|
||||||
-------------------
|
-------------------
|
||||||
Enhancements
|
Enhancements
|
||||||
|
@ -8,6 +19,7 @@ Enhancements
|
||||||
- it's no longer possible to click the "Upload" button multiple times
|
- it's no longer possible to click the "Upload" button multiple times
|
||||||
- allow delivery of mail with no subject, but alert the user
|
- allow delivery of mail with no subject, but alert the user
|
||||||
- updated Dutch, German, French translations
|
- updated Dutch, German, French translations
|
||||||
|
|
||||||
Bug Fixes
|
Bug Fixes
|
||||||
- fixed compilation under GNU/kFreeBSD
|
- fixed compilation under GNU/kFreeBSD
|
||||||
- fixed compilation for arm architecture
|
- fixed compilation for arm architecture
|
||||||
|
|
|
@ -181,7 +181,7 @@
|
||||||
{
|
{
|
||||||
static NSString *sqlFolderFormat
|
static NSString *sqlFolderFormat
|
||||||
= (@"CREATE TABLE %@ ("
|
= (@"CREATE TABLE %@ ("
|
||||||
@" c_id VARCHAR(255) PRIMARY KEY,"
|
@" c_id VARCHAR(255) NOT NULL PRIMARY KEY,"
|
||||||
@" c_value VARCHAR(255) NOT NULL,"
|
@" c_value VARCHAR(255) NOT NULL,"
|
||||||
@" c_creationdate INT4 NOT NULL,"
|
@" c_creationdate INT4 NOT NULL,"
|
||||||
@" c_lastseen INT4 NOT NULL)");
|
@" c_lastseen INT4 NOT NULL)");
|
||||||
|
|
|
@ -67,4 +67,4 @@ vtodo_class2 = "(Skrytý úkol)";
|
||||||
= "%{Attendee} %{SentByText}dosud o Vaší pozvánce k události nerozhodl/a.";
|
= "%{Attendee} %{SentByText}dosud o Vaší pozvánce k události nerozhodl/a.";
|
||||||
|
|
||||||
/* Resources */
|
/* Resources */
|
||||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"." = "Maximální počet současných rezervací (%{NumberOfSimultaneousBookings}) byl dosažen pro zdroj \"%{Cn} %{SystemEmail}\".";
|
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Byl dosažen maximální počet současných rezervací\n(%{NumberOfSimultaneousBookings}) pro zdroj \"%{Cn} %{SystemEmail}\". Konfliktní událost je \"%{EventTitle}\" a začíná %{StartDate}.";
|
|
@ -2799,6 +2799,41 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||||
tzId = [startDate value: 0 ofAttribute: @"tzid"];
|
tzId = [startDate value: 0 ofAttribute: @"tzid"];
|
||||||
if ([tzId length])
|
if ([tzId length])
|
||||||
timezone = [timezones valueForKey: tzId];
|
timezone = [timezones valueForKey: tzId];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If the start date is a "floating time", let's use the user's timezone
|
||||||
|
// during the import for both the start and end dates.
|
||||||
|
NSString *s;
|
||||||
|
|
||||||
|
s = [[startDate valuesAtIndex: 0 forKey: @""] objectAtIndex: 0];
|
||||||
|
|
||||||
|
if ([element isKindOfClass: [iCalEvent class]] &&
|
||||||
|
![(iCalEvent *)element isAllDay] &&
|
||||||
|
![s hasSuffix: @"Z"] &&
|
||||||
|
![s hasSuffix: @"z"])
|
||||||
|
{
|
||||||
|
iCalDateTime *endDate;
|
||||||
|
int delta;
|
||||||
|
|
||||||
|
timezone = [iCalTimeZone timeZoneForName: [[[self->context activeUser] userDefaults] timeZoneName]];
|
||||||
|
[calendar addTimeZone: timezone];
|
||||||
|
|
||||||
|
delta = [[timezone periodForDate: [startDate dateTime]] secondsOffsetFromGMT];
|
||||||
|
event = (iCalEvent *)element;
|
||||||
|
|
||||||
|
[event setStartDate: [[event startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -delta]];
|
||||||
|
[startDate setTimeZone: timezone];
|
||||||
|
|
||||||
|
endDate = (iCalDateTime *) [element uniqueChildWithTag: @"dtend"];
|
||||||
|
|
||||||
|
if (endDate)
|
||||||
|
{
|
||||||
|
[event setEndDate: [[event endDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -delta]];
|
||||||
|
[endDate setTimeZone: timezone];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ([element isKindOfClass: [iCalEvent class]])
|
if ([element isKindOfClass: [iCalEvent class]])
|
||||||
{
|
{
|
||||||
event = (iCalEvent *)element;
|
event = (iCalEvent *)element;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2007-2011 Inverse inc.
|
Copyright (C) 2007-2012 Inverse inc.
|
||||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||||
|
|
||||||
This file is part of SOGo
|
This file is part of SOGo
|
||||||
|
@ -456,8 +456,12 @@
|
||||||
{
|
{
|
||||||
SOGoAppointmentFolder *folder;
|
SOGoAppointmentFolder *folder;
|
||||||
NSCalendarDate *start, *end;
|
NSCalendarDate *start, *end;
|
||||||
|
NGCalendarDateRange *range;
|
||||||
NSMutableArray *fbInfo;
|
NSMutableArray *fbInfo;
|
||||||
int i;
|
NSArray *allOccurences;
|
||||||
|
|
||||||
|
BOOL must_delete;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
// We get the start/end date for our conflict range. If the event to be added is recurring, we
|
// 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.
|
// check for at least a year to start with.
|
||||||
|
@ -486,10 +490,48 @@
|
||||||
// We first remove any occurences in the freebusy that corresponds to the
|
// 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
|
// 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.
|
// 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--)
|
for (i = [fbInfo count]-1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
|
range = [NGCalendarDateRange calendarDateRangeWithStartDate: [[fbInfo objectAtIndex: i] objectForKey: @"startDate"]
|
||||||
|
endDate: [[fbInfo objectAtIndex: i] objectForKey: @"endDate"]];
|
||||||
|
|
||||||
if ([[[fbInfo objectAtIndex: i] objectForKey: @"c_uid"] compare: [theEvent uid]] == NSOrderedSame)
|
if ([[[fbInfo objectAtIndex: i] objectForKey: @"c_uid"] compare: [theEvent uid]] == NSOrderedSame)
|
||||||
|
{
|
||||||
[fbInfo removeObjectAtIndex: i];
|
[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]])
|
||||||
|
{
|
||||||
|
must_delete = NO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (must_delete)
|
||||||
|
[fbInfo removeObjectAtIndex: i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([fbInfo count])
|
if ([fbInfo count])
|
||||||
|
@ -1714,16 +1756,26 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
{
|
{
|
||||||
iCalCalendar *calendar;
|
iCalCalendar *calendar;
|
||||||
SOGoUser *ownerUser;
|
SOGoUser *ownerUser;
|
||||||
iCalEvent *event;
|
iCalEvent *event, *conflictingEvent;
|
||||||
|
|
||||||
|
NSString *eventUID;
|
||||||
BOOL scheduling;
|
BOOL scheduling;
|
||||||
|
|
||||||
calendar = [iCalCalendar parseSingleFromSource: [rq contentAsString]];
|
calendar = [iCalCalendar parseSingleFromSource: [rq contentAsString]];
|
||||||
|
|
||||||
event = [[calendar events] objectAtIndex: 0];
|
event = [[calendar events] objectAtIndex: 0];
|
||||||
|
eventUID = [event uid];
|
||||||
ownerUser = [SOGoUser userWithLogin: owner];
|
ownerUser = [SOGoUser userWithLogin: owner];
|
||||||
scheduling = [self _shouldScheduleEvent: [event organizer]];
|
scheduling = [self _shouldScheduleEvent: [event organizer]];
|
||||||
|
|
||||||
|
// make sure eventUID doesn't conflict with an existing event - see bug #1853
|
||||||
|
// TODO: send out a no-uid-conflict (DAV:href) xml element (rfc4791 section 5.3.2.1)
|
||||||
|
if (conflictingEvent = [container resourceNameForEventUID: eventUID])
|
||||||
|
{
|
||||||
|
NSString *reason = [NSString stringWithFormat: @"Event UID already in use. (%s)", eventUID];
|
||||||
|
return [NSException exceptionWithHTTPStatus:403 reason: reason];
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// New event and we're the organizer -- send invitation to all attendees
|
// New event and we're the organizer -- send invitation to all attendees
|
||||||
//
|
//
|
||||||
|
|
|
@ -516,19 +516,23 @@ static inline BOOL _occurenceHasID (iCalRepeatableEntityObject *occurence,
|
||||||
//
|
//
|
||||||
// Returs "YES" if a a group was decomposed among attendees.
|
// Returs "YES" if a a group was decomposed among attendees.
|
||||||
//
|
//
|
||||||
|
// It can also return yes if an attendee was found in the list
|
||||||
|
// matching the organizer. In which case, it was removed.
|
||||||
|
//
|
||||||
- (BOOL) expandGroupsInEvent: (iCalEvent *) theEvent
|
- (BOOL) expandGroupsInEvent: (iCalEvent *) theEvent
|
||||||
{
|
{
|
||||||
NSMutableArray *allAttendees;
|
|
||||||
NSEnumerator *enumerator;
|
|
||||||
NSString *organizerEmail, *domain;
|
NSString *organizerEmail, *domain;
|
||||||
|
NSMutableArray *allAttendees;
|
||||||
iCalPerson *currentAttendee;
|
iCalPerson *currentAttendee;
|
||||||
|
NSEnumerator *enumerator;
|
||||||
SOGoGroup *group;
|
SOGoGroup *group;
|
||||||
BOOL doesIncludeGroup;
|
|
||||||
|
BOOL eventWasModified;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
domain = [[context activeUser] domain];
|
domain = [[context activeUser] domain];
|
||||||
organizerEmail = [[theEvent organizer] rfc822Email];
|
organizerEmail = [[theEvent organizer] rfc822Email];
|
||||||
doesIncludeGroup = NO;
|
eventWasModified = NO;
|
||||||
allAttendees = [NSMutableArray arrayWithArray: [theEvent attendees]];
|
allAttendees = [NSMutableArray arrayWithArray: [theEvent attendees]];
|
||||||
enumerator = [[theEvent attendees] objectEnumerator];
|
enumerator = [[theEvent attendees] objectEnumerator];
|
||||||
while ((currentAttendee = [enumerator nextObject]))
|
while ((currentAttendee = [enumerator nextObject]))
|
||||||
|
@ -548,7 +552,7 @@ static inline BOOL _occurenceHasID (iCalRepeatableEntityObject *occurence,
|
||||||
for (i = 0; i < [members count]; i++)
|
for (i = 0; i < [members count]; i++)
|
||||||
{
|
{
|
||||||
user = [members objectAtIndex: i];
|
user = [members objectAtIndex: i];
|
||||||
doesIncludeGroup = YES;
|
eventWasModified = YES;
|
||||||
|
|
||||||
// If the organizer is part of the group, we skip it from
|
// If the organizer is part of the group, we skip it from
|
||||||
// the addition to the attendees' list
|
// the addition to the attendees' list
|
||||||
|
@ -565,12 +569,23 @@ static inline BOOL _occurenceHasID (iCalRepeatableEntityObject *occurence,
|
||||||
[allAttendees addObject: person];
|
[allAttendees addObject: person];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We remove any attendees matching the organizer. Apple iCal will do that when
|
||||||
|
// you invite someone. It'll add the organizer in the attendee list, which will
|
||||||
|
// confuse itself!
|
||||||
|
if ([[currentAttendee rfc822Email] caseInsensitiveCompare: organizerEmail] == NSOrderedSame)
|
||||||
|
{
|
||||||
|
[allAttendees removeObject: currentAttendee];
|
||||||
|
eventWasModified = YES;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} // while (currentAttendee ...
|
||||||
|
|
||||||
if (doesIncludeGroup)
|
if (eventWasModified)
|
||||||
[theEvent setAttendees: allAttendees];
|
[theEvent setAttendees: allAttendees];
|
||||||
|
|
||||||
return doesIncludeGroup;
|
return eventWasModified;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) _updateRecurrenceIDsWithEvent: (iCalRepeatableEntityObject*) newEvent
|
- (void) _updateRecurrenceIDsWithEvent: (iCalRepeatableEntityObject*) newEvent
|
||||||
|
|
|
@ -70,4 +70,6 @@
|
||||||
|
|
||||||
SOGoReminderEnabled = YES;
|
SOGoReminderEnabled = YES;
|
||||||
SOGoRemindWithASound = YES;
|
SOGoRemindWithASound = YES;
|
||||||
|
|
||||||
|
SOGoSearchMinimumWordLength = 2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,8 @@
|
||||||
|
|
||||||
- (BOOL) hideSystemEMail;
|
- (BOOL) hideSystemEMail;
|
||||||
|
|
||||||
|
- (int) searchMinimumWordLength;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#endif /* SOGODOMAINDEFAULTS_H */
|
#endif /* SOGODOMAINDEFAULTS_H */
|
||||||
|
|
|
@ -319,4 +319,9 @@
|
||||||
return [self boolForKey: @"SOGoHideSystemEMail"];
|
return [self boolForKey: @"SOGoHideSystemEMail"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (int) searchMinimumWordLength
|
||||||
|
{
|
||||||
|
return [self integerForKey: @"SOGoSearchMinimumWordLength"];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
# attendee1_delegate_username and superuser.
|
# attendee1_delegate_username and superuser.
|
||||||
# when writing new tests, avoid using superuser when not absolutely needed
|
# when writing new tests, avoid using superuser when not absolutely needed
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
# - Individual tests should set the ACLs themselves on Resources tests
|
||||||
|
|
||||||
from config import hostname, port, username, password, \
|
from config import hostname, port, username, password, \
|
||||||
superuser, superuser_password, \
|
superuser, superuser_password, \
|
||||||
attendee1, attendee1_username, \
|
attendee1, attendee1_username, \
|
||||||
|
@ -124,6 +127,8 @@ class CalDAVSchedulingTest(unittest.TestCase):
|
||||||
self.user_calendar = "/SOGo/dav/%s/Calendar/personal/" % username
|
self.user_calendar = "/SOGo/dav/%s/Calendar/personal/" % username
|
||||||
self.attendee1_calendar = "/SOGo/dav/%s/Calendar/personal/" % attendee1
|
self.attendee1_calendar = "/SOGo/dav/%s/Calendar/personal/" % attendee1
|
||||||
self.attendee1_delegate_calendar = "/SOGo/dav/%s/Calendar/personal/" % attendee1_delegate
|
self.attendee1_delegate_calendar = "/SOGo/dav/%s/Calendar/personal/" % attendee1_delegate
|
||||||
|
self.res_calendar = "/SOGo/dav/%s/Calendar/personal/" % resource_no_overbook
|
||||||
|
self.res_ob_calendar = "/SOGo/dav/%s/Calendar/personal/" % resource_can_overbook
|
||||||
|
|
||||||
# fetch non existing event to let sogo create the calendars in the db
|
# fetch non existing event to let sogo create the calendars in the db
|
||||||
self._getEvent(self.client, "%snonexistent" % self.user_calendar, exp_status=404)
|
self._getEvent(self.client, "%snonexistent" % self.user_calendar, exp_status=404)
|
||||||
|
@ -131,35 +136,24 @@ class CalDAVSchedulingTest(unittest.TestCase):
|
||||||
self._getEvent(self.attendee1_delegate_client, "%snonexistent" %
|
self._getEvent(self.attendee1_delegate_client, "%snonexistent" %
|
||||||
self.attendee1_delegate_calendar, exp_status=404)
|
self.attendee1_delegate_calendar, exp_status=404)
|
||||||
|
|
||||||
|
# list of ics used by the test.
|
||||||
|
# tearDown will loop over this and wipe them in all users' calendar
|
||||||
|
self.ics_list = []
|
||||||
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self._deleteEvent(self.client,
|
# delete all created events from all users' calendar
|
||||||
"%stest-delegation.ics" % self.user_calendar, None)
|
for ics in self.ics_list:
|
||||||
self._deleteEvent(self.attendee1_client,
|
self._deleteEvent(self.superuser_client,
|
||||||
"%stest-delegation.ics" % self.attendee1_calendar, None)
|
"%s%s" % (self.user_calendar, ics), None)
|
||||||
self._deleteEvent(self.attendee1_delegate_client,
|
self._deleteEvent(self.superuser_client,
|
||||||
"%stest-delegation.ics" % self.attendee1_delegate_calendar,
|
"%s%s" % (self.attendee1_calendar, ics), None)
|
||||||
None)
|
self._deleteEvent(self.superuser_client,
|
||||||
self._deleteEvent(self.client,
|
"%s%s" % (self.attendee1_delegate_calendar, ics), None)
|
||||||
"%stest-add-attendee.ics" % self.user_calendar, None)
|
self._deleteEvent(self.superuser_client,
|
||||||
self._deleteEvent(self.attendee1_client,
|
"%s%s" % (self.res_calendar, ics), None)
|
||||||
"%stest-add-attendee.ics" % self.attendee1_calendar, None)
|
self._deleteEvent(self.superuser_client,
|
||||||
self._deleteEvent(self.client,
|
"%s%s" % (self.res_ob_calendar, ics), None)
|
||||||
"%stest-no-overbook.ics" % self.user_calendar, None)
|
|
||||||
self._deleteEvent(self.client,
|
|
||||||
"%stest-no-overbook-overlap.ics" % self.user_calendar, None)
|
|
||||||
self._deleteEvent(self.client,
|
|
||||||
"%stest-can-overbook.ics" % self.user_calendar, None)
|
|
||||||
self._deleteEvent(self.client,
|
|
||||||
"%stest-can-overbook-overlap.ics" % self.user_calendar, None)
|
|
||||||
self._deleteEvent(self.client,
|
|
||||||
"%stest-rrule-exception-invitation-dance.ics" % self.user_calendar, None)
|
|
||||||
self._deleteEvent(self.attendee1_client,
|
|
||||||
"%stest-rrule-exception-invitation-dance.ics" % self.attendee1_calendar, None)
|
|
||||||
self._deleteEvent(self.client,
|
|
||||||
"%stest-rrule-invitation-deleted-exdate-dance.ics" % self.user_calendar, None)
|
|
||||||
self._deleteEvent(self.attendee1_client,
|
|
||||||
"%stest-rrule-invitation-deleted-exdate-dance.ics" % self.attendee1_calendar, None)
|
|
||||||
|
|
||||||
def _newEvent(self, summary="test event", uid="test", transp=0):
|
def _newEvent(self, summary="test event", uid="test", transp=0):
|
||||||
transparency = ("OPAQUE", "TRANSPARENT")
|
transparency = ("OPAQUE", "TRANSPARENT")
|
||||||
|
@ -219,6 +213,25 @@ class CalDAVSchedulingTest(unittest.TestCase):
|
||||||
if exp_status is not None:
|
if exp_status is not None:
|
||||||
self.assertEquals(delete.response["status"], exp_status)
|
self.assertEquals(delete.response["status"], exp_status)
|
||||||
|
|
||||||
|
def _getAllEvents(self, client, collection, exp_status = 207):
|
||||||
|
propfind = webdavlib.WebDAVPROPFIND(collection, None)
|
||||||
|
client.execute(propfind)
|
||||||
|
if exp_status is not None:
|
||||||
|
self.assertEquals(propfind.response["status"], exp_status)
|
||||||
|
|
||||||
|
content = []
|
||||||
|
nodes = propfind.response["document"].findall('{DAV:}response')
|
||||||
|
for node in nodes:
|
||||||
|
responseHref = node.find('{DAV:}href').text
|
||||||
|
content += [responseHref]
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
def _deleteAllEvents(self, client, collection, exp_status = 204):
|
||||||
|
content = self._getAllEvents(client, collection)
|
||||||
|
for item in content:
|
||||||
|
self._deleteEvent(client, item)
|
||||||
|
|
||||||
def _eventAttendees(self, event):
|
def _eventAttendees(self, event):
|
||||||
attendees = {}
|
attendees = {}
|
||||||
|
|
||||||
|
@ -269,6 +282,8 @@ class CalDAVSchedulingTest(unittest.TestCase):
|
||||||
|
|
||||||
# make sure the event doesn't exist
|
# make sure the event doesn't exist
|
||||||
ics_name = "test-add-attendee.ics"
|
ics_name = "test-add-attendee.ics"
|
||||||
|
self.ics_list += [ics_name]
|
||||||
|
|
||||||
self._deleteEvent(self.client,
|
self._deleteEvent(self.client,
|
||||||
"%s%s" % (self.user_calendar,ics_name), None)
|
"%s%s" % (self.user_calendar,ics_name), None)
|
||||||
self._deleteEvent(self.attendee1_client,
|
self._deleteEvent(self.attendee1_client,
|
||||||
|
@ -304,6 +319,8 @@ class CalDAVSchedulingTest(unittest.TestCase):
|
||||||
|
|
||||||
# make sure the event doesn't exist
|
# make sure the event doesn't exist
|
||||||
ics_name = "test-remove-attendee.ics"
|
ics_name = "test-remove-attendee.ics"
|
||||||
|
self.ics_list += [ics_name]
|
||||||
|
|
||||||
self._deleteEvent(self.client,
|
self._deleteEvent(self.client,
|
||||||
"%s%s" % (self.user_calendar,ics_name), None)
|
"%s%s" % (self.user_calendar,ics_name), None)
|
||||||
self._deleteEvent(self.attendee1_client,
|
self._deleteEvent(self.attendee1_client,
|
||||||
|
@ -346,51 +363,22 @@ class CalDAVSchedulingTest(unittest.TestCase):
|
||||||
# 6. verify that the attendee doesn't have the event anymore
|
# 6. verify that the attendee doesn't have the event anymore
|
||||||
attendee_event = self._getEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name), 404)
|
attendee_event = self._getEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name), 404)
|
||||||
|
|
||||||
def testAddAttendee(self):
|
|
||||||
""" add attendee after event creation """
|
|
||||||
|
|
||||||
# make sure the event doesn't exist
|
|
||||||
ics_name = "test-add-attendee.ics"
|
|
||||||
self._deleteEvent(self.client,
|
|
||||||
"%s%s" % (self.user_calendar,ics_name), None)
|
|
||||||
self._deleteEvent(self.client,
|
|
||||||
"%s%s" % (self.attendee1_calendar,ics_name), None)
|
|
||||||
|
|
||||||
# 1. create an event in the organiser's calendar
|
|
||||||
event = self._newEvent(summary="Test add attendee", uid="Test add attendee")
|
|
||||||
organizer = event.vevent.add('organizer')
|
|
||||||
organizer.cn_param = self.user_name
|
|
||||||
organizer.value = self.user_email
|
|
||||||
self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event)
|
|
||||||
|
|
||||||
# 2. add an attendee
|
|
||||||
attendee = event.vevent.add('attendee')
|
|
||||||
attendee.cn_param = self.attendee1_name
|
|
||||||
attendee.rsvp_param = "TRUE"
|
|
||||||
attendee.partstat_param = "NEEDS-ACTION"
|
|
||||||
attendee.value = self.attendee1_email
|
|
||||||
self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event,
|
|
||||||
exp_status=204)
|
|
||||||
|
|
||||||
|
|
||||||
# 3. verify that the attendee has the event
|
|
||||||
attendee_event = self._getEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name))
|
|
||||||
|
|
||||||
# 4. make sure the received event match the original one
|
|
||||||
# XXX is this enough?
|
|
||||||
self.assertEquals(event.vevent.uid, attendee_event.vevent.uid)
|
|
||||||
|
|
||||||
def testResourceNoOverbook(self):
|
def testResourceNoOverbook(self):
|
||||||
""" try to overbook a resource """
|
""" try to overbook a resource """
|
||||||
|
|
||||||
|
# make sure there are no events in the resource calendar
|
||||||
|
self._deleteAllEvents(self.superuser_client, self.res_calendar)
|
||||||
|
|
||||||
# make sure the event doesn't exist
|
# make sure the event doesn't exist
|
||||||
ics_name = "test-no-overbook.ics"
|
ics_name = "test-no-overbook.ics"
|
||||||
|
self.ics_list += [ics_name]
|
||||||
self._deleteEvent(self.client,
|
self._deleteEvent(self.client,
|
||||||
"%s%s" % (self.user_calendar,ics_name), None)
|
"%s%s" % (self.user_calendar,ics_name), None)
|
||||||
|
|
||||||
ob_ics_name = "test-no-overbook-overlap.ics"
|
ob_ics_name = "test-no-overbook-overlap.ics"
|
||||||
|
self.ics_list += [ob_ics_name]
|
||||||
self._deleteEvent(self.client,
|
self._deleteEvent(self.client,
|
||||||
"%s%s" % (self.user_calendar,ics_name), None)
|
"%s%s" % (self.user_calendar,ob_ics_name), None)
|
||||||
|
|
||||||
# 1. create an event in the organiser's calendar
|
# 1. create an event in the organiser's calendar
|
||||||
event = self._newEvent(summary="Test no overbook", uid="test no overbook")
|
event = self._newEvent(summary="Test no overbook", uid="test no overbook")
|
||||||
|
@ -421,12 +409,17 @@ class CalDAVSchedulingTest(unittest.TestCase):
|
||||||
def testResourceCanOverbook(self):
|
def testResourceCanOverbook(self):
|
||||||
""" try to overbook a resource - multiplebookings=0"""
|
""" try to overbook a resource - multiplebookings=0"""
|
||||||
|
|
||||||
|
# make sure there are no events in the resource calendar
|
||||||
|
self._deleteAllEvents(self.superuser_client, self.res_ob_calendar)
|
||||||
|
|
||||||
# make sure the event doesn't exist
|
# make sure the event doesn't exist
|
||||||
ics_name = "test-can-overbook.ics"
|
ics_name = "test-can-overbook.ics"
|
||||||
|
self.ics_list += [ics_name]
|
||||||
self._deleteEvent(self.client,
|
self._deleteEvent(self.client,
|
||||||
"%s%s" % (self.user_calendar,ics_name), None)
|
"%s%s" % (self.user_calendar,ics_name), None)
|
||||||
|
|
||||||
ob_ics_name = "test-can-overbook-overlap.ics"
|
ob_ics_name = "test-can-overbook-overlap.ics"
|
||||||
|
self.ics_list += [ob_ics_name]
|
||||||
self._deleteEvent(self.client,
|
self._deleteEvent(self.client,
|
||||||
"%s%s" % (self.user_calendar,ob_ics_name), None)
|
"%s%s" % (self.user_calendar,ob_ics_name), None)
|
||||||
|
|
||||||
|
@ -456,6 +449,95 @@ class CalDAVSchedulingTest(unittest.TestCase):
|
||||||
# put the event - should be fine since we can overbook this one
|
# put the event - should be fine since we can overbook this one
|
||||||
self._putEvent(self.client, "%s%s" % (self.user_calendar, ob_ics_name), event)
|
self._putEvent(self.client, "%s%s" % (self.user_calendar, ob_ics_name), event)
|
||||||
|
|
||||||
|
def testResourceBookingOverlapDetection(self):
|
||||||
|
""" Resource booking overlap detection - bug #1837"""
|
||||||
|
|
||||||
|
# There used to be some problems with recurring events and resources booking
|
||||||
|
# This test implements these edge cases
|
||||||
|
|
||||||
|
# 1. Create recurring event (with resource)
|
||||||
|
# 2. Create single event overlaping one instance for the previous event
|
||||||
|
# (should fail)
|
||||||
|
# 3. Create recurring event which _doesn't_ overlap the first event
|
||||||
|
# (should be OK, used to fail pre1.3.17)
|
||||||
|
# 4. Create recurring event overlapping the previous recurring event
|
||||||
|
# (should fail)
|
||||||
|
|
||||||
|
# make sure there are no events in the resource calendar
|
||||||
|
self._deleteAllEvents(self.superuser_client, self.res_calendar)
|
||||||
|
|
||||||
|
# make sure the event doesn't exist
|
||||||
|
ics_name = "test-res-overlap-detection.ics"
|
||||||
|
self.ics_list += [ics_name]
|
||||||
|
self._deleteEvent(self.client,
|
||||||
|
"%s%s" % (self.user_calendar,ics_name), None)
|
||||||
|
|
||||||
|
overlap_ics_name = "test-res-overlap-detection-overlap.ics"
|
||||||
|
self.ics_list += [overlap_ics_name]
|
||||||
|
self._deleteEvent(self.client,
|
||||||
|
"%s%s" % (self.attendee1_calendar,overlap_ics_name), None)
|
||||||
|
|
||||||
|
nooverlap_recurring_ics_name = "test-res-overlap-detection-nooverlap.ics"
|
||||||
|
self.ics_list += [nooverlap_recurring_ics_name]
|
||||||
|
self._deleteEvent(self.client,
|
||||||
|
"%s%s" % (self.user_calendar,nooverlap_recurring_ics_name), None)
|
||||||
|
|
||||||
|
overlap_recurring_ics_name = "test-res-overlap-detection-overlap-recurring.ics"
|
||||||
|
self.ics_list += [overlap_recurring_ics_name]
|
||||||
|
self._deleteEvent(self.client,
|
||||||
|
"%s%s" % (self.user_calendar,overlap_recurring_ics_name), None)
|
||||||
|
|
||||||
|
# 1. create recurring event with resource
|
||||||
|
event = self._newEvent(summary="recurring event with resource",
|
||||||
|
uid="recurring event w resource")
|
||||||
|
event.vevent.add('rrule').value = "FREQ=DAILY;COUNT=5"
|
||||||
|
organizer = event.vevent.add('organizer')
|
||||||
|
organizer.cn_param = self.user_name
|
||||||
|
organizer.value = self.user_email
|
||||||
|
attendee = event.vevent.add('attendee')
|
||||||
|
attendee.cn_param = self.res_no_ob_name
|
||||||
|
attendee.rsvp_param = "TRUE"
|
||||||
|
attendee.partstat_param = "NEEDS-ACTION"
|
||||||
|
attendee.value = self.res_no_ob_email
|
||||||
|
|
||||||
|
# keep a copy around for #3
|
||||||
|
nooverlap_event = vobject.iCalendar()
|
||||||
|
nooverlap_event.copy(event)
|
||||||
|
|
||||||
|
self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event)
|
||||||
|
|
||||||
|
# 2. Create single event overlaping one instance for the previous event
|
||||||
|
event = self._newEvent(summary="recurring event with resource",
|
||||||
|
uid="recurring event w resource - overlap")
|
||||||
|
organizer = event.vevent.add('organizer')
|
||||||
|
organizer.cn_param = self.attendee1_name
|
||||||
|
organizer.value = self.attendee1_email
|
||||||
|
attendee = event.vevent.add('attendee')
|
||||||
|
attendee.cn_param = self.res_no_ob_name
|
||||||
|
attendee.rsvp_param = "TRUE"
|
||||||
|
attendee.partstat_param = "NEEDS-ACTION"
|
||||||
|
attendee.value = self.res_no_ob_email
|
||||||
|
# should fail
|
||||||
|
self._putEvent(self.client, "%s%s" % (self.attendee1_calendar, overlap_ics_name), event, exp_status=403)
|
||||||
|
|
||||||
|
# 3. Create recurring event which _doesn't_ overlap the first event
|
||||||
|
# (should be OK, used to fail pre1.3.17)
|
||||||
|
# shift the start date to one hour after the original event end time
|
||||||
|
nstartdate = nooverlap_event.vevent.dtend.value + datetime.timedelta(0, 3600)
|
||||||
|
nooverlap_event.vevent.dtstart.value = nstartdate
|
||||||
|
nooverlap_event.vevent.dtend.value = nstartdate + datetime.timedelta(0, 3600)
|
||||||
|
nooverlap_event.vevent.uid.value = "recurring - nooverlap"
|
||||||
|
|
||||||
|
self._putEvent(self.client, "%s%s" % (self.user_calendar, nooverlap_recurring_ics_name), nooverlap_event)
|
||||||
|
|
||||||
|
# 4. Create recurring event overlapping the previous recurring event
|
||||||
|
# should fail
|
||||||
|
nstartdate = nooverlap_event.vevent.dtstart.value + datetime.timedelta(0, 300)
|
||||||
|
nooverlap_event.vevent.dtstart.value = nstartdate
|
||||||
|
nooverlap_event.vevent.dtend.value = nstartdate + datetime.timedelta(0, 3600)
|
||||||
|
nooverlap_event.vevent.uid.value = "recurring - overlap"
|
||||||
|
self._putEvent(self.client, "%s%s" % (self.user_calendar, overlap_recurring_ics_name), nooverlap_event, exp_status=403)
|
||||||
|
|
||||||
|
|
||||||
def testRruleExceptionInvitationDance(self):
|
def testRruleExceptionInvitationDance(self):
|
||||||
""" RRULE exception invitation dance """
|
""" RRULE exception invitation dance """
|
||||||
|
@ -474,6 +556,8 @@ class CalDAVSchedulingTest(unittest.TestCase):
|
||||||
# bob isn't in the master+exception event
|
# bob isn't in the master+exception event
|
||||||
|
|
||||||
ics_name = "test-rrule-exception-invitation-dance.ics"
|
ics_name = "test-rrule-exception-invitation-dance.ics"
|
||||||
|
self.ics_list += [ics_name]
|
||||||
|
|
||||||
self._deleteEvent(self.client,
|
self._deleteEvent(self.client,
|
||||||
"%s%s" % (self.user_calendar, ics_name), None)
|
"%s%s" % (self.user_calendar, ics_name), None)
|
||||||
self._deleteEvent(self.attendee1_client,
|
self._deleteEvent(self.attendee1_client,
|
||||||
|
@ -596,6 +680,8 @@ class CalDAVSchedulingTest(unittest.TestCase):
|
||||||
# and that bob is 'declined'
|
# and that bob is 'declined'
|
||||||
|
|
||||||
ics_name = "test-rrule-invitation-deleted-exdate-dance.ics"
|
ics_name = "test-rrule-invitation-deleted-exdate-dance.ics"
|
||||||
|
self.ics_list += [ics_name]
|
||||||
|
|
||||||
self._deleteEvent(self.client,
|
self._deleteEvent(self.client,
|
||||||
"%s%s" % (self.user_calendar, ics_name), None)
|
"%s%s" % (self.user_calendar, ics_name), None)
|
||||||
self._deleteEvent(self.attendee1_client,
|
self._deleteEvent(self.attendee1_client,
|
||||||
|
@ -663,17 +749,89 @@ class CalDAVSchedulingTest(unittest.TestCase):
|
||||||
self.assertEqual(org_ev_master.attendee.partstat_param, "NEEDS-ACTION");
|
self.assertEqual(org_ev_master.attendee.partstat_param, "NEEDS-ACTION");
|
||||||
self.assertEqual(org_ev_exception.attendee.partstat_param, "DECLINED");
|
self.assertEqual(org_ev_exception.attendee.partstat_param, "DECLINED");
|
||||||
|
|
||||||
|
def testOrganizerIsAttendee(self):
|
||||||
|
""" iCal organizer is attendee - bug #1839 """
|
||||||
|
|
||||||
|
# This tries to have the same behavior as iCal
|
||||||
|
# 1. create an event, add an attendee and add the organizer as an attendee
|
||||||
|
# 2. SOGo should remove the organizer from the attendee list
|
||||||
|
ics_name = "test-organizer-is-attendee.ics"
|
||||||
|
self.ics_list += [ics_name]
|
||||||
|
|
||||||
|
self._deleteEvent(self.client,
|
||||||
|
"%s%s" % (self.user_calendar, ics_name), None)
|
||||||
|
self._deleteEvent(self.attendee1_client,
|
||||||
|
"%s%s" % (self.attendee1_calendar, ics_name), None)
|
||||||
|
|
||||||
|
# 1. create a recurring event in the organiser's calendar
|
||||||
|
summary="org is attendee"
|
||||||
|
uid=summary
|
||||||
|
event = self._newEvent(summary, uid)
|
||||||
|
organizer = event.vevent.add('organizer')
|
||||||
|
organizer.cn_param = self.user_name
|
||||||
|
organizer.partstat_param = "ACCEPTED"
|
||||||
|
organizer.value = self.user_email
|
||||||
|
attendee = event.vevent.add('attendee')
|
||||||
|
attendee.cn_param = self.attendee1_name
|
||||||
|
attendee.rsvp_param = "TRUE"
|
||||||
|
attendee.role_param = "REQ-PARTICIPANT"
|
||||||
|
attendee.partstat_param = "NEEDS-ACTION"
|
||||||
|
attendee.value = self.attendee1_email
|
||||||
|
|
||||||
|
# 1.1 add the organizer as an attendee
|
||||||
|
attendee = event.vevent.add('attendee')
|
||||||
|
attendee.cn_param = self.user_name
|
||||||
|
attendee.rsvp_param = "TRUE"
|
||||||
|
attendee.role_param = "REQ-PARTICIPANT"
|
||||||
|
attendee.partstat_param = "ACCEPTED"
|
||||||
|
attendee.value = self.user_email
|
||||||
|
|
||||||
|
self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event)
|
||||||
|
|
||||||
|
# 2. Fetch the event and make sure the organizer is not in the attendee list anymore
|
||||||
|
org_ev = self._getEvent(self.client, "%s%s" % (self.user_calendar, ics_name))
|
||||||
|
|
||||||
|
for attendee in org_ev.vevent.attendee_list:
|
||||||
|
self.assertNotEqual(self.user_email, attendee.value)
|
||||||
|
|
||||||
|
def testEventsWithSameUID(self):
|
||||||
|
""" PUT 2 events with the same UID - bug #1853 """
|
||||||
|
|
||||||
|
ics_name = "test-same-uid.ics"
|
||||||
|
self.ics_list += [ics_name]
|
||||||
|
|
||||||
|
self._deleteEvent(self.client,
|
||||||
|
"%s%s" % (self.user_calendar, ics_name), None)
|
||||||
|
|
||||||
|
conflict_ics_name = "test-same-uid-conflict.ics"
|
||||||
|
self.ics_list += [ics_name]
|
||||||
|
|
||||||
|
self._deleteEvent(self.client,
|
||||||
|
"%s%s" % (self.user_calendar, conflict_ics_name), None)
|
||||||
|
|
||||||
|
# 1. create simple event
|
||||||
|
summary="same uid"
|
||||||
|
uid=summary
|
||||||
|
event = self._newEvent(summary, uid)
|
||||||
|
|
||||||
|
self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event)
|
||||||
|
|
||||||
|
# PUT the same event with a new filename - should trigger a 403
|
||||||
|
self._putEvent(self.client, "%s%s" % (self.user_calendar, conflict_ics_name), event, exp_status=403)
|
||||||
|
|
||||||
def testInvitationDelegation(self):
|
def testInvitationDelegation(self):
|
||||||
""" invitation delegation """
|
""" invitation delegation """
|
||||||
|
|
||||||
|
ics_name = "test-delegation.ics"
|
||||||
|
self.ics_list += [ics_name]
|
||||||
|
|
||||||
# the invitation must not exist
|
# the invitation must not exist
|
||||||
self._deleteEvent(self.client,
|
self._deleteEvent(self.client,
|
||||||
"%stest-delegation.ics" % self.user_calendar, None)
|
"%s%s" % (self.user_calendar, ics_name), None)
|
||||||
self._deleteEvent(self.attendee1_client,
|
self._deleteEvent(self.attendee1_client,
|
||||||
"%stest-delegation.ics" % self.attendee1_calendar, None)
|
"%s%s" % (self.attendee1_calendar, ics_name), None)
|
||||||
self._deleteEvent(self.attendee1_delegate_client,
|
self._deleteEvent(self.attendee1_delegate_client,
|
||||||
"%stest-delegation.ics" % self.attendee1_delegate_calendar,
|
"%s%s" % (self.attendee1_delegate_calendar, ics_name), None)
|
||||||
None)
|
|
||||||
|
|
||||||
# 1. org -> attendee => org: 1, attendee: 1 (pst=N-A), delegate: 0
|
# 1. org -> attendee => org: 1, attendee: 1 (pst=N-A), delegate: 0
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,7 @@
|
||||||
return rc=NO;
|
return rc=NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
sql = [NSString stringWithFormat: @"SELECT count(*) FROM %@ WHERE c_lastseen <= %d;",
|
sql = [NSString stringWithFormat: @"SELECT count(*) FROM %@ WHERE c_lastseen <= %d",
|
||||||
[tableURL gcsTableName], oldest];
|
[tableURL gcsTableName], oldest];
|
||||||
ex = [channel evaluateExpressionX: sql];
|
ex = [channel evaluateExpressionX: sql];
|
||||||
if (ex)
|
if (ex)
|
||||||
|
@ -129,7 +129,7 @@
|
||||||
if (verbose)
|
if (verbose)
|
||||||
NSLog(@"Will be removing %d sessions", sessionsToDelete);
|
NSLog(@"Will be removing %d sessions", sessionsToDelete);
|
||||||
[channel cancelFetch];
|
[channel cancelFetch];
|
||||||
sql = [NSString stringWithFormat: @"DELETE FROM %@ WHERE c_lastseen <= %d;",
|
sql = [NSString stringWithFormat: @"DELETE FROM %@ WHERE c_lastseen <= %d",
|
||||||
[tableURL gcsTableName], oldest];
|
[tableURL gcsTableName], oldest];
|
||||||
if (verbose)
|
if (verbose)
|
||||||
NSLog(@"Removing sessions older than %d minute(s)", nbMinutes);
|
NSLog(@"Removing sessions older than %d minute(s)", nbMinutes);
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||||
|
Copyright (C) 2005-2012 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
|
the terms of the GNU Lesser General Public License as published by the
|
||||||
Free Software Foundation; either version 2, or (at your option) any
|
Free Software Foundation; either version 2, or (at your option) any
|
||||||
later version.
|
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
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
License for more details.
|
License for more details.
|
||||||
|
@ -590,4 +591,9 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (int) minimumSearchLength
|
||||||
|
{
|
||||||
|
return [[[context activeUser] domainDefaults] searchMinimumWordLength];
|
||||||
|
}
|
||||||
|
|
||||||
@end /* UIxPageFrame */
|
@end /* UIxPageFrame */
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2000-2005 SKYRIX Software AG
|
Copyright (C) 2000-2005 SKYRIX Software AG
|
||||||
|
Copyright (C) 2000-2012 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
|
the terms of the GNU Lesser General Public License as published by the
|
||||||
Free Software Foundation; either version 2, or (at your option) any
|
Free Software Foundation; either version 2, or (at your option) any
|
||||||
later version.
|
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
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
License for more details.
|
License for more details.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* UIxContactsUserFolders.m - this file is part of SOGo
|
/* UIxContactsUserFolders.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2007-2010 Inverse inc.
|
* Copyright (C) 2007-2012 Inverse inc.
|
||||||
*
|
*
|
||||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* UIxListEditor.m - this file is part of SOGo
|
/* UIxListEditor.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2008-2011 Inverse inc.
|
* Copyright (C) 2008-2012 Inverse inc.
|
||||||
*
|
*
|
||||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
* Francis Lachapelle <flachapelle@inverse.ca>
|
* Francis Lachapelle <flachapelle@inverse.ca>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
"Reply" = "Odpovědět";
|
"Reply" = "Odpovědět";
|
||||||
"Reply All" = "Odp. všem";
|
"Reply All" = "Odp. všem";
|
||||||
"Print" = "Tisk";
|
"Print" = "Tisk";
|
||||||
"Stop" = "Stop";
|
"Stop" = "Zastavit";
|
||||||
"Write" = "Napsat";
|
"Write" = "Napsat";
|
||||||
|
|
||||||
"Send" = "Odeslat";
|
"Send" = "Odeslat";
|
||||||
|
@ -19,6 +19,7 @@
|
||||||
"Attach" = "Přiložit";
|
"Attach" = "Přiložit";
|
||||||
"Save" = "Uložit";
|
"Save" = "Uložit";
|
||||||
"Options" = "Možnosti";
|
"Options" = "Možnosti";
|
||||||
|
"Close" = "Zavřít";
|
||||||
"Size" = "Velikost";
|
"Size" = "Velikost";
|
||||||
|
|
||||||
/* Tooltips */
|
/* Tooltips */
|
||||||
|
@ -63,8 +64,10 @@
|
||||||
"Shared Account: " = "Sdílený účet: ";
|
"Shared Account: " = "Sdílený účet: ";
|
||||||
|
|
||||||
/* acls */
|
/* acls */
|
||||||
"Default Roles" = "Výchozí oprávnění";
|
"Access rights to" = "Přístupová práva k";
|
||||||
"User rights for:" = "Uživatelská práva pro:";
|
"For user" = "Pro uživatele";
|
||||||
|
|
||||||
|
"Any Authenticated User" = "Všichni ověření uživatelé";
|
||||||
|
|
||||||
"List and see this folder" = "Prohlížet tuto složku";
|
"List and see this folder" = "Prohlížet tuto složku";
|
||||||
"Read mails from this folder" = "Číst maily v této složce";
|
"Read mails from this folder" = "Číst maily v této složce";
|
||||||
|
@ -104,15 +107,13 @@
|
||||||
"cc" = "Kopie";
|
"cc" = "Kopie";
|
||||||
"bcc" = "Skrytá kopie";
|
"bcc" = "Skrytá kopie";
|
||||||
|
|
||||||
"Addressbook" = "Adresář";
|
|
||||||
|
|
||||||
"Edit Draft..." = "Upravit koncept...";
|
"Edit Draft..." = "Upravit koncept...";
|
||||||
"Load Images" = "Nahrát obrázky";
|
"Load Images" = "Nahrát obrázky";
|
||||||
|
|
||||||
"Return Receipt" = "Return Receipt";
|
"Return Receipt" = "Potvrzení o přečtení";
|
||||||
"The sender of this message has asked to be notified when you read this message. Do you with to notify the sender?" = "The sender of this message has asked to be notified when you read this message. Do you with to notify the sender?";
|
"The sender of this message has asked to be notified when you read this message. Do you with to notify the sender?" = "Odesílatel této zprávy si přeje být informován o tom, že jste si tuto zprávu přečetli. Chcete odesílateli poslat potvrzení?";
|
||||||
"Return Receipt (displayed) - %@"= "Return Receipt (displayed) - %@";
|
"Return Receipt (displayed) - %@"= "Potvrzení o přečtení (zobrazeno) - %@";
|
||||||
"This is a Return Receipt for the mail that you sent to %@.\n\nNote: This Return Receipt only acknowledges that the message was displayed on the recipient's computer. There is no guarantee that the recipient has read or understood the message contents." = "This is a Return Receipt for the mail that you sent to %@.\n\nNote: This Return Receipt only acknowledges that the message was displayed on the recipient's computer. There is no guarantee that the recipient has read or understood the message contents.";
|
"This is a Return Receipt for the mail that you sent to %@.\n\nNote: This Return Receipt only acknowledges that the message was displayed on the recipient's computer. There is no guarantee that the recipient has read or understood the message contents." = "Toto je potvrzení o přečtení ke zprávě, kterou jste poslali pro %@.\n\nPoznámka: Potvrzení o přijetí znamená pouze to, že se zpráva zobrazila na počítači adresáta. Není ale zaručeno, že adresát zprávu četl a porozuměl jejímu obsahu.";
|
||||||
|
|
||||||
"Priority" = "Priorita";
|
"Priority" = "Priorita";
|
||||||
"highest" = "Nejvyšší";
|
"highest" = "Nejvyšší";
|
||||||
|
@ -123,6 +124,9 @@
|
||||||
|
|
||||||
"This mail is being sent from an unsecure network!" = "Tento mail je odesílán z nezabezpečené sítě!";
|
"This mail is being sent from an unsecure network!" = "Tento mail je odesílán z nezabezpečené sítě!";
|
||||||
|
|
||||||
|
"Address Book:" = "Adresář:";
|
||||||
|
"Search For:" = "Hledat:";
|
||||||
|
|
||||||
/* Popup "show" */
|
/* Popup "show" */
|
||||||
|
|
||||||
"all" = "všechny";
|
"all" = "všechny";
|
||||||
|
@ -141,8 +145,7 @@
|
||||||
"Date" = "Datum";
|
"Date" = "Datum";
|
||||||
"View" = "Zobrazit";
|
"View" = "Zobrazit";
|
||||||
"All" = "Všechny";
|
"All" = "Všechny";
|
||||||
"Unread" = "Nepřečtené";
|
"No message" = "Žádná zpráva";
|
||||||
"No message" = "No message";
|
|
||||||
"messages" = "zprávy";
|
"messages" = "zprávy";
|
||||||
|
|
||||||
"first" = "Nejnovější";
|
"first" = "Nejnovější";
|
||||||
|
@ -196,8 +199,8 @@
|
||||||
"Delete Folder" = "Smazat složku";
|
"Delete Folder" = "Smazat složku";
|
||||||
"Use This Folder For" = "Použít tuto složku pro";
|
"Use This Folder For" = "Použít tuto složku pro";
|
||||||
"Get Messages for Account" = "Stáhnout zprávy pro účet";
|
"Get Messages for Account" = "Stáhnout zprávy pro účet";
|
||||||
"Properties..." = "Properties...";
|
"Properties..." = "Vlastnosti...";
|
||||||
"Delegation..." = "Delegation...";
|
"Delegation..." = "Delegování...";
|
||||||
|
|
||||||
/* Use This Folder menu */
|
/* Use This Folder menu */
|
||||||
"Sent Messages" = "Odeslané zprávy";
|
"Sent Messages" = "Odeslané zprávy";
|
||||||
|
@ -208,7 +211,6 @@
|
||||||
"Open Message In New Window" = "Otevřít zprávu v novém okně";
|
"Open Message In New Window" = "Otevřít zprávu v novém okně";
|
||||||
"Reply to Sender Only" = "Odpovědět pouze odesílateli";
|
"Reply to Sender Only" = "Odpovědět pouze odesílateli";
|
||||||
"Reply to All" = "Odpovědět všem";
|
"Reply to All" = "Odpovědět všem";
|
||||||
"Forward" = "Přeposlat";
|
|
||||||
"Edit As New..." = "Upravit jako novou...";
|
"Edit As New..." = "Upravit jako novou...";
|
||||||
"Move To" = "Přesunout do";
|
"Move To" = "Přesunout do";
|
||||||
"Copy To" = "Kopírovat do";
|
"Copy To" = "Kopírovat do";
|
||||||
|
@ -255,11 +257,11 @@
|
||||||
"Please select a message." = "Vyberte zprávu prosím.";
|
"Please select a message." = "Vyberte zprávu prosím.";
|
||||||
"Please select a message to print." = "Zvolte prosím zprávu, kterou chcete tisknout.";
|
"Please select a message to print." = "Zvolte prosím zprávu, kterou chcete tisknout.";
|
||||||
"Please select only one message to print." = "Zvolte pouze jednu zprávu, kterou chcete tisknout.";
|
"Please select only one message to print." = "Zvolte pouze jednu zprávu, kterou chcete tisknout.";
|
||||||
"The message you have selected doesn't exist anymore." = "The message you have selected doesn't exist anymore.";
|
"The message you have selected doesn't exist anymore." = "Zpráva, kterou jste zvolili, již neexistuje.";
|
||||||
|
|
||||||
|
|
||||||
"The folder with name \"%{0}\" could not be created."
|
"The folder with name \"%{0}\" could not be created."
|
||||||
= "Složka s názvem \"%{0}\" nemohla být vytvořen.";
|
= "Složka s názvem \"%{0}\" nemohla být vytvořena.";
|
||||||
"This folder could not be renamed to \"%{0}\"."
|
"This folder could not be renamed to \"%{0}\"."
|
||||||
= "Tato složka nemohla být přejmenována na \"%{0}\".";
|
= "Tato složka nemohla být přejmenována na \"%{0}\".";
|
||||||
"The folder could not be deleted."
|
"The folder could not be deleted."
|
||||||
|
@ -272,20 +274,20 @@
|
||||||
"You need to choose a non-virtual folder!" = "Musíte zvolit ne-virtuální složku!";
|
"You need to choose a non-virtual folder!" = "Musíte zvolit ne-virtuální složku!";
|
||||||
|
|
||||||
"Moving a message into its own folder is impossible!"
|
"Moving a message into its own folder is impossible!"
|
||||||
= "Je nemožné přesunout zprávu do své vlastní složky!";
|
= "Zprávu nelze přesunout do své vlastní složky!";
|
||||||
"Copying a message into its own folder is impossible!"
|
"Copying a message into its own folder is impossible!"
|
||||||
= "Je nemožné zkopírovat zprávu do své vlastní složky!";
|
= "Zprávu nelze zkopírovat do své vlastní složky!";
|
||||||
|
|
||||||
/* Message operations */
|
/* Message operations */
|
||||||
"The messages could not be moved to the trash folder. Would you like to delete them immediately?"
|
"The messages could not be moved to the trash folder. Would you like to delete them immediately?"
|
||||||
= "The messages could not be moved to the trash folder. Would you like to delete them immediately?";
|
= "Zprávy nemohou být přesunuty do koše. Chcete je smazat trvale?";
|
||||||
|
|
||||||
/* Message editing */
|
/* Message editing */
|
||||||
"error_validationfailed" = "Potvrzení selhalo";
|
|
||||||
"error_missingsubject" = "Chybí předmět";
|
"error_missingsubject" = "Chybí předmět";
|
||||||
"error_missingrecipients" = "Příjemci nebyli specifikováni";
|
"error_missingrecipients" = "Příjemci nebyli specifikováni";
|
||||||
|
"Send Anyway" = "Odeslat";
|
||||||
|
|
||||||
/* Message sending */
|
/* Message sending */
|
||||||
"cannot send message: (smtp) all recipients discarded" = "Cannot send message: all recipients are invalid.";
|
"cannot send message: (smtp) all recipients discarded" = "Zprávu nelze odeslat: adresy všech příjemců jsou neplatné.";
|
||||||
"cannot send message (smtp) - recipients discarded:" = "Cannot send message. The following addresses are invalid:";
|
"cannot send message (smtp) - recipients discarded:" = "Zprávu nelze odeslat: následující adresy jsou neplatné:";
|
||||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
"cannot send message: (smtp) error when connecting" = "Zprávu nelze odeslat: při spojení se SMTP serverem došlo k chybě.";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* UIxAttendeesEditor.h - this file is part of SOGo
|
/* UIxAttendeesEditor.h - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2007 Inverse inc.
|
* Copyright (C) 2007-2012 Inverse inc.
|
||||||
*
|
*
|
||||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
*
|
*
|
||||||
|
@ -28,7 +28,6 @@
|
||||||
@interface UIxAttendeesEditor : UIxComponent
|
@interface UIxAttendeesEditor : UIxComponent
|
||||||
{
|
{
|
||||||
NSString *item;
|
NSString *item;
|
||||||
// NSString *zoom;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) setItem: (NSString *) newItem;
|
- (void) setItem: (NSString *) newItem;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* UIxAttendeesEditor.m - this file is part of SOGo
|
/* UIxAttendeesEditor.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2007 Inverse inc.
|
* Copyright (C) 2007-2012 Inverse inc.
|
||||||
*
|
*
|
||||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
*
|
*
|
||||||
|
|
|
@ -130,6 +130,7 @@
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var ApplicationBaseURL = '<var:string value="modulePath"/>';
|
var ApplicationBaseURL = '<var:string value="modulePath"/>';
|
||||||
var ResourcesURL = '<var:string value="applicationPath"/>.woa/WebServerResources';
|
var ResourcesURL = '<var:string value="applicationPath"/>.woa/WebServerResources';
|
||||||
|
var minimumSearchLength = <var:string value="minimumSearchLength"/>;
|
||||||
<var:if condition="isSuperUser"
|
<var:if condition="isSuperUser"
|
||||||
>var IsSuperUser = true;
|
>var IsSuperUser = true;
|
||||||
</var:if>
|
</var:if>
|
||||||
|
|
|
@ -125,7 +125,7 @@ var SOGoAutoCompletionInterface = {
|
||||||
document.contactLookupAjaxRequest.aborted = true;
|
document.contactLookupAjaxRequest.aborted = true;
|
||||||
document.contactLookupAjaxRequest.abort();
|
document.contactLookupAjaxRequest.abort();
|
||||||
}
|
}
|
||||||
if (input.value.trim().length > 2) {
|
if (input.value.trim().length > minimumSearchLength) {
|
||||||
var urlstr = UserFolderURL + "Contacts/";
|
var urlstr = UserFolderURL + "Contacts/";
|
||||||
if (input.addressBook)
|
if (input.addressBook)
|
||||||
urlstr += input.addressBook + "/contact";
|
urlstr += input.addressBook + "/contact";
|
||||||
|
|
|
@ -177,7 +177,7 @@ function onContactKeydown(event) {
|
||||||
|
|
||||||
function performSearch(input) {
|
function performSearch(input) {
|
||||||
// Perform address completion
|
// Perform address completion
|
||||||
if (!input.value.blank()) {
|
if (input.value.trim().length > minimumSearchLength) {
|
||||||
var urlstr = (UserFolderURL
|
var urlstr = (UserFolderURL
|
||||||
+ "Contacts/allContactSearch?excludeGroups=1&search="
|
+ "Contacts/allContactSearch?excludeGroups=1&search="
|
||||||
+ encodeURIComponent(input.value));
|
+ encodeURIComponent(input.value));
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
var d;
|
var d;
|
||||||
|
|
||||||
function onSearchFormSubmit() {
|
function onSearchFormSubmit() {
|
||||||
startAnimation($("pageContent"), $("filterPanel"));
|
|
||||||
|
|
||||||
var searchValue = $("searchValue");
|
var searchValue = $("searchValue");
|
||||||
var encodedValue = encodeURI(searchValue.value);
|
var encodedValue = encodeURI(searchValue.value);
|
||||||
|
|
||||||
|
@ -16,9 +14,12 @@ function onSearchFormSubmit() {
|
||||||
document.userFoldersRequest.aborted = true;
|
document.userFoldersRequest.aborted = true;
|
||||||
document.userFoldersRequest.abort();
|
document.userFoldersRequest.abort();
|
||||||
}
|
}
|
||||||
|
if (encodedValue.trim().length > minimumSearchLength) {
|
||||||
|
startAnimation($("pageContent"), $("filterPanel"));
|
||||||
document.userFoldersRequest
|
document.userFoldersRequest
|
||||||
= triggerAjaxRequest(url, usersSearchCallback);
|
= triggerAjaxRequest(url, usersSearchCallback);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/* generic.js - this file is part of SOGo
|
/* generic.js - this file is part of SOGo
|
||||||
|
|
||||||
Copyright (C) 2005 SKYRIX Software AG
|
Copyright (C) 2005 SKYRIX Software AG
|
||||||
Copyright (C) 2006-2011 Inverse
|
Copyright (C) 2006-2012 Inverse
|
||||||
|
|
||||||
SOGo 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
|
the terms of the GNU Lesser General Public License as published by the
|
||||||
|
@ -1143,7 +1143,7 @@ function onSearchFormSubmit(event) {
|
||||||
|
|
||||||
if (searchValue.value != searchValue.ghostPhrase
|
if (searchValue.value != searchValue.ghostPhrase
|
||||||
&& (searchValue.value != searchValue.lastSearch
|
&& (searchValue.value != searchValue.lastSearch
|
||||||
|| searchValue.value.strip().length > 0)) {
|
&& searchValue.value.strip().length > minimumSearchLength)) {
|
||||||
search["criteria"] = searchCriteria.value;
|
search["criteria"] = searchCriteria.value;
|
||||||
search["value"] = searchValue.value;
|
search["value"] = searchValue.value;
|
||||||
searchValue.lastSearch = searchValue.value;
|
searchValue.lastSearch = searchValue.value;
|
||||||
|
|
|
@ -15,7 +15,7 @@ Group: Productivity/Groupware
|
||||||
Source: SOGo-%{sogo_version}.tar.gz
|
Source: SOGo-%{sogo_version}.tar.gz
|
||||||
Prefix: /usr
|
Prefix: /usr
|
||||||
AutoReqProv: off
|
AutoReqProv: off
|
||||||
Requires: gnustep-base >= 1.23, sope%{sope_major_version}%{sope_minor_version}-core, httpd, sope%{sope_major_version}%{sope_minor_version}-core, sope%{sope_major_version}%{sope_minor_version}-appserver, sope%{sope_major_version}%{sope_minor_version}-ldap, sope%{sope_major_version}%{sope_minor_version}-cards >= %{sogo_version}, sope%{sope_major_version}%{sope_minor_version}-gdl1-contentstore >= %{sogo_version}, sope%{sope_major_version}%{sope_minor_version}-sbjson, memcached, libmemcached
|
Requires: gnustep-base >= 1.23, sope%{sope_major_version}%{sope_minor_version}-core, httpd, sope%{sope_major_version}%{sope_minor_version}-core, sope%{sope_major_version}%{sope_minor_version}-appserver, sope%{sope_major_version}%{sope_minor_version}-ldap, sope%{sope_major_version}%{sope_minor_version}-cards >= %{sogo_version}, sope%{sope_major_version}%{sope_minor_version}-gdl1-contentstore >= %{sogo_version}, sope%{sope_major_version}%{sope_minor_version}-sbjson, libmemcached
|
||||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}
|
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}
|
||||||
BuildRequires: gcc-objc gnustep-base gnustep-make sope%{sope_major_version}%{sope_minor_version}-appserver-devel sope%{sope_major_version}%{sope_minor_version}-core-devel sope%{sope_major_version}%{sope_minor_version}-ldap-devel sope%{sope_major_version}%{sope_minor_version}-mime-devel sope%{sope_major_version}%{sope_minor_version}-xml-devel sope%{sope_major_version}%{sope_minor_version}-gdl1-devel sope%{sope_major_version}%{sope_minor_version}-sbjson-devel libmemcached-devel %{?oc_build_depends}
|
BuildRequires: gcc-objc gnustep-base gnustep-make sope%{sope_major_version}%{sope_minor_version}-appserver-devel sope%{sope_major_version}%{sope_minor_version}-core-devel sope%{sope_major_version}%{sope_minor_version}-ldap-devel sope%{sope_major_version}%{sope_minor_version}-mime-devel sope%{sope_major_version}%{sope_minor_version}-xml-devel sope%{sope_major_version}%{sope_minor_version}-gdl1-devel sope%{sope_major_version}%{sope_minor_version}-sbjson-devel libmemcached-devel %{?oc_build_depends}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue