HTML Composing, see changelog

Monotone-Parent: b895c283a3da52c67fd91339f704dc8cd49b743f
Monotone-Revision: 5f0e6bd5b6298ffdd908398135216841ba3b6909

Monotone-Author: crobert@inverse.ca
Monotone-Date: 2009-06-23T17:53:18
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
C Robert 2009-06-23 17:53:18 +00:00
parent 445224aa8f
commit f9337f183f
13 changed files with 236 additions and 113 deletions

View File

@ -1,3 +1,19 @@
2009-06-23 Cyril Robert <crobert@inverse.ca>
* SoObjects/Mailer/NSString+Mail.m (stringByConvertingCRLNToHTML): Added
* UI/MailPartViewers/UIxMailPartTextViewer.m (stringByConvertingCRLNToHTML):
Removed
* SoObjects/Mailer/SOGoDraftObject.m: Added support for html content type
* SoObjects/Mailer/SOGoMailObject+Draft.m: Added support for html content
type in mail reply
* SoObjects/Mailer/SOGoMailReply.m: Added support for html in mail editor
* UI/PreferencesUI/UIxPreferences.m: Added option to se content type for
mail editing
* UI/WebServerResources/GNUmakefile: Added fckeditor entry
* UI/WebServerResources/UIxMailEditor.js: Plugged-in FCKEditor
* UI/WebServerResources/UIxPreferences.js: Plugged-in FCKEditor, handle html
toggle for signature box
2009-06-19 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* SoObjects/SOGo/SOGoGCSFolder.m (-davSyncCollection:): deleted

View File

@ -28,6 +28,7 @@
@interface NSString (SOGoExtension)
- (NSString *) htmlToText;
- (NSString *) stringByConvertingCRLNToHTML;
- (int) indexOf: (unichar) _c;
- (NSString *) decodedSubject;

View File

@ -23,6 +23,7 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSException.h>
#import <SaxObjC/SaxAttributes.h>
#import <SaxObjC/SaxContentHandler.h>
#import <SaxObjC/SaxLexicalHandler.h>
@ -350,6 +351,79 @@
return [handler result];
}
#define paddingBuffer 8192
static inline char *
convertChars (const char *oldString, unsigned int oldLength,
unsigned int *newLength)
{
const char *currentChar, *upperLimit;
char *newString, *destChar, *reallocated;
unsigned int length, maxLength;
maxLength = oldLength + paddingBuffer;
newString = NSZoneMalloc (NULL, maxLength + 1);
destChar = newString;
currentChar = oldString;
length = 0;
upperLimit = oldString + oldLength;
while (currentChar < upperLimit)
{
switch (*currentChar)
{
case '\r': break;
case '\n':
length = destChar - newString;
if (length + paddingBuffer > maxLength - 6)
{
maxLength += paddingBuffer;
reallocated = NSZoneRealloc (NULL, newString, maxLength + 1);
if (reallocated)
{
newString = reallocated;
destChar = newString + length;
}
else
[NSException raise: NSMallocException
format: @"reallocation failed in %s",
__PRETTY_FUNCTION__];
}
strcpy (destChar, "<br />");
destChar += 6;
break;
default:
*destChar = *currentChar;
destChar++;
}
currentChar++;
}
*destChar = 0;
*newLength = destChar - newString;
return newString;
}
- (NSString *) stringByConvertingCRLNToHTML
{
NSString *convertedString;
char *newString;
unsigned int newLength;
newString
= convertChars ([self cStringUsingEncoding: NSUTF8StringEncoding],
[self lengthOfBytesUsingEncoding: NSUTF8StringEncoding],
&newLength);
convertedString = [[NSString alloc] initWithBytes: newString
length: newLength
encoding: NSUTF8StringEncoding];
[convertedString autorelease];
NSZoneFree (NULL, newString);
return convertedString;
}
- (int) indexOf: (unichar) _c
{
int i, len;

View File

@ -70,6 +70,7 @@
#import "SOGoDraftObject.h"
static NSString *contentTypeValue = @"text/plain; charset=utf-8";
static NSString *htmlContentTypeValue = @"text/html; charset=utf-8";
static NSString *headerKeys[] = {@"subject", @"to", @"cc", @"bcc",
@"from", @"replyTo", @"message-id",
nil};
@ -879,17 +880,24 @@ static BOOL showTextAttachmentsInline = NO;
- (NGMimeMessage *) mimeMessageForContentWithHeaderMap: (NGMutableHashMap *) map
{
NGMimeMessage *message;
NSUserDefaults *ud;
// BOOL addSuffix;
id body;
ud = [[context activeUser] userDefaults];
[map setObject: @"text/plain" forKey: @"content-type"];
body = text;
if (body)
{
// if ([body isKindOfClass:[NSString class]])
/* Note: just 'utf8' is displayed wrong in Mail.app */
[map setObject: contentTypeValue
forKey: @"content-type"];
if ([[ud stringForKey: @"ComposeMessagesType"] isEqualToString: @"html"])
[map setObject: htmlContentTypeValue
forKey: @"content-type"];
else
[map setObject: contentTypeValue
forKey: @"content-type"];
// body = [body dataUsingEncoding:NSUTF8StringEncoding];
// else if ([body isKindOfClass:[NSData class]] && addSuffix) {
// body = [[body mutableCopy] autorelease];

View File

@ -88,6 +88,26 @@
return newSubject;
}
- (NSString *) _convertRawContentForEditing: (NSString *) raw
rawHtml: (BOOL) html
{
NSString *rc;
NSUserDefaults *ud;
BOOL htmlComposition;
ud = [[context activeUser] userDefaults];
htmlComposition = [[ud stringForKey: @"ComposeMessagesType"]
isEqualToString: @"html"];
if (html && !htmlComposition)
rc = [raw htmlToText];
else if (!html && htmlComposition)
rc = [raw stringByConvertingCRLNToHTML];
else
rc = raw;
return rc;
}
- (NSString *) _contentForEditingFromKeys: (NSArray *) keys
{
@ -104,29 +124,27 @@
types = [keys objectsForKey: @"mimeType" notFoundMarker: @""];
index = [types indexOfObject: @"text/plain"];
if (index == NSNotFound)
{
index = [types indexOfObject: @"text/html"];
htmlContent = YES;
}
{
index = [types indexOfObject: @"text/html"];
htmlContent = YES;
}
else
htmlContent = NO;
htmlContent = NO;
if (index != NSNotFound)
{
contentKey = [keys objectAtIndex: index];
parts = [self fetchPlainTextStrings:
[NSArray arrayWithObject: contentKey]];
if ([parts count] > 0)
{
rawPart = [[parts allValues] objectAtIndex: 0];
if (htmlContent)
content = [rawPart htmlToText];
else
content = rawPart;
}
}
{
contentKey = [keys objectAtIndex: index];
parts = [self fetchPlainTextStrings:
[NSArray arrayWithObject: contentKey]];
if ([parts count] > 0)
{
rawPart = [[parts allValues] objectAtIndex: 0];
content = [self _convertRawContentForEditing: rawPart
rawHtml: htmlContent];
}
}
}
return content;
}

View File

@ -25,6 +25,7 @@
#import <SoObjects/SOGo/SOGoDateFormatter.h>
#import <SoObjects/SOGo/SOGoUser.h>
#import <SoObjects/SOGo/SOGoUserDefaults.h>
#import "SOGoMailObject+Draft.h"
#import "SOGoMailReply.h"
@ -74,20 +75,32 @@
- (NSString *) messageBody
{
NSString *s;
NSUserDefaults *ud;
ud = [[context activeUser] userDefaults];
s = [sourceMail contentForEditing];
if (s)
{
NSRange r;
if ([[ud objectForKey: @"ComposeMessagesType"] isEqualToString: @"html"])
{
s = [NSString stringWithFormat: @"<blockquote type=\"cite\">%@</blockquote>", s];
}
else
{
NSRange r;
r = [s rangeOfString: @"\n-- \n" options: NSBackwardsSearch];
r = [s rangeOfString: @"\n-- \n" options: NSBackwardsSearch];
if (r.length)
s = [s substringToIndex: r.location];
if (r.length)
s = [s substringToIndex: r.location];
s = [s stringByApplyingMailQuoting]; //adds "> " on each line
}
}
return [s stringByApplyingMailQuoting];
return s;
}
@end

View File

@ -33,92 +33,10 @@
#import <NGExtensions/NSString+misc.h>
#import <SoObjects/SOGo/NSString+Utilities.h>
#import <SoObjects/Mailer/NSString+Mail.h>
#import "UIxMailPartTextViewer.h"
@interface NSString (SOGoMailUIExtension)
- (NSString *) stringByConvertingCRLNToHTML;
@end
@implementation NSString (SOGoMailUIExtension)
#define paddingBuffer 8192
static inline char *
convertChars (const char *oldString, unsigned int oldLength,
unsigned int *newLength)
{
const char *currentChar, *upperLimit;
char *newString, *destChar, *reallocated;
unsigned int length, maxLength;
maxLength = oldLength + paddingBuffer;
newString = NSZoneMalloc (NULL, maxLength + 1);
destChar = newString;
currentChar = oldString;
length = 0;
upperLimit = oldString + oldLength;
while (currentChar < upperLimit)
{
switch (*currentChar)
{
case '\r': break;
case '\n':
length = destChar - newString;
if (length + paddingBuffer > maxLength - 6)
{
maxLength += paddingBuffer;
reallocated = NSZoneRealloc (NULL, newString, maxLength + 1);
if (reallocated)
{
newString = reallocated;
destChar = newString + length;
}
else
[NSException raise: NSMallocException
format: @"reallocation failed in %s",
__PRETTY_FUNCTION__];
}
strcpy (destChar, "<br />");
destChar += 6;
break;
default:
*destChar = *currentChar;
destChar++;
}
currentChar++;
}
*destChar = 0;
*newLength = destChar - newString;
return newString;
}
- (NSString *) stringByConvertingCRLNToHTML
{
NSString *convertedString;
char *newString;
unsigned int newLength;
newString
= convertChars ([self cStringUsingEncoding: NSUTF8StringEncoding],
[self lengthOfBytesUsingEncoding: NSUTF8StringEncoding],
&newLength);
convertedString = [[NSString alloc] initWithBytes: newString
length: newLength
encoding: NSUTF8StringEncoding];
[convertedString autorelease];
NSZoneFree (NULL, newString);
return convertedString;
}
@end
@implementation UIxMailPartTextViewer
- (NSString *) flatContentAsString

View File

@ -584,6 +584,27 @@ static BOOL defaultShowSubscribedFoldersOnly = NO;
return [userDefaults stringForKey: @"SignaturePlacement"];
}
- (NSArray *) composeMessagesType
{
return [NSArray arrayWithObjects: @"text", @"html", nil];
}
- (NSString *) itemComposeMessagesText
{
return [self labelForKey: [NSString stringWithFormat: @"composemessagestype_%@", item]];
}
- (NSString *) userComposeMessagesType
{
return [userDefaults stringForKey: @"ComposeMessagesType"];
}
- (void) setUserComposeMessagesType: (NSString *) newType
{
[userDefaults setObject: newType forKey: @"ComposeMessagesType"];
}
- (void) setUserSignaturePlacement: (NSString *) newSignaturePlacement
{
[userDefaults setObject: newSignaturePlacement forKey: @"SignaturePlacement"];

View File

@ -10,7 +10,7 @@
className="UIxPageFrame"
title="panelTitle"
const:popup="YES"
const:jsFiles="UIxMailToSelection.js">
const:jsFiles="UIxMailToSelection.js,fckeditor/fckeditor.js">
<script type="text/javascript">
var mailIsReply = <var:string value="isMailReply"/>;
</script>

View File

@ -10,6 +10,7 @@
className="UIxPageFrame"
title="title"
const:popup="YES"
const:jsFiles="fckeditor/fckeditor.js"
>
<form id="mainForm" var:href="ownPath">
<div class="tabsContainer" id="preferencesTabs">
@ -118,6 +119,11 @@
const:id="signaturePlacementList"
string="itemSignaturePlacementText"
selection="userSignaturePlacement"/>
<br /><label><var:string label:value="Compose messages in"/></label>
<var:popup list="composeMessagesType" item="item"
const:id="composeMessagesType"
string="itemComposeMessagesText"
selection="userComposeMessagesType"/>
<!-- <label><input
const:name="inTheOffice" type="radio" const:value="YES"
var:selection="inTheOffice"/>

View File

@ -12,6 +12,7 @@ install ::
else \
mkdir -p $(SOGO_WEBSERVERRESOURCESDIR); \
cp $(WEBSERVER_RESOURCE_FILES) $(SOGO_WEBSERVERRESOURCESDIR); \
cp -r fckeditor $(SOGO_WEBSERVERRESOURCESDIR); \
fi
clean ::

View File

@ -570,6 +570,15 @@ function initMailEditor() {
focusField.focus();
initializePriorityMenu();
var composeMode = UserDefaults["ComposeMessagesType"];
if (composeMode == "html") {
var oFCKeditor = new FCKeditor ('text');
oFCKeditor.BasePath = "/SOGo.woa/WebServerResources/fckeditor/";
oFCKeditor.ToolbarSet = 'Basic';
oFCKeditor.ReplaceTextarea ();
}
onWindowResize (null);
}
function initializePriorityMenu() {
@ -720,7 +729,14 @@ function onWindowResize(event) {
textarea.setStyle({ 'top': hr.offsetTop + 'px' });
// Resize the textarea (message content)
textarea.rows = Math.floor((window.height() - textarea.offsetTop) / rowheight);
var composeMode = UserDefaults["ComposeMessagesType"];
if (composeMode == "html") {
var editor = $('text___Frame');
editor.height = Math.floor(window.height() - editor.offsetTop) + "px";
editor.style.height = Math.floor(window.height() - editor.offsetTop) + "px";
editor.setStyle({ 'top': hr.offsetTop + 'px' });
}
textarea.rows = Math.floor((window.height() - textarea.offsetTop) / rowheight);
}
function onMailEditorClose(event) {

View File

@ -33,8 +33,22 @@ function initPreferences() {
_setupEvents(true);
if (typeof (initAdditionalPreferences) != "undefined")
initAdditionalPreferences();
$("replyPlacementList").observe("change", onReplyPlacementListChange);
$("replyPlacementList").observe ("change", onReplyPlacementListChange);
onReplyPlacementListChange();
var oFCKeditor = new FCKeditor ('signature');
oFCKeditor.BasePath = "/SOGo.woa/WebServerResources/fckeditor/";
oFCKeditor.ToolbarSet = 'Basic';
oFCKeditor.ReplaceTextarea ();
$('signature___Frame').style.height = "150px";
$('signature___Frame').height = "150px";
if (UserDefaults["ComposeMessagesType"] != "html") {
$("signature").style.display = 'inline';
$('signature___Frame').style.display = 'none';
}
$("composeMessagesType").observe ("change", onComposeMessagesTypeChange);
}
function onReplyPlacementListChange() {
@ -48,4 +62,21 @@ function onReplyPlacementListChange() {
}
}
function onComposeMessagesTypeChange () {
var textArea = $('signature');
var oEditor = FCKeditorAPI.GetInstance('signature');
var editor = $('signature___Frame');
if ($("composeMessagesType").value == 0) {
textArea.style.display = 'inline';
editor.style.display = 'none';
textArea.value = oEditor.GetData();
}
else {
textArea.style.display = 'none';
editor.style.display = '';
oEditor.SetHTML(textArea.value);
}
}
document.observe("dom:loaded", initPreferences);