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.sogomaint-2.0.2
parent
445224aa8f
commit
f9337f183f
16
ChangeLog
16
ChangeLog
|
@ -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
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
@interface NSString (SOGoExtension)
|
||||
|
||||
- (NSString *) htmlToText;
|
||||
- (NSString *) stringByConvertingCRLNToHTML;
|
||||
- (int) indexOf: (unichar) _c;
|
||||
- (NSString *) decodedSubject;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"];
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -12,6 +12,7 @@ install ::
|
|||
else \
|
||||
mkdir -p $(SOGO_WEBSERVERRESOURCESDIR); \
|
||||
cp $(WEBSERVER_RESOURCE_FILES) $(SOGO_WEBSERVERRESOURCESDIR); \
|
||||
cp -r fckeditor $(SOGO_WEBSERVERRESOURCESDIR); \
|
||||
fi
|
||||
|
||||
clean ::
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue