See ChangeLog

Monotone-Parent: 728de9d1f91919c2ef87a31aa2d11b9b1a3a9835
Monotone-Revision: e5e46473685093f9076981f90edbe581992247e7

Monotone-Author: flachapelle@inverse.ca
Monotone-Date: 2012-08-27T20:35:10
maint-2.0.2
Francis Lachapelle 2012-08-27 20:35:10 +00:00
parent 4d71ec17f6
commit da6b5696a0
15 changed files with 368 additions and 133 deletions

View File

@ -1,11 +1,19 @@
2012-08-27 Francis Lachapelle <flachapelle@inverse.ca>
* SoObjects/Appointments/SOGoAptMailInvitation.m,
SoObjects/Appointments/SOGoAptMailNotification.m,
SoObjects/Appointments/SOGoAptMailDeletion.m,
SoObjects/Appointments/SOGoAptMailUpdate.m,
SoObjects/Appointments/SOGoAptMailICalReply.m,
SoObjects/Appointments/SOGoCalendarComponent.m: send HTML messages
instead of text/plain.
* UI/WebServerResources/generic.js (openMailTo): mail addresses
must now be separated by semi-colons and not commas. This allow
display names to contain commas.
* UI/WebServerResources/SchedulerUI.js (tasksListCallback): prefix
tasks with a color box insted of changing the task's background color.
tasks with a color box instead of changing the task's background color.
* UI/Scheduler/UIxCalListingActions.m
(-_getStatusClassForStatusCode:andEndDateStamp::): set the

1
NEWS
View File

@ -5,6 +5,7 @@ Enhancements
executed, no matter if a previous condition matches)
- improved tasks list display
- RPM packages now treat logrotate file as a config file
- completed the transition from text/plain message templates to HTML
Bug Fixes
- fixed passwords that would be prefixed with '{none}' when not using a

View File

@ -11,9 +11,9 @@ vtodo_class2 = "(Confidential task)";
"The event \"%{Summary}\" was created" = "The event \"%{Summary}\" was created";
"The event \"%{Summary}\" was deleted" = "The event \"%{Summary}\" was deleted";
"The event \"%{Summary}\" was updated" = "The event \"%{Summary}\" was updated";
"The following attendees(s) were notified:" = "The following attendees(s) were notified:";
"The following attendees(s) were added:" = "The following attendees(s) were added:";
"The following attendees(s) were removed:" = "The following attendees(s) were removed:";
"The following attendees(s) were notified:" = "The following attendee(s) were notified:";
"The following attendees(s) were added:" = "The following attendee(s) were added:";
"The following attendees(s) were removed:" = "The following attendee(s) were removed:";
/* IMIP messages */
"startDate_label" = "Start:";

View File

@ -44,27 +44,14 @@
- (NSString *) getBody
{
NSString *bodyFormat;
NSString *body;
if (!values)
[self setupValues];
if ([values objectForKey: @"StartTime"] && [values objectForKey: @"EndTime"])
bodyFormat = [self labelForKey: (@"%{Organizer} %{SentByText}has cancelled"
@" this event: %{Summary}.\n\n"
@"Start: %{StartDate} at %{StartTime}\n"
@"End: %{EndDate} at %{EndTime}\n"
@"Description: %{Description}")
inContext: context];
else
bodyFormat = [self labelForKey: (@"%{Organizer} %{SentByText}has cancelled"
@" this event: %{Summary}.\n\n"
@"Start: %{StartDate}\n"
@"End: %{EndDate}\n"
@"Description: %{Description}")
inContext: context];
body = [[self generateResponse] contentAsString];
return [values keysWithFormat: bodyFormat];
return [body stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
@end

View File

@ -1,6 +1,6 @@
/* SOGoAptMailICalReply - this file is part of SOGo
*
* Copyright (C) 2010 Inverse inc.
* Copyright (C) 2010-2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
@ -19,6 +19,7 @@
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSCharacterSet.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/NSObject+Utilities.h>
@ -105,14 +106,11 @@
return [values keysWithFormat: subjectFormat];
}
- (NSString *) getBody
- (NSString *) bodyStartText
{
NSString *bodyFormat;
NSString *partStat, *delegate;
if (!values)
[self setupValues];
partStat = [[attendee partStat] lowercaseString];
if ([partStat isEqualToString: @"accepted"])
bodyFormat = @"%{Attendee} %{SentByText}has accepted your event invitation.";
@ -135,7 +133,19 @@
else
bodyFormat = @"%{Attendee} %{SentByText}has not yet decided upon your event invitation.";
return [values keysWithFormat: [self labelForKey: bodyFormat inContext: context]];
return [values keysWithFormat: bodyFormat];
}
- (NSString *) getBody
{
NSString *body;
if (!values)
[self setupValues];
body = [[self generateResponse] contentAsString];
return [body stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
@end

View File

@ -49,27 +49,14 @@
- (NSString *) getBody
{
NSString *bodyFormat;
NSString *body;
if (!values)
[self setupValues];
if ([values objectForKey: @"StartTime"] && [values objectForKey: @"EndTime"])
bodyFormat = [self labelForKey: (@"%{Organizer} %{SentByText}has invited you"
@" to %{Summary}.\n\n"
@"Start: %{StartDate} at %{StartTime}\n"
@"End: %{EndDate} at %{EndTime}\n"
@"Description: %{Description}")
inContext: context];
else
bodyFormat = [self labelForKey: (@"%{Organizer} %{SentByText}has invited you"
@" to %{Summary}.\n\n"
@"Start: %{StartDate}\n"
@"End: %{EndDate}\n"
@"Description: %{Description}")
inContext: context];
body = [[self generateResponse] contentAsString];
return [values keysWithFormat: bodyFormat];
return [body stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
@end

View File

@ -1,15 +1,15 @@
/*
Copyright (C) 2006-2010 Inverse inc.
Copyright (C) 2006-2012 Inverse inc.
Copyright (C) 2000-2005 SKYRIX Software AG
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.
@ -30,6 +30,7 @@
@class NSString;
@class NSTimeZone;
@class iCalEvent;
@class SOGoDateFormatter;
/*
* NOTE: We inherit from SoComponent in order to get the correct
@ -47,6 +48,7 @@
NSCalendarDate *newEndDate;
NSString *organizerName;
NSMutableDictionary *values;
SOGoDateFormatter *dateFormatter;
}
- (void) setupValues;
@ -66,6 +68,12 @@
- (NSCalendarDate *) oldEndDate;
- (NSCalendarDate *) newEndDate;
- (NSString *) sentByText;
- (NSString *) formattedAptStartDate;
- (NSString *) formattedAptStartTime;
- (NSString *) formattedAptEndDate;
- (NSString *) formattedAptEndTime;
- (NSString *) getSubject;
- (NSString *) getBody;

View File

@ -49,6 +49,7 @@
{
apt = nil;
values = nil;
dateFormatter = RETAIN([[context activeUser] dateFormatterInContext: context]);
}
return self;
@ -65,6 +66,7 @@
[newStartDate release];
[oldEndDate release];
[newEndDate release];
[dateFormatter release];
[super dealloc];
}
@ -153,7 +155,105 @@
return organizerName;
}
/* Helpers */
- (NSString *) sentByText
{
NSDictionary *sentByValues;
NSString *sentByText;
id value;
sentByText = @"";
if (organizerName)
{
value = [[apt organizer] sentBy];
if (value && [value length])
{
sentByValues = [NSDictionary dictionaryWithObject: value
forKey: @"SentBy"];
sentByText
= [sentByValues keysWithFormat: [self
labelForKey: @"(sent by %{SentBy}) "
inContext: context]];
}
}
return sentByText;
}
- (NSString *) formattedAptStartDate
{
NSString *s;
id value;
value = [self newStartDate];
s = @"";
if (value)
s = [dateFormatter formattedDate: value];
return s;
}
- (NSString *) formattedAptStartTime
{
NSString *s;
id value;
value = [self newStartDate];
s = @"";
if (value && ![apt isAllDay])
s = [dateFormatter formattedTime: value];
return s;
}
- (NSString *) formattedAptEndDate
{
NSString *s;
id value;
value = [self newEndDate];
s = @"";
if (value)
s = [dateFormatter formattedDate: value];
return s;
}
- (NSString *) formattedAptEndTime
{
NSString *s;
id value;
value = [self newEndDate];
s = @"";
if (value && ![apt isAllDay])
s = [dateFormatter formattedTime: value];
return s;
}
- (void) setupValues
{
SOGoUser *user;
id value;
user = [context activeUser];
viewTZ = [[user userDefaults] timeZone];
[viewTZ retain];
values = [NSMutableDictionary new];
value = [self summary];
if (!value)
value = @"";
[values setObject: value forKey: @"Summary"];
}
/* Generate Response */
@ -171,59 +271,4 @@
return nil;
}
- (void) setupValues
{
NSString *sentBy, *sentByText, *description;
NSCalendarDate *date;
NSDictionary *sentByValues;
SOGoUser *user;
SOGoDateFormatter *dateFormatter;
user = [context activeUser];
viewTZ = [[user userDefaults] timeZone];
[viewTZ retain];
values = [NSMutableDictionary new];
[values setObject: [self summary] forKey: @"Summary"];
if (organizerName)
{
[values setObject: organizerName forKey: @"Organizer"];
sentBy = [[apt organizer] sentBy];
if ([sentBy length])
{
sentByValues = [NSDictionary dictionaryWithObject: sentBy
forKey: @"SentBy"];
sentByText
= [sentByValues keysWithFormat: [self
labelForKey: @"(sent by %{SentBy}) "
inContext: context]];
}
else
sentByText = @"";
[values setObject: sentByText forKey: @"SentByText"];
}
dateFormatter = [[context activeUser] dateFormatterInContext: context];
date = [self newStartDate];
[values setObject: [dateFormatter shortFormattedDate: date]
forKey: @"StartDate"];
if (![apt isAllDay])
[values setObject: [dateFormatter formattedTime: date]
forKey: @"StartTime"];
date = [self newEndDate];
[values setObject: [dateFormatter shortFormattedDate: date]
forKey: @"EndDate"];
if (![apt isAllDay])
[values setObject: [dateFormatter formattedTime: date]
forKey: @"EndTime"];
description = [[self apt] comment];
[values setObject: (description ? description : @"")
forKey: @"Description"];
}
@end

View File

@ -33,10 +33,31 @@
#import "SOGoAptMailNotification.h"
@interface SOGoAptMailUpdate : SOGoAptMailNotification
{
NSMutableDictionary *changes;
NSString *currentItem;
}
@end
@implementation SOGoAptMailUpdate
- (id) init
{
self = [super init];
changes = [[NSMutableDictionary alloc] init];
return self;
}
- (void) dealloc
{
RELEASE(currentItem);
RELEASE(changes);
[super dealloc];
}
- (NSString *) valueForProperty: (NSString *) property
withDateFormatter: (SOGoDateFormatter *) dateFormatter
{
@ -64,7 +85,10 @@
if ([valueType isEqualToString: @"date"])
{
[value setTimeZone: viewTZ];
value = [dateFormatter formattedDateAndTime: value];
if ([apt isAllDay])
value = [dateFormatter formattedDate: value];
else
value = [dateFormatter formattedDateAndTime: value];
}
}
else
@ -75,15 +99,13 @@
- (void) _setupBodyContentWithFormatter: (SOGoDateFormatter *) dateFormatter
{
NSArray *updatedProperties;
NSMutableString *bodyContent;
NSString *property, *label, *value;
NSArray *updatedProperties;
int count, max;
updatedProperties = [[iCalEventChanges changesFromEvent: previousApt
toEvent: apt]
updatedProperties];
bodyContent = [NSMutableString new];
max = [updatedProperties count];
for (count = 0; count < max; count++)
{
@ -96,27 +118,40 @@
label = [self labelForKey: [NSString stringWithFormat: @"%@_label",
property]
inContext: context];
[bodyContent appendFormat: @" %@ %@\n", label, value];
[changes setObject: value forKey: label];
}
}
[values setObject: bodyContent forKey: @"_bodyContent"];
[bodyContent release];
}
- (void) _setupBodyValuesWithFormatter: (SOGoDateFormatter *) dateFormatter
- (NSArray *) allChangesList
{
return [changes allKeys];
}
- (void) setCurrentItem: (NSString *) theItem
{
ASSIGN(currentItem, theItem);
}
- (NSString *) currentItem
{
return currentItem;
}
- (NSString *) valueForCurrentItem
{
return [changes objectForKey: currentItem];
}
- (NSString *) bodyStartText
{
NSString *bodyText;
bodyText = [self labelForKey: @"The following parameters have changed"
@" in the \"%{Summary}\" meeting:"
inContext: context];
[values setObject: [values keysWithFormat: bodyText]
forKey: @"_bodyStart"];
[self _setupBodyContentWithFormatter: dateFormatter];
[values setObject: [self labelForKey: @"Please accept"
@" or decline those changes."
inContext: context]
forKey: @"_bodyEnd"];
return [values keysWithFormat: bodyText];
}
- (void) setupValues
@ -136,7 +171,7 @@
[values setObject: [dateFormatter formattedTime: date]
forKey: @"OldStartTime"];
[self _setupBodyValuesWithFormatter: dateFormatter];
[self _setupBodyContentWithFormatter: dateFormatter];
}
- (NSString *) getSubject
@ -162,11 +197,14 @@
- (NSString *) getBody
{
NSString *body;
if (!values)
[self setupValues];
return [values keysWithFormat:
@"%{_bodyStart}\n%{_bodyContent}\n%{_bodyEnd}\n"];
body = [[self generateResponse] contentAsString];
return [body stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
@end

View File

@ -845,8 +845,8 @@
/* text part */
headerMap = [NGMutableHashMap hashMapWithCapacity: 1];
[headerMap setObject: @"text/plain; charset=\"UTF-8\""
forKey: @"content-type"];
[headerMap setObject: @"text/html; charset=utf-8"
forKey: @"content-type"];
bodyPart = [NGMimeBodyPart bodyPartWithHeader: headerMap];
[bodyPart setBody: [text dataUsingEncoding: NSUTF8StringEncoding]];
@ -927,8 +927,8 @@
/* text part */
headerMap = [NGMutableHashMap hashMapWithCapacity: 1];
[headerMap setObject: @"text/plain; charset=utf-8"
forKey: @"content-type"];
[headerMap setObject: @"text/html; charset=utf-8"
forKey: @"content-type"];
bodyPart = [NGMimeBodyPart bodyPartWithHeader: headerMap];
bodyData = [[p getBody] dataUsingEncoding: NSUTF8StringEncoding];
[bodyPart setBody: bodyData];

View File

@ -1,3 +1,44 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE container>
<container xmlns="http://www.w3.org/1999/xhtml"></container>
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label">
<head>
<style type="text/css">
.container { max-width: 600px; }
h1 { font-size: 18px; font-weight: normal; padding-bottom: 9px; border-bottom: 1px solid #ccc; }
h1 small { font-size: 12px; color: #999; }
.dl-horizontal dt { float: left; width: 120px; overflow: hidden; clear: left; text-align: right; text-overflow: ellipsis; white-space: nowrap; }
dt { font-weight: bold; line-height: 17px; }
dt, dd { font-size: 12px; line-height: 18px; color: #999; }
dt { display: block; }
h1, dd, .dl-list dt { margin-left: 130px; }
</style>
</head>
<body>
<div class="container">
<h1><var:string value="getSubject" const:escapeHTML="NO"/>
<small><var:string value="organizerName" const:escapeHTML="NO"/><var:string value="sentByText" const:escapeHTML="NO"/></small></h1>
<dl class="dl-horizontal">
<var:if condition="apt.location.length"
><dt><var:string label:value="location_label" const:escapeHTML="NO"/></dt>
<dd><var:string value="apt.location" const:escapeHTML="NO"/></dd></var:if>
<dt><var:string label:value="startDate_label" const:escapeHTML="NO"/></dt>
<dd><var:string value="formattedAptStartDate" const:escapeHTML="NO"
/><var:if condition="formattedAptStartTime.length"> - <var:string value="formattedAptStartTime" const:escapeHTML="NO"/> <var:string value="viewTZ" const:escapeHTML="NO"/>
</var:if></dd>
<dt><var:string label:value="endDate_label" const:escapeHTML="NO"/></dt>
<dd><var:string value="formattedAptEndDate" const:escapeHTML="NO"
/><var:if condition="formattedAptEndTime.length"> - <var:string value="formattedAptEndTime" const:escapeHTML="NO"/> <var:string value="viewTZ" const:escapeHTML="NO"/>
</var:if></dd>
<var:if condition="apt.comment.length"
><dt><var:string label:value="comment_label" const:escapeHTML="NO"/></dt>
<dd><var:string value="apt.comment" const:escapeHTML="NO"/></dd></var:if>
</dl>
</div>
</body>
</html>

View File

@ -1,3 +1,30 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE container>
<container xmlns="http://www.w3.org/1999/xhtml"></container>
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label">
<head>
<style type="text/css">
.container { max-width: 600px; }
h1 { font-size: 18px; font-weight: normal; padding-bottom: 9px; border-bottom: 1px solid #ccc; }
.dl-horizontal dt { float: left; width: 120px; overflow: hidden; clear: left; text-align: right; text-overflow: ellipsis; white-space: nowrap; }
dt { font-weight: bold; line-height: 17px; }
dt, dd { font-size: 12px; line-height: 18px; }
dt { display: block; }
h1, dd, .dl-list dt { margin-left: 130px; }
</style>
</head>
<body>
<div class="container">
<h1><var:string value="getSubject" const:escapeHTML="NO"/></h1>
<dl class="dl-horizontal">
<dt></dt>
<dd><var:string value="bodyStartText" const:escapeHTML="NO"/></dd>
</dl>
</div>
</body>
</html>

View File

@ -1,3 +1,44 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE container>
<container xmlns="http://www.w3.org/1999/xhtml"></container>
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label">
<head>
<style type="text/css">
.container { max-width: 600px; }
h1 { font-size: 18px; font-weight: normal; padding-bottom: 9px; border-bottom: 1px solid #ccc; }
h1 small { font-size: 12px; color: #999; }
.dl-horizontal dt { float: left; width: 120px; overflow: hidden; clear: left; text-align: right; text-overflow: ellipsis; white-space: nowrap; }
dt { font-weight: bold; line-height: 17px; }
dt, dd { font-size: 12px; line-height: 18px; }
dt { display: block; }
h1, dd, .dl-list dt { margin-left: 130px; }
</style>
</head>
<body>
<div class="container">
<h1><var:string value="getSubject" const:escapeHTML="NO"/>
<small><var:string value="organizerName" const:escapeHTML="NO"/><var:string value="sentByText" const:escapeHTML="NO"/></small></h1>
<dl class="dl-horizontal">
<var:if condition="apt.location.length"
><dt><var:string label:value="location_label" const:escapeHTML="NO"/></dt>
<dd><var:string value="apt.location" const:escapeHTML="NO"/></dd></var:if>
<dt><var:string label:value="startDate_label" const:escapeHTML="NO"/></dt>
<dd><var:string value="formattedAptStartDate" const:escapeHTML="NO"
/><var:if condition="formattedAptStartTime.length"> - <var:string value="formattedAptStartTime" const:escapeHTML="NO"/> <var:string value="viewTZ" const:escapeHTML="NO"/>
</var:if></dd>
<dt><var:string label:value="endDate_label" const:escapeHTML="NO"/></dt>
<dd><var:string value="formattedAptEndDate" const:escapeHTML="NO"
/><var:if condition="formattedAptEndTime.length"> - <var:string value="formattedAptEndTime" const:escapeHTML="NO"/> <var:string value="viewTZ" const:escapeHTML="NO"/>
</var:if></dd>
<var:if condition="apt.comment.length"
><dt><var:string label:value="comment_label" const:escapeHTML="NO"/></dt>
<dd><var:string value="apt.comment" const:escapeHTML="NO"/></dd></var:if>
</dl>
</div>
</body>
</html>

View File

@ -9,7 +9,7 @@
<head>
<style type="text/css">
.container { width: 600px; }
.container { max-width: 600px; }
h1 { font-size: 18px; font-weight: normal; padding-bottom: 9px; border-bottom: 1px solid #ccc; }
.dl-horizontal dt { float: left; width: 120px; overflow: hidden; clear: left; text-align: right; text-overflow: ellipsis; white-space: nowrap; }
dt { font-weight: bold; line-height: 17px; }

View File

@ -1,3 +1,45 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE container>
<container xmlns="http://www.w3.org/1999/xhtml"></container>
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label">
<head>
<style type="text/css">
.container { max-width: 600px; }
h1 { font-size: 18px; font-weight: normal; padding-bottom: 9px; border-bottom: 1px solid #ccc; }
.dl-horizontal dt { float: left; width: 120px; overflow: hidden; clear: left; text-align: right; text-overflow: ellipsis; white-space: nowrap; }
h1 small { font-size: 12px; color: #999; }
dt { font-weight: bold; line-height: 17px; }
dt, dd { font-size: 12px; line-height: 18px; }
dt { display: block; }
h1, dd, .dl-list dt { margin-left: 130px; }
</style>
</head>
<body>
<div class="container">
<h1><var:string value="getSubject" const:escapeHTML="NO"/>
<small><var:string value="organizerName" const:escapeHTML="NO"/><var:string value="sentByText" const:escapeHTML="NO"/></small></h1>
<dl class="dl-horizontal">
<dt></dt>
<dd><var:string value="bodyStartText" const:escapeHTML="NO"/></dd>
</dl>
<dl class="dl-horizontal">
<var:foreach list="allChangesList" item="currentItem"
><dt><var:string var:value="currentItem"/></dt>
<dd><var:string var:value="valueForCurrentItem"/></dd>
</var:foreach>
</dl>
<dl class="dl-horizontal">
<dt></dt>
<dd><var:string label:value="Please accept or decline those changes." const:escapeHTML="NO"/></dd>
</dl>
</div>
</body>
</html>