From 9c830f74613fb86a61104909a36ac0ca2d35ebb0 Mon Sep 17 00:00:00 2001 From: Klaus Leithoff Date: Tue, 5 Mar 2013 14:09:35 +0000 Subject: [PATCH] get some more actions to work --- mail/inc/class.mail_bo.inc.php | 920 +++++++++++++++++++++++++++++- mail/inc/class.mail_hooks.inc.php | 2 +- mail/inc/class.mail_ui.inc.php | 56 +- mail/js/app.js | 72 +++ mail/templates/default/app.css | 13 +- 5 files changed, 1043 insertions(+), 20 deletions(-) diff --git a/mail/inc/class.mail_bo.inc.php b/mail/inc/class.mail_bo.inc.php index 1c42e1f8f0..dab100ce75 100644 --- a/mail/inc/class.mail_bo.inc.php +++ b/mail/inc/class.mail_bo.inc.php @@ -50,6 +50,13 @@ class mail_bo */ var $htmlOptions; + /** + * Active mimeType + * + * @var string + */ + var $activeMimeType; + /** * Active incomming (IMAP) Server Object * @@ -886,16 +893,21 @@ class mail_bo if (!is_array($folderInfo[0])) { // no folder info, but there is a status returned for the folder: something is wrong, try to cope with it - $folderInfo = array(0 => array('HIERACHY_DELIMITER'=>$this->getHierarchyDelimiter(), - 'ATTRIBUTES' => '')); + $folderInfo = array(0 => (is_array($folderInfo)?$folderInfo:array('HIERACHY_DELIMITER'=>$this->getHierarchyDelimiter(), + 'ATTRIBUTES' => ''))); + if (empty($folderInfo[0]['HIERACHY_DELIMITER']) || (isset($folderInfo[0]['delimiter']) && empty($folderInfo[0]['delimiter']))) + { + //error_log(__METHOD__.__LINE__.array2string($folderInfo)); + $folderInfo[0]['HIERACHY_DELIMITER'] = $this->getHierarchyDelimiter(); + } } } #if(!is_array($folderInfo[0])) { # return false; #} - $retValue['delimiter'] = $folderInfo[0]['HIERACHY_DELIMITER']; - $retValue['attributes'] = $folderInfo[0]['ATTRIBUTES']; + $retValue['delimiter'] = ($folderInfo[0]['HIERACHY_DELIMITER']?$folderInfo[0]['HIERACHY_DELIMITER']:$folderInfo[0]['delimiter']); + $retValue['attributes'] = ($folderInfo[0]['ATTRIBUTES']?$folderInfo[0]['ATTRIBUTES']:$folderInfo[0]['attributes']); $shortNameParts = explode($retValue['delimiter'], $_folderName); $retValue['shortName'] = array_pop($shortNameParts); $retValue['displayName'] = $this->encodeFolderName($_folderName); @@ -911,7 +923,7 @@ class mail_bo } if (!($folderInfo instanceof PEAR_Error)) $folderBasicInfo[$this->profileID][$_folderName]=$retValue; egw_cache::setCache(egw_cache::INSTANCE,'email','folderBasicInfo'.trim($GLOBALS['egw_info']['user']['account_id']),$folderBasicInfo,$expiration=60*60*1); - if ($basicInfoOnly || stripos(array2string($retValue['attributes']),'noselect')!==false) + if ($basicInfoOnly || (isset($retValue['attributes']) && stripos(array2string($retValue['attributes']),'noselect')!==false)) { return $retValue; } @@ -2360,8 +2372,8 @@ class mail_bo if ($_forceDeleteMethod === 'no' || !in_array($_forceDeleteMethod,array('move_to_trash',"mark_as_deleted","remove_immediately"))) $deleteOptions = $this->mailPreferences->preferences['deleteOptions']; //error_log(__METHOD__.__LINE__.'->'.array2string($_messageUID).','.$_folder.'/'.$this->sessionData['mailbox'].' Option:'.$deleteOptions); $trashFolder = $this->getTrashFolder(); - $draftFolder = $this->getDraftFolder(); //$GLOBALS['egw_info']['user']['preferences']['felamimail']['draftFolder']; - $templateFolder = $this->getTemplateFolder(); //$GLOBALS['egw_info']['user']['preferences']['felamimail']['templateFolder']; + $draftFolder = $this->getDraftFolder(); //$GLOBALS['egw_info']['user']['preferences']['mail']['draftFolder']; + $templateFolder = $this->getTemplateFolder(); //$GLOBALS['egw_info']['user']['preferences']['mail']['templateFolder']; if(($_folder == $trashFolder && $deleteOptions == "move_to_trash") || ($_folder == $draftFolder)) { $deleteOptions = "remove_immediately"; @@ -2613,10 +2625,124 @@ class mail_bo if ($_charset===false) $_charset = self::$displayCharset; $_stringORG = $_string; $_string = @htmlentities($_string,ENT_QUOTES,$_charset, false); - if (empty($_string) && !empty($_stringORG)) $_string = @htmlentities(translation::convert($_stringORG,self::detect_encoding($_stringORG),$_charset),ENT_QUOTES | ENT_IGNORE,$_charset, false); + if (empty($_string) && !empty($_stringORG)) $_string = @htmlentities(translation::convert($_stringORG,translation::detect_encoding($_stringORG),$_charset),ENT_QUOTES | ENT_IGNORE,$_charset, false); return $_string; } + /** + * htmlspecialchars + * helperfunction to cope with wrong encoding in strings + * @param string $_string input to be converted + * @param mixed $charset false or string -> Target charset, if false mail displayCharset will be used + * @return string + */ + static function htmlspecialchars($_string, $_charset=false) + { + //setting the charset (if not given) + if ($_charset===false) $_charset = self::$displayCharset; + $_stringORG = $_string; + $_string = @htmlspecialchars($_string,ENT_QUOTES,$_charset, false); + if (empty($_string) && !empty($_stringORG)) $_string = @htmlspecialchars(translation::convert($_stringORG,self::detect_encoding($_stringORG),$_charset),ENT_QUOTES | ENT_IGNORE,$_charset, false); + return $_string; + } + + /** + * strip tags out of the message completely with their content + * param $_body is the text to be processed + * param $tag is the tagname which is to be removed. Note, that only the name of the tag is to be passed to the function + * without the enclosing brackets + * param $endtag can be different from tag but should be used only, if begin and endtag are known to be different e.g.: + */ + static function replaceTagsCompletley(&$_body,$tag,$endtag='',$addbracesforendtag=true) + { + translation::replaceTagsCompletley($_body,$tag,$endtag,$addbracesforendtag); + } + + /** + * clean a message from elements regarded as potentially harmful + * param string/reference $_html is the text to be processed + * param boolean $usepurify - obsolet, as we always use htmlLawed + * param boolean $cleanTags - use tidy (if available) to clean/balance tags + * return nothing + */ + static function getCleanHTML(&$_html, $usepurify = false, $cleanTags=true) + { + // remove CRLF and TAB as it is of no use in HTML. + // but they matter in
, so we rather don't
+		//$_html = str_replace("\r\n",' ',$_html);
+		//$_html = str_replace("\t",' ',$_html);
+		//error_log($_html);
+		//repair doubleencoded ampersands, and some stuff htmLawed stumbles upon with balancing switched on
+		$_html = str_replace(array('&','

',"
 
",'
 
','','
','','','',''), + array('&', '
', '
', '
', '','', '', '', '', ''),$_html); + //$_html = str_replace(array('&'),array('&'),$_html); + if (stripos($_html,'style')!==false) self::replaceTagsCompletley($_html,'style'); // clean out empty or pagewide style definitions / left over tags + if (stripos($_html,'head')!==false) self::replaceTagsCompletley($_html,'head'); // Strip out stuff in head + //if (stripos($_html,'![if')!==false && stripos($_html,'')!==false) self::replaceTagsCompletley($_html,'!\[if','',false); // Strip out stuff in ifs + //if (stripos($_html,'!--[if')!==false && stripos($_html,'')!==false) self::replaceTagsCompletley($_html,'!--\[if','',false); // Strip out stuff in ifs + //error_log(__METHOD__.__LINE__.$_html); + // force the use of kses, as it is still have the edge over purifier with some stuff + $usepurify = true; + if ($usepurify) + { + // we need a customized config, as we may allow external images, $GLOBALS['egw_info']['user']['preferences']['mail']['allowExternalIMGs'] + if (get_magic_quotes_gpc() === 1) $_html = stripslashes($_html); + // Strip out doctype in head, as htmlLawed cannot handle it TODO: Consider extracting it and adding it afterwards + if (stripos($_html,'!doctype')!==false) self::replaceTagsCompletley($_html,'!doctype'); + if (stripos($_html,'?xml:namespace')!==false) self::replaceTagsCompletley($_html,'\?xml:namespace','/>',false); + if (stripos($_html,'?xml version')!==false) self::replaceTagsCompletley($_html,'\?xml version','\?>',false); + if (strpos($_html,'!CURSOR')!==false) self::replaceTagsCompletley($_html,'!CURSOR'); + // htmLawed filter only the 'body' + //preg_match('`(]*>)(.+?)(.*?)`ims', $_html, $matches); + //if ($matches[2]) + //{ + // $hasOther = true; + // $_html = $matches[2]; + //} + // purify got switched to htmLawed + // some testcode to test purifying / htmlawed + //$_html = "
hi
there
kram
".$_html; + $_html = html::purify($_html,self::$htmLawed_config,array(),true); + //if ($hasOther) $_html = $matches[1]. $_html. $matches[3]; + // clean out comments , should not be needed as purify should do the job. + $search = array( + '@url\(http:\/\/[^\)].*?\)@si', // url calls e.g. in style definitions + '@@', // Strip multi-line comments including CDATA + ); + $_html = preg_replace($search,"",$_html); + // remove non printable chars + $_html = preg_replace('/([\000-\012])/','',$_html); + //error_log(__METHOD__.':'.__LINE__.':'.$_html); + } + // using purify above should have tidied the tags already sufficiently + if ($usepurify == false && $cleanTags==true) + { + if (extension_loaded('tidy')) + { + $tidy = new tidy(); + $cleaned = $tidy->repairString($_html, self::$tidy_config,'utf8'); + // Found errors. Strip it all so there's some output + if($tidy->getStatus() == 2) + { + error_log(__METHOD__.__LINE__.' ->'.$tidy->errorBuffer); + } + else + { + $_html = $cleaned; + } + } + else + { + //$to = ini_get('max_execution_time'); + //@set_time_limit(10); + $htmLawed = new egw_htmLawed(); + $_html = $htmLawed->egw_htmLawed($_html); + //error_log(__METHOD__.__LINE__.$_html); + //@set_time_limit($to); + } + } + } + /** * Header and Bodystructure stuff */ @@ -2653,6 +2779,599 @@ class mail_bo return $structure[$this->icServer->ImapServerId][$_folder][$_uid]; } + /** + * getMimePartCharset - fetches the charset mimepart if it exists + * @param $_mimePartObject structure object + * @return mixed mimepart or false if no CHARSET is found, the missing charset has to be handled somewhere else, + * as we cannot safely assume any charset as we did earlier + */ + function getMimePartCharset($_mimePartObject) + { + //$charSet = 'iso-8859-1';//self::$displayCharset; //'iso-8859-1'; // self::displayCharset seems to be asmarter fallback than iso-8859-1 + $CharsetFound=false; + //echo "#".$_mimePartObject->encoding.'#
'; + if(is_array($_mimePartObject->parameters)) { + if(isset($_mimePartObject->parameters['CHARSET'])) { + $charSet = $_mimePartObject->parameters['CHARSET']; + $CharsetFound=true; + } + } + // this one is dirty, but until I find something that does the trick of detecting the encoding, .... + //if ($CharsetFound == false && $_mimePartObject->encoding == "QUOTED-PRINTABLE") $charSet = 'iso-8859-1'; //assume quoted-printable to be ISO + //if ($CharsetFound == false && $_mimePartObject->encoding == "BASE64") $charSet = 'utf-8'; // assume BASE64 to be UTF8 + return ($CharsetFound ? $charSet : $CharsetFound); + } + + /** + * decodeMimePart - fetches the charset mimepart if it exists + * @param string $_mimeMessage - the message to be decoded + * @param string $_encoding - the encoding used BASE64 and QUOTED-PRINTABLE is supported + * @param string $_charset - not used + * @return string decoded mimePart + */ + function decodeMimePart($_mimeMessage, $_encoding, $_charset = '') + { + // decode the part + if (self::$debug) error_log(__METHOD__."() with $_encoding and $_charset:".print_r($_mimeMessage,true)); + switch (strtoupper($_encoding)) + { + case 'BASE64': + // use imap_base64 to decode, not any longer, as it is strict, and fails if it encounters invalid chars + return base64_decode($_mimeMessage); //imap_base64($_mimeMessage); + break; + case 'QUOTED-PRINTABLE': + // use imap_qprint to decode + return quoted_printable_decode($_mimeMessage); + break; + default: + // it is either not encoded or we don't know about it + return $_mimeMessage; + break; + } + } + + /** + * getMultipartAlternative + * get part of the message, if its stucture is indicating its of multipart alternative style + * a wrapper for multipartmixed + * @param string/int $_uid the messageuid, + * @param array $_structure='', if given use structure for parsing + * @param string $_htmlMode, how to display a message, html, plain text, ... + * @param boolean $_preserveSeen flag to preserve the seenflag by using body.peek + * @return array containing the desired part + */ + function getMultipartAlternative($_uid, $_structure, $_htmlMode, $_preserveSeen = false) + { + // a multipart/alternative has exactly 2 parts (text and html OR text and something else) + // sometimes there are 3 parts, when there is an ics/ical attached/included-> we want to show that + // as attachment AND as abstracted ical information (we use our notification style here). + $partText = false; + $partHTML = false; + if (self::$debug) _debug_array(array("METHOD"=>__METHOD__,"LINE"=>__LINE__,"STRUCTURE"=>$_structure)); + foreach($_structure as $mimePart) { + if($mimePart->type == 'TEXT' && ($mimePart->subType == 'PLAIN' || $mimePart->subType == 'CALENDAR') && $mimePart->bytes > 0) { + if ($mimePart->subType == 'CALENDAR' && $partText === false) $partText = $mimePart; // only if there is no partText set already + if ($mimePart->subType == 'PLAIN') $partText = $mimePart; + } elseif($mimePart->type == 'TEXT' && $mimePart->subType == 'HTML' && $mimePart->bytes > 0) { + $partHTML = $mimePart; + } elseif ($mimePart->type == 'MULTIPART' && ($mimePart->subType == 'RELATED' || $mimePart->subType == 'MIXED') && is_array($mimePart->subParts)) { + // in a multipart alternative we treat the multipart/related as html part + #$partHTML = array($mimePart); + if (self::$debug) error_log(__METHOD__." process MULTIPART/RELATED with array as subparts"); + $partHTML = $mimePart; + } elseif ($mimePart->type == 'MULTIPART' && $mimePart->subType == 'ALTERNATIVE' && is_array($mimePart->subParts)) { + //cascading multipartAlternative structure, assuming only the first one is to be used + return $this->getMultipartAlternative($_uid,$mimePart->subParts,$_htmlMode, $_preserveSeen); + } + } + //error_log(__METHOD__.__LINE__.$_htmlMode); + switch($_htmlMode) { + case 'html_only': + case 'always_display': + if(is_object($partHTML)) { + if($partHTML->subType == 'RELATED') { + return $this->getMultipartRelated($_uid, $partHTML, $_htmlMode, $_preserveSeen); + } elseif($partHTML->subType == 'MIXED') { + return $this->getMultipartMixed($_uid, $partHTML, $_htmlMode, $_preserveSeen); + } else { + return $this->getTextPart($_uid, $partHTML, $_htmlMode, $_preserveSeen); + } + } elseif(is_object($partText) && $_htmlMode=='always_display') { + return $this->getTextPart($_uid, $partText, $_htmlMode, $_preserveSeen); + } + + break; + case 'only_if_no_text': + if(is_object($partText)) { + return $this->getTextPart($_uid, $partText, $_htmlMode, $_preserveSeen); + } elseif(is_object($partHTML)) { + if($partHTML->type) { + return $this->getMultipartRelated($_uid, $partHTML, $_htmlMode, $_preserveSeen); + } else { + return $this->getTextPart($_uid, $partHTML, 'always_display', $_preserveSeen); + } + } + + break; + + default: + if(is_object($partText)) { + return $this->getTextPart($_uid, $partText, $_htmlMode, $_preserveSeen); + } else { + $bodyPart = array( + 'body' => lang("no plain text part found"), + 'mimeType' => 'text/plain', + 'charSet' => self::$displayCharset, + ); + } + + break; + } + + return $bodyPart; + } + + /** + * getMultipartMixed + * get part of the message, if its stucture is indicating its of multipart mixed style + * @param string/int $_uid the messageuid, + * @param array $_structure='', if given use structure for parsing + * @param string $_htmlMode, how to display a message, html, plain text, ... + * @param boolean $_preserveSeen flag to preserve the seenflag by using body.peek + * @return array containing the desired part + */ + function getMultipartMixed($_uid, $_structure, $_htmlMode, $_preserveSeen = false) + { + if (self::$debug) echo __METHOD__."$_uid, $_htmlMode
"; + $bodyPart = array(); + if (self::$debug) _debug_array($_structure); + if (!is_array($_structure)) $_structure = array($_structure); + foreach($_structure as $part) { + if (self::$debug) echo $part->type."/".$part->subType."
"; + switch($part->type) { + case 'MULTIPART': + switch($part->subType) { + case 'ALTERNATIVE': + $bodyPart[] = $this->getMultipartAlternative($_uid, $part->subParts, $_htmlMode, $_preserveSeen); + break; + + case 'MIXED': + case 'SIGNED': + $bodyPart = array_merge($bodyPart, $this->getMultipartMixed($_uid, $part->subParts, $_htmlMode, $_preserveSeen)); + break; + + case 'RELATED': + $bodyPart = array_merge($bodyPart, $this->getMultipartRelated($_uid, $part->subParts, $_htmlMode, $_preserveSeen)); + break; + } + break; + + case 'TEXT': + switch($part->subType) { + case 'PLAIN': + case 'HTML': + case 'CALENDAR': // inline ics/ical files + if($part->disposition != 'ATTACHMENT') { + $bodyPart[] = $this->getTextPart($_uid, $part, $_htmlMode, $_preserveSeen); + } + //error_log(__METHOD__.__LINE__.' ->'.$part->type."/".$part->subType.' -> BodyPart:'.array2string($bodyPart[count($bodyPart)-1])); + break; + } + break; + + case 'MESSAGE': + if($part->subType == 'delivery-status') { + $bodyPart[] = $this->getTextPart($_uid, $part, $_htmlMode, $_preserveSeen); + } + break; + + default: + // do nothing + // the part is a attachment + #$bodyPart[] = $this->getMessageBody($_uid, $_htmlMode, $part->partID, $part); + #if (!($part->type == 'TEXT' && ($part->subType == 'PLAIN' || $part->subType == 'HTML'))) { + # $bodyPart[] = $this->getMessageAttachments($_uid, $part->partID, $part); + #} + } + } + + return $bodyPart; + } + + /** + * getMultipartRelated + * get part of the message, if its stucture is indicating its of multipart related style + * a wrapper for multipartmixed + * @param string/int $_uid the messageuid, + * @param array $_structure='', if given use structure for parsing + * @param string $_htmlMode, how to display a message, html, plain text, ... + * @param boolean $_preserveSeen flag to preserve the seenflag by using body.peek + * @return array containing the desired part + */ + function getMultipartRelated($_uid, $_structure, $_htmlMode, $_preserveSeen = false) + { + return $this->getMultipartMixed($_uid, $_structure, $_htmlMode, $_preserveSeen); + } + + /** + * getTextPart + * get Body from message + * @param string/int $_uid the messageuid, + * @param array $_structure='', if given use structure for parsing + * @param string $_htmlMode, how to display a message, html, plain text, ... + * @param boolean $_preserveSeen flag to preserve the seenflag by using body.peek + * @return array containing the desired text part, mimeType and charset + */ + function getTextPart($_uid, $_structure, $_htmlMode = '', $_preserveSeen = false) + { + //error_log(__METHOD__.__LINE__.'->'.$_uid.':'.array2string($_structure).' '.function_backtrace()); + $bodyPart = array(); + if (self::$debug) _debug_array(array($_structure,function_backtrace())); + $partID = $_structure->partID; + $mimePartBody = $this->icServer->getBodyPart($_uid, $partID, true, $_preserveSeen); + if (PEAR::isError($mimePartBody)) + { + error_log(__METHOD__.__LINE__.' failed:'.$mimePartBody->message); + return false; + } + //_debug_array($mimePartBody); + //error_log(__METHOD__.__LINE__.' UID:'.$_uid.' PartID:'.$partID.' HTMLMode:'.$_htmlMode.' ->'.array2string($_structure).' body:'.array2string($mimePartBody)); + if (empty($mimePartBody)) return array( + 'body' => '', + 'mimeType' => ($_structure->type == 'TEXT' && $_structure->subType == 'HTML') ? 'text/html' : 'text/plain', + 'charSet' => self::$displayCharset, + ); + //_debug_array(preg_replace('/PropertyFile___$/','',$this->decodeMimePart($mimePartBody, $_structure->encoding))); + if($_structure->subType == 'HTML' && $_htmlMode!= 'html_only' && $_htmlMode != 'always_display' && $_htmlMode != 'only_if_no_text') { + $bodyPart = array( + 'error' => 1, + 'body' => lang("displaying html messages is disabled"), + 'mimeType' => 'text/html', + 'charSet' => self::$displayCharset, + ); + } elseif ($_structure->subType == 'PLAIN' && $_htmlMode == 'html_only') { + $bodyPart = array( + 'error' => 1, + 'body' => lang("displaying plain messages is disabled"), + 'mimeType' => 'text/plain', // make sure we do not return mimeType text/html + 'charSet' => self::$displayCharset, + ); + } else { + // some Servers append PropertyFile___ ; strip that here for display + $bodyPart = array( + 'body' => preg_replace('/PropertyFile___$/','',$this->decodeMimePart($mimePartBody, $_structure->encoding, $this->getMimePartCharset($_structure))), + 'mimeType' => ($_structure->type == 'TEXT' && $_structure->subType == 'HTML') ? 'text/html' : 'text/plain', + 'charSet' => $this->getMimePartCharset($_structure), + ); + if ($_structure->type == 'TEXT' && $_structure->subType == 'PLAIN' && + is_array($_structure->parameters) && isset($_structure->parameters['FORMAT']) && + trim(strtolower($_structure->parameters['FORMAT']))=='flowed' + ) + { + if (self::$debug) error_log(__METHOD__.__LINE__." detected TEXT/PLAIN Format:flowed -> removing leading blank ('\r\n ') per line"); + $bodyPart['body'] = str_replace("\r\n ","\r\n", $bodyPart['body']); + } + if ($_structure->subType == 'CALENDAR') + { + // we get an inline CALENDAR ical/ics, we display it using the calendar notification style + $calobj = new calendar_ical; + $calboupdate = new calendar_boupdate; + // timezone stuff + $tz_diff = $GLOBALS['egw_info']['user']['preferences']['common']['tz_offset'] - $this->common_prefs['tz_offset']; + // form an event out of ical + $event = $calobj->icaltoegw($bodyPart['body']); + $event= $event[0]; + // preset the olddate + $olddate = $calboupdate->format_date($event['start']+$tz_diff); + // search egw, if we can find it + $eventid = $calobj->find_event(array('uid'=>$event['uid'])); + if ((int)$eventid[0]>0) + { + // we found an event, we use the first one + $oldevent = $calobj->read($eventid); + // we set the olddate, to comply with the possible merge params for the notification message + if($oldevent != False && $oldevent[$eventid[0]]['start']!=$event[$eventid[0]]['start']) { + $olddate = $calboupdate->format_date($oldevent[$eventid[0]]['start']+$tz_diff); + } + // we merge the changes and the original event + $event = array_merge($oldevent[$eventid[0]],$event); + // for some strange reason, the title of the old event is not replaced with the new title + // if you klick on the ics and import it into egw, so we dont show the title here. + // so if it is a mere reply, we dont use the new title (more detailed info/work needed here) + if ($_structure->parameters['METHOD']=='REPLY') $event['title'] = $oldevent[$eventid[0]]['title']; + } + // we prepare the message + $details = $calboupdate->_get_event_details($event,$action,$event_arr); + $details['olddate']=$olddate; + //_debug_array($_structure); + list($subject,$info) = $calboupdate->get_update_message($event,($_structure->parameters['METHOD']=='REPLY'?false:true)); + $info = $GLOBALS['egw']->preferences->parse_notify($info,$details); + // we set the bodyPart, we only show the event, we dont actually do anything, as we expect the user to + // click on the attached ics to update his own eventstore + $bodyPart['body'] = $subject; + $bodyPart['body'] .= "\n".$info; + $bodyPart['body'] .= "\n\n".lang('Event Details follow').":\n"; + foreach($event_arr as $key => $val) + { + if(strlen($details[$key])) { + switch($key){ + case 'access': + case 'priority': + case 'link': + break; + default: + $bodyPart['body'] .= sprintf("%-20s %s\n",$val['field'].':',$details[$key]); + break; + } + } + } + } + } + //_debug_array($bodyPart); + return $bodyPart; + } + + /** + * getMessageBody + * get Body from message + * @param string/int $_uid the messageuid, + * @param string $_htmlOptions, how to display a message, html, plain text, ... + * @param string/int $_partID='' , the partID, may be omitted + * @param array $_structure='', if given use structure for parsing + * @param boolean $_preserveSeen flag to preserve the seenflag by using body.peek + * @return array containing the message body, mimeType and charset + */ + function getMessageBody($_uid, $_htmlOptions='', $_partID='', $_structure = '', $_preserveSeen = false, $_folder = '') + { + if (self::$debug) echo __METHOD__."$_uid, $_htmlOptions, $_partID
"; + if($_htmlOptions != '') { + $this->htmlOptions = $_htmlOptions; + } + if(is_object($_structure)) { + $structure = $_structure; + } else { + $this->icServer->_cmd_counter = rand($this->icServer->_cmd_counter+1,$this->icServer->_cmd_counter+100); + $structure = $this->_getStructure($_uid, true, false, $_folder); + if($_partID != '') { + $structure = $this->_getSubStructure($structure, $_partID); + } + } + if (self::$debug) _debug_array($structure); + switch($structure->type) { + case 'APPLICATION': + return array( + array( + 'body' => '', + 'mimeType' => 'text/plain', + 'charSet' => 'iso-8859-1', + ) + ); + break; + case 'MULTIPART': + switch($structure->subType) { + case 'ALTERNATIVE': + $bodyParts = array($this->getMultipartAlternative($_uid, $structure->subParts, $this->htmlOptions, $_preserveSeen)); + + break; + + case 'NIL': // multipart with no Alternative + case 'MIXED': + case 'REPORT': + case 'SIGNED': + $bodyParts = $this->getMultipartMixed($_uid, $structure->subParts, $this->htmlOptions, $_preserveSeen); + break; + + case 'RELATED': + $bodyParts = $this->getMultipartRelated($_uid, $structure->subParts, $this->htmlOptions, $_preserveSeen); + break; + } + return self::normalizeBodyParts($bodyParts); + break; + case 'VIDEO': + case 'AUDIO': // some servers send audiofiles and imagesfiles directly, without any stuff surround it + case 'IMAGE': // they are displayed as Attachment NOT INLINE + return array( + array( + 'body' => '', + 'mimeType' => $structure->subType, + ), + ); + break; + case 'TEXT': + $bodyPart = array(); + if ( $structure->disposition != 'ATTACHMENT') { + switch($structure->subType) { + case 'CALENDAR': + // this is handeled in getTextPart + case 'HTML': + case 'PLAIN': + default: + $bodyPart = array($this->getTextPart($_uid, $structure, $this->htmlOptions, $_preserveSeen)); + } + } else { + // what if the structure->disposition is attachment ,... + } + return self::normalizeBodyParts($bodyPart); + break; + case 'ATTACHMENT': + case 'MESSAGE': + switch($structure->subType) { + case 'RFC822': + $newStructure = array_shift($structure->subParts); + if (self::$debug) {echo __METHOD__." Message -> RFC -> NewStructure:"; _debug_array($newStructure);} + return self::normalizeBodyParts($this->getMessageBody($_uid, $_htmlOptions, $newStructure->partID, $newStructure)); + break; + } + break; + default: + if (self::$debug) _debug_array($structure); + return array( + array( + 'body' => lang('The mimeparser can not parse this message.'), + 'mimeType' => 'text/plain', + 'charSet' => 'iso-8859-1', + ) + ); + break; + } + } + + /** + * normalizeBodyParts - function to gather and normalize all body Information + * @param _bodyParts - Body Array + * @return array - a normalized Bodyarray + */ + static function normalizeBodyParts($_bodyParts) + { + if (is_array($_bodyParts)) + { + foreach($_bodyParts as $singleBodyPart) + { + if (!isset($singleBodyPart['body'])) { + $buff = self::normalizeBodyParts($singleBodyPart); + foreach ((array)$buff as $val) $body2return[] = $val; + continue; + } + $body2return[] = $singleBodyPart; + } + } + else + { + $body2return = $_bodyParts; + } + return $body2return; + } + + /** + * getdisplayableBody - creates the bodypart of the email as textual representation + * @param object $mailClass the mailClass object to be used + * @param array $bodyParts with the bodyparts + * @return string a preformatted string with the mails converted to text + */ + static function &getdisplayableBody(&$mailClass, $bodyParts, $preserveHTML = false) + { + for($i=0; $i'.$bodyParts[$i]['body']); + $newBody = translation::convert($bodyParts[$i]['body'], $bodyParts[$i]['charSet']); + //error_log(__METHOD__.__LINE__.' MimeType:'.$bodyParts[$i]['mimeType'].'->'.$newBody); + /* + // in a way, this tests if we are having real utf-8 (the displayCharset) by now; we should if charsets reported (or detected) are correct + if (strtoupper(self::$displayCharset) == 'UTF-8') + { + $test = json_encode($newBody); + //error_log(__METHOD__.__LINE__.'#'.$test.'# ->'.strlen($newBody).' Error:'.json_last_error()); + if (json_last_error() != JSON_ERROR_NONE && strlen($newBody)>0) + { + // this should not be needed, unless something fails with charset detection/ wrong charset passed + error_log(__METHOD__.__LINE__.' Charset Reported:'.$bodyParts[$i]['charSet'].' Carset Detected:'.translation::detect_encoding($bodyParts[$i]['body'])); + $newBody = utf8_encode($newBody); + } + } + */ + //error_log(__METHOD__.__LINE__.' before purify:'.$newBody); + $mailClass->activeMimeType = 'text/plain'; + if ($bodyParts[$i]['mimeType'] == 'text/html') { + $mailClass->activeMimeType = $bodyParts[$i]['mimeType']; + // as translation::convert reduces \r\n to \n and purifier eats \n -> peplace it with a single space + $newBody = str_replace("\n"," ",$newBody); + // convert HTML to text, as we dont want HTML in infologs + if (extension_loaded('tidy')) + { + $tidy = new tidy(); + $cleaned = $tidy->repairString($newBody, self::$tidy_config,'utf8'); + // Found errors. Strip it all so there's some output + if($tidy->getStatus() == 2) + { + error_log(__METHOD__.__LINE__.' ->'.$tidy->errorBuffer); + } + else + { + $newBody = $cleaned; + } + if (!$preserveHTML) + { + // filter only the 'body', as we only want that part, if we throw away the html + preg_match('`(]*>)(.+?)(.*?)`ims', $newBody, $matches); + if ($matches[2]) + { + $hasOther = true; + $newBody = $matches[2]; + } + } + } + else + { + // htmLawed filter only the 'body' + preg_match('`(]*>)(.+?)(.*?)`ims', $newBody, $matches); + if ($matches[2]) + { + $hasOther = true; + $newBody = $matches[2]; + } + $htmLawed = new egw_htmLawed(); + // the next line should not be needed + $newBody = str_replace(array('&','

',"
 
",'
 
'),array('&','
','
','
'),$newBody); + $newBody = $htmLawed->egw_htmLawed($newBody); + if ($hasOther && $preserveHTML) $newBody = $matches[1]. $newBody. $matches[3]; + } + //error_log(__METHOD__.__LINE__.' after purify:'.$newBody); + if ($preserveHTML==false) $newBody = $mailClass->convertHTMLToText($newBody,true); + //error_log(__METHOD__.__LINE__.' after convertHTMLToText:'.$newBody); + if ($preserveHTML==false) $newBody = nl2br($newBody); // we need this, as htmLawed removes \r\n + $mailClass->getCleanHTML($newBody,false,$preserveHTML); // remove stuff we regard as unwanted + if ($preserveHTML==false) $newBody = str_replace("
","\r\n",$newBody); + //error_log(__METHOD__.__LINE__.' after getClean:'.$newBody); + $message .= $newBody; + continue; + } + $newBody =self::htmlspecialchars($newBody); + //error_log(__METHOD__.__LINE__.' Body(after specialchars):'.$newBody); + $newBody = strip_tags($newBody); //we need to fix broken tags (or just stuff like "<800 USD/p" ) + //error_log(__METHOD__.__LINE__.' Body(after strip tags):'.$newBody); + $newBody = htmlspecialchars_decode($newBody,ENT_QUOTES); + //error_log(__METHOD__.__LINE__.' Body (after hmlspc_decode):'.$newBody); + $message .= $newBody; + //continue; + } + return $message; + } + /** * getMessageRawHeader * get parsed headers from message @@ -3012,6 +3731,191 @@ class mail_bo } } + /** + * functions to allow access to mails through other apps to fetch content + * used in infolog, tracker + */ + + /** + * get_mailcontent - fetches the actual mailcontent, and returns it as well defined array + * @param object mailClass the mailClassobject to be used + * @param uid the uid of the email to be processed + * @param partid the partid of the email + * @param mailbox the mailbox, that holds the message + * @param preserveHTML flag to pass through to getdisplayableBody + * @return array with 'mailaddress'=>$mailaddress, + * 'subject'=>$subject, + * 'message'=>$message, + * 'attachments'=>$attachments, + * 'headers'=>$headers, + */ + static function get_mailcontent(&$mailClass,$uid,$partid='',$mailbox='', $preserveHTML = false) + { + //echo __METHOD__." called for $uid,$partid
"; + $headers = $mailClass->getMessageHeader($uid,$partid,true); + // dont force retrieval of the textpart, let mailClass preferences decide + $bodyParts = $mailClass->getMessageBody($uid,($preserveHTML?'always_display':'only_if_no_text'),$partid); + //error_log(array2string($bodyParts)); + $attachments = $mailClass->getMessageAttachments($uid,$partid); + + if ($mailClass->isSentFolder($mailbox)) $mailaddress = $headers['TO']; + elseif (isset($headers['FROM'])) $mailaddress = $headers['FROM']; + elseif (isset($headers['SENDER'])) $mailaddress = $headers['SENDER']; + if (isset($headers['CC'])) $mailaddress .= ','.$headers['CC']; + //_debug_array($headers); + $subject = $headers['SUBJECT']; + + $message = self::getdisplayableBody($mailClass, $bodyParts, $preserveHTML); + if ($preserveHTML && $mailClass->activeMimeType == 'text/plain') $message = '
'.$message.'
'; + $headdata = self::createHeaderInfoSection($headers, '',$preserveHTML); + $message = $headdata.$message; + //echo __METHOD__.'
'; + //_debug_array($attachments); + if (is_array($attachments)) + { + foreach ($attachments as $num => $attachment) + { + if ($attachment['mimeType'] == 'MESSAGE/RFC822') + { + //_debug_array($mailClass->getMessageHeader($uid, $attachment['partID'])); + //_debug_array($mailClass->getMessageBody($uid,'', $attachment['partID'])); + //_debug_array($mailClass->getMessageAttachments($uid, $attachment['partID'])); + $mailcontent = self::get_mailcontent($mailClass,$uid,$attachment['partID']); + $headdata =''; + if ($mailcontent['headers']) + { + $headdata = self::createHeaderInfoSection($mailcontent['headers'],'',$preserveHTML); + } + if ($mailcontent['message']) + { + $tempname =tempnam($GLOBALS['egw_info']['server']['temp_dir'],$GLOBALS['egw_info']['flags']['currentapp']."_"); + $attachedMessages[] = array( + 'type' => 'TEXT/PLAIN', + 'name' => $mailcontent['subject'].'.txt', + 'tmp_name' => $tempname, + ); + $tmpfile = fopen($tempname,'w'); + fwrite($tmpfile,$headdata.$mailcontent['message']); + fclose($tmpfile); + } + foreach($mailcontent['attachments'] as $tmpattach => $tmpval) + { + $attachedMessages[] = $tmpval; + } + unset($attachments[$num]); + } + else + { + $attachments[$num] = array_merge($attachments[$num],$mailClass->getAttachment($uid, $attachment['partID'])); + if (isset($attachments[$num]['charset'])) { + if ($attachments[$num]['charset']===false) $attachments[$num]['charset'] = translation::detect_encoding($attachments[$num]['attachment']); + translation::convert($attachments[$num]['attachment'],$attachments[$num]['charset']); + } + $attachments[$num]['type'] = $attachments[$num]['mimeType']; + $attachments[$num]['tmp_name'] = tempnam($GLOBALS['egw_info']['server']['temp_dir'],$GLOBALS['egw_info']['flags']['currentapp']."_"); + $tmpfile = fopen($attachments[$num]['tmp_name'],'w'); + fwrite($tmpfile,$attachments[$num]['attachment']); + fclose($tmpfile); + unset($attachments[$num]['attachment']); + } + } + if (is_array($attachedMessages)) $attachments = array_merge($attachments,$attachedMessages); + } + return array( + 'mailaddress'=>$mailaddress, + 'subject'=>$subject, + 'message'=>$message, + 'attachments'=>$attachments, + 'headers'=>$headers, + ); + } + + /** + * createHeaderInfoSection - creates a textual headersection from headerobject + * @param array header headerarray may contain SUBJECT,FROM,SENDER,TO,CC,BCC,DATE,PRIORITY,IMPORTANCE + * @param string headline Text tom use for headline + * @param bool createHTML do it with HTML breaks + * @return string a preformatted string with the information of the header worked into it + */ + static function createHeaderInfoSection($header,$headline='', $createHTML = false) + { + $headdata = null; + if ($header['SUBJECT']) $headdata = lang('subject').': '.$header['SUBJECT'].($createHTML?"
":"\n"); + if ($header['FROM']) $headdata .= lang('from').': '.self::convertAddressArrayToString($header['FROM'], $createHTML).($createHTML?"
":"\n"); + if ($header['SENDER']) $headdata .= lang('sender').': '.self::convertAddressArrayToString($header['SENDER'], $createHTML).($createHTML?"
":"\n"); + if ($header['TO']) $headdata .= lang('to').': '.self::convertAddressArrayToString($header['TO'], $createHTML).($createHTML?"
":"\n"); + if ($header['CC']) $headdata .= lang('cc').': '.self::convertAddressArrayToString($header['CC'], $createHTML).($createHTML?"
":"\n"); + if ($header['BCC']) $headdata .= lang('bcc').': '.self::convertAddressArrayToString($header['BCC'], $createHTML).($createHTML?"
":"\n"); + if ($header['DATE']) $headdata .= lang('date').': '.$header['DATE'].($createHTML?"
":"\n"); + if ($header['PRIORITY'] && $header['PRIORITY'] != 'normal') $headdata .= lang('priority').': '.$header['PRIORITY'].($createHTML?"
":"\n"); + if ($header['IMPORTANCE'] && $header['IMPORTANCE'] !='normal') $headdata .= lang('importance').': '.$header['IMPORTANCE'].($createHTML?"
":"\n"); + //if ($mailcontent['headers']['ORGANIZATION']) $headdata .= lang('organization').': '.$mailcontent['headers']['ORGANIZATION']."\ + if (!empty($headdata)) + { + if (!empty($headline)) $headdata = "---------------------------- $headline ----------------------------".($createHTML?"
":"\n").$headdata; + if (empty($headline)) $headdata = "--------------------------------------------------------".($createHTML?"
":"\n").$headdata; + $headdata .= "--------------------------------------------------------".($createHTML?"
":"\n"); + } + else + { + $headdata = "--------------------------------------------------------".($createHTML?"
":"\n"); + } + return $headdata; + } + + /** + * convertAddressArrayToString - converts an mail envelope Address Array To String + * @param array $rfcAddressArray an addressarray as provided by mail retieved via egw_pear.... + * @return string a comma separated string with the mailaddress(es) converted to text + */ + static function convertAddressArrayToString($rfcAddressArray, $createHTML = false) + { + //error_log(__METHOD__.__LINE__.array2string($rfcAddressArray)); + $returnAddr =''; + if (is_array($rfcAddressArray)) + { + foreach((array)$rfcAddressArray as $addressData) { + //error_log(__METHOD__.__LINE__.array2string($addressData)); + if($addressData['MAILBOX_NAME'] == 'NIL') { + continue; + } + if(strtolower($addressData['MAILBOX_NAME']) == 'undisclosed-recipients') { + continue; + } + if ($addressData['RFC822_EMAIL']) + { + $addressObjectA = imap_rfc822_parse_adrlist((get_magic_quotes_gpc()?stripslashes($addressData['RFC822_EMAIL']):$addressData['RFC822_EMAIL']),''); + } + else + { + $emailaddress = ($addressData['PERSONAL_NAME']?$addressData['PERSONAL_NAME'].' <'.$addressData['EMAIL'].'>':$addressData['EMAIL']); + $addressObjectA = imap_rfc822_parse_adrlist((get_magic_quotes_gpc()?stripslashes($emailaddress):$emailaddress),''); + } + $addressObject = $addressObjectA[0]; + //error_log(__METHOD__.__LINE__.array2string($addressObject)); + if ($addressObject->host == '.SYNTAX-ERROR.') continue; + //$mb =(string)$addressObject->mailbox; + //$h = (string)$addressObject->host; + //$p = (string)$addressObject->personal; + $returnAddr .= (strlen($returnAddr)>0?',':''); + //error_log(__METHOD__.__LINE__.$p.' <'.$mb.'@'.$h.'>'); + $buff = imap_rfc822_write_address($addressObject->mailbox, self::$idna2->decode($addressObject->host), $addressObject->personal); + $buff = str_replace(array('<','>'),array('[',']'),$buff); + if ($createHTML) $buff = mail_bo::htmlspecialchars($buff); + //error_log(__METHOD__.__LINE__.' Address: '.$returnAddr); + $returnAddr .= $buff; + } + } + else + { + // do not mess with strings, return them untouched /* ToDo: validate string as Address */ + $rfcAddressArray = self::decode_header($rfcAddressArray,true); + $rfcAddressArray = str_replace(array('<','>'),array('[',']'),$rfcAddressArray); + if (is_string($rfcAddressArray)) return ($createHTML ? mail_bo::htmlspecialchars($rfcAddressArray) : $rfcAddressArray); + } + return $returnAddr; + } + /** * Hook stuff */ diff --git a/mail/inc/class.mail_hooks.inc.php b/mail/inc/class.mail_hooks.inc.php index d41c8e507a..40688c01d1 100644 --- a/mail/inc/class.mail_hooks.inc.php +++ b/mail/inc/class.mail_hooks.inc.php @@ -812,7 +812,7 @@ class mail_hooks // Destination div for folder tree $file[] = array( 'no_lang' => true, - 'text'=>'', + 'text'=>'', 'link'=>false, 'icon' => false ); diff --git a/mail/inc/class.mail_ui.inc.php b/mail/inc/class.mail_ui.inc.php index 5db18e4cb3..9b9fffd1e3 100644 --- a/mail/inc/class.mail_ui.inc.php +++ b/mail/inc/class.mail_ui.inc.php @@ -25,6 +25,7 @@ class mail_ui 'index' => True, 'displayHeader' => True, 'saveMessage' => True, + 'vfsSaveMessage' => True, 'TestConnection' => True, ); @@ -504,7 +505,7 @@ class mail_ui if ($_nodeID) { $node = self::findNode($out,$_nodeID); - error_log(__METHOD__.__LINE__.array2string($node)); + //error_log(__METHOD__.__LINE__.array2string($node)); return $node; } return ($c?$out:array('id'=>0, 'item'=>array('text'=>'INBOX','tooltip'=>'INBOX'.' '.lang('(not connected)'),'im0'=>'kfm_home.png'))); @@ -1057,11 +1058,11 @@ unset($query['actions']); * @param string $_rowID, string - a colon separated string in the form accountID:profileID:folder:message_uid * @return array populated named result array (accountID,profileID,folder,msgUID) */ - function splitRowID($_rowID) + static function splitRowID($_rowID) { $res = explode(self::$delimiter,$_rowID); - // as a rowID is perceeded by app::, we ignore the first part - return array('accountID'=>$res[1], 'profileID'=>$res[2], 'folder'=>base64_decode($res[3]), 'msgUID'=>$res[4]); + // as a rowID is perceeded by app::, should be mail! + return array('app'=>$res[0], 'accountID'=>$res[1], 'profileID'=>$res[2], 'folder'=>base64_decode($res[3]), 'msgUID'=>$res[4]); } /** @@ -1385,7 +1386,7 @@ unset($query['actions']); if(isset($_GET['id'])) $rowID = $_GET['id']; if(isset($_GET['part'])) $partID = $_GET['part']; - $hA = $this->splitRowID($rowID); + $hA = self::splitRowID($rowID); $uid = $hA['msgUID']; $mailbox = $hA['folder']; @@ -1434,7 +1435,7 @@ unset($query['actions']); if(isset($_GET['part'])) $partID = $_GET['part']; if (isset($_GET['location'])&& ($_GET['location']=='display'||$_GET['location']=='filemanager')) $display = $_GET['location']; - $hA = $this->splitRowID($rowID); + $hA = self::splitRowID($rowID); $uid = $hA['msgUID']; $mailbox = $hA['folder']; @@ -1471,6 +1472,41 @@ unset($query['actions']); } } + /** + * Save an Message in the vfs + * + * @param string|array $ids use splitRowID, to separate values + * @param string $path path in vfs (no egw_vfs::PREFIX!), only directory for multiple id's ($ids is an array) + * @return string javascript eg. to close the selector window + */ + function vfsSaveMessage($ids,$path) + { + return "alert('".__METHOD__.'("'.array2string($id).'","'.$path."\")'); window.close();"; +/* + if (is_array($ids) && !egw_vfs::is_writable($path) || !is_array($ids) && !egw_vfs::is_writable(dirname($path))) + { + return 'alert("'.addslashes(lang('%1 is NOT writable by you!',$path)).'"); window.close();'; + } + foreach((array)$ids as $id) + { + $hA = self::splitRowID($id); + $uid = $hA['msgUID']; + $mailbox = $hA['folder']; + if ($mb != $this->mail_bo->mailbox) $this->mail_bo->reopen($mb = $mailbox); + $message = $this->mail_bo->getMessageRawBody($uid, $partID=''); + if (!($fp = egw_vfs::fopen($file=$path.($name ? '/'.$name : ''),'wb')) || + !fwrite($fp,$message)) + { + $err .= 'alert("'.addslashes(lang('Error saving %1!',$file)).'");'; + } + if ($fp) fclose($fp); + } + $this->mail_bo->closeConnection(); + + return $err.'window.close();'; +*/ + } + /** * getFolderStatus - its called via json, so the function must start with ajax (or the class-name must contain ajax) * gets the counters and sets the text of a treenode if needed (unread Messages found) @@ -1578,12 +1614,12 @@ unset($query['actions']); } else { - $uidA = $this->splitRowID($_messageList['msg'][0]); + $uidA = self::splitRowID($_messageList['msg'][0]); $folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder } foreach($_messageList['msg'] as $rowID) { - $hA = $this->splitRowID($rowID); + $hA = self::splitRowID($rowID); $messageList[] = $hA['msgUID']; } $this->mail_bo->flagMessages($_flag, ($_messageList=='all' ? 'all':$messageList),$folder); @@ -1624,12 +1660,12 @@ unset($query['actions']); } else { - $uidA = $this->splitRowID($_messageList['msg'][0]); + $uidA = self::splitRowID($_messageList['msg'][0]); $folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder } foreach($_messageList['msg'] as $rowID) { - $hA = $this->splitRowID($rowID); + $hA = self::splitRowID($rowID); $messageList[] = $hA['msgUID']; } $this->mail_bo->deleteMessages(($_messageList=='all' ? 'all':$messageList),$folder); diff --git a/mail/js/app.js b/mail/js/app.js index 84d3079b64..d1d341053a 100644 --- a/mail/js/app.js +++ b/mail/js/app.js @@ -350,6 +350,78 @@ function mail_mailsource(_action, _elems) mail_displayHeaderLines(url); } +/** + * Save a message + * + * @param _action + * @param _elems _elems[0].id is the row-id + */ +function mail_save(_action, _elems) +{ + //alert('mail_save('+_elems[0].id+')'); + var url = window.egw_webserverUrl+'/index.php?'; + url += 'menuaction=mail.mail_ui.saveMessage'; // todo compose for Draft folder + url += '&id='+_elems[0].id; + //window.open(url,'_blank','dependent=yes,width=100,height=100,scrollbars=yes,status=yes') + document.location = url; +} + +/** + * Save a message to filemanager + * + * @param _action + * @param _elems _elems[0].id is the row-id + */ +function mail_save2fm(_action, _elems) +{ + var _id = _elems[0].id; + var dataElem = egw.dataGetUIDdata(_id); + var url = window.egw_webserverUrl+'/index.php?'; + url += 'menuaction=filemanager.filemanager_select.select'; // todo compose for Draft folder + url += '&mode=saveas'; + var filename =dataElem.data.subject.replace(/[\f\n\t\v/\\:*#?<>\|]/g,"_"); + url += '&name='+encodeURIComponent(filename+'.eml'); + url += '&mime=message'+encodeURIComponent('/')+'rfc822'; + url += '&method=mail.mail_ui.vfsSaveMessage' + url += '&id='+_elems[0].id; + url += '&label=Save'; + //window.open(url,'_blank','dependent=yes,width=100,height=100,scrollbars=yes,status=yes') + //document.location = url; + egw_openWindowCentered(url,'vfs_save_message_'+_elems[0].id,'640','570',window.outerWidth/2,window.outerHeight/2); + +} + +/** + * Save message as InfoLog + * + * @param _action + * @param _elems _elems[0].id is the row-id + */ +function mail_infolog(_action, _elems) +{ + //alert('mail_infolog('+_elems[0].id+')');return; + var url = window.egw_webserverUrl+'/index.php?'; + url += 'menuaction=infolog.infolog_ui.import_mail'; // todo compose for Draft folder + url += '&rowid='+_elems[0].id; + egw_openWindowCentered(url,'import_mail_'+_elems[0].id,_action.data.width,_action.data.height); +} + +/** + * Save message as ticket + * + * @param _action _action.id is 'read', 'unread', 'flagged' or 'unflagged' + * @param _elems + */ +function mail_tracker(_action, _elems) +{ + //alert('mail_tracker('+_elems[0].id+')'); + var url = window.egw_webserverUrl+'/index.php?'; + url += 'menuaction=tracker.tracker_ui.import_mail'; // todo compose for Draft folder + url += '&rowid='+_elems[0].id; + egw_openWindowCentered(url,'import_tracker_'+_elems[0].id,_action.data.width,_action.data.height); +} + + /** * mail_getFormData * diff --git a/mail/templates/default/app.css b/mail/templates/default/app.css index 89c11fd10d..bf60dc9fcd 100644 --- a/mail/templates/default/app.css +++ b/mail/templates/default/app.css @@ -1,3 +1,9 @@ +textarea +{ + font-family: monospace, sans-serif; + font-size: 110%; +} + .defaultProfile { color:#000000; font-weight:bold !important; } @@ -217,6 +223,9 @@ pre { color: #666; white-space: nowrap; } +.dtree div { + width: 100%; +} .dtree img { border: 0px; vertical-align: middle; @@ -254,12 +263,14 @@ pre { } /* avoid the vertical scrollbar within the sidebox section (triggered by the vertical dimension of the tree) -*/.divSidebox { +*/ +.divSidebox { overflow: hidden; } /* avoid the vertical scrollbar within the actual tablecontainer (of the tree) */ .containerTableStyle { + width: 100%; overflow: hidden; }