From a0109c4933e0bfb5ed9dbcf94f932991ca689542 Mon Sep 17 00:00:00 2001 From: svncommit Date: Thu, 14 Sep 2006 03:49:28 +0000 Subject: Initial TinyMCE editor support (still need to work on spellcheck and skins) --- program/steps/mail/compose.inc | 245 +++++++++++++++++++++++-------- program/steps/mail/func.inc | 60 +++++++- program/steps/mail/sendmail.inc | 75 +++++++++- program/steps/settings/edit_identity.inc | 34 ++++- program/steps/settings/func.inc | 9 +- program/steps/settings/save_identity.inc | 15 +- program/steps/settings/save_prefs.inc | 3 +- 7 files changed, 363 insertions(+), 78 deletions(-) (limited to 'program/steps') diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 29fdddd06..0b09065ac 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -53,7 +53,7 @@ rcmail_compose_cleanup(); $_SESSION['compose'] = array('id' => uniqid(rand())); // add some labels to client -rcube_add_label('nosubject', 'norecipientwarning', 'nosubjectwarning', 'nobodywarning', 'notsentwarning', 'savingmessage', 'sendingmessage', 'messagesaved'); +rcube_add_label('nosubject', 'norecipientwarning', 'nosubjectwarning', 'nobodywarning', 'notsentwarning', 'savingmessage', 'sendingmessage', 'messagesaved', 'converting'); // add config parameter to client script $OUTPUT->add_script(sprintf("%s.set_env('draft_autosave', %d);", $JS_OBJECT_NAME, !empty($CONFIG['drafts_mbox']) ? $CONFIG['draft_autosave'] : 0)); @@ -272,39 +272,42 @@ function rcmail_compose_header_from($attrib) } // get this user's identities - $sql_result = $DB->query("SELECT identity_id, name, email, signature + $sql_result = $DB->query("SELECT identity_id, name, email, signature, html_signature FROM ".get_table_name('identities')." WHERE user_id=? AND del<>1 ORDER BY ".$DB->quoteIdentifier('standard')." DESC, name ASC", $_SESSION['user_id']); - + if ($DB->num_rows($sql_result)) { $from_id = 0; $a_signatures = array(); - + $field_attrib['onchange'] = "$JS_OBJECT_NAME.change_identity(this)"; $select_from = new select($field_attrib); - + while ($sql_arr = $DB->fetch_assoc($sql_result)) { - $select_from->add(format_email_recipient($sql_arr['email'], $sql_arr['name']), $sql_arr['identity_id']); + $identity_id = $sql_arr['identity_id']; + $select_from->add(format_email_recipient($sql_arr['email'], $sql_arr['name']), $identity_id); // add signature to array if (!empty($sql_arr['signature'])) - $a_signatures[$sql_arr['identity_id']] = $sql_arr['signature']; - + { + $a_signatures[$identity_id]['text'] = $sql_arr['signature']; + $a_signatures[$identity_id]['is_html'] = ($sql_arr['html_signature'] == 1) ? true : false; + } + // set identity if it's one of the reply-message recipients if (in_array($sql_arr['email'], $a_recipients)) $from_id = $sql_arr['identity_id']; - + if ($compose_mode == RCUBE_COMPOSE_REPLY && is_array($MESSAGE['FROM'])) $MESSAGE['FROM'][] = $sql_arr['email']; if ($compose_mode == RCUBE_COMPOSE_DRAFT && strstr($MESSAGE['headers']->from, $sql_arr['email'])) $from_id = $sql_arr['identity_id']; - } // overwrite identity selection with post parameter @@ -312,7 +315,6 @@ function rcmail_compose_header_from($attrib) $from_id = get_input_value('_from', RCUBE_INPUT_POST); $out = $select_from->show($from_id); - // add signatures to client $OUTPUT->add_script(sprintf("%s.set_env('signatures', %s);", $JS_OBJECT_NAME, array2js($a_signatures))); @@ -340,40 +342,76 @@ function rcmail_compose_body($attrib) if (empty($attrib['id'])) $attrib['id'] = 'rcmComposeMessage'; - + $attrib['name'] = '_message'; - $textarea = new textarea($attrib); + + if ($CONFIG['htmleditor']) + $isHtml = true; + else + $isHtml = false; $body = ''; - + // use posted message body if (!empty($_POST['_message'])) + { $body = get_input_value('_message', RCUBE_INPUT_POST, TRUE); - + } // compose reply-body else if ($compose_mode == RCUBE_COMPOSE_REPLY) { - $body = rcmail_first_text_part($MESSAGE); + $hasHtml = rcmail_has_html_part($MESSAGE['parts']); + if ($hasHtml && $CONFIG['htmleditor']) + { + $body = rcmail_first_html_part($MESSAGE); + $isHtml = true; + } + else + { + $body = rcmail_first_text_part($MESSAGE); + $isHtml = false; + } if (strlen($body)) - $body = rcmail_create_reply_body($body); + $body = rcmail_create_reply_body($body, $isHtml); } - // forward message body inline else if ($compose_mode == RCUBE_COMPOSE_FORWARD) { - $body = rcmail_first_text_part($MESSAGE); + $hasHtml = rcmail_has_html_part($MESSAGE['parts']); + if ($hasHtml && $CONFIG['htmleditor']) + { + $body = rcmail_first_html_part($MESSAGE); + $isHtml = true; + } + else + { + $body = rcmail_first_text_part($MESSAGE); + $isHtml = false; + } if (strlen($body)) - $body = rcmail_create_forward_body($body); + $body = rcmail_create_forward_body($body, $isHtml); } - - // forward message body inline else if ($compose_mode == RCUBE_COMPOSE_DRAFT) { - $body = rcmail_first_text_part($MESSAGE); + $hasHtml = rcmail_has_html_part($MESSAGE['parts']); + if ($hasHtml && $CONFIG['htmleditor']) + { + $body = rcmail_first_html_part($MESSAGE); + $isHtml = true; + } + else + { + $body = rcmail_first_text_part($MESSAGE); + $isHtml = false; + } if (strlen($body)) - $body = rcmail_create_draft_body($body); + $body = rcmail_create_draft_body($body, $isHtml); } - + + $OUTPUT->include_script('tiny_mce/tiny_mce.js'); + $OUTPUT->include_script("editor.js"); + $OUTPUT->add_script('rcmail_editor_init($__skin_path);'); + $out = $form_start ? "$form_start\n" : ''; $saveid = new hiddenfield(array('name' => '_draft_saveid', 'value' => str_replace(array('<','>'),"",$MESSAGE['headers']->messageID) )); @@ -382,11 +420,18 @@ function rcmail_compose_body($attrib) $drafttoggle = new hiddenfield(array('name' => '_draft', 'value' => 'yes')); $out .= $drafttoggle->show(); + $msgtype = new hiddenfield(array('name' => '_is_html', 'value' => ($isHtml?"1":"0"))); + $out .= $msgtype->show(); + + // If desired, set this text area to be editable by TinyMCE + if ($isHtml) + $attrib['mce_editable'] = "true"; + $textarea = new textarea($attrib); $out .= $textarea->show($body); $out .= $form_end ? "\n$form_end" : ''; - + // include GoogieSpell - if (!empty($CONFIG['enable_spellcheck'])) + if (!empty($CONFIG['enable_spellcheck']) && !$isHtml) { $lang_set = ''; if (!empty($CONFIG['spellcheck_languages']) && is_array($CONFIG['spellcheck_languages'])) @@ -422,57 +467,88 @@ function rcmail_compose_body($attrib) } -function rcmail_create_reply_body($body) +function rcmail_create_reply_body($body, $bodyIsHtml) { global $IMAP, $MESSAGE; - // soft-wrap message first - $body = wordwrap($body, 75); + if (! $bodyIsHtml) + { + // soft-wrap message first + $body = wordwrap($body, 75); - // split body into single lines - $a_lines = preg_split('/\r?\n/', $body); + // split body into single lines + $a_lines = preg_split('/\r?\n/', $body); - // add > to each line - for($n=0; $n')===0) - $a_lines[$n] = '>'.$a_lines[$n]; - else - $a_lines[$n] = '> '.$a_lines[$n]; - } + // add > to each line + for($n=0; $n')===0) + $a_lines[$n] = '>'.$a_lines[$n]; + else + $a_lines[$n] = '> '.$a_lines[$n]; + } - $body = join("\n", $a_lines); + $body = join("\n", $a_lines); - // add title line - $pefix = sprintf("\n\n\nOn %s, %s wrote:\n", - $MESSAGE['headers']->date, - $IMAP->decode_header($MESSAGE['headers']->from)); - + // add title line + $prefix = sprintf("\n\n\nOn %s, %s wrote:\n", + $MESSAGE['headers']->date, + $IMAP->decode_header($MESSAGE['headers']->from)); - // try to remove the signature - if ($sp = strrpos($body, '-- ')) - { - if ($body{$sp+3}==' ' || $body{$sp+3}=="\n" || $body{$sp+3}=="\r") - $body = substr($body, 0, $sp-1); - } + // try to remove the signature + if ($sp = strrstr($body, '-- ')) + { + if ($body{$sp+3}==' ' || $body{$sp+3}=="\n" || $body{$sp+3}=="\r") + $body = substr($body, 0, $sp-1); + } + $suffix = ''; + } + else + { + $prefix = sprintf("

On %s, %s wrote:
", + $MESSAGE['headers']->date, + $IMAP->decode_header($MESSAGE['headers']->from)); + + $suffix = "
"; + } - return $pefix.$body; + return $prefix.$body.$suffix; } -function rcmail_create_forward_body($body) +function rcmail_create_forward_body($body, $bodyIsHtml) { global $IMAP, $MESSAGE; - // soft-wrap message first - $body = wordwrap($body, 80); - - $prefix = sprintf("\n\n\n-------- Original Message --------\nSubject: %s\nDate: %s\nFrom: %s\nTo: %s\n\n", - $MESSAGE['subject'], - $MESSAGE['headers']->date, - $IMAP->decode_header($MESSAGE['headers']->from), - $IMAP->decode_header($MESSAGE['headers']->to)); - + if (! $bodyIsHtml) + { + // soft-wrap message first + $body = wordwrap($body, 80); + + $prefix = sprintf("\n\n\n-------- Original Message --------\nSubject: %s\nDate: %s\nFrom: %s\nTo: %s\n\n", + $MESSAGE['subject'], + $MESSAGE['headers']->date, + $IMAP->decode_header($MESSAGE['headers']->from), + $IMAP->decode_header($MESSAGE['headers']->to)); + } + else + { + $prefix = sprintf( + "

-------- Original Message --------" . + "" . + "" . + "" . + "" . + "" . + "
Subject: %s
Date: %s
From: %s
To: %s

", + $MESSAGE['subject'], + $MESSAGE['headers']->date, + $IMAP->decode_header($MESSAGE['headers']->from), + $IMAP->decode_header($MESSAGE['headers']->to)); + } + // add attachments if (!isset($_SESSION['compose']['forward_attachments']) && is_array($MESSAGE['parts']) && sizeof($MESSAGE['parts'])>1) @@ -482,7 +558,7 @@ function rcmail_create_forward_body($body) } -function rcmail_create_draft_body($body) +function rcmail_create_draft_body($body, $bodyIsHtml) { global $IMAP, $MESSAGE; @@ -581,7 +657,7 @@ function rcmail_compose_subject($attrib) function rcmail_compose_attachment_list($attrib) { - global $OUTPUT, $JS_OBJECT_NAME; + global $OUTPUT, $JS_OBJECT_NAME, $CONFIG; // add ID if not given if (!$attrib['id']) @@ -706,6 +782,45 @@ function rcmail_receipt_checkbox($attrib) } +function rcmail_editor_selector($attrib) +{ + global $CONFIG, $MESSAGE, $compose_mode; + + $choices = array( + 'html' => 'HTML', + 'plain' => 'Plain text' + ); + + // determine whether HTML or plain text should be checked + if ($CONFIG['htmleditor']) + $useHtml = true; + else + $useHtml = false; + + if ($compose_mode == RCUBE_COMPOSE_REPLY || + $compose_mode == RCUBE_COMPOSE_FORWARD || + $compose_mode == RCUBE_COMPOSE_DRAFT) + { + $hasHtml = rcmail_has_html_part($MESSAGE['parts']); + $useHtml = ($hasHtml && $CONFIG['htmleditor']); + } + + $selector = ''; + foreach ($choices as $value => $text) + { + $checked = ''; + if ((($text == 'HTML') && $useHtml) || + (($text != 'HTML') && !$useHtml)) + $checked = 'checked'; + + $selector .= sprintf("%s\n", + $value, $checked, $text); + } + + return $selector; +} + + function get_form_tags($attrib) { global $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $MESSAGE_FORM, $SESS_HIDDEN_FIELD; diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index b51ec4b80..bfddb5aa8 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -689,7 +689,7 @@ function rcmail_print_body($part, $safe=FALSE, $plain=FALSE) global $IMAP, $REMOTE_OBJECTS, $JS_OBJECT_NAME; $body = is_array($part->replaces) ? strtr($part->body, $part->replaces) : $part->body; - + // text/html if ($part->ctype_secondary=='html') { @@ -1083,11 +1083,11 @@ function rcmail_message_body($attrib) { if (empty($part->ctype_parameters) || empty($part->ctype_parameters['charset'])) $$part->ctype_parameters['charset'] = $MESSAGE['headers']->charset; - + // fetch part if not available if (!isset($part->body)) $part->body = $IMAP->get_message_part($MESSAGE['UID'], $part->mime_id, $part); - + $body = rcmail_print_body($part, $safe_mode); $out .= '
'; @@ -1187,13 +1187,13 @@ function rcmail_mod_html_body($body, $container_id) '/(<\/?meta[^>]*>)/i'), '', $body); - + $out = preg_replace(array('/(]*>)/i', '/(<\/body>)/i'), array('
', '
'), $out); - + return $out; } @@ -1239,6 +1239,56 @@ function rcmail_mod_css_styles($source, $container_id) } +function rcmail_has_html_part($message_parts) +{ + if (!is_array($message_parts)) + return FALSE; + + // check all message parts + foreach ($message_parts as $pid => $part) + { + $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary); + if ($mimetype=='text/html') + { + return TRUE; + } + } + + return FALSE; +} + +// return first HTML part of a message +function rcmail_first_html_part($message_struct) + { + global $IMAP; + + if (!is_array($message_struct['parts'])) + return FALSE; + + $html_part = NULL; + + // check all message parts + foreach ($message_struct['parts'] as $pid => $part) + { + $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary); + if ($mimetype=='text/html') + { + $html_part = $IMAP->get_message_part($message_struct['UID'], $pid, $part); + } + } + + if ($html_part) + { + // remove special chars encoding + //$trans = array_flip(get_html_translation_table(HTML_ENTITIES)); + //$html_part = strtr($html_part, $trans); + + return $html_part; + } + + return FALSE; +} + // return first text part of a message function rcmail_first_text_part($message_struct) diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc index 375fc92d9..b4a6b7c20 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -23,6 +23,7 @@ //require_once('lib/smtp.inc'); require_once('include/rcube_smtp.inc'); +require_once('lib/html2text.inc'); require_once('Mail/mime.php'); @@ -62,6 +63,54 @@ function rcmail_get_identity($id) return FALSE; } +/** + * go from this: + * Cool + * + * to this: + * + * + * ... + * ------part... + * Content-Type: image/gif + * Content-Transfer-Encoding: base64 + * Content-ID: + */ +function rcmail_attach_emoticons(&$mime_message) +{ + global $CONFIG, $INSTALL_PATH; + + $htmlContents = $mime_message->getHtmlBody(); + + // remove any null-byte characters before parsing + $body = preg_replace('/\x00/', '', $htmlContents); + + $last_img_pos = 0; + + $searchstr = 'program/js/tiny_mce/plugins/emotions/images/'; + + // find emoticon image tags + while ($pos = strpos($body, $searchstr, $last_img_pos)) + { + $pos2 = strpos($body, '"', $pos); + $body_pre = substr($body, 0, $pos); + $image_name = substr($body, $pos + strlen($searchstr), $pos2 - ($pos + strlen($searchstr))); + $body_post = substr($body, $pos2); + + // add the image to the MIME message + $img_file = $INSTALL_PATH . '/' . $searchstr . $image_name; + if(! $mime_message->addHTMLImage($img_file, 'image/gif', '', true, '_' . $image_name)) + { + show_message("emoticonerror", 'error'); + } + + $body = $body_pre . 'cid:_' . $image_name . $body_post; + + $last_img_pos = $pos2; + } + + $mime_message->setHTMLBody($body); +} if (strlen($_POST['_draft_saveid']) > 3) $olddraftmessageid = get_input_value('_draft_saveid', RCUBE_INPUT_POST); @@ -184,9 +233,31 @@ else $header_delm = "\n"; // create PEAR::Mail_mime instance + +$isHtmlVal = strtolower(get_input_value('_is_html', RCUBE_INPUT_POST)); +$isHtml = ($isHtmlVal == "1"); + $MAIL_MIME = new Mail_mime($header_delm); -$MAIL_MIME->setTXTBody($message_body, FALSE, TRUE); -//$MAIL_MIME->setTXTBody(wordwrap($message_body), FALSE, TRUE); + +// For HTML-formatted messages, construct the MIME message with both +// the HTML part and the plain-text part + +if ($isHtml) + { + $MAIL_MIME->setHTMLBody($message_body); + + // add a plain text version of the e-mail as an alternative part. + $h2t = new html2text($message_body); + $plainTextPart = $h2t->get_text(); + $MAIL_MIME->setTXTBody($plainTextPart); + + // look for "emoticon" images from TinyMCE and copy into message as attachments + rcmail_attach_emoticons($MAIL_MIME); + } +else + { + $MAIL_MIME->setTXTBody($message_body, FALSE, TRUE); + } // add stored attachments, if any diff --git a/program/steps/settings/edit_identity.inc b/program/steps/settings/edit_identity.inc index 316eec785..af9cb26cc 100644 --- a/program/steps/settings/edit_identity.inc +++ b/program/steps/settings/edit_identity.inc @@ -42,7 +42,17 @@ else function rcube_identity_form($attrib) { - global $IDENTITY_RECORD, $JS_OBJECT_NAME; + global $IDENTITY_RECORD, $JS_OBJECT_NAME, $OUTPUT; + + $OUTPUT->include_script('tiny_mce/tiny_mce_src.js'); + $OUTPUT->add_script("tinyMCE.init({ mode : 'specific_textareas'," . + "apply_source_formatting : true," . + "theme : 'advanced'," . + "theme_advanced_toolbar_location : 'top'," . + "theme_advanced_toolbar_align : 'left'," . + "theme_advanced_buttons1 : 'bold,italic,underline,strikethrough,justifyleft,justifycenter,justifyright,justifyfull,separator,outdent,indent,charmap,hr'," . + "theme_advanced_buttons2 : 'link,unlink,forecolor,fontselect,fontsizeselect'," . + "theme_advanced_buttons3 : '' });"); if (!$IDENTITY_RECORD && $GLOBALS['_action']!='add-identity') return rcube_label('notfound'); @@ -62,7 +72,8 @@ function rcube_identity_form($attrib) 'organization' => array('type' => 'text'), 'reply-to' => array('type' => 'text', 'label' => 'replyto'), 'bcc' => array('type' => 'text'), - 'signature' => array('type' => 'textarea'), + 'signature' => array('type' => 'textarea', 'size' => "40", 'rows' => "6"), + 'html_signature'=>array('type' => 'checkbox', 'label' => 'htmlsignature', 'onclick' => 'return rcmail.toggle_editor(this, \'_signature\');'), 'standard' => array('type' => 'checkbox', 'label' => 'setdefault')); @@ -87,6 +98,25 @@ function rcube_identity_form($attrib) foreach ($a_show_cols as $col => $colprop) { $attrib['id'] = 'rcmfd_'.$col; + + if (strlen($colprop['onclick'])) + $attrib['onclick'] = $colprop['onclick']; + else + unset($attrib['onclick']); + + if ($col == 'signature') + { + $attrib['size'] = $colprop['size']; + $attrib['rows'] = $colprop['rows']; + $attrib['mce_editable'] = $IDENTITY_RECORD['html_signature'] ? "true" : "false"; + } + else + { + unset($attrib['size']); + unset($attrib['rows']); + unset($attrib['mce_editable']); + } + $label = strlen($colprop['label']) ? $colprop['label'] : $col; $value = rcmail_get_edit_field($col, $IDENTITY_RECORD[$col], $attrib, $colprop['type']); diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc index f08b75af8..fd985726d 100644 --- a/program/steps/settings/func.inc +++ b/program/steps/settings/func.inc @@ -147,6 +147,14 @@ function rcmail_user_prefs_form($attrib) rep_specialchars_output(rcube_label('prettydate')), $input_prettydate->show($CONFIG['prettydate']?1:0)); + // Show checkbox for HTML Editor + $field_id = 'rcmfd_htmleditor'; + $input_htmleditor = new checkbox(array('name' => '_htmleditor', 'id' => $field_id, 'value' => 1)); + $out .= sprintf("%s\n", + $field_id, + rep_specialchars_output(rcube_label('htmleditor')), + $input_htmleditor->show($CONFIG['htmleditor']?1:0)); + if (!empty($CONFIG['drafts_mbox'])) { $field_id = 'rcmfd_autosave'; @@ -168,7 +176,6 @@ function rcmail_user_prefs_form($attrib) - function rcmail_identities_list($attrib) { global $DB, $CONFIG, $OUTPUT, $JS_OBJECT_NAME; diff --git a/program/steps/settings/save_identity.inc b/program/steps/settings/save_identity.inc index ffbcfe3d9..60a6855f0 100644 --- a/program/steps/settings/save_identity.inc +++ b/program/steps/settings/save_identity.inc @@ -19,9 +19,9 @@ */ -$a_save_cols = array('name', 'email', 'organization', 'reply-to', 'bcc', 'standard', 'signature'); +$a_save_cols = array('name', 'email', 'organization', 'reply-to', 'bcc', 'standard', 'signature', 'html_signature'); $a_html_cols = array('signature'); - +$a_boolean_cols = array('standard', 'html_signature'); // check input if (empty($_POST['_name']) || empty($_POST['_email'])) @@ -48,6 +48,17 @@ if ($_POST['_iid']) $DB->quote(get_input_value($fname, RCUBE_INPUT_POST, in_array($col, $a_html_cols)))); } + // set "off" values for checkboxes that were not checked, and therefore + // not included in the POST body. + foreach ($a_boolean_cols as $col) + { + $fname = '_' . $col; + if (!isset($_POST[$fname])) + { + $a_write_sql[] = sprintf("%s=0", $DB->quoteIdentifier($col)); + } + } + if (sizeof($a_write_sql)) { $DB->query("UPDATE ".get_table_name('identities')." diff --git a/program/steps/settings/save_prefs.inc b/program/steps/settings/save_prefs.inc index 73fb231f2..a438de002 100644 --- a/program/steps/settings/save_prefs.inc +++ b/program/steps/settings/save_prefs.inc @@ -28,6 +28,7 @@ $a_user_prefs['timezone'] = isset($_POST['_timezone']) ? floatval($_POST['_timez $a_user_prefs['dst_active'] = isset($_POST['_dst_active']) ? TRUE : FALSE; $a_user_prefs['pagesize'] = is_numeric($_POST['_pagesize']) ? (int)$_POST['_pagesize'] : $CONFIG['pagesize']; $a_user_prefs['prefer_html'] = isset($_POST['_prefer_html']) ? TRUE : FALSE; +$a_user_prefs['htmleditor'] = isset($_POST['_htmleditor']) ? TRUE : FALSE; $a_user_prefs['draft_autosave'] = isset($_POST['_draft_autosave']) ? intval($_POST['_draft_autosave']) : 0; // MM: Date format toggle (Pretty / Standard) @@ -49,4 +50,4 @@ $_action = 'preferences'; // overwrite action variable $OUTPUT->add_script(sprintf("\n%s.set_env('action', '%s');", $JS_OBJECT_NAME, $_action)); -?> \ No newline at end of file +?> -- cgit v1.2.3