* Mail: Implement mail integration with new structure and add new feature

- Add mail to calendar as calendar entry, including all mail attachments as links
- Add user choice for saving mail into an existing ticket or a new ticket
- Fix mail attachments integration into infolog, tracker or calendar entry
This commit is contained in:
Hadi Nategh 2015-05-05 08:03:13 +00:00
commit 50782cfa8b
19 changed files with 840 additions and 576 deletions

View File

@ -856,6 +856,19 @@ END:VALARM';
unset($data); // not used, but in function signature for hooks unset($data); // not used, but in function signature for hooks
return true; return true;
} }
/**
* Mail integration hook to import mail message contents into a calendar entry
*
* @return string method to be executed for calendar mail integration
*/
public static function mail_import($args)
{
return array (
'menuaction' => 'calendar.calendar_uiforms.mail_import',
'popup' => egw_link::get_registry('calendar', 'edit_popup')
);
}
} }
// Not part of the class, since config hooks are still using the old style // Not part of the class, since config hooks are still using the old style

View File

@ -32,6 +32,7 @@ class calendar_uiforms extends calendar_ui
'import' => true, 'import' => true,
'cat_acl' => true, 'cat_acl' => true,
'meeting' => true, 'meeting' => true,
'mail_import' => true,
); );
/** /**
@ -1317,13 +1318,12 @@ class calendar_uiforms extends calendar_ui
$event['whole_day'] = !$start['hour'] && !$start['minute'] && $end['hour'] == 23 && $end['minute'] == 59; $event['whole_day'] = !$start['hour'] && !$start['minute'] && $end['hour'] == 23 && $end['minute'] == 59;
$link_to_id = $event['id']; $link_to_id = $event['id'];
if (!$add_link && !$event['id'] && isset($_REQUEST['link_app']) && isset($_REQUEST['link_id'])) if (!$event['id'] && isset($_REQUEST['link_app']) && isset($_REQUEST['link_id']))
{ {
$link_ids = is_array($_REQUEST['link_id']) ? $_REQUEST['link_id'] : array($_REQUEST['link_id']); $link_ids = is_array($_REQUEST['link_id']) ? $_REQUEST['link_id'] : array($_REQUEST['link_id']);
foreach(is_array($_REQUEST['link_app']) ? $_REQUEST['link_app'] : array($_REQUEST['link_app']) as $n => $link_app) foreach(is_array($_REQUEST['link_app']) ? $_REQUEST['link_app'] : array($_REQUEST['link_app']) as $n => $link_app)
{ {
$link_id = $link_ids[$n]; $link_id = $link_ids[$n];
$app_entry = array();
if(!preg_match('/^[a-z_0-9-]+:[:a-z_0-9-]+$/i',$link_app.':'.$link_id)) // guard against XSS if(!preg_match('/^[a-z_0-9-]+:[:a-z_0-9-]+$/i',$link_app.':'.$link_id)) // guard against XSS
{ {
continue; continue;
@ -1687,9 +1687,9 @@ class calendar_uiforms extends calendar_ui
* Display for FMail an iCal meeting request and allow to accept, tentative or reject it or a reply and allow to apply it * Display for FMail an iCal meeting request and allow to accept, tentative or reject it or a reply and allow to apply it
* *
* @todo Handle situation when user is NOT invited, but eg. can view that mail ... * @todo Handle situation when user is NOT invited, but eg. can view that mail ...
* @param array $event=null; special usage if $event is array('event'=>null,'msg'=>'','useSession'=>true) we * @param array $event = null; special usage if $event is array('event'=>null,'msg'=>'','useSession'=>true) we
* are called by new mail-app; and we intend to use the stuff passed on by session * are called by new mail-app; and we intend to use the stuff passed on by session
* @param string $msg=null * @param string $msg = null
*/ */
function meeting(array $event=null, $msg=null) function meeting(array $event=null, $msg=null)
{ {
@ -1866,7 +1866,7 @@ class calendar_uiforms extends calendar_ui
$event['ics_method_label'] = strtolower($ical_method) == 'request' ? $event['ics_method_label'] = strtolower($ical_method) == 'request' ?
lang('Meeting request') : lang('Reply to meeting request'); lang('Meeting request') : lang('Reply to meeting request');
$tpl = new etemplate_new('calendar.meeting'); $tpl = new etemplate_new('calendar.meeting');
$tpl->exec('calendar.calendar_uiforms.meeting', $event, $sel_options, $readonlys, $event, 2); $tpl->exec('calendar.calendar_uiforms.meeting', $event, array(), $readonlys, $event, 2);
} }
/** /**
@ -2058,7 +2058,7 @@ class calendar_uiforms extends calendar_ui
{ {
if (!$content['duration']) $content['duration'] = $content['end'] - $content['start']; if (!$content['duration']) $content['duration'] = $content['end'] - $content['start'];
$weekds = 0; $weekds = 0;
foreach ($content['weekdays'] as $keys =>$wdays) foreach ($content['weekdays'] as &$wdays)
{ {
$weekds = $weekds + $wdays; $weekds = $weekds + $wdays;
} }
@ -2288,8 +2288,6 @@ class calendar_uiforms extends calendar_ui
} }
if (!is_array($content)) if (!is_array($content))
{ {
$view = $GLOBALS['egw']->session->appsession('view','calendar');
$content = array( $content = array(
'start' => $this->bo->date2ts($_REQUEST['start'] ? $_REQUEST['start'] : $this->date), 'start' => $this->bo->date2ts($_REQUEST['start'] ? $_REQUEST['start'] : $this->date),
'end' => $this->bo->date2ts($_REQUEST['end'] ? $_REQUEST['end'] : $this->date), 'end' => $this->bo->date2ts($_REQUEST['end'] ? $_REQUEST['end'] : $this->date),
@ -2562,4 +2560,100 @@ class calendar_uiforms extends calendar_ui
} }
} }
} }
/**
* imports a mail as Calendar
*
* @param array $mailContent = null mail content
* @return array
*/
function mail_import(array $mailContent=null)
{
// It would get called from compose as a popup with egw_data
if (!is_array($mailContent) && ($_GET['egw_data']))
{
// get raw mail data
egw_link::get_data ($_GET['egw_data']);
return false;
}
if (is_array($mailContent))
{
// Addressbook
$AB = new addressbook_bo();
$accounts = array(0 => $GLOBALS['egw_info']['user']['account_id']);
$participants[0] = array (
'uid' => $GLOBALS['egw_info']['user']['account_id'],
'delete_id' => $GLOBALS['egw_info']['user']['account_id'],
'status' => 'A',
'old_status' => 'A',
'app' => 'User',
'role' => 'REQ-PARTICIPANT'
);
foreach($mailContent['addresses'] as $address)
{
// Get available contacts from the email
$contacts = $AB->search(array(
'email' => $address['email'],
'email_home' => $address['email']
),'contact_id,contact_email,contact_email_home,egw_addressbook.account_id as account_id','','','',false,'OR',false,array('owner' => 0),'',false);
if (is_array($contacts))
{
foreach($contacts as $account)
{
$accounts[] = $account['account_id'];
}
}
else
{
$participants []= array (
'app' => 'email',
'uid' => 'e'.$address['email'],
'status' => 'U',
'old_status' => 'U'
);
}
}
$participants = array_merge($participants , array(
"account" => $accounts,
"role" => "REQ-PARTICIPANT",
"add" => "pressed"
));
// Prepare calendar event draft
$event = array(
'title' => $mailContent['subject'],
'description' => $mailContent['message'],
'participants' => $participants,
'link_to' => array(
'to_app' => 'calendar',
'to_id' => 0,
),
'start' => $mailContent['date'],
'duration' => 60 * $this->cal_prefs['interval']
);
if (is_array($mailContent['attachments']))
{
foreach ($mailContent['attachments'] as $attachment)
{
if($attachment['egw_data'])
{
egw_link::link('calendar',$event['link_to']['to_id'],egw_link::DATA_APPNAME, $attachment);
}
else if(is_readable($attachment['tmp_name']))
{
egw_link::link('calendar',$event['link_to']['to_id'],'file', $attachment);
}
}
}
}
else
{
egw_framework::window_close(lang('No content found to show up as calendar entry.'));
}
return $this->process_edit($event);
}
} }

View File

@ -46,6 +46,7 @@ $setup_info['calendar']['hooks']['infolog_set'] = 'calendar.calendar_bo.infolog_
$setup_info['calendar']['hooks']['export_limit'] = 'calendar_hooks::getAppExportLimit'; $setup_info['calendar']['hooks']['export_limit'] = 'calendar_hooks::getAppExportLimit';
$setup_info['calendar']['hooks']['acl_rights'] = 'calendar_hooks::acl_rights'; $setup_info['calendar']['hooks']['acl_rights'] = 'calendar_hooks::acl_rights';
$setup_info['calendar']['hooks']['categories'] = 'calendar_hooks::categories'; $setup_info['calendar']['hooks']['categories'] = 'calendar_hooks::categories';
$setup_info['calendar']['hooks']['mail_import'] = 'calendar_hooks::mail_import';
/* Dependencies for this app to work */ /* Dependencies for this app to work */
$setup_info['calendar']['depends'][] = array( $setup_info['calendar']['depends'][] = array(

File diff suppressed because it is too large Load Diff

View File

@ -1672,7 +1672,7 @@ var et2_link_list = et2_link_string.extend(
link.dom_id = 'temp_'+egw.uid(); link.dom_id = 'temp_'+egw.uid();
} }
// Icon should be in registry // Icon should be in registry
if(typeof link.icon == 'undefined') if(!link.icon)
{ {
link.icon = egw.link_get_registry(link.app,'icon'); link.icon = egw.link_get_registry(link.app,'icon');
// No icon, try by mime type - different place for un-saved entries // No icon, try by mime type - different place for un-saved entries
@ -1684,7 +1684,7 @@ var et2_link_list = et2_link_string.extend(
} }
} }
// Special handling for file - if not existing, we can't ask for title // Special handling for file - if not existing, we can't ask for title
if(link.app == 'file' && typeof link.title == 'undefined') if(typeof link.id =='object' && !link.title)
{ {
link.title = link.id.name || ''; link.title = link.id.name || '';
} }

View File

@ -297,7 +297,7 @@ class infolog_bo
* checks if there are customfields for typ $typ * checks if there are customfields for typ $typ
* *
* @param string $type * @param string $type
* @param boolean $links=false if true check only customfields containing links, default false = all custom fields * @param boolean $links = false if true check only customfields containing links, default false = all custom fields
* @return boolean True if there are customfields for $typ, else False * @return boolean True if there are customfields for $typ, else False
*/ */
function has_customfields($type,$links=false) function has_customfields($type,$links=false)
@ -321,7 +321,7 @@ class infolog_bo
* @param int|array $info data or info_id of infolog entry to check * @param int|array $info data or info_id of infolog entry to check
* @param int $required_rights EGW_ACL_{READ|EDIT|ADD|DELETE} * @param int $required_rights EGW_ACL_{READ|EDIT|ADD|DELETE}
* @param int $other uid to check (if info==0) or 0 to check against $this->user * @param int $other uid to check (if info==0) or 0 to check against $this->user
* @param int $user=null user whos rights to check, default current user * @param int $user = null user whos rights to check, default current user
* @return boolean * @return boolean
*/ */
function check_access($info,$required_rights,$other=0,$user=null) function check_access($info,$required_rights,$other=0,$user=null)
@ -413,8 +413,8 @@ class infolog_bo
* convert a link_id value into an info_from text * convert a link_id value into an info_from text
* *
* @param array &$info infolog entry, key info_from gets set by this function * @param array &$info infolog entry, key info_from gets set by this function
* @param string $not_app='' app to exclude * @param string $not_app = '' app to exclude
* @param string $not_id='' id to exclude * @param string $not_id = '' id to exclude
* @return boolean True if we have a linked item, False otherwise * @return boolean True if we have a linked item, False otherwise
*/ */
function link_id2from(&$info,$not_app='',$not_id='') function link_id2from(&$info,$not_app='',$not_id='')
@ -482,8 +482,8 @@ class infolog_bo
* and $fromTZId is only used to qualify dates. * and $fromTZId is only used to qualify dates.
* *
* @param array $values to modify * @param array $values to modify
* @param string $fromTZId=null * @param string $fromTZId = null
* @param string $toTZId=false * @param string $toTZId = false
* TZID timezone name e.g. 'UTC' * TZID timezone name e.g. 'UTC'
* or NULL for timestamps in user-time * or NULL for timestamps in user-time
* or false for timestamps in server-time * or false for timestamps in server-time
@ -555,7 +555,7 @@ class infolog_bo
* convert a date from server to user-time * convert a date from server to user-time
* *
* @param int $ts timestamp in server-time * @param int $ts timestamp in server-time
* @param string $date_format='ts' date-formats: 'ts'=timestamp, 'server'=timestamp in server-time, 'array'=array or string with date-format * @param string $date_format = 'ts' date-formats: 'ts'=timestamp, 'server'=timestamp in server-time, 'array'=array or string with date-format
* @return mixed depending of $date_format * @return mixed depending of $date_format
*/ */
function date2usertime($ts,$date_format='ts') function date2usertime($ts,$date_format='ts')
@ -569,11 +569,11 @@ class infolog_bo
* Read an infolog entry specified by $info_id * Read an infolog entry specified by $info_id
* *
* @param int|array $info_id integer id or array with id's or array with column=>value pairs of the entry to read * @param int|array $info_id integer id or array with id's or array with column=>value pairs of the entry to read
* @param boolean $run_link_id2from=true should link_id2from run, default yes, * @param boolean $run_link_id2from = true should link_id2from run, default yes,
* need to be set to false if called from link-title to prevent an infinit recursion * need to be set to false if called from link-title to prevent an infinit recursion
* @param string $date_format='ts' date-formats: 'ts'=timestamp, 'server'=timestamp in server-time, * @param string $date_format = 'ts' date-formats: 'ts'=timestamp, 'server'=timestamp in server-time,
* 'array'=array or string with date-format * 'array'=array or string with date-format
* @param boolean $ignore_acl=false if true, do NOT check access, default false * @param boolean $ignore_acl = false if true, do NOT check access, default false
* *
* @return array|boolean infolog entry, null if not found or false if no permission to read it * @return array|boolean infolog entry, null if not found or false if no permission to read it
*/ */
@ -705,12 +705,12 @@ class infolog_bo
* checks and asures ACL * checks and asures ACL
* *
* @param array &$values values to write * @param array &$values values to write
* @param boolean $check_defaults=true check and set certain defaults * @param boolean $check_defaults = true check and set certain defaults
* @param boolean|int $touch_modified=true touch the modification date and sets the modifier's user-id, 2: only modifier * @param boolean|int $touch_modified = true touch the modification date and sets the modifier's user-id, 2: only modifier
* @param boolean $user2server=true conversion between user- and server-time necessary * @param boolean $user2server = true conversion between user- and server-time necessary
* @param boolean $skip_notification=false true = do NOT send notification, false (default) = send notifications * @param boolean $skip_notification = false true = do NOT send notification, false (default) = send notifications
* @param boolean $throw_exception=false Throw an exception (if required fields are not set) * @param boolean $throw_exception = false Throw an exception (if required fields are not set)
* @param string $purge_cfs=null null=dont, 'ical'=only iCal X-properties (cfs name starting with "#"), 'all'=all cfs * @param string $purge_cfs = null null=dont, 'ical'=only iCal X-properties (cfs name starting with "#"), 'all'=all cfs
* *
* @return int|boolean info_id on a successfull write or false * @return int|boolean info_id on a successfull write or false
*/ */
@ -1102,7 +1102,7 @@ class infolog_bo
/** /**
* Query ctag for infolog * Query ctag for infolog
* *
* @param array $filter=array('filter'=>'own','info_type'=>'task') * @param array $filter = array('filter'=>'own','info_type'=>'task')
* @return string * @return string
*/ */
public function getctag(array $filter=array('filter'=>'own','info_type'=>'task')) public function getctag(array $filter=array('filter'=>'own','info_type'=>'task'))
@ -1131,33 +1131,29 @@ class infolog_bo
* *
* @author Cornelius Weiss <nelius@cwtech.de> * @author Cornelius Weiss <nelius@cwtech.de>
* @todo search if infolog with from and subject allready exists ->appned body & inform user * @todo search if infolog with from and subject allready exists ->appned body & inform user
* @param string $_email_address rfc822 conform emailaddresses * @param array $_addresses array of addresses
* - array (email,name)
* @param string $_subject * @param string $_subject
* @param string $_message * @param string $_message
* @param array $_attachments * @param array $_attachments
* @param string $_date * @param string $_date
* @return array $content array for uiinfolog * @return array $content array for uiinfolog
*/ */
function import_mail($_email_address,$_subject,$_message,$_attachments,$_date) function import_mail($_addresses,$_subject,$_message,$_attachments,$_date)
{ {
$address_array = imap_rfc822_parse_adrlist($_email_address,''); foreach($_addresses as $address)
foreach ((array)$address_array as $address)
{ {
$email[] = $emailadr = sprintf('%s@%s', $names[] = $address['name'];
trim($address->mailbox), $emails[] =$address['email'];
trim($address->host));
$name[] = !empty($address->personal) ? $address->personal : $emailadr;
} }
// shorten long (> $this->max_line_chars) lines of "line" chars (-_+=~) in mails
$_message = preg_replace_callback('/[-_+=~\.]{'.$this->max_line_chars.',}/m',
create_function('$matches',"return substr(\$matches[0],0,$this->max_line_chars);"),$_message);
$type = isset($this->enums['type']['email']) ? 'email' : 'note'; $type = isset($this->enums['type']['email']) ? 'email' : 'note';
$status = isset($this->status['defaults'][$type]) ? $this->status['defaults'][$type] : 'done'; $status = isset($this->status['defaults'][$type]) ? $this->status['defaults'][$type] : 'done';
$info = array( $info = array(
'info_id' => 0, 'info_id' => 0,
'info_type' => $type, 'info_type' => $type,
'info_from' => implode(', ',$name), 'info_from' => implode(', ',$names),
'info_addr' => implode(', ',$email), 'info_addr' => implode(', ',$emails),
'info_subject' => $_subject, 'info_subject' => $_subject,
'info_des' => $_message, 'info_des' => $_message,
'info_startdate' => egw_time::server2user($_date), 'info_startdate' => egw_time::server2user($_date),
@ -1174,7 +1170,7 @@ class infolog_bo
// find the addressbookentry to link with // find the addressbookentry to link with
$addressbook = new addressbook_bo(); $addressbook = new addressbook_bo();
$contacts = array(); $contacts = array();
foreach ($email as $mailadr) foreach ($emails as $mailadr)
{ {
$contacts = array_merge($contacts,(array)$addressbook->search( $contacts = array_merge($contacts,(array)$addressbook->search(
array( array(
@ -1202,14 +1198,13 @@ class infolog_bo
{ {
foreach ($_attachments as $attachment) foreach ($_attachments as $attachment)
{ {
$is_vfs = false; if($attachment['egw_data'])
if (parse_url($attachment['tmp_name'],PHP_URL_SCHEME) == 'vfs' && egw_vfs::is_readable($attachment['tmp_name']))
{ {
$is_vfs = true; egw_link::link('infolog',$info['link_to']['to_id'],egw_link::DATA_APPNAME, $attachment);
} }
if(is_readable($attachment['tmp_name']) || $is_vfs) else if(is_readable($attachment['tmp_name']))
{ {
egw_link::link('infolog',$info['link_to']['to_id'],'file',$attachment); egw_link::link('infolog',$info['link_to']['to_id'],'file', $attachment);
} }
} }
} }
@ -1294,8 +1289,8 @@ class infolog_bo
* *
* @param int|array $id id of entry or entry array * @param int|array $id id of entry or entry array
* @param int $check EGW_ACL_READ for read and EGW_ACL_EDIT for write or delete access * @param int $check EGW_ACL_READ for read and EGW_ACL_EDIT for write or delete access
* @param string $rel_path=null currently not used in InfoLog * @param string $rel_path = null currently not used in InfoLog
* @param int $user=null for which user to check, default current user * @param int $user = null for which user to check, default current user
* @return boolean true if access is granted or false otherwise * @return boolean true if access is granted or false otherwise
*/ */
function file_access($id,$check,$rel_path=null,$user=null) function file_access($id,$check,$rel_path=null,$user=null)
@ -1439,7 +1434,7 @@ class infolog_bo
* currently used for ical/sif import * currently used for ical/sif import
* *
* @param array $catname_list names of the categories which should be found or added * @param array $catname_list names of the categories which should be found or added
* @param int $info_id=-1 match against existing infolog and expand the returned category ids * @param int $info_id = -1 match against existing infolog and expand the returned category ids
* by the ones the user normally does not see due to category permissions - used to preserve categories * by the ones the user normally does not see due to category permissions - used to preserve categories
* @return array category ids (found, added and preserved categories) * @return array category ids (found, added and preserved categories)
*/ */
@ -1692,8 +1687,8 @@ class infolog_bo
* *
* As status value can have different translations depending on type, we list all translations * As status value can have different translations depending on type, we list all translations
* *
* @param string $type=null * @param string $type = null
* @param array &$icons=null on return name of icons * @param array &$icons = null on return name of icons
* @return array value => (commaseparated) translations * @return array value => (commaseparated) translations
*/ */
function get_status($type=null, array &$icons=null) function get_status($type=null, array &$icons=null)
@ -1763,8 +1758,8 @@ class infolog_bo
* This expects timestamps to be in server-time. * This expects timestamps to be in server-time.
* *
* @param array $infoData the infolog data we try to find * @param array $infoData the infolog data we try to find
* @param boolean $relax=false if asked to relax, we only match against some key fields * @param boolean $relax = false if asked to relax, we only match against some key fields
* @param string $tzid=null timezone, null => user time * @param string $tzid = null timezone, null => user time
* *
* @return array of infolog_ids of matching entries * @return array of infolog_ids of matching entries
*/ */

View File

@ -558,4 +558,17 @@ class infolog_hooks
unset($location); // not used, but part of hook signature unset($location); // not used, but part of hook signature
return true; return true;
} }
/**
* Mail integration hook to import mail message contents into an infolog entry
*
* @return array
*/
public static function mail_import($args)
{
return array (
'menuaction' => 'infolog.infolog_ui.mail_import',
'popup' => egw_link::get_registry('infolog', 'edit_popup')
);
}
} }

View File

@ -23,7 +23,7 @@ class infolog_ui
'admin' => True, 'admin' => True,
'hook_view' => True, 'hook_view' => True,
'writeLangFile' => True, 'writeLangFile' => True,
'import_mail' => True, 'mail_import' => True
); );
/** /**
* reference to the infolog preferences of the user * reference to the infolog preferences of the user
@ -2482,185 +2482,25 @@ class infolog_ui
/** /**
* imports a mail as infolog * imports a mail as infolog
* two possible calls:
* 1. with function args set. (we come from send mail)
* 2. with $_GET['uid] = someuid (we come from display mail)
* *
* @author Cornelius Weiss <nelius@cwtech.de> * @param array $mailContent = null content of mail
* @param string $_to_emailAddress * @return array
* @param string $_subject
* @param string $_body
* @param array $_attachments
* @param string $_date
* @param resource $_rawMail
*/ */
function import_mail($_to_emailAddress=false,$_subject=false,$_body=false,$_attachments=false,$_date=false,$_rawMail=null) function mail_import(array $mailContent=null)
{ {
$uid = $_GET['uid']; // It would get called from compose as a popup with egw_data
$partid = $_GET['part']; if (!is_array($mailContent) && ($_GET['egw_data']))
$mailbox = base64_decode($_GET['mailbox']);
$mailClass = 'mail_bo';
$sessionLocation = 'mail';
// if rowid is set, we are called from new mail module.
if (method_exists('mail_ui','splitRowID') && isset($_GET['rowid']) && !empty($_GET['rowid']))
{ {
// rowid holds all needed information: server, folder, uid, etc. // get the mail raw data
$rowID = $_GET['rowid']; egw_link::get_data ($_GET['egw_data']);
$hA = mail_ui::splitRowID($rowID); return false;
$sessionLocation = $hA['app']; // THIS is part of the row ID, we may use this for validation
if ($sessionLocation != 'mail') throw new egw_exception_assertion_failed(lang('Application mail expected but got: %1',$sessionLocation));
$uid = $hA['msgUID'];
$mailbox = $hA['folder'];
$icServerID = $hA['profileID'];
$mailClass = 'mail_bo';
}
if ($_date == false || empty($_date)) $_date = $this->bo->user_time_now;
if (!empty($_to_emailAddress))
{
$GLOBALS['egw_info']['flags']['currentapp'] = 'infolog';
if (!($GLOBALS['egw_info']['user']['preferences'][$sessionLocation]['saveAsOptions']==='text_only')&&is_array($_attachments))
{
//echo __METHOD__.'<br>';
//_debug_array($_attachments);
if (!isset($icServerID)) $icServerID =& egw_cache::getSession($sessionLocation,'activeProfileID');
$mailobject = $mailClass::getInstance(true,$icServerID);
$mailobject->openConnection();
foreach ($_attachments as $attachment)
{
//error_log(__METHOD__.__LINE__.array2string($attachment));
if (trim(strtoupper($attachment['type'])) == 'MESSAGE/RFC822' && !empty($attachment['uid']) && !empty($attachment['folder']))
{
$mailobject->reopen(($attachment['folder']?$attachment['folder']:$mailbox));
// get the message itself, and attach it, as we are able to display it in egw
// instead of fetching only the attachments attached files (as we did previously)
$message = $mailobject->getMessageRawBody($attachment['uid'],$attachment['partID'],($attachment['folder']?$attachment['folder']:$mailbox));
$headers = $mailobject->getMessageHeader($attachment['uid'],$attachment['partID'],true,false,($attachment['folder']?$attachment['folder']:$mailbox));
$subject = $mailClass::adaptSubjectForImport($headers['SUBJECT']);
$attachment_file =tempnam($GLOBALS['egw_info']['server']['temp_dir'],$GLOBALS['egw_info']['flags']['currentapp']."_");
$tmpfile = fopen($attachment_file,'w');
fwrite($tmpfile,$message);
fclose($tmpfile);
$size = filesize($attachment_file);
$attachments[] = array(
'name' => trim($subject).'.eml',
'mimeType' => 'message/rfc822',
'type' => 'message/rfc822',
'tmp_name' => $attachment_file,
'size' => $size,
);
}
else
{
if (!empty($attachment['folder']))
{
$is_winmail = $_GET['is_winmail'] ? $_GET['is_winmail'] : 0;
$mailobject->reopen($attachment['folder']);
$attachmentData = $mailobject->getAttachment($attachment['uid'],$attachment['partID'],$is_winmail);
$attachment['file'] =tempnam($GLOBALS['egw_info']['server']['temp_dir'],$GLOBALS['egw_info']['flags']['currentapp']."_");
$tmpfile = fopen($attachment['file'],'w');
fwrite($tmpfile,$attachmentData['attachment']);
fclose($tmpfile);
}
//make sure we search for our attached file in our configured temp_dir
if (isset($attachment['file']) && parse_url($attachment['file'],PHP_URL_SCHEME) != 'vfs' &&
file_exists($GLOBALS['egw_info']['server']['temp_dir'].SEP.basename($attachment['file'])))
{
$attachment['file'] = $GLOBALS['egw_info']['server']['temp_dir'].SEP.basename($attachment['file']);
}
$attachments[] = array(
'name' => $attachment['name'],
'mimeType' => $attachment['type'],
'type' => $attachment['type'],
'tmp_name' => $attachment['file'],
'size' => $attachment['size'],
);
}
}
$mailobject->closeConnection();
}
// this one adds the mail itself (as message/rfc822 (.eml) file) to the infolog as additional attachment
// this is done to have a simple archive functionality (ToDo: opening .eml in email module)
if (is_resource($_rawMail) && $GLOBALS['egw_info']['user']['preferences'][$sessionLocation]['saveAsOptions']==='add_raw')
{
$subject = $mailClass::adaptSubjectForImport($_subject);
$attachment_file =tempnam($GLOBALS['egw_info']['server']['temp_dir'],$GLOBALS['egw_info']['flags']['currentapp']."_");
$tmpfile = fopen($attachment_file,'w');
fseek($_rawMail, 0, SEEK_SET);
stream_copy_to_stream($_rawMail, $tmpfile);
fclose($tmpfile);
$size = filesize($attachment_file);
$attachments[] = array(
'name' => trim($subject).'.eml',
'mimeType' => 'message/rfc822',
'type' => 'message/rfc822',
'tmp_name' => $attachment_file,
'size' => $size,
);
} }
//_debug_array($_to_emailAddress); return $this->edit($this->bo->import_mail($mailContent['addresses'],
$toaddr = array(); $mailContent['subject'],
foreach(array('to','cc','bcc') as $x) $mailContent['message'],
{ $mailContent['attachments'],
if (is_array($_to_emailAddress[$x]) && !empty($_to_emailAddress[$x])) $mailContent['date']));
{
$toaddr = array_merge($toaddr,$_to_emailAddress[$x]);
}
}
//_debug_array($attachments);
$body_striped = strip_tags($_body); //we need to fix broken tags (or just stuff like "<800 USD/p" )
$body_decoded = htmlspecialchars_decode($body_striped,ENT_QUOTES);
$body = $mailClass::createHeaderInfoSection(array('FROM'=>$_to_emailAddress['from'],
'TO'=>(!empty($_to_emailAddress['to'])?implode(',',$_to_emailAddress['to']):null),
'CC'=>(!empty($_to_emailAddress['cc'])?implode(',',$_to_emailAddress['cc']):null),
'BCC'=>(!empty($_to_emailAddress['bcc'])?implode(',',$_to_emailAddress['bcc']):null),
'SUBJECT'=>$_subject,
'DATE'=>$mailClass::_strtotime($_date))).$body_decoded;
$this->edit($this->bo->import_mail(
implode(',',$toaddr),$_subject,$body,$attachments,$_date
));
exit;
}
elseif ($uid && $mailbox)
{
if (!isset($icServerID)) $icServerID =& egw_cache::getSession($sessionLocation,'activeProfileID');
$mailobject = $mailClass::getInstance(true,$icServerID);
$mailobject->openConnection();
$mailobject->reopen($mailbox);
$mailcontent = $mailClass::get_mailcontent($mailobject,$uid,$partid,$mailbox,false,true,(!($GLOBALS['egw_info']['user']['preferences'][$sessionLocation]['saveAsOptions']==='text_only')));
// this one adds the mail itself (as message/rfc822 (.eml) file) to the infolog as additional attachment
// this is done to have a simple archive functionality (ToDo: opening .eml in email module)
if ($GLOBALS['egw_info']['user']['preferences'][$sessionLocation]['saveAsOptions']==='add_raw')
{
$message = $mailobject->getMessageRawBody($uid, $partid,$mailbox);
$headers = $mailobject->getMessageHeader($uid, $partid,true,false,$mailbox);
$subject = $mailClass::adaptSubjectForImport($headers['SUBJECT']);
$attachment_file =tempnam($GLOBALS['egw_info']['server']['temp_dir'],$GLOBALS['egw_info']['flags']['currentapp']."_");
$tmpfile = fopen($attachment_file,'w');
fwrite($tmpfile,$message);
fclose($tmpfile);
$size = filesize($attachment_file);
$mailcontent['attachments'][] = array(
'name' => trim($subject).'.eml',
'mimeType' => 'message/rfc822',
'type' => 'message/rfc822',
'tmp_name' => $attachment_file,
'size' => $size,
);
}
//_debug_array($mailcontent);
return $this->edit($this->bo->import_mail(
$mailcontent['mailaddress'],
$mailcontent['subject'],
$mailcontent['message'],
$mailcontent['attachments'],
strtotime($mailcontent['headers']['DATE'])
));
}
egw_framework::window_close(lang('Error: no mail (Mailbox / UID) given!'));
} }
/** /**

View File

@ -63,6 +63,7 @@ $setup_info['infolog']['hooks']['search_link'] = 'infolog_hooks::search_link';
$setup_info['infolog']['hooks']['pm_custom_app_icons'] = 'infolog.infolog_bo.pm_icons'; $setup_info['infolog']['hooks']['pm_custom_app_icons'] = 'infolog.infolog_bo.pm_icons';
$setup_info['infolog']['hooks']['timesheet_set'] = 'infolog.infolog_ui.timesheet_set'; $setup_info['infolog']['hooks']['timesheet_set'] = 'infolog.infolog_ui.timesheet_set';
$setup_info['infolog']['hooks']['calendar_set'] = 'infolog.infolog_ui.calendar_set'; $setup_info['infolog']['hooks']['calendar_set'] = 'infolog.infolog_ui.calendar_set';
$setup_info['infolog']['hooks']['mail_import'] = 'infolog_hooks::mail_import';
/* Dependencies for this app to work */ /* Dependencies for this app to work */
$setup_info['infolog']['depends'][] = array( $setup_info['infolog']['depends'][] = array(

View File

@ -163,6 +163,14 @@ class mail_compose
'hint' => 'check to save as trackerentry on send', 'hint' => 'check to save as trackerentry on send',
'onExecute' => 'javaScript:app.mail.compose_setToggle' 'onExecute' => 'javaScript:app.mail.compose_setToggle'
), ),
'to_calendar' => array(
'caption' => 'Calendar',
'icon' => 'to_calendar',
'group' => $group,
'checkbox' => true,
'hint' => 'check to save as calendar event on send',
'onExecute' => 'javaScript:app.mail.compose_setToggle'
),
'disposition' => array( 'disposition' => array(
'caption' => 'Notification', 'caption' => 'Notification',
'icon' => 'high', 'icon' => 'high',
@ -1002,14 +1010,11 @@ class mail_compose
} }
} }
} }
//Since the ckeditor is not stable enough on mobile devices, we always convert html messages to plain text
//TODO: needs to remove html:$ua_mobile condition after better mobile support from CKeditor if ($content['mimeType'] == 'html' && html::htmlarea_availible()===false)
//mobile compatibility is disabled expilcitly in ckeditor.js plugin too, which needs to be removed
if ($content['mimeType'] == 'html' && html::htmlarea_availible()===false || html::$ua_mobile)
{ {
$_content['mimeType'] = $content['mimeType'] = 'plain'; $_content['mimeType'] = $content['mimeType'] = 'plain';
$content['body'] = $this->convertHTMLToText($content['body']); $content['body'] = $this->convertHTMLToText($content['body']);
$readonlys['mimeType'] = true;
} }
// is a certain signature requested? // is a certain signature requested?
// only the following values are supported (and make sense) // only the following values are supported (and make sense)
@ -1290,7 +1295,7 @@ class mail_compose
$content['to'] = self::resolveEmailAddressList($content['to']); $content['to'] = self::resolveEmailAddressList($content['to']);
//error_log(__METHOD__.__LINE__.array2string($content)); //error_log(__METHOD__.__LINE__.array2string($content));
$etpl->exec('mail.mail_compose.compose',$content,$sel_options,$readonlys,$preserv,2); $etpl->exec('mail.mail_compose.compose',$content,$sel_options,array(),$preserv,2);
} }
/** /**
@ -2448,7 +2453,7 @@ class mail_compose
*/ */
public function ajax_saveAsDraft ($content, $action='button[saveAsDraft]') public function ajax_saveAsDraft ($content, $action='button[saveAsDraft]')
{ {
//error_log(__METHOD__."(, action=$action)"); //error_log(__METHOD__.__LINE__.array2string($content)."(, action=$action)");
$response = egw_json_response::get(); $response = egw_json_response::get();
$success = true; $success = true;
@ -3047,43 +3052,44 @@ class mail_compose
if (is_array($this->sessionData['cc'])) $mailaddresses['cc'] = $this->sessionData['cc']; if (is_array($this->sessionData['cc'])) $mailaddresses['cc'] = $this->sessionData['cc'];
if (is_array($this->sessionData['bcc'])) $mailaddresses['bcc'] = $this->sessionData['bcc']; if (is_array($this->sessionData['bcc'])) $mailaddresses['bcc'] = $this->sessionData['bcc'];
if (!empty($mailaddresses)) $mailaddresses['from'] = $GLOBALS['egw']->translation->decodeMailHeader($fromAddress); if (!empty($mailaddresses)) $mailaddresses['from'] = $GLOBALS['egw']->translation->decodeMailHeader($fromAddress);
// attention: we dont return from infolog/tracker. You cannot check both. cleanups will be done there.
if ($_formData['to_infolog'] == 'on') { if ($_formData['to_infolog'] == 'on' || $_formData['to_tracker'] == 'on' || $_formData['to_calendar'] == 'on' )
$uiinfolog = new infolog_ui(); {
$uiinfolog->import_mail( foreach(array('to_infolog','to_tracker','to_calendar') as $app_key)
{
if ($_formData[$app_key] == 'on')
{
$app_name = substr($app_key,3);
// Get registered hook data of the app called for integration
$hook = $GLOBALS['egw']->hooks->single(array('location'=> 'mail_import'),$app_name);
// store mail / eml in temp. file to not have to download it from mail-server again
$eml = tempnam($GLOBALS['egw_info']['server']['temp_dir'],'mail_integrate');
$eml_fp = fopen($eml, 'w');
stream_copy_to_stream($mail->getRaw(), $eml_fp);
fclose($eml_fp);
// Open the app called for integration in a popup
// and store the mail raw data as egw_data, in order to
// be stored from registered app method later
egw_framework::popup(egw::link('/index.php', array(
'menuaction' => $hook['menuaction'],
'egw_data' => egw_link::set_data(null,'mail_integration::integrate',array(
$mailaddresses, $mailaddresses,
$this->sessionData['subject'], $this->sessionData['subject'],
$this->convertHTMLToText($this->sessionData['body']), $this->convertHTMLToText($this->sessionData['body']),
$this->sessionData['attachments'], $this->sessionData['attachments'],
false, // date false, // date
$mail->getRaw() $eml,
); $_formData['serverID']),true),
'app' => $app_name
)),'_blank',$hook['popup']);
} }
if ($_formData['to_tracker'] == 'on') {
$uitracker = new tracker_ui();
$uitracker->import_mail(
$mailaddresses,
$this->sessionData['subject'],
$this->convertHTMLToText($this->sessionData['body']),
$this->sessionData['attachments'],
false, // date
$mail->getRaw()
);
} }
/*
if ($_formData['to_calendar'] == 'on') {
$uical =& CreateObject('calendar.calendar_uiforms');
$uical->import_mail(
$mailaddresses,
$this->sessionData['subject'],
$this->convertHTMLToText($this->sessionData['body']),
$this->sessionData['attachments']
);
} }
*/ // only clean up temp-files, if we dont need them for mail_integration::integrate
elseif(is_array($this->sessionData['attachments']))
{
if(is_array($this->sessionData['attachments'])) {
foreach($this->sessionData['attachments'] as $value) { foreach($this->sessionData['attachments'] as $value) {
if (!empty($value['file']) && parse_url($value['file'],PHP_URL_SCHEME) != 'vfs') { // happens when forwarding mails if (!empty($value['file']) && parse_url($value['file'],PHP_URL_SCHEME) != 'vfs') { // happens when forwarding mails
unlink($GLOBALS['egw_info']['server']['temp_dir'].'/'.$value['file']); unlink($GLOBALS['egw_info']['server']['temp_dir'].'/'.$value['file']);
@ -3302,6 +3308,22 @@ class mail_compose
} }
} }
// Add groups
$group_options = array('account_type' => 'groups');
$groups = $GLOBALS['egw']->accounts->link_query($_searchString, $group_options);
foreach($groups as $g_id => $name)
{
$group = $GLOBALS['egw']->accounts->read($g_id);
if(!$group['account_email']) continue;
$completeMailString = trim($name) .' <'. trim($group['account_email']) .'>';
$results[] = array(
'id' => $completeMailString,
'label' => $completeMailString,
'name' => $name,
'title' => $group['account_email']
);
}
// Add up to 5 matching mailing lists // Add up to 5 matching mailing lists
if($include_lists) if($include_lists)
{ {

View File

@ -0,0 +1,288 @@
<?php
/**
* EGroupware - Mail - integration interface class
*
* @link http://www.egroupware.org
* @package mail
* @author Hadi Nategh [hn@stylite.de]
* @copyright (c) 2015 by Stylite AG <info-AT-stylite.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id:$
*/
/**
* Class cotains methods and functions
* to be used to integrate mail's message into other applications
*
*/
class mail_integration {
/**
* Public functions
* @var type
*/
var $public_functions = array(
'integrate' => true
);
/**
* Maximum number of line characters (-_+=~) allowed in a mail, to not stall the layout.
* Longer lines / biger number of these chars are truncated to that max. number or chars.
*
* @var int
*/
const MAX_LINE_CHARS = 40;
/**
* Gets requested mail information and sets them as data link
* -Execute registered hook method from the requested app for integration
* -with provided content from mail:
*
* -array( 'addresses' => array (
* 'email'=> stirng,
* 'personel' => string),
* 'attachments' => array (
* 'name' => string, // file name
* 'type' => string, // mime type
* 'egw_data'=> string, // hash md5 id of an stored attachment in session (attachment which is in IMAP server)
* // NOTE: the attachmet either have egw_data OR tmp_name (e.g. raw mail eml file stores in tmp)
* 'tmp_name' => string), // tmp dir path
* 'message' => string,
* 'date' => string,
* 'subject' => string,
* 'entry_id => string // Id of the app entry which mail content will append to
* )
*
* @param string $_to_emailAddress
* @param string $_subject mail subject
* @param string $_body mail message
* @param array $_attachments attachments
* @param string $_date
* @param string $_rawMail path to file with raw mail
* @param int $_icServerID mail profile id
* @throws egw_exception_assertion_failed
*/
public static function integrate ($_to_emailAddress=false,$_subject=false,$_body=false,$_attachments=false,$_date=false,$_rawMail=null,$_icServerID=null)
{
// App name which is called for integration
$app = isset($GLOBALS['egw_info']['user']['apps'][$_GET['app']])? $_GET['app'] : null;
// preset app entry id, selected by user from app_entry_dialog
$app_entry_id = $_GET['entry_id'];
// Set the date
if (!$_date)
{
$time = time();
$_date = egw_time::server2user($time->now,'ts');
}
// Integrate not yet saved mail
if (empty($_GET['rowid']) && $_to_emailAddress && $app)
{
$sessionLocation = 'mail';
$mailbox = base64_decode($_GET['mailbox']);
$GLOBALS['egw_info']['flags']['currentapp'] = $app;
if (!($GLOBALS['egw_info']['user']['preferences'][$sessionLocation]['saveAsOptions']==='text_only')&&is_array($_attachments))
{
// initialize mail open connection requirements
if (!isset($_icServerID)) $_icServerID =& egw_cache::getSession($sessionLocation,'activeProfileID');
$mo = mail_bo::getInstance(true,$_icServerID);
$mo->openConnection();
foreach ($_attachments as $attachment)
{
if (trim(strtoupper($attachment['type'])) == 'MESSAGE/RFC822' && !empty($attachment['uid']) && !empty($attachment['folder']))
{
$mo->reopen(($attachment['folder']?$attachment['folder']:$mailbox));
// get the message itself, and attach it, as we are able to display it in egw
// instead of fetching only the attachments attached files (as we did previously)
$message = $mo->getMessageRawBody($attachment['uid'],$attachment['partID'],($attachment['folder']?$attachment['folder']:$mailbox));
$headers = $mo->getMessageHeader($attachment['uid'],$attachment['partID'],true,false,($attachment['folder']?$attachment['folder']:$mailbox));
$subject = mail_bo::adaptSubjectForImport($headers['SUBJECT']);
$attachment_file =tempnam($GLOBALS['egw_info']['server']['temp_dir'],$GLOBALS['egw_info']['flags']['currentapp']."_");
$tmpfile = fopen($attachment_file,'w');
fwrite($tmpfile,$message);
fclose($tmpfile);
$size = filesize($attachment_file);
$attachments[] = array(
'name' => trim($subject).'.eml',
'mimeType' => 'message/rfc822',
'type' => 'message/rfc822',
'tmp_name' => $attachment_file,
'size' => $size,
);
}
else
{
if (!empty($attachment['folder']))
{
$is_winmail = $_GET['is_winmail'] ? $_GET['is_winmail'] : 0;
$mo->reopen($attachment['folder']);
$attachmentData = $mo->getAttachment($attachment['uid'],$attachment['partID'],$is_winmail);
$attachment['file'] =tempnam($GLOBALS['egw_info']['server']['temp_dir'],$GLOBALS['egw_info']['flags']['currentapp']."_");
$tmpfile = fopen($attachment['file'],'w');
fwrite($tmpfile,$attachmentData['attachment']);
fclose($tmpfile);
}
//make sure we search for our attached file in our configured temp_dir
if (isset($attachment['file']) && parse_url($attachment['file'],PHP_URL_SCHEME) != 'vfs' &&
file_exists($GLOBALS['egw_info']['server']['temp_dir'].SEP.basename($attachment['file'])))
{
$attachment['file'] = $GLOBALS['egw_info']['server']['temp_dir'].SEP.basename($attachment['file']);
}
$attachments[] = array(
'name' => $attachment['name'],
'mimeType' => $attachment['type'],
'type' => $attachment['type'],
'tmp_name' => $attachment['file'],
'size' => $attachment['size'],
);
}
}
$mo->closeConnection();
}
// this one adds the mail itself (as message/rfc822 (.eml) file) to the app as additional attachment
// this is done to have a simple archive functionality (ToDo: opening .eml in email module)
if ($GLOBALS['egw_info']['user']['preferences'][$sessionLocation]['saveAsOptions']==='add_raw' &&
$_rawMail && file_exists($_rawMail))
{
$subject = mail_bo::adaptSubjectForImport($_subject);
$attachments[] = array(
'name' => trim($subject).'.eml',
'mimeType' => 'message/rfc822',
'type' => 'message/rfc822',
'tmp_name' => $_rawMail,
'size' => filesize($_rawMail),
'add_raw' => true
);
}
$toaddr = array();
foreach(array('to','cc','bcc') as $x)
{
if (is_array($_to_emailAddress[$x]) && !empty($_to_emailAddress[$x]))
{
$toaddr = array_merge($toaddr,$_to_emailAddress[$x]);
}
}
$body_striped = strip_tags($_body); //we need to fix broken tags (or just stuff like "<800 USD/p" )
$body_decoded = htmlspecialchars_decode($body_striped,ENT_QUOTES);
$body = mail_bo::createHeaderInfoSection(array('FROM'=>$_to_emailAddress['from'],
'TO'=>(!empty($_to_emailAddress['to'])?implode(',',$_to_emailAddress['to']):null),
'CC'=>(!empty($_to_emailAddress['cc'])?implode(',',$_to_emailAddress['cc']):null),
'BCC'=>(!empty($_to_emailAddress['bcc'])?implode(',',$_to_emailAddress['bcc']):null),
'SUBJECT'=>$_subject,
'DATE'=>mail_bo::_strtotime($_date))).$body_decoded;
$mailcontent = array(
'mailaddress' => implode(',',$toaddr),
'subject' => $_subject,
'message' => $body,
'attachments' => $attachments,
'date' => $_date
);
}
// Integrate already saved mail with ID
else
{
// Initializing mail connection requirements
$hA = mail_ui::splitRowID($_GET['rowid']);
$sessionLocation = $hA['app']; // THIS is part of the row ID, we may use this for validation
// Check the mail app
if ($sessionLocation != 'mail') throw new egw_exception_assertion_failed(lang('Application mail expected but got: %1',$sessionLocation));
$uid = $hA['msgUID'];
$mailbox = $hA['folder'];
$icServerID = $hA['profileID'];
if ($uid && $mailbox)
{
if (!isset($icServerID)) $icServerID =& egw_cache::getSession($sessionLocation,'activeProfileID');
$mo = mail_bo::getInstance(true,$icServerID);
$mo->openConnection();
$mo->reopen($mailbox);
$mailcontent = mail_bo::get_mailcontent($mo,$uid,'',$mailbox,false,true,(!($GLOBALS['egw_info']['user']['preferences'][$sessionLocation]['saveAsOptions']==='text_only')));
// this one adds the mail itself (as message/rfc822 (.eml) file) to the app as additional attachment
// this is done to have a simple archive functionality (ToDo: opening .eml in email module)
if ($GLOBALS['egw_info']['user']['preferences'][$sessionLocation]['saveAsOptions']==='add_raw')
{
$message = $mo->getMessageRawBody($uid, '',$mailbox);
$headers = $mo->getMessageHeader($uid, '',true,false,$mailbox);
$subject = mail_bo::adaptSubjectForImport($headers['SUBJECT']);
$attachment_file =tempnam($GLOBALS['egw_info']['server']['temp_dir'],$GLOBALS['egw_info']['flags']['currentapp']."mail_integrate");
$tmpfile = fopen($attachment_file,'w');
fwrite($tmpfile,$message);
fclose($tmpfile);
$size = filesize($attachment_file);
$mailcontent['attachments'][] = array(
'name' => trim($subject).'.eml',
'mimeType' => 'message/rfc822',
'type' => 'message/rfc822',
'tmp_name' => $attachment_file,
'size' => $size,
'add_raw' => true
);
}
$mailcontent['date'] = strtotime($mailcontent['headers']['DATE']);
}
}
// Convert addresses to email and personal
$addresses = imap_rfc822_parse_adrlist($mailcontent['mailaddress'],'');
foreach ($addresses as $address)
{
$email = sprintf('%s@%s',trim($address->mailbox),trim($address->host));
$data_addresses[] = array (
'email' => $email,
'name' => !empty($address->personal) ? $address->personal : $email
);
}
// shorten long (> self::max_line_chars) lines of "line" chars (-_+=~) in mails
$data_message = preg_replace_callback(
'/[-_+=~\.]{'.self::MAX_LINE_CHARS.',}/m',
function($matches) {
return substr($matches[0],0,self::MAX_LINE_CHARS);
},
$mailcontent['message']
);
// Get attachments ready for integration as link
if (is_array($mailcontent['attachments']))
{
foreach($mailcontent['attachments'] as $key => $attachment)
{
$data_attachments[$key] = array(
'name' => $mailcontent['attachments'][$key]['name'],
'type' => $mailcontent['attachments'][$key]['type'],
'size' => $mailcontent['attachments'][$key]['size'],
'tmp_name' => $mailcontent['attachments'][$key]['tmp_name']
);
if ($uid && !$mailcontent['attachments'][$key]['add_raw'])
{
$data_attachments[$key]['egw_data'] = egw_link::set_data($mailcontent['attachments'][$key]['mimeType'],
'emailadmin_imapbase::getAttachmentAccount',array($icServerID, $mailbox, $uid, $attachment['partID'], $is_winmail, true),true);
}
unset($mailcontent['attachments'][$key]['add_raw']);
}
}
// Get the registered hook method of requested app for integration
$hook = $GLOBALS['egw']->hooks->single(array('location' => 'mail_import'),$app);
// Execute import mail with provided content
ExecMethod($hook['menuaction'],array (
'addresses' => $data_addresses,
'attachments' => $data_attachments,
'message' => $data_message,
'date' => $mailcontent['date'],
'subject' => $mailcontent['subject'],
'entry_id' => $app_entry_id
));
}
}

View File

@ -125,12 +125,12 @@ class mail_ui
if ($_GET["resetConnection"]) if ($_GET["resetConnection"])
{ {
unset($_GET["resetConnection"]); unset($_GET["resetConnection"]);
if (mail_bo::$debug) error_log(__METHOD__.__LINE__.' Connection Reset triggered:'.$connectionReset.' for Profile with ID:'.self::$icServerID); if (mail_bo::$debug) error_log(__METHOD__.__LINE__.' Connection Reset triggered: for Profile with ID:'.self::$icServerID);
emailadmin_imapbase::unsetCachedObjects(self::$icServerID); emailadmin_imapbase::unsetCachedObjects(self::$icServerID);
} }
try { try {
$this->mail_bo = mail_bo::getInstance(true,self::$icServerID,$_validate=true, $_oldImapServerObject=false, $_reuseCache=true); $this->mail_bo = mail_bo::getInstance(true,self::$icServerID, true, false, true);
if (mail_bo::$debug) error_log(__METHOD__.__LINE__.' Fetched IC Server:'.self::$icServerID.'/'.$this->mail_bo->profileID.':'.function_backtrace()); if (mail_bo::$debug) error_log(__METHOD__.__LINE__.' Fetched IC Server:'.self::$icServerID.'/'.$this->mail_bo->profileID.':'.function_backtrace());
//error_log(__METHOD__.__LINE__.array2string($this->mail_bo->icServer)); //error_log(__METHOD__.__LINE__.array2string($this->mail_bo->icServer));
@ -1224,8 +1224,7 @@ class mail_ui
'hint' => 'Save as InfoLog', 'hint' => 'Save as InfoLog',
'icon' => 'infolog/navbar', 'icon' => 'infolog/navbar',
'group' => ++$group, 'group' => ++$group,
'onExecute' => 'javaScript:app.mail.mail_infolog', 'onExecute' => 'javaScript:app.mail.mail_integrate',
'url' => 'menuaction=infolog.infolog_ui.import_mail',
'popup' => egw_link::get_registry('infolog', 'add_popup'), 'popup' => egw_link::get_registry('infolog', 'add_popup'),
'allowOnMultiple' => false, 'allowOnMultiple' => false,
'toolbarDefault' => true 'toolbarDefault' => true
@ -1235,11 +1234,21 @@ class mail_ui
'hint' => 'Save as ticket', 'hint' => 'Save as ticket',
'group' => $group, 'group' => $group,
'icon' => 'tracker/navbar', 'icon' => 'tracker/navbar',
'onExecute' => 'javaScript:app.mail.mail_tracker', 'onExecute' => 'javaScript:app.mail.mail_integrate',
'url' => 'menuaction=tracker.tracker_ui.import_mail',
'popup' => egw_link::get_registry('tracker', 'add_popup'), 'popup' => egw_link::get_registry('tracker', 'add_popup'),
'mail_import' => $GLOBALS['egw']->hooks->single(array('location' => 'mail_import'),'tracker'),
'allowOnMultiple' => false, 'allowOnMultiple' => false,
), ),
'calendar' => array(
'caption' => 'Calendar',
'hint' => 'Save as Calendar',
'icon' => 'calendar/navbar',
'group' => $group,
'onExecute' => 'javaScript:app.mail.mail_integrate',
'popup' => egw_link::get_registry('calendar', 'add_popup'),
'allowOnMultiple' => false,
'toolbarDefault' => true
),
'print' => array( 'print' => array(
'caption' => 'Print', 'caption' => 'Print',
'group' => ++$group, 'group' => ++$group,
@ -3239,7 +3248,6 @@ class mail_ui
$file = $content['uploadForImport']; $file = $content['uploadForImport'];
} }
$destination = $content['FOLDER'][0]; $destination = $content['FOLDER'][0];
$rememberServerID = $icServerID = $this->mail_bo->profileID;
if (stripos($destination,self::$delimiter)!==false) list($icServerID,$destination) = explode(self::$delimiter,$destination,2); if (stripos($destination,self::$delimiter)!==false) list($icServerID,$destination) = explode(self::$delimiter,$destination,2);
if ($icServerID && $icServerID != $this->mail_bo->profileID) if ($icServerID && $icServerID != $this->mail_bo->profileID)
@ -3462,7 +3470,6 @@ class mail_ui
$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder $folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
$messageID = $uidA['msgUID']; $messageID = $uidA['msgUID'];
$icServerID = $uidA['profileID']; $icServerID = $uidA['profileID'];
$rememberServerID = $this->mail_bo->profileID;
if ($icServerID && $icServerID != $this->mail_bo->profileID) if ($icServerID && $icServerID != $this->mail_bo->profileID)
{ {
//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID); //error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
@ -4693,7 +4700,7 @@ class mail_ui
//error_log(__METHOD__.__LINE__.$uID); //error_log(__METHOD__.__LINE__.$uID);
if ($_copyOrMove=='move') if ($_copyOrMove=='move')
{ {
$messageListForRefresh[] = self::generateRowID($sourceProfileID, $folderName, $uID, $_prependApp=false); $messageListForRefresh[] = self::generateRowID($sourceProfileID, $_folderName, $uID, $_prependApp=false);
} }
} }
} }

View File

@ -2565,16 +2565,23 @@ app.classes.mail = AppJS.extend(
}, },
/** /**
* Save message as InfoLog * Integrate mail message into another app's entry
* *
* @param _action * @param _action
* @param _elems _elems[0].id is the row-id * @param _elems _elems[0].id is the row-id
*/ */
mail_infolog: function(_action, _elems) mail_integrate: function(_action, _elems)
{ {
//define/preset w_h in case something fails var app = _action.id;
var reg = '750x580'; var w_h = ['750','580']; // define a default wxh if there's no popup size registered
var w_h =reg.split('x');
var add_as_new = true;
if (typeof _action.data != 'undefined' )
{
if (typeof _action.data.popup != 'undefined') w_h = _action.data.popup.split('x');
if (typeof _action.data.mail_import != 'undefined') var mail_import_hook = _action.data.mail_import;
}
if (typeof _elems == 'undefined' || _elems.length==0) if (typeof _elems == 'undefined' || _elems.length==0)
{ {
@ -2591,77 +2598,72 @@ app.classes.mail = AppJS.extend(
_elems.push({id:this.mail_currentlyFocussed}); _elems.push({id:this.mail_currentlyFocussed});
} }
} }
if (typeof _action.data.width == 'undefined' && typeof _action.data.height == 'undefined' && !(typeof _action.data.event == 'undefined' &&typeof _action.data.event.popup == 'undefined'))
{
if (_action.data.event.popup)
{
var app_registry = _action.data.event.popup;
} }
else
{ var url = window.egw_webserverUrl+ '/index.php?menuaction=mail.mail_integration.integrate&rowid=' + _elems[0].id + '&app='+app;
var app_registry = egw.link_get_registry('infolog');//this.appname);
}
if (typeof app_registry['edit'] != 'undefined' && typeof app_registry['edit_popup'] != 'undefined' )
{
w_h =app_registry['edit_popup'].split('x');
}
}
}
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.width:w_h[0]),(_action.data.height?_action.data.height:w_h[1]));
},
/** /**
* Save message as ticket * Checks the application entry existance and offers user
* to select desire app id to append mail content into it,
* or add the mail content as a new app entry
* *
* @param _action _action.id is 'read', 'unread', 'flagged' or 'unflagged' * @param {string} _title select app entry title
* @param _elems * @param {string} _appName app to be integrated
* @param {string} _appCheckCallback registered mail_import hook method
* for check app entry existance
*/ */
mail_tracker: function(_action, _elems) check_app_entry = function (_title, _appName, _appCheckCallback)
{ {
//define/preset w_h in case something fails var data = egw.dataGetUIDdata(_elems[0].id);
var reg = '780x535'; var subject = (data && typeof data.data != 'undefined')? data.data.subject : '';
var w_h =reg.split('x'); egw.json(_appCheckCallback, subject,function(_entryId){
if (typeof _elems == 'undefined' || _elems.length==0)
// if there's no entry saved already
// open dialog in order to select one
if (!_entryId)
{ {
if (this.et2.getArrayMgr("content").getEntry('mail_id')) var buttons = [
{text: 'Append', id: 'append', image: 'check', default:true},
{text: 'Add as new', id: 'new', image: 'check'},
{text: 'Cancel', id: 'cancel', image: 'check'}
];
et2_createWidget("dialog",
{ {
var _elems = []; callback: function(_buttons, _value)
_elems.push({id:this.et2.getArrayMgr("content").getEntry('mail_id') || ''}); {
if (_buttons == 'cancel') return;
if (_buttons == 'append' && _value)
{
url += '&entry_id=' + _value.id;
} }
if ((typeof _elems == 'undefined' || _elems.length==0) && this.mail_isMainWindow) egw_openWindowCentered(url,'import_mail_'+_elems[0].id,w_h[0],w_h[1]);
{ },
if (this.mail_currentlyFocussed) title: egw.lang(_title),
{ buttons: buttons||et2_dialog.BUTTONS_OK_CANCEL,
var _elems = []; value:{
_elems.push({id:this.mail_currentlyFocussed}); content:{
appName:_appName // appName to search on its list later
}},
template: egw.webserverUrl+'/mail/templates/default/integration_to_entry_dialog.xet'
},et2_dialog._create_parent('mail'));
} }
else // there is an entry saved related to this mail's subject
{
egw_openWindowCentered(url,'import_mail_'+_elems[0].id,w_h[0],w_h[1]);
} }
if (typeof _action.data.width == 'undefined' && typeof _action.data.height == 'undefined' && !(typeof _action.data.event == 'undefined' &&typeof _action.data.event.popup == 'undefined')) },this,true,this).sendRequest();
}
if (mail_import_hook && typeof mail_import_hook.app_entry_method != 'undefined')
{ {
if (_action.data.event.popup) check_app_entry('Select '+ app + ' entry', app, mail_import_hook.app_entry_method);
{
var app_registry = _action.data.event.popup;
} }
else else
{ {
var app_registry = egw.link_get_registry('tracker');//this.appname); egw_openWindowCentered(url,'import_mail_'+_elems[0].id,w_h[0],w_h[1]);
} }
if (typeof app_registry['add'] != 'undefined' && typeof app_registry['add_popup'] != 'undefined' )
{
w_h =app_registry['add_popup'].split('x');
}
}
}
//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.width:w_h[0]),(_action.data.height?_action.data.height:w_h[1]));
},
},
/** /**
* mail_getFormData * mail_getFormData

View File

@ -373,6 +373,7 @@ saving of message %1 failed. destination folder %2 does not exist. mail de Speic
saving of message %1 succeeded. check folder %2. mail de Speichern der Nachricht %1 war erfolgreich. Prüfen Sie den Ziel Ordner %2 saving of message %1 succeeded. check folder %2. mail de Speichern der Nachricht %1 war erfolgreich. Prüfen Sie den Ziel Ordner %2
saving the rule failed: mail de Speichern der Regel ist fehlgeschlagen: saving the rule failed: mail de Speichern der Regel ist fehlgeschlagen:
select all mail de Alle Auswählen select all mail de Alle Auswählen
select an existing entry in order to append mail content to it mail de Bestehendes Ticket auswählen, zu dem die Mail als Kommentar hinzugefügt werden soll.
select file to attach to message mail de Wählen Sie die Dateien aus, die Sie an diese Nachricht anhängen möchten. select file to attach to message mail de Wählen Sie die Dateien aus, die Sie an diese Nachricht anhängen möchten.
select file to import into folder mail de Wählen Sie ein E-Mail als Datei aus, damit Sie in ein Ordner importiert werden kann. select file to import into folder mail de Wählen Sie ein E-Mail als Datei aus, damit Sie in ein Ordner importiert werden kann.
select file(s) from vfs mail de Dateien aus dem EGroupware Dateimanager anhängen. select file(s) from vfs mail de Dateien aus dem EGroupware Dateimanager anhängen.

View File

@ -373,6 +373,7 @@ saving of message %1 failed. destination folder %2 does not exist. mail en Savin
saving of message %1 succeeded. check folder %2. mail en Saving of message %1 succeeded. Check Folder %2. saving of message %1 succeeded. check folder %2. mail en Saving of message %1 succeeded. Check Folder %2.
saving the rule failed: mail en Saving the rule failed: saving the rule failed: mail en Saving the rule failed:
select all mail en Select all select all mail en Select all
select an existing entry in order to append mail content to it mail en Select an existing entry in order to append mail content to it
select file to attach to message mail en Select file to attach to message select file to attach to message mail en Select file to attach to message
select file to import into folder mail en Select file to import into Folder select file to import into folder mail en Select file to import into Folder
select file(s) from vfs mail en Select file(s) from VFS select file(s) from vfs mail en Select file(s) from VFS

View File

@ -10,6 +10,7 @@
<file class="mail-compose_fileselector" statustext="Select file to attach to message" multiple='true' progress='attachments' onFinish="app.mail.uploadForCompose" onStart="app.mail.composeUploadStart" id="uploadForCompose" drop_target ="mail-compose"/> <file class="mail-compose_fileselector" statustext="Select file to attach to message" multiple='true' progress='attachments' onFinish="app.mail.uploadForCompose" onStart="app.mail.composeUploadStart" id="uploadForCompose" drop_target ="mail-compose"/>
<checkbox statustext="check to save as infolog on send" id="to_infolog" options="on,off"/> <checkbox statustext="check to save as infolog on send" id="to_infolog" options="on,off"/>
<checkbox statustext="check to save as trackerentry on send" id="to_tracker" options="on,off"/> <checkbox statustext="check to save as trackerentry on send" id="to_tracker" options="on,off"/>
<checkbox statustext="check to save as calendar event on send" id="to_calendar" options="on,off"/>
<checkbox statustext="check to recieve a notification when the message is read (note: not all clients support this and/or the reciever may not authorize the notification)" id="disposition" options="on,off"/> <checkbox statustext="check to recieve a notification when the message is read (note: not all clients support this and/or the reciever may not authorize the notification)" id="disposition" options="on,off"/>
<menulist> <menulist>
<menupopup id="priority"/> <menupopup id="priority"/>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE overlay PUBLIC "-//Stylite AG//eTemplate 2//EN" "http://www.egroupware.org/etemplate2.dtd">
<!-- $Id$ -->
<overlay>
<template id="mail.integration_to_entry_dialog" template="" lang="" group="0" version="1.9.001">
<vbox width="100%">
<description value="Select an existing entry in order to append mail content to it"/>
<description/>
<link-entry id="id" only_app="@appName" class="et2_fullWidth"/>
</vbox>
</template>
</overlay>

View File

@ -112,6 +112,11 @@ class egw_link extends solink
*/ */
const VFS_APPNAME = 'file'; // pseudo-appname for own file-attachments in vfs, this is NOT the vfs-app const VFS_APPNAME = 'file'; // pseudo-appname for own file-attachments in vfs, this is NOT the vfs-app
/**
* Appname used of files stored via egw_link::set_data()
*/
const DATA_APPNAME = 'egw-data';
/** /**
* appname used for linking existing files to VFS * appname used for linking existing files to VFS
*/ */
@ -188,9 +193,10 @@ class egw_link extends solink
static function init_static( ) static function init_static( )
{ {
// FireFox 36 can not display pdf with it's internal viewer in an iframe used by mobile theme/template for popups // FireFox 36 can not display pdf with it's internal viewer in an iframe used by mobile theme/template for popups
if (html::$user_agent == 'firefox' && $GLOBALS['egw_info']['user']['preferences']['common']['theme'] == 'mobile') // same is true for all mobile devices
if (html::$user_agent == 'firefox' && $GLOBALS['egw_info']['user']['preferences']['common']['theme'] == 'mobile' || html::$ua_mobile)
{ {
unset(self::$app_register['home']['mime']['application/pdf']['mime_popup']); unset(self::$app_register['home']['mime']['application/pdf']);
} }
// other apps can participate in the linking by implementing a search_link hook, which // other apps can participate in the linking by implementing a search_link hook, which
// has to return an array in the format of an app_register entry // has to return an array in the format of an app_register entry
@ -307,13 +313,12 @@ class egw_link extends solink
* of not created item or $file-array if $app1 == self::VFS_APPNAME (see below). * of not created item or $file-array if $app1 == self::VFS_APPNAME (see below).
* If $id==0 it will be set on return to an array with the links for the new item. * If $id==0 it will be set on return to an array with the links for the new item.
* @param string|array $app2 app of 2.linkend or array with links ($id2 not used) * @param string|array $app2 app of 2.linkend or array with links ($id2 not used)
* @param string $id2 ='' id of 2. item of $file-array if $app2 == self::VFS_APPNAME (see below)<br> * @param string $id2 ='' id of 2. item of $file-array if $app2 == self::VFS_APPNAME or self::DATA_APPNAME
* $file array with informations about the file in format of the etemplate file-type<br> * $file array with informations about the file in format of the etemplate file-type
* $file['name'] name of the file (no directory)<br> * $file['name'] name of the file (no directory)
* $file['type'] mine-type of the file<br> * $file['type'] mime-type of the file
* $file['tmp_name'] name of the uploaded file (incl. directory)<br> * $file['tmp_name'] name of the uploaded file (incl. directory) for self::VFS_APPNAME or
* $file['path'] path of the file on the client computer<br> * $file['egw_data'] id of egw_link::set_data() call for self::DATA_APPNAME
* $file['ip'] of the client (path and ip in $file are only needed if u want a symlink (if possible))
* @param string $remark ='' Remark to be saved with the link (defaults to '') * @param string $remark ='' Remark to be saved with the link (defaults to '')
* @param int $owner =0 Owner of the link (defaults to user) * @param int $owner =0 Owner of the link (defaults to user)
* @param int $lastmod =0 timestamp of last modification (defaults to now=time()) * @param int $lastmod =0 timestamp of last modification (defaults to now=time())
@ -346,22 +351,30 @@ class egw_link extends solink
self::link($app1, $id1, $link['app'], $link['id'], $link['remark'],$link['owner'],$link['lastmod']); self::link($app1, $id1, $link['app'], $link['id'], $link['remark'],$link['owner'],$link['lastmod']);
continue; continue;
} }
if ($link['app'] == self::VFS_APPNAME) switch ($link['app'])
{ {
case self::DATA_APPNAME:
if (!($link['id']['tmp_name'] = self::get_data($link['id']['egw_data'], true)))
{
$link_id = false;
break;
}
// fall through
case self::VFS_APPNAME:
$link_id = self::attach_file($app1,$id1,$link['id'],$link['remark']); $link_id = self::attach_file($app1,$id1,$link['id'],$link['remark']);
} break;
else if ($link['app'] == self::VFS_LINK)
{ case self::VFS_LINK:
$link_id = self::link_file($app1,$id1, $link['id'],$link['remark']); $link_id = self::link_file($app1,$id1, $link['id'],$link['remark']);
} break;
else
{ default:
$link_id = solink::link($app1,$id1,$link['app'],$link['id'], $link_id = solink::link($app1,$id1,$link['app'],$link['id'],
$link['remark'],$link['owner'],$link['lastmod']); $link['remark'],$link['owner'],$link['lastmod']);
// notify both sides // notify both sides
if (!($no_notify&2)) self::notify('link',$link['app'],$link['id'],$app1,$id1,$link_id); if (!($no_notify&2)) self::notify('link',$link['app'],$link['id'],$app1,$id1,$link_id);
if (!($no_notify&1)) self::notify('link',$app1,$id1,$link['app'],$link['id'],$link_id); if (!($no_notify&1)) self::notify('link',$app1,$id1,$link['app'],$link['id'],$link_id);
break;
} }
} }
return $link_id; return $link_id;
@ -421,7 +434,7 @@ class egw_link extends solink
*/ */
static function temp_link_id($app,$id) static function temp_link_id($app,$id)
{ {
return $app.':'.($app != self::VFS_APPNAME && $app != self::VFS_LINK ? $id : $id['name']); return $app.':'.(!in_array($app, array(self::VFS_APPNAME,self::VFS_LINK, self::DATA_APPNAME)) ? $id : $id['name']);
} }
/** /**
@ -1178,9 +1191,7 @@ class egw_link extends solink
* @param array $file informations about the file in format of the etemplate file-type * @param array $file informations about the file in format of the etemplate file-type
* $file['name'] name of the file (no directory) * $file['name'] name of the file (no directory)
* $file['type'] mine-type of the file * $file['type'] mine-type of the file
* $file['tmp_name'] name of the uploaded file (incl. directory) * $file['tmp_name'] name of the uploaded file (incl. directory) or resource of opened file
* $file['path'] path of the file on the client computer
* $file['ip'] of the client (path and ip are only needed if u want a symlink (if possible))
* @param string $comment ='' comment to add to the link * @param string $comment ='' comment to add to the link
* @return int negative id of egw_sqlfs table as negative link-id's are for vfs attachments * @return int negative id of egw_sqlfs table as negative link-id's are for vfs attachments
*/ */
@ -1552,16 +1563,18 @@ class egw_link extends solink
* @param string $mime_type * @param string $mime_type
* @param string $method * @param string $method
* @param array $params * @param array $params
* @param boolean $ignore_mime =false true: return id, even if nothing registered for given mime-type
* @return string|null md5 hash of stored data of server-side supported mime-type or null otherwise * @return string|null md5 hash of stored data of server-side supported mime-type or null otherwise
*/ */
public static function set_data($mime_type, $method, array $params) public static function set_data($mime_type, $method, array $params, $ignore_mime=false)
{ {
if (!($info = self::get_mime_info($mime_type)) || empty($info['mime_data'])) if (!$ignore_mime && (!($info = self::get_mime_info($mime_type)) || empty($info['mime_data'])))
{ {
return null; return null;
} }
array_unshift($params, $method); array_unshift($params, $method);
$id = md5(json_encode($params)); $id = md5(serialize($params));
//error_log(__METHOD__."('$mime_type', '$method', ...) params=".array2string($params)." --> json=".array2string(serialize($params)).' --> id='.array2string($id));
egw_cache::setSession(__CLASS__, $id, $params); egw_cache::setSession(__CLASS__, $id, $params);
return $id; return $id;
} }
@ -1584,6 +1597,8 @@ class egw_link extends solink
} }
$ret = call_user_func_array('ExecMethod2', $data); $ret = call_user_func_array('ExecMethod2', $data);
if (is_resource($ret)) fseek($ret, 0);
if ($return_resource != is_resource($ret)) if ($return_resource != is_resource($ret))
{ {
if ($return_resource && ($fp = fopen('php://temp', 'w'))) if ($return_resource && ($fp = fopen('php://temp', 'w')))
@ -1596,7 +1611,6 @@ class egw_link extends solink
{ {
$fp = $ret; $fp = $ret;
$ret = ''; $ret = '';
fseek($fp, 0);
while(!feof($fp)) while(!feof($fp))
{ {
$ret .= fread($fp, 8192); $ret .= fread($fp, 8192);

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB