Completed
Push — 16.1 ( ff57e6...7a6d97 )
by Hadi
60:09 queued 46:48
created

mail_ui::header2gridelements()   F

Complexity

Conditions 74
Paths > 20000

Size

Total Lines 215
Code Lines 134

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 74
eloc 134
nc 429496.7295
nop 4
dl 0
loc 215
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * EGroupware - Mail - interface class
4
 *
5
 * @link http://www.egroupware.org
6
 * @package mail
7
 * @author EGroupware GmbH [[email protected]]
8
 * @copyright (c) 2013-2016 by EGroupware GmbH <info-AT-egroupware.org>
9
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
10
 * @version $Id$
11
 */
12
13
use EGroupware\Api;
14
use EGroupware\Api\Link;
15
use EGroupware\Api\Framework;
16
use EGroupware\Api\Egw;
17
use EGroupware\Api\Vfs;
18
use EGroupware\Api\Etemplate;
19
use EGroupware\Api\Etemplate\KeyManager;
20
use EGroupware\Api\Mail;
21
22
/**
23
 * Mail User Interface
24
 *
25
 * As we do NOT want to connect to previous imap server, when a profile change is triggered
26
 * by user get_rows and ajax_changeProfile are not static methods and instanciates there own
27
 * mail_ui object.
28
 *
29
 * If they detect a profile change is to be triggered they call:
30
 *		$mail_ui = new mail_ui(false);	// not call constructor / connect to imap server
31
 *		$mail_ui->changeProfile($_profileID);
32
 * If no profile change is needed they just call:
33
 *		$mail_ui = new mail_ui();
34
 * Afterwards they use $mail_ui instead of $this.
35
 */
36
class mail_ui
37
{
38
	/**
39
	 * Methods callable via menuaction
40
	 *
41
	 * @var array
42
	 */
43
	var $public_functions = array
44
	(
45
		'index' => True,
46
		'displayHeader'	=> True,
47
		'displayMessage'	=> True,
48
		'displayImage'		=> True,
49
		'getAttachment'		=> True,
50
		'download_zip'		=> True,
51
		'saveMessage'	=> True,
52
		'vfsSaveAttachment' => True,
53
		'vfsSaveMessage' => True,
54
		'loadEmailBody'	=> True,
55
		'importMessage'	=> True,
56
		'importMessageFromVFS2DraftAndDisplay'=>True,
57
		'subscription'	=> True,
58
		'folderManagement' => true,
59
	);
60
61
	/**
62
	 * current icServerID
63
	 *
64
	 * @var int
65
	 */
66
	static $icServerID;
67
68
	/**
69
	 * delimiter - used to separate profileID from foldertreestructure, and separate keyinformation in rowids
70
	 *
71
	 * @var string
72
	 */
73
	static $delimiter = '::';
74
75
	/**
76
	 * nextMatch name for index
77
	 *
78
	 * @var string
79
	 */
80
	static $nm_index = 'nm';
81
82
	/**
83
	 * instance of Mail
84
	 *
85
	 * @var Mail
86
	 */
87
	var $mail_bo;
88
89
	/**
90
	 * definition of available / supported search types
91
	 *
92
	 * @var array
93
	 */
94
	var $searchTypes = array(
95
		'quick'		=> 'quicksearch',	// lang('quicksearch')
96
		'quickwithcc'=> 'quicksearch (with cc)',	// lang('quicksearch (with cc)')
97
		'subject'	=> 'subject',		// lang('subject')
98
		'body'		=> 'message body',	// lang('message body')
99
		'from'		=> 'from',			// lang('from')
100
		'to'		=> 'to',			// lang('to')
101
		'cc'		=> 'cc',			// lang('cc')
102
		'text'		=> 'whole message',	// lang('whole message')
103
		'larger'		=> 'greater than',	// lang('greater than')
104
		'smaller'		=> 'less than',	// lang('less than')
105
		'bydate' 	=> 'Selected date range (with quicksearch)',// lang('Selected date range (with quicksearch)')
106
	);
107
108
	/**
109
	 * definition of available / supported status types
110
	 *
111
	 * @var array
112
	 */
113
	var $statusTypes = array(
114
		'any'		=> 'any status',// lang('any status')
115
		'flagged'	=> 'flagged',	// lang('flagged')
116
		'unseen'	=> 'unread',	// lang('unread')
117
		'answered'	=> 'replied',	// lang('replied')
118
		'seen'		=> 'read',		// lang('read')
119
		'deleted'	=> 'deleted',	// lang('deleted')
120
	);
121
122
	/**
123
	 * Constructor
124
	 *
125
	 * @param boolean $run_constructor =true false: no not run constructor and therefore do NOT connect to imap server
126
	 */
127
	function __construct($run_constructor=true)
128
	{
129
		$this->mail_tree = new mail_tree($this);
130
		if (!$run_constructor) return;
131
132
		if (Mail::$debugTimes) $starttime = microtime (true);
133
		// no autohide of the sidebox, as we use it for folderlist now.
134
		unset($GLOBALS['egw_info']['user']['preferences']['common']['auto_hide_sidebox']);
135
136 View Code Duplication
		if (isset($GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID']) && !empty($GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID']))
137
		{
138
			self::$icServerID = (int)$GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'];
139
		}
140
		if ($_GET["resetConnection"])
141
		{
142
			unset($_GET["resetConnection"]);
143
			if (Mail::$debug) error_log(__METHOD__.__LINE__.' Connection Reset triggered: for Profile with ID:'.self::$icServerID);
144
			Mail::unsetCachedObjects(self::$icServerID);
145
		}
146
147
		try {
148
			$this->mail_bo = Mail::getInstance(true,self::$icServerID, true, false, true);
149 View Code Duplication
			if (Mail::$debug) error_log(__METHOD__.__LINE__.' Fetched IC Server:'.self::$icServerID.'/'.$this->mail_bo->profileID.':'.function_backtrace());
150
			//error_log(__METHOD__.__LINE__.array2string($this->mail_bo->icServer));
151
152
			// RegEx to minimize extra openConnection
153
			$needle = '/^(?!mail)/';
154
			if (!preg_match($needle,$_GET['menuaction']) && !Api\Json\Request::isJSONRequest())
155
			{
156
				//error_log(__METHOD__.__LINE__.' Fetched IC Server openConnection:'.self::$icServerID.'/'.$this->mail_bo->profileID.':'.function_backtrace());
157
				//openConnection gathers SpecialUseFolderInformation and Delimiter Info
158
				$this->mail_bo->openConnection(self::$icServerID);
159
			}
160
		}
161
		catch (Exception $e)
162
		{
163
			// we need this to handle failed JSONRequests
164
			if (Api\Json\Request::isJSONRequest() && $_GET['menuaction'] != 'mail.mail_ui.index')
165
			{
166
				$response = Api\Json\Response::get();
167
				$response->call('egw.message',$e->getMessage(),'error');
168
			}
169
			// redirect to mail wizard to handle it (redirect works for ajax too), unless index is called. we want the sidebox
170
			if ($_GET['menuaction'] != 'mail.mail_ui.index') self::callWizard($e->getMessage(),true,'error',false);
171
		}
172
		if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'',__METHOD__.__LINE__);
173
	}
174
175
	/**
176
	 * callWizard
177
	 *
178
	 * @param string $message
179
	 * @param boolean $exit If true, will call exit() after opening the wizardpopup
180
	 * @param string $msg_type = 'success' message type
181
	 */
182
	static function callWizard($message, $exit=true, $msg_type='success',$reset_sidebox_on_index=true)
183
	{
184
		//error_log(__METHOD__."('$message', $exit) ".function_backtrace());
185
		$linkData=(self::$icServerID ? array(
186
				'menuaction' => 'mail.mail_wizard.edit',
187
				'acc_id' => self::$icServerID,
188
			) : array(
189
				'menuaction' => 'mail.mail_wizard.add',
190
			)) + array(
191
				'msg' => $message,
192
				'msg_type' => $msg_type
193
			);
194
195
		if (Api\Json\Response::isJSONResponse())
196
		{
197
			$response = Api\Json\Response::get();
198
			$windowName = "editMailAccount".self::$icServerID;
199
			$response->call("egw.open_link", Egw::link('/index.php', $linkData), $windowName, "600x480",null,true);
200
			Framework::message($message, 'error');
201
			if ($_GET['menuaction'] == 'mail.mail_ui.index' && $reset_sidebox_on_index)
202
			{
203
				$response->call('framework.setSidebox','mail',array(),'md5');
204
			}
205
			if ($exit)
206
			{
207
				exit();
208
			}
209
		}
210
		else	// regular GET request eg. in idots template
211
		{
212
			$windowName = "editMailAccount".self::$icServerID;
213
			Framework::popup(Framework::link('/index.php',$linkData),$windowName);
214
			$GLOBALS['egw']->framework->render($message,'',true);
215
			if ($exit)
216
			{
217
				exit();
218
			}
219
		}
220
	}
221
222
	/**
223
	 * changeProfile
224
	 *
225
	 * @param int $_icServerID
226
	 * @param boolean $unsetCache
227
	 *
228
	 * @throws Api\Exception
229
	 */
230
	function changeProfile($_icServerID,$unsetCache=false)
231
	{
232
		if (Mail::$debugTimes) $starttime = microtime (true);
233
		if (self::$icServerID != $_icServerID)
234
		{
235
			self::$icServerID = $_icServerID;
236
		}
237
		if (Mail::$debug) error_log(__METHOD__.__LINE__.'->'.self::$icServerID.'<->'.$_icServerID);
238
239
		if ($unsetCache) Mail::unsetCachedObjects(self::$icServerID);
240
		$this->mail_bo = Mail::getInstance(false,self::$icServerID,true, false, true);
241 View Code Duplication
		if (Mail::$debug) error_log(__METHOD__.__LINE__.' Fetched IC Server:'.self::$icServerID.'/'.$this->mail_bo->profileID.':'.function_backtrace());
242
		// no icServer Object: something failed big time
243
		if (!isset($this->mail_bo) || !isset($this->mail_bo->icServer) || $this->mail_bo->icServer->ImapServerId<>$_icServerID)
244
		{
245
			self::$icServerID = $_icServerID;
246
			throw new Api\Exception('Profile change failed!');
247
		}
248
249
		// save session varchar
250
		$oldicServerID =& Api\Cache::getSession('mail','activeProfileID');
251
		if ($oldicServerID <> self::$icServerID) $this->mail_bo->openConnection(self::$icServerID);
252
		if (true) $oldicServerID = self::$icServerID;
253
		if (!Mail::storeActiveProfileIDToPref($this->mail_bo->icServer, self::$icServerID, true ))
0 ignored issues
show
Bug Best Practice introduced by
The expression \EGroupware\Api\Mail::st...elf::$icServerID, true) of type false|integer is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
254
		{
255
			throw new Api\Exception(__METHOD__." failed to change Profile to $_icServerID");
256
		}
257
258
		if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'',__METHOD__.__LINE__);
259
	}
260
261
	/**
262
	 * Ajax function to request next branch of a tree branch
263
	 */
264
	static function ajax_tree_autoloading ($_id = null)
265
	{
266
		$mail_ui = new mail_ui();
267
		$id = $_id ? $_id : $_GET['id'];
268
		Etemplate\Widget\Tree::send_quote_json($mail_ui->mail_tree->getTree($id,'',1,false));
269
	}
270
271
	/**
272
	 * Subscription popup window
273
	 *
274
	 * @param array $content
275
	 * @param type $msg
276
	 */
277
	function subscription(array $content=null ,$msg=null)
278
	{
279
		$stmpl = new Etemplate('mail.subscribe');
280
281
		if(is_array($content))
282
		{
283
			$profileId = $content['profileId'];
284
		}
285
		elseif (!($profileId = (int)$_GET['acc_id']))
286
		{
287
			Framework::window_close('Missing acc_id!');
288
		}
289
		// Initial tree's options, the rest would be loaded dynamicaly by autoloading,
290
		// triggered from client-side. Also, we keep this here as
291
		$sel_options['foldertree'] =  $this->mail_tree->getTree(null,$profileId,1,true,false,true);
292
293
		//Get all subscribed folders
294
		// as getting all subscribed folders is very fast operation
295
		// we can use it to get a comparison base for folders which
296
		// got subscribed or unsubscribed by the user
297
		try {
298
			$subscribed = $this->mail_bo->icServer->listSubscribedMailboxes('',0,true);
299
		} catch (Exception $ex) {
300
			Framework::message($ex->getMessage());
301
		}
302
303
		if (!is_array($content))
304
		{
305
			$content['foldertree'] = array();
306
307
			foreach ($subscribed as $folder)
308
			{
309
				$folderName = $profileId . self::$delimiter . $folder['MAILBOX'];
310
				array_push($content['foldertree'], $folderName);
311
			}
312
		}
313
		else
314
		{
315
			list($button) = @each($content['button']);
316
			switch ($button)
317
			{
318
				case 'save':
319
				case 'apply':
320
				{
321
					// do not let user (un)subscribe namespace roots eg. "other", "user" or "INBOX", same for tree-root/account itself
322
					$namespace_roots = array($profileId);
323
					foreach($this->mail_bo->_getNameSpaces() as $namespace)
324
					{
325
						$namespace_roots[] = $profileId . self::$delimiter . str_replace($namespace['delimiter'], '', $namespace['prefix']);
326
					}
327
					$to_unsubscribe = $to_subscribe = array();
328
					foreach ($content['foldertree'] as $path => $value)
329
					{
330
						list(,$node) = explode($profileId.self::$delimiter, $path);
331
						if ($node)
332
						{
333
							if (is_array($subscribed) && $subscribed[$node] && !$value['value']) $to_unsubscribe []= $node;
334
							if (is_array($subscribed) && !$subscribed[$node] && $value['value']) $to_subscribe [] = $node;
335
							if ($value['value']) $cont[] = $path;
336
						}
337
338
					}
339
					$content['foldertree'] = $cont;
340
					// set foldertree options to basic node in order to avoid initial autoloading
341
					// from client side, as no options would trigger that.
342
					$sel_options['foldertree'] = array('id' => '0', 'item'=> array());
343
					foreach(array_merge($to_subscribe, $to_unsubscribe) as $mailbox)
344
					{
345
						if (in_array($profileId.self::$delimiter.$mailbox, $namespace_roots, true))
346
						{
347
							continue;
348
						}
349
						$subscribe = in_array($mailbox, $to_subscribe);
350
						try {
351
							$this->mail_bo->icServer->subscribeMailbox($mailbox, $subscribe);
352
						}
353
						catch (Exception $ex)
354
						{
355
							$msg_type = 'error';
356
							if ($subscribe)
357
							{
358
								$msg .= lang('Failed to subscribe folder %1!', $mailbox).' '.$ex->getMessage();
359
							}
360
							else
361
							{
362
								$msg .= lang('Failed to unsubscribe folder %1!', $mailbox).' '.$ex->getMessage();
363
							}
364
						}
365
					}
366
					if (!isset($msg))
367
					{
368
						$msg_type = 'success';
369
						if ($to_subscribe || $to_unsubscribe)
370
						{
371
							$msg = lang('Subscription successfully saved.');
372
						}
373
						else
374
						{
375
							$msg = lang('Nothing to change.');
376
						}
377
					}
378
					// update foldertree in main window
379
					$parentFolder='INBOX';
380
					$refreshData = array(
381
						$profileId => lang($parentFolder),
382
					);
383
					$response = Api\Json\Response::get();
384
					foreach($refreshData as $folder => &$name)
385
					{
386
						$name = $this->mail_tree->getTree($folder, $profileId,1,true,true,true);
387
					}
388
					// give success/error message to opener and popup itself
389
					//$response->call('opener.app.mail.subscription_refresh',$refreshData);
390
					$response->call('opener.app.mail.mail_reloadNode',$refreshData);
391
392
					Framework::refresh_opener($msg, 'mail', null, null, null, null, null, $msg_type);
393
					if ($button == 'apply')
394
					{
395
						Framework::message($msg, $msg_type);
396
						break;
397
					}
398
				}
399
				case 'cancel':
400
				{
401
					Framework::window_close();
402
				}
403
			}
404
		}
405
406
		$preserv['profileId'] = $profileId;
407
408
		$readonlys = array();
409
410
		$stmpl->exec('mail.mail_ui.subscription', $content,$sel_options,$readonlys,$preserv,2);
411
	}
412
413
	/**
414
	 * Main mail page
415
	 *
416
	 * @param array $content
417
	 * @param string $msg
418
	 */
419
	function index(array $content=null,$msg=null)
420
	{
421
		//error_log(__METHOD__.__LINE__.array2string($content));
422
		try	{
423
				if (!isset($this->mail_bo)) throw new Api\Exception\WrongUserinput(lang('Initialization of mail failed. Please use the Wizard to cope with the problem.'));
424
				//error_log(__METHOD__.__LINE__.function_backtrace());
425
				if (Mail::$debugTimes) $starttime = microtime (true);
426
				$this->mail_bo->restoreSessionData();
427
				$sessionFolder = $this->mail_bo->sessionData['mailbox'];
428
				if ($this->mail_bo->folderExists($sessionFolder))
429
				{
430
					$this->mail_bo->reopen($sessionFolder); // needed to fetch full set of capabilities
431
				}
432
				else
433
				{
434
					$sessionFolder = $this->mail_bo->sessionData['mailbox'] = 'INBOX';
435
				}
436
				//error_log(__METHOD__.__LINE__.' SessionFolder:'.$sessionFolder.' isToSchema:'.$toSchema);
437
				if (!is_array($content))
438
				{
439
					$content = array(
440
						self::$nm_index => Api\Cache::getSession('mail', 'index'),
441
					);
442
					if (!is_array($content[self::$nm_index]))
443
					{
444
						// These only set on first load
445
						$content[self::$nm_index] = array(
446
							'filter'         => 'any',	// filter is used to choose the mailbox
447
							'lettersearch'   => false,	// I  show a lettersearch
448
							'searchletter'   =>	false,	// I0 active letter of the lettersearch or false for [all]
449
							'start'          =>	0,		// IO position in list
450
							'order'          =>	'date',	// IO name of the column to sort after (optional for the sortheaders)
451
							'sort'           =>	'DESC',	// IO direction of the sort: 'ASC' or 'DESC'
452
						);
453
					}
454
					if (Api\Header\UserAgent::mobile()) $content[self::$nm_index]['header_row'] = 'mail.index.header_right';
455
				}
456
457
				// These must always be set, even if $content is an array
458
				$content[self::$nm_index]['cat_is_select'] = true;    // Category select is just a normal selectbox
459
				$content[self::$nm_index]['no_filter2'] = false;       // Disable second filter
460
				$content[self::$nm_index]['actions'] = self::get_actions();
461
				$content[self::$nm_index]['row_id'] = 'row_id';	     // is a concatenation of trim($GLOBALS['egw_info']['user']['account_id']):profileID:base64_encode(FOLDERNAME):uid
462
				$content[self::$nm_index]['placeholder_actions'] = array('composeasnew');
463
				$content[self::$nm_index]['get_rows'] = 'mail_ui::get_rows';
464
				$content[self::$nm_index]['num_rows'] = 0;      // Do not send any rows with initial request
465
				$content[self::$nm_index]['default_cols'] = 'status,attachments,subject,address,date,size';	// I  columns to use if there's no user or default pref (! as first char uses all but the named columns), default all columns
466
				$content[self::$nm_index]['csv_fields'] = false;
467
				if ($msg)
468
				{
469
					$content['msg'] = $msg;
470
				}
471
				else
472
				{
473
					unset($msg);
474
					unset($content['msg']);
475
				}
476
				// call getQuotaRoot asynchronously in getRows by initiating a client Server roundtrip
477
				$quota = false;//$this->mail_bo->getQuotaRoot();
478
				if($quota !== false && $quota['limit'] != 'NOT SET') {
479
					$quotainfo = $this->quotaDisplay($quota['usage'], $quota['limit']);
480
					$content[self::$nm_index]['quota'] = $sel_options[self::$nm_index]['quota'] = $quotainfo['text'];
481
					$content[self::$nm_index]['quotainpercent'] = $sel_options[self::$nm_index]['quotainpercent'] =  (string)$quotainfo['percent'];
482
					$content[self::$nm_index]['quotaclass'] = $sel_options[self::$nm_index]['quotaclass'] = $quotainfo['class'];
483
					$content[self::$nm_index]['quotanotsupported'] = $sel_options[self::$nm_index]['quotanotsupported'] = "";
484
				} else {
485
					$content[self::$nm_index]['quota'] = $sel_options[self::$nm_index]['quota'] = lang("Quota not provided by server");
486
					$content[self::$nm_index]['quotaclass'] = $sel_options[self::$nm_index]['quotaclass'] = "mail_DisplayNone";
487
					$content[self::$nm_index]['quotanotsupported'] = $sel_options[self::$nm_index]['quotanotsupported'] = "mail_DisplayNone";
488
				}
489
				// call gatherVacation asynchronously in getRows by initiating a client Server roundtrip
490
				$vacation = false;//$this->gatherVacation();
491
				//error_log(__METHOD__.__LINE__.' Server:'.self::$icServerID.' Sieve Enabled:'.array2string($vacation));
492
				if($vacation) {
493
					if (is_array($vacation) && ($vacation['status'] == 'on' || $vacation['status']=='by_date' && $vacation['end_date'] > time()))
494
					{
495
						$dtfrmt = $GLOBALS['egw_info']['user']['preferences']['common']['dateformat']/*.' '.($GLOBALS['egw_info']['user']['preferences']['common']['timeformat']!='24'?'h:i a':'H:i')*/;
496
						$content[self::$nm_index]['vacationnotice'] = $sel_options[self::$nm_index]['vacationnotice'] = lang('Vacation notice is active');
497
						$content[self::$nm_index]['vacationrange'] = $sel_options[self::$nm_index]['vacationrange'] = ($vacation['status']=='by_date'? Api\DateTime::server2user($vacation['start_date'],$dtfrmt,true).($vacation['end_date']>$vacation['start_date']?'->'.Api\DateTime::server2user($vacation['end_date']+ 24*3600-1,$dtfrmt,true):''):'');
0 ignored issues
show
Unused Code introduced by
The call to DateTime::server2user() has too many arguments starting with true.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
498
					}
499
				}
500
				if ($vacation==false)
501
				{
502
					$content[self::$nm_index]['vacationnotice'] = $sel_options[self::$nm_index]['vacationnotice'] = '';
503
					$content[self::$nm_index]['vacationrange'] = $sel_options[self::$nm_index]['vacationrange'] = '';
504
				}
505
				//$zstarttime = microtime (true);
506
				$sel_options[self::$nm_index]['foldertree'] = $this->mail_tree->getInitialIndexTree(null, $this->mail_bo->profileID, null, !$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'],!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
507
				//$zendtime = microtime(true) - $zstarttime;
508
				//error_log(__METHOD__.__LINE__. " time used: ".$zendtime);
509
				$content[self::$nm_index]['selectedFolder'] = $this->mail_bo->profileID.self::$delimiter.(!empty($this->mail_bo->sessionData['mailbox'])?$this->mail_bo->sessionData['mailbox']:'INBOX');
510
				// since we are connected,(and selected the folder) we check for capabilities SUPPORTS_KEYWORDS to eventually add the keyword filters
511 View Code Duplication
				if ( $this->mail_bo->icServer->hasCapability('SUPPORTS_KEYWORDS'))
512
				{
513
					$this->statusTypes = array_merge($this->statusTypes,array(
514
						'keyword1'	=> 'important',//lang('important'),
515
						'keyword2'	=> 'job',	//lang('job'),
516
						'keyword3'	=> 'personal',//lang('personal'),
517
						'keyword4'	=> 'to do',	//lang('to do'),
518
						'keyword5'	=> 'later',	//lang('later'),
519
					));
520
				}
521
				else
522
				{
523
					$keywords = array('keyword1','keyword2','keyword3','keyword4','keyword5');
524
					foreach($keywords as &$k)
525
					{
526
						if (array_key_exists($k,$this->statusTypes)) unset($this->statusTypes[$k]);
527
					}
528
				}
529
530 View Code Duplication
				if (!isset($content[self::$nm_index]['foldertree'])) $content[self::$nm_index]['foldertree'] = $this->mail_bo->profileID.self::$delimiter.'INBOX';
531 View Code Duplication
				if (!isset($content[self::$nm_index]['selectedFolder'])) $content[self::$nm_index]['selectedFolder'] = $this->mail_bo->profileID.self::$delimiter.'INBOX';
532
533
				$content[self::$nm_index]['foldertree'] = $content[self::$nm_index]['selectedFolder'];
534
535
				if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
536
				{
537
					Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE, 'email', 'supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
538
					if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
539
				}
540 View Code Duplication
				if (!Mail::$supportsORinQuery[$this->mail_bo->profileID])
541
				{
542
					unset($this->searchTypes['quick']);
543
					unset($this->searchTypes['quickwithcc']);
544
				}
545
				$sel_options['cat_id'] = $this->searchTypes;
546
				//error_log(__METHOD__.__LINE__.array2string($sel_options['cat_id']));
547
				//error_log(__METHOD__.__LINE__.array2string($GLOBALS['egw_info']['user']['preferences']['mail']['ActiveSearchType']));
548
				$content[self::$nm_index]['cat_id'] = $GLOBALS['egw_info']['user']['preferences']['mail']['ActiveSearchType'];
549
				$sel_options['filter'] = $this->statusTypes;
550
				$sel_options['filter2'] = array(''=>lang('No Sneak Preview in list'),1=>lang('Sneak Preview in list'));
551
				$content[self::$nm_index]['filter2'] = $GLOBALS['egw_info']['user']['preferences']['mail']['ShowDetails'];
552
553
				$etpl = new Etemplate('mail.index');
554
				//apply infolog_filter_change javascript method (hide/show of date filter form) over onchange filter
555
				$content[self::$nm_index]['cat_id_onchange'] = "app.mail.mail_searchtype_change()";
556
				// set the actions on tree
557
				$etpl->setElementAttribute(self::$nm_index.'[foldertree]','actions', $this->get_tree_actions());
558
559
				// sending preview toolbar actions
560
				if ($content['mailSplitter']) $etpl->setElementAttribute('mailPreview[toolbar]', 'actions', $this->get_toolbar_actions());
561
562
				// We need to send toolbar actions to client-side because view template needs them
563
				if (Api\Header\UserAgent::mobile()) $sel_options['toolbar'] = $this->get_toolbar_actions();
564
565
				//we use the category "filter" option as specifier where we want to search (quick, subject, from, to, etc. ....)
566
				if (empty($content[self::$nm_index]['cat_id']) || empty($content[self::$nm_index]['search']))
567
				{
568
					$content[self::$nm_index]['cat_id']=($content[self::$nm_index]['cat_id']?(!Mail::$supportsORinQuery[$this->mail_bo->profileID]&&($content[self::$nm_index]['cat_id']=='quick'||$content[self::$nm_index]['cat_id']=='quickwithcc')?'subject':$content[self::$nm_index]['cat_id']):(Mail::$supportsORinQuery[$this->mail_bo->profileID]?'quick':'subject'));
569
				}
570
				$readonlys = $preserv = array();
571
				if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'',__METHOD__.__LINE__);
572
		}
573
		catch (Exception $e)
574
		{
575
			// do not exit here. mail-tree should be build. if we exit here, we never get there.
576
			error_log(__METHOD__.__LINE__.$e->getMessage().($e->details?', '.$e->details:'').' Menuaction:'.$_GET['menuaction'].'.'.function_backtrace());
0 ignored issues
show
Bug introduced by
The property details does not seem to exist in Exception.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
577
			if (isset($this->mail_bo))
578
			{
579
				if (empty($etpl))
580
				{
581
					$sel_options[self::$nm_index]['foldertree'] = $this->mail_tree->getInitialIndexTree(null, $this->mail_bo->profileID, null, !$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'],!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
582
					$etpl = new Etemplate('mail.index');
583
				}
584
				$etpl->setElementAttribute(self::$nm_index.'[foldertree]','actions', $this->get_tree_actions(false));
585
			}
586
			$readonlys = $preserv = array();
587
			if (empty($content)) $content=array();
588
589
			self::callWizard($e->getMessage().($e->details?', '.$e->details:''),(isset($this->mail_bo)?false:true), 'error',false);
590
			//return false;
591
		}
592
		// Check preview pane is enabled, then show splitter
593
		if ($this->mail_bo->mailPreferences['previewPane']) $etpl->setElementAttribute('splitter', 'template', 'mail.index.nosplitter');
594
595
		return $etpl->exec('mail.mail_ui.index',$content,$sel_options,$readonlys,$preserv);
596
	}
597
598
	/**
599
	 * Get tree actions / context menu for tree
600
	 *
601
	 * Changes here, may require to log out, as $content[self::$nm_index] get stored in session!
602
	 * @param {boolean} $imap_actions set to false if you want to avoid to talk to the imap-server
603
	 * @return array
604
	 */
605
	function get_tree_actions($imap_actions=true)
606
	{
607
		// Start at 2 so auto-added copy+paste actions show up as second group
608
		// Needed because there's no 'select all' action to push things down
609
		$group=1;
610
		// Set tree actions
611
		$tree_actions = array(
612
			'drop_move_mail' => array(
613
				'type' => 'drop',
614
				'acceptedTypes' => 'mail',
615
				'icon' => 'move',
616
				'caption' => 'Move to',
617
				'onExecute' => 'javaScript:app.mail.mail_move'
618
			),
619
			'drop_copy_mail' => array(
620
				'type' => 'drop',
621
				'acceptedTypes' => 'mail',
622
				'icon' => 'copy',
623
				'caption' => 'Copy to',
624
				'onExecute' => 'javaScript:app.mail.mail_copy'
625
			),
626
			'drop_cancel' => array(
627
				'icon' => 'cancel',
628
				'caption' => 'Cancel',
629
				'acceptedTypes' => 'mail',
630
				'type' => 'drop',
631
			),
632
			'drop_move_folder' => array(
633
				'caption' => 'Move folder',
634
				'hideOnDisabled' => true,
635
				'type' => 'drop',
636
				'acceptedTypes' => 'mailFolder',
637
				'onExecute' => 'javaScript:app.mail.mail_MoveFolder'
638
			),
639
			// Tree does support this one
640
			'add' => array(
641
				'caption' => 'Add Folder',
642
				'onExecute' => 'javaScript:app.mail.mail_AddFolder',
643
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
644
				'group'		=> $group,
645
			),
646
			'edit' => array(
647
				'caption' => 'Rename Folder',
648
				'onExecute' => 'javaScript:app.mail.mail_RenameFolder',
649
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
650
				'group'		=> $group,
651
			),
652
			'move' => array(
653
				'caption' => 'Move Folder',
654
				'type' => 'drag',
655
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
656
				'dragType' => array('mailFolder'),
657
				'group'		=> $group,
658
			),
659
			'delete' => array(
660
				'caption' => 'Delete Folder',
661
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
662
				'onExecute' => 'javaScript:app.mail.mail_DeleteFolder',
663
				'group'		=> $group,
664
			),
665
			'subscribe' => array(
666
				'caption' => 'Subscribe folder ...',
667
				//'icon' => 'configure',
668
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
669
				'onExecute' => 'javaScript:app.mail.edit_subscribe',
670
				'group'		=> $group
671
			),
672
			'unsubscribe' => array(
673
				'caption' => 'Unsubscribe folder',
674
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
675
				'onExecute' => 'javaScript:app.mail.unsubscribe_folder',
676
				'group'		=> $group,
677
			),
678
			'foldermanagement' => array(
679
				'caption' => 'Folder Management ...',
680
				'icon' => 'folder_management',
681
				'enabled'	=> 'javaScript:app.mail.mail_CheckFolderNoSelect',
682
				'onExecute' => 'javaScript:app.mail.folderManagement',
683
				'group'		=> $group,
684
				'hideOnMobile' => true
685
			),
686
			'sieve' => array(
687
				'caption' => 'Mail filter',
688
				'onExecute' => 'javaScript:app.mail.edit_sieve',
689
690
				'enabled'	=> 'javaScript:app.mail.sieve_enabled',
691
				'icon' => 'mail/filter',	// funnel
692
				'hideOnMobile' => true
693
			),
694
			'vacation' => array(
695
				'caption' => 'Vacation notice',
696
				'icon' => 'mail/navbar',	// mail as in admin
697
				'onExecute' => 'javaScript:app.mail.edit_vacation',
698
				'enabled'	=> 'javaScript:app.mail.sieve_enabled',
699
			),
700
			'edit_account' => array(
701
				'caption' => 'Edit account ...',
702
				'icon' => 'configure',
703
				'onExecute' => 'javaScript:app.mail.edit_account',
704
			),
705
			'edit_acl'	=> array(
706
				'caption' => 'Edit folder ACL ...',
707
				'icon'	=> 'lock',
708
				'enabled'	=> 'javaScript:app.mail.acl_enabled',
709
				'onExecute' => 'javaScript:app.mail.edit_acl',
710
			),
711
		);
712
		// the preference prefaskformove controls actually if there is a popup on target or not
713
		// if there are multiple options there is a popup on target, 0 for prefaskformove means
714
		// that only move is available; 1 stands for move and cancel; 2 (should be the default if
715
		// not set); so we are assuming this, when not set
716
		if (isset($this->mail_bo->mailPreferences['prefaskformove']))
717
		{
718
			switch ($this->mail_bo->mailPreferences['prefaskformove'])
719
			{
720
				case 0:
721
					unset($tree_actions['drop_copy_mail']);
722
					unset($tree_actions['drop_cancel']);
723
					break;
724
				case 1:
725
					unset($tree_actions['drop_copy_mail']);
726
					break;
727
				default:
728
					// everything is fine
729
			}
730
		}
731
		//error_log(__METHOD__.__LINE__.' showAllFoldersInFolderPane:'.$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'].'/'.$GLOBALS['egw_info']['user']['preferences']['mail']['showAllFoldersInFolderPane']);
732
		if ($this->mail_bo->mailPreferences['showAllFoldersInFolderPane'])
733
		{
734
			unset($tree_actions['subscribe']);
735
			unset($tree_actions['unsubscribe']);
736
		}
737
		++$group;	// put delete in own group
738
		switch($GLOBALS['egw_info']['user']['preferences']['mail']['deleteOptions'])
739
		{
740
			case 'move_to_trash':
741
				$tree_actions['empty_trash'] = array(
742
					'caption' => 'empty trash',
743
					'icon' => 'dhtmlxtree/MailFolderTrash',
744
					'onExecute' => 'javaScript:app.mail.mail_emptyTrash',
745
					'group'	=> $group,
746
				);
747
				break;
748
			case 'mark_as_deleted':
749
				$tree_actions['compress_folder'] = array(
750
					'caption' => 'compress folder',
751
					'icon' => 'dhtmlxtree/MailFolderTrash',
752
					'onExecute' => 'javaScript:app.mail.mail_compressFolder',
753
					'group'	=> $group,
754
				);
755
				break;
756
		}
757
		$junkFolder = ($imap_actions?$this->mail_bo->getJunkFolder():null);
758
759
		//error_log(__METHOD__.__LINE__.$junkFolder);
760
		if ($junkFolder && !empty($junkFolder))
761
		{
762
			$tree_actions['empty_spam'] = array(
763
				'caption' => 'empty junk',
764
				'icon' => 'dhtmlxtree/MailFolderJunk',
765
				'enabled'	=> 'javaScript:app.mail.spamfolder_enabled',
766
				'onExecute' => 'javaScript:app.mail.mail_emptySpam',
767
				'group'	=> $group,
768
			);
769
		}
770
		$tree_actions['sieve']['group']	= $tree_actions['vacation']['group'] = ++$group;	// new group for filter
771
		$tree_actions['edit_account']['group'] = $tree_actions['edit_acl']['group']	= ++$group;
772
773
774
		// enforce global (group-specific) ACL
775
		if (!mail_hooks::access('aclmanagement'))
776
		{
777
			unset($tree_actions['edit_acl']);
778
		}
779
		if (!mail_hooks::access('editfilterrules'))
780
		{
781
			unset($tree_actions['sieve']);
782
		}
783
		if (!mail_hooks::access('absentnotice'))
784
		{
785
			unset($tree_actions['vacation']);
786
		}
787
		if (!mail_hooks::access('managefolders'))
788
		{
789
			unset($tree_actions['add']);
790
			unset($tree_actions['move']);
791
			unset($tree_actions['delete']);
792
			unset($tree_actions['foldermanagement']);
793
			// manage folders should not affect the ability to subscribe or unsubscribe
794
			// to existing folders, it should only affect add/rename/move/delete
795
		}
796
		return $tree_actions;
797
	}
798
799
	/**
800
	 * Ajax callback to subscribe / unsubscribe a Mailbox of an account
801
	 *
802
	 * @param {int} $_acc_id profile Id of selected mailbox
803
	 * @param {string} $_folderName name of mailbox needs to be subcribe or unsubscribed
804
	 * @param {boolean} $_status set true for subscribe and false to unsubscribe
805
	 */
806
	public function ajax_foldersubscription($_acc_id,$_folderName, $_status)
807
	{
808
		//Change the Mail object to related profileId
809
		$this->changeProfile($_acc_id);
810
		try{
811
			$this->mail_bo->icServer->subscribeMailbox($_folderName, $_status);
812
			$this->mail_bo->resetFolderObjectCache($_acc_id);
813
			$this->ajax_reloadNode($_acc_id,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
814
		} catch (Horde_Imap_Client_Exception $ex) {
0 ignored issues
show
Bug introduced by
The class Horde_Imap_Client_Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
815
			error_log(__METHOD__.__LINE__."()". lang('Folder %1 %2 failed because of %3!',$_folderName,$_status?'subscribed':'unsubscribed', $ex));
816
			Framework::message(lang('Folder %1 %2 failed!',$_folderName,$_status));
817
		}
818
	}
819
820
	/**
821
	 * Ajax callback to fetch folders for given profile
822
	 *
823
	 * We currently load all folders of a given profile, tree can also load parts of a tree.
824
	 *
825
	 * @param string $_nodeID if of node whos children are requested
826
	 * @param boolean $_subscribedOnly flag to tell wether to fetch all or only subscribed (default)
827
	 */
828
	public function ajax_foldertree($_nodeID = null,$_subscribedOnly=null)
829
	{
830
		$nodeID = $_GET['id'];
831
		if (!is_null($_nodeID)) $nodeID = $_nodeID;
832
		$subscribedOnly = (bool)(!is_null($_subscribedOnly)?$_subscribedOnly:!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
833
		$fetchCounters = !is_null($_nodeID);
834
		list($_profileID,$_folderName) = explode(self::$delimiter,$nodeID,2);
835
836
		if (!empty($_folderName)) $fetchCounters = true;
837
838
		// Check if it is called for refresh root
839
		// then we need to reinitialized the index tree
840
		if(!$nodeID && !$_profileID)
841
		{
842
			$data = $this->mail_tree->getInitialIndexTree(null,null,null,null,true,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
843
		}
844
		else
845
		{
846
			$data = $this->mail_tree->getTree($nodeID,$_profileID,0, false,$subscribedOnly,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']);
847
		}
848
		if (!is_null($_nodeID)) return $data;
849
		Etemplate\Widget\Tree::send_quote_json($data);
850
	}
851
852
	/**
853
	 * findNode - helper function to return only a branch of the tree
854
	 *
855
	 * @param array $_out out array (to be searched)
856
	 * @param string $_nodeID node to search for
857
	 * @param boolean $childElements return node itself, or only its child items
858
	 * @return array structured subtree
859
	 */
860
	static function findNode($_out, $_nodeID, $childElements = false)
861
	{
862
		foreach($_out['item'] as $node)
863
		{
864
			if (strcmp($node['id'],$_nodeID)===0)
865
			{
866
				//error_log(__METHOD__.__LINE__.':'.$_nodeID.'->'.$node['id']);
867
				return ($childElements?$node['item']:$node);
868
			}
869
			elseif (is_array($node['item']) && strncmp($node['id'],$_nodeID,strlen($node['id']))===0 && strlen($_nodeID)>strlen($node['id']))
870
			{
871
				//error_log(__METHOD__.__LINE__.' descend into '.$node['id']);
872
				return self::findNode($node,$_nodeID,$childElements);
873
			}
874
		}
875
	}
876
877
878
	/**
879
	 * Get actions / context menu for index
880
	 *
881
	 * Changes here, require to log out, as $content[self::$nm_index] get stored in session!
882
	 * @return array see nextmatch_widget::egw_actions()
883
	 */
884
	private function get_actions()
885
	{
886
		static $accArray=array(); // buffer identity names on single request
887
		// duplicated from mail_hooks
888
		static $deleteOptions = array(
889
			'move_to_trash'		=> 'move to trash',
890
			'mark_as_deleted'	=> 'mark as deleted',
891
			'remove_immediately' =>	'remove immediately',
892
		);
893
		// todo: real hierarchical folder list
894
		$lastFolderUsedForMove = null;
895
		$moveactions = array();
896
		$archiveFolder = $this->mail_bo->getArchiveFolder();
897
		$lastFoldersUsedForMoveCont = Api\Cache::getCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),null,array(),$expiration=60*60*1);
898
		//error_log(__METHOD__.__LINE__." StoredFolders->".array2string($lastFoldersUsedForMoveCont));
899
		//error_log(__METHOD__.__LINE__.' ProfileId:'.$this->mail_bo->profileID." StoredFolders->(".count($lastFoldersUsedForMoveCont[$this->mail_bo->profileID]).") ".array2string($lastFoldersUsedForMoveCont[$this->mail_bo->profileID]));
900
		if (is_null($accArray))
901
		{
902
			foreach(Mail\Account::search($only_current_user=true, false) as $acc_id => $accountObj)
903
			{
904
				//error_log(__METHOD__.__LINE__.array2string($accountObj));
905
				if (!$accountObj->is_imap())
0 ignored issues
show
Bug introduced by
The method is_imap cannot be called on $accountObj (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
906
				{
907
					// not to be used for IMAP Foldertree, as there is no Imap host
908
					continue;
909
				}
910
				$identity_name = Mail\Account::identity_name($accountObj,true,$GLOBALS['egw_info']['user']['acount_id']);
911
				$accArray[$acc_id] = str_replace(array('<','>'),array('[',']'),$identity_name);// as angle brackets are quoted, display in Javascript messages when used is ugly, so use square brackets instead
912
			}
913
		}
914
		if (!is_array($lastFoldersUsedForMoveCont)) $lastFoldersUsedForMoveCont=array();
915
		foreach (array_keys($lastFoldersUsedForMoveCont) as $pid)
916
		{
917
			if ($this->mail_bo->profileID==$pid && isset($lastFoldersUsedForMoveCont[$this->mail_bo->profileID]))
918
			{
919
				$_folder = $this->mail_bo->icServer->getCurrentMailbox();
920
				//error_log(__METHOD__.__LINE__.' '.$_folder."<->".$lastFoldersUsedForMoveCont[$this->mail_bo->profileID].function_backtrace());
921
				$counter =1;
922
				foreach ($lastFoldersUsedForMoveCont[$this->mail_bo->profileID] as $i => $lastFolderUsedForMoveCont)
923
				{
924
					$moveaction = 'move_';
925
					if ($_folder!=$i)
926
					{
927
						$moveaction .= $lastFolderUsedForMoveCont;
928
						//error_log(__METHOD__.__LINE__.'#'.$moveaction);
929
						//error_log(__METHOD__.__LINE__.'#'.$currentArchiveActionKey);
930
						if ($this->mail_bo->folderExists($i)) // only 10 entries per mailaccount.Control this on setting the buffered folders
931
						{
932
							$fS['profileID'] = $this->mail_bo->profileID;
933
							$fS['profileName'] = $accArray[$this->mail_bo->profileID];
934
							$fS['shortDisplayName'] = $i;
935
							$moveactions[$moveaction] = $fS;
936
							$counter ++;
937
						}
938
						else
939
						{
940
							unset($lastFoldersUsedForMoveCont[$this->mail_bo->profileID][$i]);
941
						}
942
						//error_log(array2string($moveactions[$moveaction]));
943
					}
944
				}
945
			}
946
			elseif ($this->mail_bo->profileID!=$pid && isset($lastFoldersUsedForMoveCont[$pid]) && !empty($lastFoldersUsedForMoveCont[$pid]))
947
			{
948
				$counter =1;
949
				foreach ($lastFoldersUsedForMoveCont[$pid] as $i => $lastFolderUsedForMoveCont)
950
				{
951
					//error_log(__METHOD__.__LINE__."$i => $lastFolderUsedForMoveCont");
952
					if (!empty($lastFolderUsedForMoveCont)) // only 10 entries per mailaccount.Control this on setting the buffered folders
953
					{
954
						$moveaction = 'move_'.$lastFolderUsedForMoveCont;
955
						//error_log(__METHOD__.__LINE__.'#'.$moveaction);
956
						$fS = array();
957
						$fS['profileID'] = $pid;
958
						$fS['profileName'] = $accArray[$pid];
959
						$fS['shortDisplayName'] = $i;
960
						$moveactions[$moveaction] = $fS;
961
						$counter ++;
962
					}
963
				}
964
			}
965
		}
966
		Api\Cache::setCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),$lastFoldersUsedForMoveCont, $expiration=60*60*1);
967
		$group = 0;
968
		$actions =  array(
969
			'open' => array(
970
				'caption' => lang('Open'),
971
				'icon' => 'view',
972
				'group' => ++$group,
973
				'onExecute' => Api\Header\UserAgent::mobile()?'javaScript:app.mail.mobileView':'javaScript:app.mail.mail_open',
974
				'allowOnMultiple' => false,
975
				'default' => true,
976
				'mobileViewTemplate' => 'view?'.filemtime(Api\Etemplate\Widget\Template::rel2path('/mail/templates/mobile/view.xet'))
977
			),
978
			'reply' => array(
979
				'caption' => 'Reply',
980
				'icon' => 'mail_reply',
981
				'group' => ++$group,
982
				'onExecute' => 'javaScript:app.mail.mail_compose',
983
				'allowOnMultiple' => false,
984
				'toolbarDefault' => true
985
			),
986
			'reply_all' => array(
987
				'caption' => 'Reply All',
988
				'icon' => 'mail_replyall',
989
				'group' => $group,
990
				'onExecute' => 'javaScript:app.mail.mail_compose',
991
				'allowOnMultiple' => false,
992
			),
993
			'forward' => array(
994
				'caption' => 'Forward',
995
				'icon' => 'mail_forward',
996
				'group' => $group,
997
				'children' => array(
998
					'forwardinline' => array(
999
						'caption' => 'Inline',
1000
						'icon' => 'mail_forward',
1001
						'group' => $group,
1002
						'hint' => 'forward inline',
1003
						'onExecute' => 'javaScript:app.mail.mail_compose',
1004
						'allowOnMultiple' => false,
1005
						'toolbarDefault' => true
1006
					),
1007
					'forwardasattach' => array(
1008
						'caption' => 'Attachment',
1009
						'hint' => 'forward as attachment',
1010
						'icon' => 'mail_forward_attach',
1011
						'group' => $group,
1012
						'onExecute' => 'javaScript:app.mail.mail_compose',
1013
					),
1014
				),
1015
				'hideOnMobile' => true
1016
			),
1017
			'composeasnew' => array(
1018
				'caption' => 'Compose',
1019
				'icon' => 'new',
1020
				'hint' => 'Compose as new',
1021
				'group' => $group,
1022
				'onExecute' => 'javaScript:app.mail.mail_compose',
1023
				'allowOnMultiple' => false,
1024
			)
1025
		);
1026
		$macounter=0;
1027
		if (!empty($moveactions))
1028
		{
1029
			//error_log(__METHOD__.__LINE__.array2string($moveactions));
1030
			$children=array();
1031
			$pID=0;
1032
			foreach ($moveactions as $moveaction => $lastFolderUsedForMove)
1033
			{
1034
				$group = ($pID != $lastFolderUsedForMove['profileID'] && $macounter>0? $group+1 : $group);
1035
				//error_log(__METHOD__.__LINE__."#$pID != ".$lastFolderUsedForMove['profileID']."#".$macounter.'#'.$groupCounter.'#');
1036
				$children = array_merge($children,
1037
					array(
1038
						$moveaction => array(
1039
							'caption' => (!empty($lastFolderUsedForMove['profileName'])?$lastFolderUsedForMove['profileName']:'('.$lastFolderUsedForMove['profileID'].')').': '.(isset($lastFolderUsedForMove['shortDisplayName'])?$lastFolderUsedForMove['shortDisplayName']:''),
1040
							'icon' => 'move',
1041
							'group' => $group,
1042
							'onExecute' => 'javaScript:app.mail.mail_move2folder',
1043
							'allowOnMultiple' => true,
1044
						)
1045
					)
1046
				);
1047
				$pID = $lastFolderUsedForMove['profileID'];
1048
				$macounter++;
1049
			}
1050
			$actions['moveto'] =	array(
1051
				'caption' => lang('Move selected to'),
1052
				'icon' => 'move',
1053
				'group' => $group,
1054
				'children' => $children,
1055
			);
1056
1057
		} else {
1058
			$group++;
1059
		}
1060
1061
		//error_log(__METHOD__.__LINE__.$archiveFolder);
1062
		$actions['move2'.$this->mail_bo->profileID.self::$delimiter.$archiveFolder] = array( //toarchive
1063
			'caption' => 'Move to archive',
1064
			'hint' => 'move selected mails to archive',
1065
			'icon' => 'archive',
1066
			'group' => $group++,
1067
			'enabled' => 'javaScript:app.mail.archivefolder_enabled',
1068
			//'hideOnDisabled' => true, // does not work as expected on message-list
1069
			'onExecute' => 'javaScript:app.mail.mail_move2folder',
1070
			'shortcut' => KeyManager::shortcut(KeyManager::V, true, true),
1071
			'allowOnMultiple' => true,
1072
			'toolbarDefault' => false
1073
		);
1074
1075
		$actions += array(
1076
			'infolog' => array(
1077
				'caption' => 'InfoLog',
1078
				'hint' => 'Save as InfoLog',
1079
				'icon' => 'infolog/navbar',
1080
				'group' => ++$group,
1081
				'onExecute' => 'javaScript:app.mail.mail_integrate',
1082
				'popup' => Link::get_registry('infolog', 'add_popup'),
1083
				'allowOnMultiple' => false,
1084
				'toolbarDefault' => true
1085
			),
1086
			'tracker' => array(
1087
				'caption' => 'Tracker',
1088
				'hint' => 'Save as ticket',
1089
				'group' => $group,
1090
				'icon' => 'tracker/navbar',
1091
				'onExecute' => 'javaScript:app.mail.mail_integrate',
1092
				'popup' => Link::get_registry('tracker', 'add_popup'),
1093
				'mail_import' => Api\Hooks::single(array('location' => 'mail_import'),'tracker'),
1094
				'allowOnMultiple' => false,
1095
			),
1096
			'calendar' => array(
1097
				'caption' => 'Calendar',
1098
				'hint' => 'Save as Calendar',
1099
				'icon' => 'calendar/navbar',
1100
				'group' => $group,
1101
				'onExecute' => 'javaScript:app.mail.mail_integrate',
1102
				'popup' => Link::get_registry('calendar', 'add_popup'),
1103
				'allowOnMultiple' => false,
1104
				'toolbarDefault' => true
1105
			),
1106
			'print' => array(
1107
				'caption' => 'Print',
1108
				'group' => ++$group,
1109
				'onExecute' => 'javaScript:app.mail.mail_print',
1110
				'allowOnMultiple' => false,
1111
				'hideOnMobile' => true
1112
			),
1113
			'save' => array(
1114
				'caption' => 'Save',
1115
				'group' => $group,
1116
				'icon' => 'fileexport',
1117
				'children' => array(
1118
					'save2disk' => array(
1119
						'caption' => 'Save to disk',
1120
						'hint' => 'Save message to disk',
1121
						'group' => $group,
1122
						'icon' => 'fileexport',
1123
						'onExecute' => 'javaScript:app.mail.mail_save',
1124
						'allowOnMultiple' => false,
1125
						'hideOnMobile' => true
1126
					),
1127
					'save2filemanager' => array(
1128
						'caption' => 'Filemanager',
1129
						'hint' => 'Save to filemanager',
1130
						'group' => $group,
1131
						'icon' => 'filemanager/navbar',
1132
						'onExecute' => 'javaScript:app.mail.mail_save2fm',
1133
						'allowOnMultiple' => false,
1134
					),
1135
				),
1136
				'hideOnMobile' => true
1137
			),
1138
			'view' => array(
1139
				'caption' => 'View',
1140
				'group' => $group,
1141
				'icon' => 'kmmsgread',
1142
				'children' => array(
1143
					'header' => array(
1144
						'caption' => 'Header',
1145
						'hint' => 'View header lines',
1146
						'group' => $group,
1147
						'icon' => 'kmmsgread',
1148
						'onExecute' => 'javaScript:app.mail.mail_header',
1149
						'allowOnMultiple' => false,
1150
					),
1151
					'mailsource' => array(
1152
						'caption' => 'Source',
1153
						'hint' => 'View full Mail Source',
1154
						'group' => $group,
1155
						'icon' => 'source',
1156
						'onExecute' => 'javaScript:app.mail.mail_mailsource',
1157
						'allowOnMultiple' => false,
1158
					),
1159
					'openastext' => array(
1160
						'caption' => lang('Text mode'),
1161
						'hint' => 'Open in Text mode',
1162
						'group' => ++$group,
1163
						'icon' => 'textmode',
1164
						'onExecute' => 'javaScript:app.mail.mail_openAsText',
1165
						'allowOnMultiple' => false,
1166
					),
1167
					'openashtml' => array(
1168
						'caption' => lang('HTML mode'),
1169
						'hint' => 'Open in HTML mode',
1170
						'group' => $group,
1171
						'icon' => 'htmlmode',
1172
						'onExecute' => 'javaScript:app.mail.mail_openAsHtml',
1173
						'allowOnMultiple' => false,
1174
					),
1175
				),
1176
				'hideOnMobile' => true
1177
			),
1178
			'mark' => array(
1179
				'caption' => 'Set / Remove Flags',
1180
				'icon' => 'read_small',
1181
				'group' => ++$group,
1182
				'children' => array(
1183
					// icons used from http://creativecommons.org/licenses/by-sa/3.0/
1184
					// Artist: Led24
1185
					// Iconset Homepage: http://led24.de/iconset
1186
					// License: CC Attribution 3.0
1187
					'setLabel' => array(
1188
						'caption' => 'Set / Remove Labels',
1189
						'icon' => 'tag_message',
1190
						'group' => ++$group,
1191
						// note this one is NOT a real CAPABILITY reported by the server, but added by selectMailbox
1192
						'enabled' => $this->mail_bo->icServer->hasCapability('SUPPORTS_KEYWORDS'),
1193
						'hideOnDisabled' => true,
1194
						'children' => array(
1195
							'unlabel' => array(
1196
								'group' => ++$group,
1197
								'caption' => "<font color='#ff0000'>".lang('remove all')."</font>",
1198
								'icon' => 'mail_label',
1199
								'onExecute' => 'javaScript:app.mail.mail_flag',
1200
								'shortcut' => KeyManager::shortcut(KeyManager::_0, true, true),
1201
							),
1202
							'label1' => array(
1203
								'group' => ++$group,
1204
								'caption' => "<font color='#ff0000'>".lang('important')."</font>",
1205
								'icon' => 'mail_label1',
1206
								'onExecute' => 'javaScript:app.mail.mail_flag',
1207
								'shortcut' => KeyManager::shortcut(KeyManager::_1, true, true),
1208
							),
1209
							'label2' => array(
1210
								'group' => $group,
1211
								'caption' => "<font color='#ff8000'>".lang('job')."</font>",
1212
								'icon' => 'mail_label2',
1213
								'onExecute' => 'javaScript:app.mail.mail_flag',
1214
								'shortcut' => KeyManager::shortcut(KeyManager::_2, true, true),
1215
							),
1216
							'label3' => array(
1217
								'group' => $group,
1218
								'caption' => "<font color='#008000'>".lang('personal')."</font>",
1219
								'icon' => 'mail_label3',
1220
								'onExecute' => 'javaScript:app.mail.mail_flag',
1221
								'shortcut' => KeyManager::shortcut(KeyManager::_3, true, true),
1222
							),
1223
							'label4' => array(
1224
								'group' => $group,
1225
								'caption' => "<font color='#0000ff'>".lang('to do')."</font>",
1226
								'icon' => 'mail_label4',
1227
								'onExecute' => 'javaScript:app.mail.mail_flag',
1228
								'shortcut' => KeyManager::shortcut(KeyManager::_4, true, true),
1229
							),
1230
							'label5' => array(
1231
								'group' => $group,
1232
								'caption' => "<font color='#8000ff'>".lang('later')."</font>",
1233
								'icon' => 'mail_label5',
1234
								'onExecute' => 'javaScript:app.mail.mail_flag',
1235
								'shortcut' => KeyManager::shortcut(KeyManager::_5, true, true),
1236
							),
1237
						),
1238
					),
1239
					// modified icons from http://creativecommons.org/licenses/by-sa/3.0/
1240
					'flagged' => array(
1241
						'group' => ++$group,
1242
						'caption' => 'Flag / Unflag',
1243
						'icon' => 'unread_flagged_small',
1244
						'onExecute' => 'javaScript:app.mail.mail_flag',
1245
						'hint' => 'Flag or Unflag a mail',
1246
						'shortcut' => KeyManager::shortcut(KeyManager::F, true, true),
1247
						'toolbarDefault' => true
1248
					),
1249
					'read' => array(
1250
						'group' => $group,
1251
						'caption' => 'Read / Unread',
1252
						'icon' => 'read_small',
1253
						'onExecute' => 'javaScript:app.mail.mail_flag',
1254
						'shortcut' => KeyManager::shortcut(KeyManager::U, true, true),
1255
1256
					),
1257
					'readall' => array(
1258
						'group' => ++$group,
1259
						'caption' => "<font color='#ff0000'>".lang('mark all as read')."</font>",
1260
						'icon' => 'read_small',
1261
						'onExecute' => 'javaScript:app.mail.mail_flag',
1262
						'hint' => 'mark all messages in folder as read',
1263
						'toolbarDefault' => false
1264
					),
1265
					'undelete' => array(
1266
						'group' => $group,
1267
						'caption' => 'Undelete',
1268
						'icon' => 'revert',
1269
						'onExecute' => 'javaScript:app.mail.mail_flag',
1270
					),
1271
				),
1272
			),
1273
			'delete' => array(
1274
				'caption' => 'Delete',
1275
				'hint' => $deleteOptions[$this->mail_bo->mailPreferences['deleteOptions']],
1276
				'group' => ++$group,
1277
				'onExecute' => 'javaScript:app.mail.mail_delete',
1278
				'toolbarDefault' => true
1279
			),
1280
			'drag_mail' => array(
1281
				'dragType' => array('mail'),
1282
				'type' => 'drag',
1283
				//'onExecute' => 'javaScript:app.mail.mail_dragStart',
1284
			)
1285
		);
1286
		//error_log(__METHOD__.__LINE__.array2string(array_keys($actions)));
1287
		// save as tracker, save as infolog, as this are actions that are either available for all, or not, we do that for all and not via css-class disabling
1288 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['apps']['infolog']))
1289
		{
1290
			unset($actions['infolog']);
1291
		}
1292 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['apps']['tracker']))
1293
		{
1294
			unset($actions['tracker']);
1295
		}
1296 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['apps']['calendar']))
1297
		{
1298
			unset($actions['calendar']);
1299
		}
1300
		return $actions;
1301
	}
1302
1303
	/**
1304
	 * Callback to fetch the rows for the nextmatch widget
1305
	 *
1306
	 * Function is static to not automatic call constructor in case profile is changed.
1307
	 *
1308
	 * @param array $query
1309
	 * @param array &$rows
1310
	 * @param array &$readonlys
1311
	 */
1312
	public static function get_rows(&$query,&$rows,&$readonlys)
1313
	{
1314
		unset($readonlys);	// not used, but required by function signature
1315
1316
		// handle possible profile change in get_rows
1317
		if (!empty($query['selectedFolder']))
1318
		{
1319
			list($_profileID,$folderName) = explode(self::$delimiter, $query['selectedFolder'], 2);
1320
			if (is_numeric(($_profileID)) && $_profileID != $GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'])
1321
			{
1322
				try {
1323
					$mail_ui = new mail_ui(false);	// do NOT run constructor, as we change profile anyway
1324
					$mail_ui->changeProfile($_profileID);
1325
					$query['actions'] = $mail_ui->get_actions();
1326
				}
1327
				catch(Exception $e)
1328
				{
1329
					unset($e);
1330
					$rows=array();
1331
					return 0;
1332
				}
1333
				if (empty($folderName)) $query['selectedFolder'] = $_profileID.self::$delimiter.'INBOX';
1334
			}
1335
		}
1336
		if (!isset($mail_ui))
1337
		{
1338
			try
1339
			{
1340
				$mail_ui = new mail_ui(true);	// run constructor for current profile
1341
			}
1342
			catch(Exception $e)
1343
			{
1344
				unset($e);
1345
				$rows=array();
1346
				return 0;
1347
			}
1348
			if (empty($query['selectedFolder'])) $query['selectedFolder'] = $mail_ui->mail_bo->profileID.self::$delimiter.'INBOX';
1349
		}
1350
		//error_log(__METHOD__.__LINE__.' SelectedFolder:'.$query['selectedFolder'].' Start:'.$query['start'].' NumRows:'.$query['num_rows'].array2string($query['order']).'->'.array2string($query['sort']));
1351
		//Mail::$debugTimes=true;
1352
		if (Mail::$debugTimes) $starttime = microtime(true);
1353
		//$query['search'] is the phrase in the searchbox
1354
1355
		$mail_ui->mail_bo->restoreSessionData();
1356
		if (isset($query['selectedFolder'])) $mail_ui->mail_bo->sessionData['mailbox']=$query['selectedFolder'];
1357
1358
		$sRToFetch = null;
1359
		list($_profileID,$_folderName) = explode(self::$delimiter,$query['selectedFolder'],2);
0 ignored issues
show
Unused Code introduced by
The assignment to $_profileID is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
1360
		if (strpos($_folderName,self::$delimiter)!==false)
1361
		{
1362
			list($app,$_profileID,$_folderName) = explode(self::$delimiter,$_folderName,3);
0 ignored issues
show
Unused Code introduced by
The assignment to $_profileID is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
1363
			unset($app);
1364
		}
1365
		//save selected Folder to sessionData (mailbox)->currentFolder
1366
		if (isset($query['selectedFolder'])) $mail_ui->mail_bo->sessionData['mailbox']=$_folderName;
1367
		$toSchema = false;//decides to select list schema with column to selected (if false fromaddress is default)
1368
		if ($mail_ui->mail_bo->folderExists($_folderName))
1369
		{
1370
			$toSchema = $mail_ui->mail_bo->isDraftFolder($_folderName,false)||$mail_ui->mail_bo->isSentFolder($_folderName,false)||$mail_ui->mail_bo->isTemplateFolder($_folderName,false);
1371
		}
1372
		else
1373
		{
1374
			// take the extra time on failure
1375
			if (!$mail_ui->mail_bo->folderExists($_folderName,true))
1376
			{
1377
				//error_log(__METHOD__.__LINE__.' Test on Folder:'.$_folderName.' failed; Using INBOX instead');
1378
				$query['selectedFolder']=$mail_ui->mail_bo->sessionData['mailbox']=$_folderName='INBOX';
1379
			}
1380
		}
1381
		$mail_ui->mail_bo->saveSessionData();
1382
		$rowsFetched['messages'] = null;
1383
		$offset = $query['start']+1; // we always start with 1
1384
		$maxMessages = $query['num_rows'];
1385
		//error_log(__METHOD__.__LINE__.array2string($query));
1386
		$sort = ($query['order']=='address'?($toSchema?'toaddress':'fromaddress'):$query['order']);
1387
		if (!empty($query['search'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
1388
		{
1389
			if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]))
1390
			{
1391
				Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
1392
				if (!isset(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]))
1393
				{
1394
					Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]=true;
1395
				}
1396
			}
1397
			//error_log(__METHOD__.__LINE__.' Startdate:'.$query['startdate'].' Enddate'.$query['enddate']);
1398
			$cutoffdate = $cutoffdate2 = null;
1399
			if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
1400
			if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
1401
			//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
1402
			$filter = array(
1403
				'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
1404
				'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
1405
				'string' => $query['search'],
1406
				'status' => 'any',
1407
				//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
1408
			);
1409
			if ($query['enddate']||$query['startdate']) {
1410
				$filter['range'] = "BETWEEN";
1411
				if ($cutoffdate) {
1412
					$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
1413
					if (empty($cutoffdate2)) $filter['range'] = "SINCE";
1414
				}
1415
				if ($cutoffdate2) {
1416
					$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
1417
					if (empty($cutoffdate)) $filter['range'] = "BEFORE";
1418
				}
1419
			}
1420
		}
1421
		else
1422
		{
1423
			$filter = array();
1424
		}
1425
		if ($query['filter'])
1426
		{
1427
			$filter['status'] = $query['filter'];
1428
		}
1429
		$reverse = ($query['sort']=='ASC'?false:true);
1430
		$prefchanged = false;
1431 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['preferences']['mail']['ActiveSearchType']) || ($query['cat_id'] !=$GLOBALS['egw_info']['user']['preferences']['mail']['ActiveSearchType']))
1432
		{
1433
			//error_log(__METHOD__.__LINE__.' Changing userPref ActivesearchType:'.$query['cat_id']);
1434
			$GLOBALS['egw']->preferences->add('mail','ActiveSearchType',$query['cat_id'],'user');
1435
			$prefchanged = true;
1436
		}
1437 View Code Duplication
		if (!isset($GLOBALS['egw_info']['user']['preferences']['mail']['ShowDetails']) || ($query['filter2'] !=$GLOBALS['egw_info']['user']['preferences']['mail']['ShowDetails']))
1438
		{
1439
			$GLOBALS['egw']->preferences->add('mail','ShowDetails',$query['filter2'],'user');
1440
			$prefchanged = true;
1441
		}
1442
		if ($prefchanged)
1443
		{
1444
			// save prefs
1445
			$GLOBALS['egw']->preferences->save_repository(true);
1446
		}
1447
		//error_log(__METHOD__.__LINE__.' maxMessages:'.$maxMessages.' Offset:'.$offset.' Filter:'.array2string($mail_ui->sessionData['messageFilter']));
1448
/*
1449
$cutoffdate = Api\DateTime::to('now','ts')-(3600*24*6);//SINCE, enddate
1450
$cutoffdate2 = Api\DateTime::to('now','ts')-(3600*24*3);//BEFORE, startdate
1451
$filter['range'] = "BETWEEN";// we support SINCE, BEFORE, BETWEEN and ON
1452
$filter['since'] = date("d-M-Y", $cutoffdate);
1453
$filter['before']= date("d-M-Y", $cutoffdate2);
1454
*/
1455
		try
1456
		{
1457
			if ($maxMessages > 75)
1458
			{
1459
				$rByUid = true;
1460
				$_sR = $mail_ui->mail_bo->getSortedList(
1461
					$_folderName,
1462
					$sort,
1463
					$reverse,
1464
					$filter,
1465
					$rByUid
1466
				);
1467
				$rowsFetched['messages'] = $_sR['count'];
1468
				$ids = $_sR['match']->ids;
1469
				// if $sR is false, something failed fundamentally
1470
				if($reverse === true) $ids = ($ids===false?array():array_reverse((array)$ids));
1471
				$sR = array_slice((array)$ids,($offset==0?0:$offset-1),$maxMessages); // we need only $maxMessages of uids
1472
				$sRToFetch = $sR;//array_slice($sR,0,50); // we fetch only the headers of a subset of the fetched uids
1473
				//error_log(__METHOD__.__LINE__.' Rows fetched (UID only):'.count($sR).' Data:'.array2string($sR));
1474
				$maxMessages = 75;
1475
				$sortResultwH['header'] = array();
1476
				if (count($sRToFetch)>0)
1477
				{
1478
					//error_log(__METHOD__.__LINE__.' Headers to fetch with UIDs:'.count($sRToFetch).' Data:'.array2string($sRToFetch));
1479
					$sortResult = array();
1480
					// fetch headers
1481
					$sortResultwH = $mail_ui->mail_bo->getHeaders(
1482
						$_folderName,
1483
						$offset,
1484
						$maxMessages,
1485
						$sort,
1486
						$reverse,
1487
						$filter,
1488
						$sRToFetch,
1489
						true, //cacheResult
1490
						($query['filter2']?true:false) // fetchPreview
1491
					);
1492
				}
1493
			}
1494
			else
1495
			{
1496
				$sortResult = array();
1497
				// fetch headers
1498
				$sortResultwH = $mail_ui->mail_bo->getHeaders(
1499
					$_folderName,
1500
					$offset,
1501
					$maxMessages,
1502
					$sort,
1503
					$reverse,
1504
					$filter,
1505
					null, // this uids only
1506
					true, // cacheResult
1507
					($query['filter2']?true:false) // fetchPreview
1508
				);
1509
				$rowsFetched['messages'] = $sortResultwH['info']['total'];
1510
			}
1511
		}
1512
		catch (Exception $e)
1513
		{
1514
			$sortResultwH=array();
1515
			$sR=array();
1516
			self::callWizard($e->getMessage(), false, 'error');
1517
		}
1518
		$response = Api\Json\Response::get();
1519
		// unlock immediately after fetching the rows
1520
		if (stripos($_GET['menuaction'],'ajax_get_rows')!==false)
1521
		{
1522
			//error_log(__METHOD__.__LINE__.' unlock tree ->'.$_GET['menuaction']);
1523
			$response->call('app.mail.unlock_tree');
1524
		}
1525
1526
		if (is_array($sR) && count($sR)>0)
1527
		{
1528
			foreach ((array)$sR as $key => $v)
1529
			{
1530
				if (array_key_exists($key,(array)$sortResultwH['header'])==true)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1531
				{
1532
					$sortResult['header'][] = $sortResultwH['header'][$key];
1533
				}
1534
				else
1535
				{
1536
					if (!empty($v)) $sortResult['header'][] = array('uid'=>$v);
1537
				}
1538
			}
1539
		}
1540
		else
1541
		{
1542
			$sortResult = $sortResultwH;
1543
		}
1544
		$rowsFetched['rowsFetched'] = count($sortResult['header']);
1545
		if (empty($rowsFetched['messages'])) $rowsFetched['messages'] = $rowsFetched['rowsFetched'];
1546
1547
		//error_log(__METHOD__.__LINE__.' Rows fetched:'.$rowsFetched.' Data:'.array2string($sortResult));
1548
		$cols = array('row_id','uid','status','attachments','subject','address','toaddress','fromaddress','ccaddress','additionaltoaddress','date','size','modified','bodypreview');
1549
		if ($GLOBALS['egw_info']['user']['preferences']['common']['select_mode']=='EGW_SELECTMODE_TOGGLE') unset($cols[0]);
1550
		$rows = $mail_ui->header2gridelements($sortResult['header'],$cols, $_folderName, $folderType=$toSchema);
1551
		//error_log(__METHOD__.__LINE__.array2string($rows));
1552
1553
		if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'Folder:'.$_folderName.' Start:'.$query['start'].' NumRows:'.$query['num_rows'],__METHOD__.__LINE__);
1554
		return $rowsFetched['messages'];
1555
	}
1556
1557
	/**
1558
	 * function createRowID - create a unique rowID for the grid
1559
	 *
1560
	 * @param string $_folderName used to ensure the uniqueness of the uid over all folders
1561
	 * @param string $message_uid the message_Uid to be used for creating the rowID
1562
	 * @param boolean $_prependApp to indicate that the app 'mail' is to be used for creating the rowID
1563
	 * @return string - a colon separated string in the form [app:]accountID:profileID:folder:message_uid
1564
	 */
1565
	function createRowID($_folderName, $message_uid, $_prependApp=false)
1566
	{
1567
		return self::generateRowID($this->mail_bo->profileID, $_folderName, $message_uid, $_prependApp);
1568
	}
1569
1570
	/**
1571
	 * static function generateRowID - create a unique rowID for the grid
1572
	 *
1573
	 * @param integer $_profileID profile ID for the rowid to be used
1574
	 * @param string $_folderName to ensure the uniqueness of the uid over all folders
1575
	 * @param string $message_uid the message_Uid to be used for creating the rowID
1576
	 * @param boolean $_prependApp to indicate that the app 'mail' is to be used for creating the rowID
1577
	 * @return string - a colon separated string in the form [app:]accountID:profileID:folder:message_uid
1578
	 */
1579
	static function generateRowID($_profileID, $_folderName, $message_uid, $_prependApp=false)
1580
	{
1581
		return ($_prependApp?'mail'.self::$delimiter:'').trim($GLOBALS['egw_info']['user']['account_id']).self::$delimiter.$_profileID.self::$delimiter.base64_encode($_folderName).self::$delimiter.$message_uid;
1582
	}
1583
1584
	/**
1585
	 * function splitRowID - split the rowID into its parts
1586
	 *
1587
	 * @param string $_rowID string - a colon separated string in the form accountID:profileID:folder:message_uid
1588
	 * @return array populated named result array (accountID,profileID,folder,msgUID)
1589
	 */
1590
	static function splitRowID($_rowID)
1591
	{
1592
		$res = explode(self::$delimiter,$_rowID);
1593
		// as a rowID is perceeded by app::, should be mail!
1594
		//error_log(__METHOD__.__LINE__.array2string($res).' [0] isInt:'.is_int($res[0]).' [0] isNumeric:'.is_numeric($res[0]).' [0] isString:'.is_string($res[0]).' Count:'.count($res));
1595
		if (count($res)==4 && is_numeric($res[0]) )
1596
		{
1597
			// we have an own created rowID; prepend app=mail
1598
			array_unshift($res,'mail');
1599
		}
1600
		return array('app'=>$res[0], 'accountID'=>$res[1], 'profileID'=>$res[2], 'folder'=>base64_decode($res[3]), 'msgUID'=>$res[4]);
1601
	}
1602
1603
	/**
1604
	 * Get actions for preview toolbar
1605
	 *
1606
	 * @return array
1607
	 */
1608
	function get_toolbar_actions()
1609
	{
1610
		$actions = $this->get_actions();
1611
		$arrActions = array('composeasnew', 'reply', 'reply_all', 'forward', 'flagged', 'delete', 'print',
1612
			'infolog', 'tracker', 'calendar', 'save', 'view', 'read', 'label1',	'label2', 'label3',	'label4', 'label5');
1613
		foreach( $arrActions as &$act)
1614
		{
1615
			//error_log(__METHOD__.__LINE__.' '.$act.'->'.array2string($actions[$act]));
1616
			switch ($act)
1617
			{
1618
				case 'forward':
1619
					$actionsenabled[$act]=$actions[$act];
1620
					break;
1621
				case 'save':
1622
					$actionsenabled[$act]=$actions[$act];
1623
1624
					break;
1625
				case 'view':
1626
					$actionsenabled[$act]=$actions[$act];
1627
					break;
1628
				case 'flagged':
1629
					$actionsenabled[$act]= $actions['mark']['children'][$act];
1630
					break;
1631
				case 'read':
1632
					$actionsenabled[$act]= $actions['mark']['children'][$act];
1633
					break;
1634 View Code Duplication
				case 'label1':
1635
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('important');
1636
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1637
					break;
1638 View Code Duplication
				case 'label2':
1639
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('job');
1640
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1641
					break;
1642 View Code Duplication
				case 'label3':
1643
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('personal');
1644
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1645
					break;
1646 View Code Duplication
				case 'label4':
1647
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('to do');
1648
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1649
					break;
1650 View Code Duplication
				case 'label5':
1651
					$actions['mark']['children']['setLabel']['children'][$act]['caption'] = lang('later');
1652
					$actionsenabled[$act]= $actions['mark']['children']['setLabel']['children'][$act];
1653
					break;
1654
				default:
1655
					if (isset($actions[$act])) $actionsenabled[$act]=$actions[$act];
1656
			}
1657
		}
1658
		unset($actionsenabled['drag_mail']);
1659
		//error_log(array2string($actionsenabled['view']));
1660
		unset($actionsenabled['view']['children']['openastext']);//not supported in preview
1661
		unset($actionsenabled['view']['children']['openashtml']);//not supported in preview
1662
1663
		return $actionsenabled;
1664
	}
1665
1666
	/**
1667
	 * function header2gridelements - to populate the grid elements with the collected Data
1668
	 *
1669
	 * @param array $_headers headerdata to process
1670
	 * @param array $cols cols to populate
1671
	 * @param array $_folderName to ensure the uniqueness of the uid over all folders
1672
	 * @param array $_folderType used to determine if we need to populate from/to
1673
	 * @return array populated result array
1674
	 */
1675
	public function header2gridelements($_headers, $cols, $_folderName, $_folderType=0)
1676
	{
1677
		if (Mail::$debugTimes) $starttime = microtime(true);
1678
		$rv = array();
1679
		$i=0;
1680
		foreach((array)$_headers as $header)
1681
		{
1682
			$i++;
1683
			$data = array();
1684
			//error_log(__METHOD__.array2string($header));
1685
			$message_uid = $header['uid'];
1686
			$data['uid'] = $message_uid;
1687
			$data['row_id']=$this->createRowID($_folderName,$message_uid);
1688
1689
			$flags = "";
1690
			if(!empty($header['recent'])) $flags .= "R";
1691
			if(!empty($header['flagged'])) $flags .= "F";
1692
			if(!empty($header['answered'])) $flags .= "A";
1693
			if(!empty($header['forwarded'])) $flags .= "W";
1694
			if(!empty($header['deleted'])) $flags .= "D";
1695
			if(!empty($header['seen'])) $flags .= "S";
1696
			if(!empty($header['label1'])) $flags .= "1";
1697
			if(!empty($header['label2'])) $flags .= "2";
1698
			if(!empty($header['label3'])) $flags .= "3";
1699
			if(!empty($header['label4'])) $flags .= "4";
1700
			if(!empty($header['label5'])) $flags .= "5";
1701
1702
			$data["status"] = "<span class=\"status_img\"></span>";
1703
			//error_log(__METHOD__.array2string($header).' Flags:'.$flags);
1704
1705
			// the css for this row
1706
			$is_recent=false;
1707
			$css_styles = array("mail");
1708
			if ($header['deleted']) {
1709
				$css_styles[] = 'deleted';
1710
			}
1711
			if ($header['recent'] && !($header['deleted'] || $header['seen'] || $header['answered'] || $header['forwarded'])) {
1712
				$css_styles[] = 'recent';
1713
				$is_recent=true;
1714
			}
1715
			if ($header['priority'] < 3) {
1716
				$css_styles[] = 'prio_high';
1717
			}
1718
			if ($header['flagged']) {
1719
				$css_styles[] = 'flagged';
1720
			}
1721
			if (!$header['seen']) {
1722
				$css_styles[] = 'unseen'; // different status image for recent // solved via css !important
1723
			}
1724
			if ($header['answered']) {
1725
				$css_styles[] = 'replied';
1726
			}
1727
			if ($header['forwarded']) {
1728
				$css_styles[] = 'forwarded';
1729
			}
1730
			if ($header['label1']) {
1731
				$css_styles[] = 'labelone';
1732
			}
1733
			if ($header['label2']) {
1734
				$css_styles[] = 'labeltwo';
1735
			}
1736
			if ($header['label3']) {
1737
				$css_styles[] = 'labelthree';
1738
			}
1739
			if ($header['label4']) {
1740
				$css_styles[] = 'labelfour';
1741
			}
1742
			if ($header['label5']) {
1743
				$css_styles[] = 'labelfive';
1744
			}
1745
1746
			//error_log(__METHOD__.array2string($css_styles));
1747
1748
			if (in_array("subject", $cols))
1749
			{
1750
				// filter out undisplayable characters
1751
				$search = array('[\016]','[\017]',
1752
					'[\020]','[\021]','[\022]','[\023]','[\024]','[\025]','[\026]','[\027]',
1753
					'[\030]','[\031]','[\032]','[\033]','[\034]','[\035]','[\036]','[\037]');
1754
				$replace = '';
1755
1756
				$header['subject'] = preg_replace($search,$replace,$header['subject']);
1757
				// curly brackets get messed up by the template!
1758
1759
				if (!empty($header['subject'])) {
1760
					// make the subject shorter if it is to long
1761
					$subject = $header['subject'];
1762
				} else {
1763
					$subject = '('. lang('no subject') .')';
1764
				}
1765
1766
				$data["subject"] = $subject; // the mailsubject
1767
			}
1768
1769
			$imageHTMLBlock = '';
1770
			//error_log(__METHOD__.__LINE__.array2string($header));
1771
			if (in_array("attachments", $cols))
1772
			{
1773
				if($header['mimetype'] == 'multipart/mixed' ||
1774
					$header['mimetype'] == 'multipart/signed' ||
1775
					$header['mimetype'] == 'multipart/related' ||
1776
					$header['mimetype'] == 'multipart/report' ||
1777
					$header['mimetype'] == 'text/calendar' ||
1778
					$header['mimetype'] == 'text/html' ||
1779
					substr($header['mimetype'],0,11) == 'application' ||
1780
					substr($header['mimetype'],0,5) == 'audio' ||
1781
					substr($header['mimetype'],0,5) == 'video' ||
1782
					$header['mimetype'] == 'multipart/alternative')
1783
				{
1784
					$image = Api\Html::image('mail','attach');
1785
					$imageHTMLBlock = '';
1786
					$datarowid = $this->createRowID($_folderName,$message_uid,true);
1787
					$attachments = $header['attachments'];
1788
					if (count($attachments)<1)
1789
					{
1790
						$image = '&nbsp;';
1791
					}
1792
					if (count($attachments)==1)
1793
					{
1794
						$imageHTMLBlock = self::createAttachmentBlock($attachments, $datarowid, $header['uid'],$_folderName);
1795
						$image = Api\Html::image('mail','attach',$attachments[0]['name'].(!empty($attachments[0]['mimeType'])?' ('.$attachments[0]['mimeType'].')':''));
1796
					}
1797
					if (count($attachments)>1)
1798
					{
1799
						$imageHTMLBlock = self::createAttachmentBlock($attachments, $datarowid, $header['uid'],$_folderName);
1800
						$image = Api\Html::image('mail','attach',lang('%1 attachments',count($attachments)));
1801
					}
1802
1803
					$attachmentFlag = $image;
1804
				} else {
1805
					$attachmentFlag ='&nbsp;';
1806
				}
1807
				// show priority flag
1808
				if ($header['priority'] < 3) {
1809
					 $image = Api\Html::image('mail','prio_high');
1810
				} elseif ($header['priority'] > 3) {
1811
					$image = Api\Html::image('mail','prio_low');
1812
				} else {
1813
					$image = '';
1814
				}
1815
				// show a flag for flagged messages
1816
				$imageflagged ='';
1817
				if ($header['flagged'])
1818
				{
1819
					$imageflagged = Api\Html::image('mail','unread_flagged_small');
1820
				}
1821
				$data["attachments"] = $image.$attachmentFlag.$imageflagged; // icon for attachments available
1822
			}
1823
1824
			// sent or draft or template folder -> to address
1825
			if (in_array("toaddress", $cols))
1826
			{
1827
				// sent or drafts or template folder means foldertype > 0, use to address instead of from
1828
				$data["toaddress"] = $header['to_address'];//Mail::htmlentities($header['to_address'],$this->charset);
1829
			}
1830
1831
			if (in_array("additionaltoaddress", $cols))
1832
			{
1833
				$data['additionaltoaddress'] = $header['additional_to_addresses'];
1834
			}
1835
			//fromaddress
1836
			if (in_array("fromaddress", $cols))
1837
			{
1838
				$data["fromaddress"] = $header['sender_address'];
1839
			}
1840
			if (in_array("ccaddress", $cols))
1841
			{
1842
				$data['ccaddress'] = $header['cc_addresses'];
1843
			}
1844
			if (in_array("date", $cols))
1845
			{
1846
				$data["date"] = $header['date'];
1847
			}
1848
			if (in_array("modified", $cols))
1849
			{
1850
				$data["modified"] = $header['internaldate'];
1851
			}
1852
1853
			if (in_array("size", $cols))
1854
				$data["size"] = $header['size']; /// size
1855
1856
			$data["class"] = implode(' ', $css_styles);
1857
			//translate style-classes back to flags
1858
			$data['flags'] = Array();
1859
			if ($header['seen']) $data["flags"]['read'] = 'read';
1860
			foreach ($css_styles as &$flag) {
1861
				if ($flag!='mail')
1862
				{
1863
					if ($flag=='labelone') {$data["flags"]['label1'] = 'label1';}
1864
					elseif ($flag=='labeltwo') {$data["flags"]['label2'] = 'label2';}
1865
					elseif ($flag=='labelthree') {$data["flags"]['label3'] = 'label3';}
1866
					elseif ($flag=='labelfour') {$data["flags"]['label4'] = 'label4';}
1867
					elseif ($flag=='labelfive') {$data["flags"]['label5'] = 'label5';}
1868
					elseif ($flag=='unseen') {unset($data["flags"]['read']);}
1869
					else $data["flags"][$flag] = $flag;
1870
				}
1871
			}
1872
			if ($header['disposition-notification-to']) $data['dispositionnotificationto'] = $header['disposition-notification-to'];
1873
			if (($header['mdnsent']||$header['mdnnotsent']|$header['seen'])&&isset($data['dispositionnotificationto'])) unset($data['dispositionnotificationto']);
1874
			$data['attachmentsBlock'] = $imageHTMLBlock;
1875
			$data['address'] = ($_folderType?$data["toaddress"]:$data["fromaddress"]);
1876
			if (in_array("bodypreview", $cols)&&$header['bodypreview'])
1877
			{
1878
				$data["bodypreview"] = $header['bodypreview'];
1879
			}
1880
			$rv[] = $data;
1881
			//error_log(__METHOD__.__LINE__.array2string($data));
1882
		}
1883
		if (Mail::$debugTimes) Mail::logRunTimes($starttime,null,'Folder:'.$_folderName,__METHOD__.__LINE__);
1884
1885
		// ToDo: call this ONLY if labels change
1886
		Etemplate\Widget::setElementAttribute('toolbar', 'actions', $this->get_toolbar_actions());
1887
1888
		return $rv;
1889
	}
1890
1891
	/**
1892
	 * display messages header lines
1893
	 *
1894
	 * all params are passed as GET Parameters
1895
	 */
1896
	function displayHeader()
1897
	{
1898
		if(isset($_GET['id'])) $rowID	= $_GET['id'];
1899
		if(isset($_GET['part'])) $partID = $_GET['part'];
1900
1901
		$hA = self::splitRowID($rowID);
1902
		$uid = $hA['msgUID'];
1903
		$mailbox = $hA['folder'];
1904
		$icServerID = $hA['profileID'];
1905
		$rememberServerID = $this->mail_bo->profileID;
1906
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
1907
		{
1908
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
1909
			$this->changeProfile($icServerID);
1910
		}
1911
1912
		$this->mail_bo->reopen($mailbox);
1913
		$headers_in	= $this->mail_bo->getMessageRawHeader($uid, $partID);
1914
1915
		// add line breaks to $rawheaders
1916
		$newRawHeaders = explode("\n",$headers_in);
1917
		reset($newRawHeaders);
1918
1919
		// reset $rawheaders
1920
		$rawheaders 	= "";
1921
		// create it new, with good line breaks
1922
		reset($newRawHeaders);
1923
		while(list($key,$value) = @each($newRawHeaders)) {
0 ignored issues
show
Unused Code introduced by
The assignment to $key is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
1924
			$rawheaders .= wordwrap($value, 90, "\n     ");
1925
		}
1926
1927
		$this->mail_bo->closeConnection();
1928
		if ($rememberServerID != $this->mail_bo->profileID)
1929
		{
1930
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from->'.$rememberServerID);
1931
			$this->changeProfile($rememberServerID);
1932
		}
1933
1934
		header('Content-type: text/html; charset=iso-8859-1');
1935
		print '<pre>'. htmlspecialchars($rawheaders, ENT_NOQUOTES, 'iso-8859-1') .'</pre>';
1936
1937
	}
1938
1939
	/**
1940
	 * display messages
1941
	 * @param array $_requesteddata etemplate content
1942
	 * all params are passed as GET Parameters, but can be passed via ExecMethod2 as array too
1943
	 */
1944
	function displayMessage($_requesteddata = null)
1945
	{
1946
		if (is_null($_requesteddata)) $_requesteddata = $_GET;
1947
1948
		$preventRedirect=false;
1949
		if(isset($_requesteddata['id'])) $rowID	= $_requesteddata['id'];
1950
		if(isset($_requesteddata['part'])) $partID = $_requesteddata['part']!='null'?$_requesteddata['part']:null;
1951
		if(isset($_requesteddata['mode'])) $preventRedirect   = (($_requesteddata['mode']=='display' || $_requesteddata['mode'] == 'print')?true:false);
1952
1953
		$hA = self::splitRowID($rowID);
1954
		$uid = $hA['msgUID'];
1955
		$mailbox = $hA['folder'];
1956
		$icServerID = $hA['profileID'];
1957
		$rememberServerID = $this->mail_bo->profileID;
1958
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
1959
		{
1960
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
1961
			$this->changeProfile($icServerID);
1962
		}
1963
		$htmlOptions = $this->mail_bo->htmlOptions;
1964
		if (!empty($_requesteddata['tryastext'])) $htmlOptions  = "only_if_no_text";
1965
		if (!empty($_requesteddata['tryashtml'])) $htmlOptions  = "always_display";
1966
1967
		//error_log(__METHOD__.__LINE__.array2string($hA));
1968
		if (($this->mail_bo->isDraftFolder($mailbox)) && $_requesteddata['mode'] == 'print')
1969
		{
1970
			$response = Api\Json\Response::get();
1971
			$response->call('app.mail.print_for_compose', $rowID);
1972
		}
1973
		if (!$preventRedirect && ($this->mail_bo->isDraftFolder($mailbox) || $this->mail_bo->isTemplateFolder($mailbox)))
1974
		{
1975
			Egw::redirect_link('/index.php',array('menuaction'=>'mail.mail_compose.compose','id'=>$rowID,'from'=>'composefromdraft'));
1976
		}
1977
		$this->mail_bo->reopen($mailbox);
1978
		// retrieve the flags of the message, before touching it.
1979
		try
1980
		{
1981
			$headers	= $this->mail_bo->getMessageHeader($uid, $partID,true,true,$mailbox);
1982
		}
1983
		catch (Api\Exception $e)
1984
		{
1985
			$error_msg[] = lang("ERROR: Message could not be displayed.");
1986
			$error_msg[] = lang("In Mailbox: %1, with ID: %2, and PartID: %3",$mailbox,$uid,$partID);
1987
			Framework::message($e->getMessage(), 'error');
1988
		}
1989
		if (!empty($uid)) $this->mail_bo->getFlags($uid);
1990
		$envelope	= $this->mail_bo->getMessageEnvelope($uid, $partID,true,$mailbox);
1991
		//error_log(__METHOD__.__LINE__.array2string($envelope));
1992
		$this->mail_bo->getMessageRawHeader($uid, $partID,$mailbox);
1993
		$fetchEmbeddedImages = false;
1994
		// if we are in HTML so its likely that we should show the embedded images; as a result
1995
		// we do NOT want to see those, that are embedded in the list of attachments
1996
		if ($htmlOptions !='always_display') $fetchEmbeddedImages = true;
1997
		$attachments	= $this->mail_bo->getMessageAttachments($uid, $partID, null, $fetchEmbeddedImages,true,true,$mailbox);
1998
		//error_log(__METHOD__.__LINE__.array2string($attachments));
1999
		$attachmentHTMLBlock = self::createAttachmentBlock($attachments, $rowID, $uid, $mailbox);
2000
2001
		$nonDisplayAbleCharacters = array('[\016]','[\017]',
2002
				'[\020]','[\021]','[\022]','[\023]','[\024]','[\025]','[\026]','[\027]',
2003
				'[\030]','[\031]','[\032]','[\033]','[\034]','[\035]','[\036]','[\037]');
2004
2005
		//error_log(__METHOD__.__LINE__.$mailBody);
2006
		$this->mail_bo->closeConnection();
2007
		//$GLOBALS['egw_info']['flags']['currentapp'] = 'mail';//should not be needed
2008
		$etpl = new Etemplate('mail.display');
2009
		$subject = $this->mail_bo->decode_subject(preg_replace($nonDisplayAbleCharacters,'',$envelope['SUBJECT']),false);
2010
2011
		// Set up data for taglist widget(s)
2012
		if ($envelope['FROM']==$envelope['SENDER']) unset($envelope['SENDER']);
2013
		$sel_options = array();
2014
		foreach(array('SENDER','FROM','TO','CC','BCC') as $field)
2015
		{
2016
			if (!isset($envelope[$field])) continue;
2017
			foreach($envelope[$field] as $field_data)
2018
			{
2019
				//error_log(__METHOD__.__LINE__.array2string($field_data));
2020
				$content[$field][] = $field_data;
2021
				$sel_options[$field][] = array(
2022
					// taglist requires these - not optional
2023
					'id' => $field_data,
2024
					'label' => str_replace('"',"'",$field_data),
2025
				);
2026
			}
2027
		}
2028
		$actionsenabled = $this->getDisplayToolbarActions();
2029
		$content['displayToolbaractions'] = json_encode($actionsenabled);
2030
		if (empty($subject)) $subject = lang('no subject');
2031
		$content['msg'] = (is_array($error_msg)?implode("<br>",$error_msg):$error_msg);
2032
		// Send mail ID so we can use it for actions
2033
		$content['mail_id'] = $rowID;
2034
		if (!is_array($headers) || !isset($headers['DATE']))
2035
		{
2036
			$headers['DATE'] = (is_array($envelope)&&$envelope['DATE']?$envelope['DATE']:'');
2037
		}
2038
		$content['mail_displaydate'] = Mail::_strtotime($headers['DATE'],'ts',true);
2039
		$content['mail_displaysubject'] = $subject;
2040
		$linkData = array('menuaction'=>"mail.mail_ui.loadEmailBody","_messageID"=>$rowID);
2041
		if (!empty($partID)) $linkData['_partID']=$partID;
2042
		if ($htmlOptions != $this->mail_bo->htmlOptions) $linkData['_htmloptions']=$htmlOptions;
2043
		$content['mailDisplayBodySrc'] = Egw::link('/index.php',$linkData);
2044
		$content['mail_displayattachments'] = $attachmentHTMLBlock;
2045
		$content['mail_id']=$rowID;
2046
		$content['mailDisplayContainerClass']=(count($attachments)?"mailDisplayContainer mailDisplayContainerFixedHeight":"mailDisplayContainer mailDisplayContainerFullHeight");
2047
		$content['mailDisplayAttachmentsClass']=(count($attachments)?"mailDisplayAttachments":"mail_DisplayNone");
2048
2049
		// DRAG attachments actions
2050
		$etpl->setElementAttribute('mail_displayattachments', 'actions', array(
2051
			'file_drag' => array(
2052
				'dragType' => 'file',
2053
				'type' => 'drag',
2054
				'onExecute' => 'javaScript:app.mail.drag_attachment'
2055
			)
2056
		));
2057
		$readonlys = $preserv = $content;
2058
		if ($rememberServerID != $this->mail_bo->profileID)
2059
		{
2060
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from->'.$rememberServerID);
2061
			$this->changeProfile($rememberServerID);
2062
		}
2063
2064
		$etpl->exec('mail.mail_ui.displayMessage',$content,$sel_options,$readonlys,$preserv,2);
2065
	}
2066
2067
	/**
2068
	 * Build actions for display toolbar
2069
	 */
2070
	function getDisplayToolbarActions ()
2071
	{
2072
		$actions = $this->get_toolbar_actions();
2073
		$actions['mark']['children']['flagged']=array(
2074
			'group' => $actions['mark']['children']['flagged']['group'],
2075
			'caption' => 'Flagged',
2076
			'icon' => 'unread_flagged_small',
2077
			'onExecute' => 'javaScript:app.mail.mail_flag',
2078
		);
2079
		$actions['mark']['children']['unflagged']=array(
2080
			'group' => $actions['mark']['children']['flagged']['group'],
2081
			'caption' => 'Unflagged',
2082
			'icon' => 'read_flagged_small',
2083
			'onExecute' => 'javaScript:app.mail.mail_flag',
2084
		);
2085
		$actions['tracker']['toolbarDefault'] = true;
2086
		$actions['forward']['toolbarDefault'] = true;
2087
2088
		$compose = $actions['composeasnew'];
2089
		unset($actions['composeasnew']);
2090
2091
		$actions2 = array_reverse($actions,true);
2092
		$actions2['composeasnew']= $compose;
2093
		return array_reverse($actions2,true);
2094
	}
2095
2096
	/**
2097
	 * helper function to create the attachment block/table
2098
	 *
2099
	 * @param array $attachments array with the attachments information
2100
	 * @param string $rowID rowid of the message
2101
	 * @param int $uid uid of the message
2102
	 * @param string $mailbox mailbox identifier
2103
	 * @param boolean $_returnFullHTML flag wether to return HTML or data array
2104
	 * @return array|string data array or html or empty string
2105
	 */
2106
	static function createAttachmentBlock($attachments, $rowID, $uid, $mailbox,$_returnFullHTML=false)
2107
	{
2108
		$attachmentHTMLBlock='';
2109
		$attachmentHTML = array();
2110
		if (is_array($attachments) && count($attachments) > 0) {
2111
			$url_img_vfs = Api\Html::image('filemanager','navbar', lang('Filemanager'), ' height="16"');
2112
			$url_img_vfs_save_all = Api\Html::image('mail','save_all', lang('Save all'));
2113
2114
			foreach ($attachments as $key => $value)
2115
			{
2116
				$attachmentHTML[$key]['filename']= ($value['name'] ? ( $value['filename'] ? $value['filename'] : $value['name'] ) : lang('(no subject)'));
2117
				$attachmentHTML[$key]['filename'] = Api\Translation::convert_jsonsafe($attachmentHTML[$key]['filename'],'utf-8');
2118
				//error_log(array2string($value));
2119
				//error_log(strtoupper($value['mimeType']) .'<->'. Api\MimeMagic::filename2mime($attachmentHTML[$key]['filename']));
2120
				if (strtoupper($value['mimeType']=='APPLICATION/OCTET-STREAM')) $value['mimeType'] = Api\MimeMagic::filename2mime($attachmentHTML[$key]['filename']);
2121
				$attachmentHTML[$key]['type']=$value['mimeType'];
2122
				$attachmentHTML[$key]['mimetype'] = Api\MimeMagic::mime2label($value['mimeType']);
2123
				$hA = self::splitRowID($rowID);
2124
				$uid = $hA['msgUID'];
2125
				$mailbox = $hA['folder'];
2126
				$acc_id = $hA['profileID'];
2127
2128
				$attachmentHTML[$key]['mime_data'] = Link::set_data($value['mimeType'], 'EGroupware\\Api\\Mail::getAttachmentAccount', array(
2129
					$acc_id, $mailbox, $uid, $value['partID'], $value['is_winmail'], true
2130
				));
2131
				$attachmentHTML[$key]['size']=Vfs::hsize($value['size']);
2132
				$attachmentHTML[$key]['attachment_number']=$key;
2133
				$attachmentHTML[$key]['partID']=$value['partID'];
2134
				$attachmentHTML[$key]['mail_id'] = $rowID;
2135
				$attachmentHTML[$key]['winmailFlag']=$value['is_winmail'];
2136
				$attachmentHTML[$key]['classSaveAllPossiblyDisabled'] = "mail_DisplayNone";
2137
				// reset mode array as it should be considered differently for
2138
				// each attachment
2139
				$mode = array();
2140
				switch(strtoupper($value['mimeType']))
2141
				{
2142
					case 'MESSAGE/RFC822':
2143
						$linkData = array
2144
						(
2145
							'menuaction'	=> 'mail.mail_ui.displayMessage',
2146
							'mode'		=> 'display', //message/rfc822 attachments should be opened in display mode
2147
							'id'		=> $rowID,
2148
							'part'		=> $value['partID'],
2149
							'is_winmail'    => $value['is_winmail']
2150
						);
2151
						$windowName = 'displayMessage_'. $rowID.'_'.$value['partID'];
2152
						$linkView = "egw_openWindowCentered('".Egw::link('/index.php',$linkData)."','$windowName',700,egw_getWindowOuterHeight());";
2153
						break;
2154
					case 'IMAGE/JPEG':
2155
					case 'IMAGE/PNG':
2156
					case 'IMAGE/GIF':
2157
					case 'IMAGE/BMP':
2158
						// set mode for media mimetypes because we need
2159
						// to structure a download url to be used maybe in expose.
2160
						$mode = array(
2161
							'mode' => 'save'
2162
						);
2163
					case 'APPLICATION/PDF':
2164
					case 'TEXT/PLAIN':
2165
					case 'TEXT/HTML':
2166
					case 'TEXT/DIRECTORY':
2167
						$sfxMimeType = $value['mimeType'];
2168
						$buff = explode('.',$value['name']);
2169
						$suffix = '';
2170
						if (is_array($buff)) $suffix = array_pop($buff); // take the last extension to check with ext2mime
2171
						if (!empty($suffix)) $sfxMimeType = Api\MimeMagic::ext2mime($suffix);
2172
						if (strtoupper($sfxMimeType) == 'TEXT/VCARD' || strtoupper($sfxMimeType) == 'TEXT/X-VCARD')
2173
						{
2174
							$attachments[$key]['mimeType'] = $sfxMimeType;
2175
							$value['mimeType'] = strtoupper($sfxMimeType);
2176
						}
2177
					case 'TEXT/X-VCARD':
2178
					case 'TEXT/VCARD':
2179
					case 'TEXT/CALENDAR':
2180
					case 'TEXT/X-VCALENDAR':
2181
						$linkData = array_merge(array
2182
						(
2183
							'menuaction'	=> 'mail.mail_ui.getAttachment',
2184
							'id'		=> $rowID,
2185
							'part'		=> $value['partID'],
2186
							'is_winmail'    => $value['is_winmail'],
2187
							'mailbox'   => base64_encode($mailbox),
2188
						) , $mode);
2189
						$windowName = 'displayAttachment_'. $uid;
2190
						$reg = '800x600';
2191
						// handle calendar/vcard
2192
						if (strtoupper($value['mimeType'])=='TEXT/CALENDAR')
2193
						{
2194
							$windowName = 'displayEvent_'. $rowID;
2195
							$reg2 = Link::get_registry('calendar','view_popup');
2196
							$attachmentHTML[$key]['popup']=(!empty($reg2) ? $reg2 : $reg);
2197
						}
2198
						if (strtoupper($value['mimeType'])=='TEXT/X-VCARD' || strtoupper($value['mimeType'])=='TEXT/VCARD')
2199
						{
2200
							$windowName = 'displayContact_'. $rowID;
2201
							$reg2 = Link::get_registry('addressbook','add_popup');
2202
							$attachmentHTML[$key]['popup']=(!empty($reg2) ? $reg2 : $reg);
2203
						}
2204
						// apply to action
2205
						list($width,$height) = explode('x',(!empty($reg2) ? $reg2 : $reg));
2206
						$linkView = "egw_openWindowCentered('".Egw::link('/index.php',$linkData)."','$windowName',$width,$height);";
2207
						break;
2208
					default:
2209
						$linkData = array
2210
						(
2211
							'menuaction'	=> 'mail.mail_ui.getAttachment',
2212
							'id'		=> $rowID,
2213
							'part'		=> $value['partID'],
2214
							'is_winmail'    => $value['is_winmail'],
2215
							'mailbox'   => base64_encode($mailbox),
2216
						);
2217
						$linkView = "window.location.href = '".Egw::link('/index.php',$linkData)."';";
2218
						break;
2219
				}
2220
				// we either use mime_data for server-side supported mime-types or mime_url for client-side or download
2221
				if (empty($attachmentHTML[$key]['mime_data']))
2222
				{
2223
					$attachmentHTML[$key]['mime_url'] = Egw::link('/index.php',$linkData);
2224
					unset($attachmentHTML[$key]['mime_data']);
2225
				}
2226
				$attachmentHTML[$key]['windowName'] = $windowName;
2227
2228
				//error_log(__METHOD__.__LINE__.$linkView);
2229
				$attachmentHTML[$key]['link_view'] = '<a href="#" ." title="'.$attachmentHTML[$key]['filename'].'" onclick="'.$linkView.' return false;"><b>'.
2230
					($value['name'] ? $value['name'] : lang('(no subject)')).
2231
					'</b></a>';
2232
2233
				$linkData = array
2234
				(
2235
					'menuaction'	=> 'mail.mail_ui.getAttachment',
2236
					'mode'		=> 'save',
2237
					'id'		=> $rowID,
2238
					'part'		=> $value['partID'],
2239
					'is_winmail'    => $value['is_winmail'],
2240
					'mailbox'   => base64_encode($mailbox),
2241
				);
2242
				$attachmentHTML[$key]['link_save'] ="<a href='".Egw::link('/index.php',$linkData)."' title='".$attachmentHTML[$key]['filename']."'>".Api\Html::image('mail','fileexport')."</a>";
2243
2244
				if ($GLOBALS['egw_info']['user']['apps']['filemanager'])
2245
				{
2246
					$link_vfs_save = Egw::link('/index.php',array(
2247
						'menuaction' => 'filemanager.filemanager_select.select',
2248
						'mode' => 'saveas',
2249
						'name' => $value['name'],
2250
						'mime' => strtolower($value['mimeType']),
2251
						'method' => 'mail.mail_ui.vfsSaveAttachment',
2252
						'id' => $rowID.'::'.$value['partID'].'::'.$value['is_winmail'],
2253
						'label' => lang('Save'),
2254
					));
2255
					$vfs_save = "<a href='#' onclick=\"egw_openWindowCentered('$link_vfs_save','vfs_save_attachment','640','570',window.outerWidth/2,window.outerHeight/2); return false;\">$url_img_vfs</a>";
2256
					// add save-all icon for first attachment
2257
					if (!$key && count($attachments) > 1)
2258
					{
2259
						$attachmentHTML[$key]['classSaveAllPossiblyDisabled'] = "";
2260
						foreach ($attachments as $ikey => $value)
2261
						{
2262
							//$rowID
2263
							$ids["id[$ikey]"] = $rowID.'::'.$value['partID'].'::'.$value['is_winmail'].'::'.$value['name'];
2264
						}
2265
						$link_vfs_save = Egw::link('/index.php',array(
2266
							'menuaction' => 'filemanager.filemanager_select.select',
2267
							'mode' => 'select-dir',
2268
							'method' => 'mail.mail_ui.vfsSaveAttachment',
2269
							'label' => lang('Save all'),
2270
						)+$ids);
2271
						$vfs_save .= "<a href='#' onclick=\"egw_openWindowCentered('$link_vfs_save','vfs_save_attachment','640','530',window.outerWidth/2,window.outerHeight/2); return false;\">$url_img_vfs_save_all</a>";
2272
					}
2273
					$attachmentHTML[$key]['link_save'] .= $vfs_save;
2274
					//error_log(__METHOD__.__LINE__.$attachmentHTML[$key]['link_save']);
2275
				}
2276
			}
2277
			$attachmentHTMLBlock="<table width='100%'>";
2278
			foreach ((array)$attachmentHTML as $row)
2279
			{
2280
				$attachmentHTMLBlock .= "<tr><td><div class='useEllipsis'>".$row['link_view'].'</div></td>';
2281
				$attachmentHTMLBlock .= "<td>".$row['mimetype'].'</td>';
2282
				$attachmentHTMLBlock .= "<td>".$row['size'].'</td>';
2283
				$attachmentHTMLBlock .= "<td>".$row['link_save'].'</td></tr>';
2284
			}
2285
			$attachmentHTMLBlock .= "</table>";
2286
		}
2287
		if (!$_returnFullHTML)
2288
		{
2289
			foreach ((array)$attachmentHTML as $ikey => $value)
2290
			{
2291
				unset($attachmentHTML[$ikey]['link_view']);
2292
				unset($attachmentHTML[$ikey]['link_save']);
2293
			}
2294
		}
2295
		return ($_returnFullHTML?$attachmentHTMLBlock:$attachmentHTML);
2296
	}
2297
2298
	/**
2299
	 * fetch vacation info from active Server using icServer object
2300
	 *
2301
	 * @param array $cachedVacations an array of cached vacations for an user
2302
	 * @return array|boolean array with vacation on success or false on failure
2303
	 */
2304
	function gatherVacation($cachedVacations = array())
2305
	{
2306
		$isVacationEnabled = $this->mail_bo->icServer->acc_sieve_enabled && ($this->mail_bo->icServer->acc_sieve_host||$this->mail_bo->icServer->acc_imap_host);
2307
		//error_log(__METHOD__.__LINE__.' Server:'.self::$icServerID.' Sieve Enabled:'.array2string($vacation));
2308
2309
		if ($isVacationEnabled)
2310
		{
2311
			$sieveServer = $this->mail_bo->icServer;
2312
			try
2313
			{
2314
				$sieveServer->retrieveRules();
2315
				$vacation = $sieveServer->getVacation();
2316
2317
				$cachedVacations = array($sieveServer->acc_id => $vacation) + (array)$cachedVacations;
2318
				// Set vacation to the instance cache for particular account with expiration of one day
2319
				Api\Cache::setCache(Api\Cache::INSTANCE, 'email', 'vacationNotice'.$GLOBALS['egw_info']['user']['account_lid'], $cachedVacations, 60*60*24);
2320
			}
2321
			catch (PEAR_Exception $ex)
0 ignored issues
show
Bug introduced by
The class PEAR_Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
2322
			{
2323
				$this->callWizard($ex->getMessage(), true, 'error');
2324
			}
2325
		}
2326
		//error_log(__METHOD__.__LINE__.' Server:'.self::$icServerID.' Vacation retrieved:'.array2string($vacation));
2327
		return $vacation;
2328
	}
2329
2330
	/**
2331
	 * gather Info on how to display the quota info
2332
	 *
2333
	 * @param int $_usage amount of usage in Kb
2334
	 * @param int $_limit amount of limit in Kb
2335
	 * @return array  returns an array of info used for quota
2336
	 *		array(
2337
	 *			class		=> string,
2338
	 *			text		=> string,
2339
	 *			percent		=> string,
2340
	 *			freespace	=> integer
2341
	 *		)
2342
	 */
2343
	function quotaDisplay($_usage, $_limit)
2344
	{
2345
		$percent = $_limit == 0 ? 100 : round(($_usage*100)/$_limit);
2346
		$limit = Mail::show_readable_size($_limit*1024);
2347
		$usage = Mail::show_readable_size($_usage*1024);
2348
2349
		if ($_limit > 0)
2350
		{
2351
			$text = $usage .'/'.$limit;
2352
			switch ($percent)
2353
			{
2354
				case 90:
2355
					$class ='mail-index_QuotaRed';
2356
					break;
2357
				case 80:
2358
					$class ='mail-index_QuotaYellow';
2359
					break;
2360
				default:
2361
					$class ='mail-index_QuotaGreen';
2362
			}
2363
		}
2364
		else
2365
		{
2366
			$text = $usage;
2367
			$class ='mail-index_QuotaGreen';
2368
		}
2369
		return array (
2370
			'class'		=> $class,
2371
			'text'		=> lang('Quota: %1',$text),
2372
			'percent'	=> $percent,
2373
			'freespace'	=> $_limit*1024 - $_usage*1024
2374
		);
2375
	}
2376
2377
	/**
2378
	 * display image
2379
	 *
2380
	 * all params are passed as GET Parameters
2381
	 */
2382
	function displayImage()
2383
	{
2384
		$uid	= $_GET['uid'];
2385
		$cid	= base64_decode($_GET['cid']);
2386
		$partID = urldecode($_GET['partID']);
2387
		if (!empty($_GET['mailbox'])) $mailbox  = base64_decode($_GET['mailbox']);
2388
2389
		//error_log(__METHOD__.__LINE__.":$uid, $cid, $partID");
2390
		$this->mail_bo->reopen($mailbox);
2391
2392
		$attachment = $this->mail_bo->getAttachmentByCID($uid, $cid, $partID, true);	// true get contents as stream
2393
2394
		$this->mail_bo->closeConnection();
2395
2396
		$GLOBALS['egw']->session->commit_session();
2397
2398
		if ($attachment)
2399
		{
2400
			header("Content-Type: ". $attachment->getType());
2401
			header('Content-Disposition: inline; filename="'. $attachment->getDispositionParameter('filename') .'"');
2402
			//header("Expires: 0");
2403
			// the next headers are for IE and SSL
2404
			//header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
2405
			//header("Pragma: public");
2406
			Api\Session::cache_control(true);
2407
			echo $attachment->getContents();
2408
		}
2409
		else
2410
		{
2411
			// send a 404 Not found
2412
			header("HTTP/1.1 404 Not found");
2413
		}
2414
		exit();
2415
	}
2416
2417
	function getAttachment()
2418
	{
2419
		if(isset($_GET['id'])) $rowID	= $_GET['id'];
2420
2421
		$hA = self::splitRowID($rowID);
2422
		$uid = $hA['msgUID'];
2423
		$mailbox = $hA['folder'];
2424
		$icServerID = $hA['profileID'];
2425
		$rememberServerID = $this->mail_bo->profileID;
2426
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
2427
		{
2428
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2429
			$this->changeProfile($icServerID);
2430
		}
2431
		$part		= $_GET['part'];
2432
		$is_winmail = $_GET['is_winmail'] ? $_GET['is_winmail'] : 0;
2433
2434
		$this->mail_bo->reopen($mailbox);
2435
		$attachment = $this->mail_bo->getAttachment($uid,$part,$is_winmail,false);
2436
		$this->mail_bo->closeConnection();
2437
		if ($rememberServerID != $this->mail_bo->profileID)
2438
		{
2439
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from->'.$rememberServerID);
2440
			$this->changeProfile($rememberServerID);
2441
		}
2442
2443
		$GLOBALS['egw']->session->commit_session();
2444
		//error_log(__METHOD__.print_r($_GET,true));
2445
		if ($_GET['mode'] != "save")
2446
		{
2447 View Code Duplication
			if (strtoupper($attachment['type']) == 'TEXT/DIRECTORY' || empty($attachment['type']))
2448
			{
2449
				$sfxMimeType = $attachment['type'];
2450
				$buff = explode('.',$attachment['filename']);
2451
				$suffix = '';
2452
				if (is_array($buff)) $suffix = array_pop($buff); // take the last extension to check with ext2mime
2453
				if (!empty($suffix)) $sfxMimeType = Api\MimeMagic::ext2mime($suffix);
2454
				$attachment['type'] = $sfxMimeType;
2455
				if (strtoupper($sfxMimeType) == 'TEXT/VCARD' || strtoupper($sfxMimeType) == 'TEXT/X-VCARD') $attachment['type'] = strtoupper($sfxMimeType);
2456
			}
2457
			//error_log(__METHOD__.print_r($attachment,true));
2458
			if (strtoupper($attachment['type']) == 'TEXT/CALENDAR' || strtoupper($attachment['type']) == 'TEXT/X-VCALENDAR')
2459
			{
2460
				//error_log(__METHOD__."about to call calendar_ical");
2461
				$calendar_ical = new calendar_ical();
2462
				$eventid = $calendar_ical->search($attachment['attachment'],-1);
2463
				//error_log(__METHOD__.array2string($eventid));
2464
				if (!$eventid) $eventid = -1;
2465
				$event = $calendar_ical->importVCal($attachment['attachment'],(is_array($eventid)?$eventid[0]:$eventid),null,true,0,'',null,$attachment['charset']);
2466
				//error_log(__METHOD__.$event);
2467 View Code Duplication
				if ((int)$event > 0)
2468
				{
2469
					$vars = array(
2470
						'menuaction'      => 'calendar.calendar_uiforms.edit',
2471
						'cal_id'      => $event,
2472
					);
2473
					Egw::redirect_link('../index.php',$vars);
2474
				}
2475
				//Import failed, download content anyway
2476
			}
2477 View Code Duplication
			if (strtoupper($attachment['type']) == 'TEXT/X-VCARD' || strtoupper($attachment['type']) == 'TEXT/VCARD')
2478
			{
2479
				$addressbook_vcal = new addressbook_vcal();
2480
				// double \r\r\n seems to end a vcard prematurely, so we set them to \r\n
2481
				//error_log(__METHOD__.__LINE__.$attachment['attachment']);
2482
				$attachment['attachment'] = str_replace("\r\r\n", "\r\n", $attachment['attachment']);
2483
				$vcard = $addressbook_vcal->vcardtoegw($attachment['attachment'], $attachment['charset']);
2484
				if ($vcard['uid'])
2485
				{
2486
					$vcard['uid'] = trim($vcard['uid']);
2487
					//error_log(__METHOD__.__LINE__.print_r($vcard,true));
2488
					$contact = $addressbook_vcal->find_contact($vcard,false);
2489
				}
2490
				if (!$contact) $contact = null;
2491
				// if there are not enough fields in the vcard (or the parser was unable to correctly parse the vcard (as of VERSION:3.0 created by MSO))
2492
				if ($contact || count($vcard)>2)
2493
				{
2494
					$contact = $addressbook_vcal->addVCard($attachment['attachment'],(is_array($contact)?array_shift($contact):$contact),true,$attachment['charset']);
2495
				}
2496
				if ((int)$contact > 0)
2497
				{
2498
					$vars = array(
2499
						'menuaction'	=> 'addressbook.addressbook_ui.edit',
2500
						'contact_id'	=> $contact,
2501
					);
2502
					Egw::redirect_link('../index.php',$vars);
2503
				}
2504
				//Import failed, download content anyway
2505
			}
2506
		}
2507
		//error_log(__METHOD__.__LINE__.'->'.array2string($attachment));
2508
		$filename = ($attachment['name']?$attachment['name']:($attachment['filename']?$attachment['filename']:$mailbox.'_uid'.$uid.'_part'.$part));
2509
		Api\Header\Content::safe($attachment['attachment'], $filename, $attachment['type'], $size=0, True, $_GET['mode'] == "save");
0 ignored issues
show
Bug introduced by
$size = 0 cannot be passed to safe() as the parameter $length expects a reference.
Loading history...
2510
		echo $attachment['attachment'];
2511
2512
		exit();
2513
	}
2514
2515
2516
	/**
2517
	 * save messages on disk or filemanager, or display it in popup
2518
	 *
2519
	 * all params are passed as GET Parameters
2520
	 */
2521
	function saveMessage()
2522
	{
2523
		$display = false;
2524
		if(isset($_GET['id'])) $rowID	= $_GET['id'];
2525
		if(isset($_GET['part'])) $partID = $_GET['part'];
2526
		if (isset($_GET['location'])&& ($_GET['location']=='display'||$_GET['location']=='filemanager')) $display	= $_GET['location'];
2527
2528
		$hA = self::splitRowID($rowID);
2529
		$uid = $hA['msgUID'];
2530
		$mailbox = $hA['folder'];
2531
		$icServerID = $hA['profileID'];
2532
		$rememberServerID = $this->mail_bo->profileID;
2533
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
2534
		{
2535
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2536
			$this->changeProfile($icServerID);
2537
		}
2538
2539
		$this->mail_bo->reopen($mailbox);
2540
2541
		$message = $this->mail_bo->getMessageRawBody($uid, $partID, $mailbox);
2542
2543
		$this->mail_bo->closeConnection();
2544
		if ($rememberServerID != $this->mail_bo->profileID)
2545
		{
2546
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
2547
			$this->changeProfile($rememberServerID);
2548
		}
2549
2550
		$GLOBALS['egw']->session->commit_session();
2551
		if (!$display)
2552
		{
2553
			$headers = Horde_Mime_Headers::parseHeaders($message);
2554
			$subject = str_replace('$$','__',Mail::decode_header($headers['SUBJECT']));
2555
			Api\Header\Content::safe($message, $subject.".eml", $mime='message/rfc822', $size=0, true, true);
0 ignored issues
show
Bug introduced by
$mime = 'message/rfc822' cannot be passed to safe() as the parameter $mime expects a reference.
Loading history...
Bug introduced by
$size = 0 cannot be passed to safe() as the parameter $length expects a reference.
Loading history...
2556
			echo $message;
2557
		}
2558
		else
2559
		{
2560
			Api\Header\Content::safe($message, $subject.".eml", $mime='text/html', $size=0, true, false);
0 ignored issues
show
Bug introduced by
$mime = 'text/html' cannot be passed to safe() as the parameter $mime expects a reference.
Loading history...
Bug introduced by
$size = 0 cannot be passed to safe() as the parameter $length expects a reference.
Loading history...
Bug introduced by
The variable $subject seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
2561
			print '<pre>'. htmlspecialchars($message, ENT_NOQUOTES|ENT_SUBSTITUTE, 'utf-8') .'</pre>';
2562
		}
2563
	}
2564
2565
	/**
2566
	 * Save an Message in the vfs
2567
	 *
2568
	 * @param string|array $ids use splitRowID, to separate values
2569
	 * @param string $path path in vfs (no Vfs::PREFIX!), only directory for multiple id's ($ids is an array)
2570
	 * @param boolean $close Return javascript to close the window
2571
	 * @return string|boolean javascript eg. to close the selector window if $close is true, or success/fail if $close is false
2572
	 */
2573
	function vfsSaveMessage($ids,$path, $close = true)
2574
	{
2575
		//error_log(__METHOD__.' IDs:'.array2string($ids).' SaveToPath:'.$path);
2576
2577 View Code Duplication
		if (is_array($ids) && !Vfs::is_writable($path) || !is_array($ids) && !Vfs::is_writable(dirname($path)))
2578
		{
2579
			return 'alert("'.addslashes(lang('%1 is NOT writable by you!',$path)).'"); Egw(window).close();';
2580
		}
2581
		Api\Translation::add_app('mail');
2582
2583
		$rememberServerID = $this->mail_bo->profileID;
2584
		foreach((array)$ids as $id)
2585
		{
2586
			$hA = self::splitRowID($id);
2587
			$uid = $hA['msgUID'];
2588
			$mailbox = $hA['folder'];
2589
			$icServerID = $hA['profileID'];
2590
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
2591
			{
2592
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2593
				$this->changeProfile($icServerID);
2594
			}
2595
			$message = $this->mail_bo->getMessageRawBody($uid, $partID='', $mailbox);
2596
			$err=null;
2597
			if(Vfs::is_dir($path))
2598
			{
2599
				$headers = $this->mail_bo->getMessageHeader($uid,$partID,true,false,$mailbox);
2600
				$file = $path . '/'.preg_replace('/[\f\n\t\v\\:*#?<>\|]/',"_",$headers['SUBJECT']).'.eml';
2601
			}
2602
			else
2603
			{
2604
				$file = $path;
2605
			}
2606
			if (!($fp = Vfs::fopen($file,'wb')) || !fwrite($fp,$message))
2607
			{
2608
				$err .= lang('Error saving %1!',$file);
2609
				$succeeded = false;
2610
			}
2611
			else
2612
			{
2613
				$succeeded = true;
2614
			}
2615
			if ($fp) fclose($fp);
2616
			if ($succeeded)
2617
			{
2618
				unset($headers['SUBJECT']);//already in filename
2619
				$infoSection = Mail::createHeaderInfoSection($headers, 'SUPPRESS', false);
2620
				$props = array(array('name' => 'comment','val' => $infoSection));
2621
				Vfs::proppatch($file,$props);
2622
			}
2623
		}
2624
		if ($rememberServerID != $this->mail_bo->profileID)
2625
		{
2626
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
2627
			$this->changeProfile($rememberServerID);
2628
		}
2629
2630
		if($close)
2631
		{
2632
			Framework::window_close(($err?$err:null));
2633
		}
2634
		else
2635
		{
2636
			return $succeeded;
2637
		}
2638
	}
2639
2640
	/**
2641
	 * Save an attachment in the vfs
2642
	 *
2643
	 * @param string|array $ids '::' delimited mailbox::uid::part-id::is_winmail::name (::name for multiple id's)
2644
	 * @param string $path path in vfs (no Vfs::PREFIX!), only directory for multiple id's ($ids is an array)
2645
	 * @return string javascript eg. to close the selector window
2646
	 */
2647
	function vfsSaveAttachment($ids,$path)
2648
	{
2649
		//error_log(__METHOD__.__LINE__.'("'.array2string($ids).'","'.$path."\")');");
2650
2651 View Code Duplication
		if (is_array($ids) && !Vfs::is_writable($path) || !is_array($ids) && !Vfs::is_writable(dirname($path)))
2652
		{
2653
			return 'alert("'.addslashes(lang('%1 is NOT writable by you!',$path)).'"); Egw(window).close();';
2654
		}
2655
		$err=null;
2656
		$dupe_count = array();
2657
		$rememberServerID = $this->mail_bo->profileID;
2658
2659
		/**
2660
		 * Extract all parameteres from the given id
2661
		 * @param int $id message id ('::' delimited mailbox::uid::part-id::is_winmail::name)
2662
		 *
2663
		 * @return array an array of parameters
2664
		 */
2665
		$getParams = function ($id) {
2666
			list($app,$user,$serverID,$mailbox,$uid,$part,$is_winmail,$name) = explode('::',$id,8);
2667
			$lId = implode('::',array($app,$user,$serverID,$mailbox,$uid));
2668
			$hA = mail_ui::splitRowID($lId);
2669
			return array(
2670
				'is_winmail' => $is_winmail == "null" || !$is_winmail?false:$is_winmail,
2671
				'user' => $user,
2672
				'name' => $name,
2673
				'part' => $part,
2674
				'uid' => $hA['msgUID'],
2675
				'mailbox' => $hA['folder'],
2676
				'icServer' => $hA['profileID']
2677
			);
2678
		};
2679
2680
		//Examine the first attachment to see if attachment
2681
		//is winmail.dat embedded attachments.
2682
		$isMultipleDownload=is_array($ids);
2683
		$p = $getParams((is_array($ids)?$ids[0]:$ids));
2684
		if ($p['is_winmail'])
2685
		{
2686 View Code Duplication
			if ($p['icServer'] && $p['icServer'] != $this->mail_bo->profileID)
2687
			{
2688
				$this->changeProfile($p['icServer']);
2689
			}
2690
			$this->mail_bo->reopen($p['mailbox']);
2691
			// Retrive all embedded attachments at once
2692
			// avoids to fetch heavy winmail.dat content
2693
			// for each file.
2694
			$attachments = $this->mail_bo->getTnefAttachments($p['uid'],$p['part']);
2695
		}
2696
2697
		foreach((array)$ids as $id)
2698
		{
2699
			$params = $getParams($id);
2700
			// when downloading a single file, name is not set
2701
			if (!$params['name']&&isset($_GET['name'])&&!$isMultipleDownload) $params['name'] = $_GET['name'];
2702 View Code Duplication
			if ($params['icServer'] && $params['icServer'] != $this->mail_bo->profileID)
2703
			{
2704
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2705
				$this->changeProfile($params['icServer']);
2706
			}
2707
			//error_log(__METHOD__.__LINE__.array2string($hA));
2708
			$this->mail_bo->reopen($params['mailbox']);
2709
			if ($params['is_winmail'])
2710
			{
2711
				// Try to find the right content for file id
2712
				foreach ($attachments as $key => $val)
0 ignored issues
show
Bug introduced by
The expression $attachments of type false|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
2713
				{
2714
					if ($key == $params['is_winmail']) $attachment = $val;
2715
				}
2716
			}
2717
			else
2718
			{
2719
				$attachment = $this->mail_bo->getAttachment($params['uid'],$params['part'],$params['is_winmail'],false);
2720
			}
2721
2722
			$file = $params['name'];
2723
			// when $isMultipleDownload the path holds no filename
2724
			while(Vfs::file_exists($path.($file && $isMultipleDownload ? '/'.$file : '')))
2725
			{
2726
				$dupe_count[$params['name']]++;
2727
				$file = pathinfo($params['name'], PATHINFO_FILENAME) .
2728
					' ('.($dupe_count[$params['name']] + 1).')' . '.' .
2729
					pathinfo($params['name'], PATHINFO_EXTENSION);
2730
			}
2731
			$params['name'] = $file;
2732
			//error_log(__METHOD__.__LINE__.array2string($attachment));
2733
			// when $isMultipleDownload the path holds no filename
2734
			if (!($fp = Vfs::fopen($file=$path.($params['name'] && $isMultipleDownload ? '/'.$params['name'] : ''),'wb')) ||
2735
				!fwrite($fp,$attachment['attachment']))
2736
			{
2737
				$err .= lang('Error saving %1!',$file);
2738
			}
2739
			if ($fp)
2740
			{
2741
				fclose($fp);
2742
			}
2743
		}
2744
		$this->mail_bo->closeConnection();
2745
		if ($rememberServerID != $this->mail_bo->profileID)
2746
		{
2747
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
2748
			$this->changeProfile($rememberServerID);
2749
		}
2750
		Framework::window_close(($err?$err:null));
2751
	}
2752
2753
	/**
2754
	 * Zip all attachments and send to user
2755
	 * @param string $message_id = null
2756
	 */
2757
	function download_zip($message_id=null)
2758
	{
2759
		//error_log(__METHOD__.__LINE__.array2string($_GET));
2760
		// First, get all attachment IDs
2761
		if(isset($_GET['id'])) $message_id	= $_GET['id'];
2762
		//error_log(__METHOD__.__LINE__.$message_id);
2763
		$rememberServerID = $this->mail_bo->profileID;
2764
		if(!is_numeric($message_id))
2765
		{
2766
			$hA = self::splitRowID($message_id);
2767
			$message_id = $hA['msgUID'];
2768
			$mailbox = $hA['folder'];
2769
			$icServerID = $hA['profileID'];
2770
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
2771
			{
2772
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
2773
				$this->changeProfile($icServerID);
2774
			}
2775
		}
2776
		else
2777
		{
2778
			$mailbox = $this->mail_bo->sessionData['mailbox'];
2779
		}
2780
		// always fetch all, even inline (images)
2781
		$fetchEmbeddedImages = true;
2782
		$attachments = $this->mail_bo->getMessageAttachments($message_id,null, null, $fetchEmbeddedImages, true,true,$mailbox);
2783
		// put them in VFS so they can be zipped
2784
		$header = $this->mail_bo->getMessageHeader($message_id,'',true,false,$mailbox);
2785
		//get_home_dir may fetch the users startfolder if set; if not writeable, action will fail. TODO: use temp_dir
2786
		$homedir = '/home/'.$GLOBALS['egw_info']['user']['account_lid'];
2787
		$temp_path = $homedir/*Vfs::get_home_dir()*/ . "/.mail_$message_id";
2788
		if(Vfs::is_dir($temp_path)) Vfs::remove ($temp_path);
2789
2790
		// Add subject to path, so it gets used as the file name
2791
		$path = $temp_path . '/' . ($header['SUBJECT'] ? Vfs::encodePathComponent($header['SUBJECT']) : lang('mail')) .'/';
2792
		if(!Vfs::mkdir($path, 0700, true))
2793
		{
2794
			Framework::message("Unable to open temp directory $path",'error');
2795
			return;
2796
		}
2797
2798
		$file_list = array();
2799
		$dupe_count = array();
2800
		$this->mail_bo->reopen($mailbox);
2801
		if ($attachments[0]['is_winmail'] && $attachments[0]['is_winmail']!='null')
2802
		{
2803
			$tnefAttachments = $this->mail_bo->getTnefAttachments($message_id, $attachments[0]['partID'],true);
2804
		}
2805
		foreach($attachments as $file)
2806
		{
2807
			if ($file['is_winmail'])
2808
			{
2809
				// Try to find the right content for file id
2810
				foreach ($tnefAttachments as $key => $val)
0 ignored issues
show
Bug introduced by
The expression $tnefAttachments of type false|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
2811
				{
2812
					error_log(__METHOD__.' winmail = '.$key);
2813
					if ($key == $file['is_winmail']) $attachment = $val;
2814
				}
2815
			}
2816
			else
2817
			{
2818
				$attachment = $this->mail_bo->getAttachment($message_id,$file['partID'],$file['is_winmail'],false,true);
2819
			}
2820
			$success=true;
2821
			if (empty($file['filename'])) $file['filename'] = $file['name'];
2822
			if(in_array($path.$file['filename'], $file_list))
2823
			{
2824
				$dupe_count[$path.$file['filename']]++;
2825
				$file['filename'] = pathinfo($file['filename'], PATHINFO_FILENAME) .
2826
					' ('.($dupe_count[$path.$file['filename']] + 1).')' . '.' .
2827
					pathinfo($file['filename'], PATHINFO_EXTENSION);
2828
			}
2829
			if (!($fp = Vfs::fopen($path.$file['filename'],'wb')) ||
2830
				!(!fseek($attachment['attachment'], 0, SEEK_SET) && stream_copy_to_stream($attachment['attachment'], $fp)))
2831
			{
2832
				$success=false;
2833
				Framework::message("Unable to zip {$file['filename']}",'error');
2834
			}
2835
			if ($success) $file_list[] = $path.$file['filename'];
2836
			if ($fp) fclose($fp);
2837
		}
2838
		$this->mail_bo->closeConnection();
2839
		if ($rememberServerID != $this->mail_bo->profileID)
2840
		{
2841
			//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
2842
			$this->changeProfile($rememberServerID);
2843
		}
2844
2845
		// Zip it up
2846
		Vfs::download_zip($file_list);
2847
2848
		// Clean up
2849
		Vfs::remove($temp_path);
2850
2851
		exit();
2852
	}
2853
2854
	function get_load_email_data($uid, $partID, $mailbox,$htmlOptions=null)
2855
	{
2856
		// seems to be needed, as if we open a mail from notification popup that is
2857
		// located in a different folder, we experience: could not parse message
2858
		$this->mail_bo->reopen($mailbox);
2859
		$this->mailbox = $mailbox;
2860
		$this->uid = $uid;
2861
		$this->partID = $partID;
2862
		$bufferHtmlOptions = $this->mail_bo->htmlOptions;
2863
		if (empty($htmlOptions)) $htmlOptions = $this->mail_bo->htmlOptions;
2864
		// fetching structure now, to supply it to getMessageBody and getMessageAttachment, so it does not get fetched twice
2865
		$structure = $this->mail_bo->getStructure($uid, $partID, $mailbox, false);
2866
		$calendar_part = null;
2867
		$bodyParts	= $this->mail_bo->getMessageBody($uid, ($htmlOptions?$htmlOptions:''), $partID, $structure, false, $mailbox, $calendar_part);
2868
2869
		// for meeting requests (multipart alternative with text/calendar part) let calendar render it
2870
		if ($calendar_part && isset($GLOBALS['egw_info']['user']['apps']['calendar']))
2871
		{
2872
			$charset = $calendar_part->getContentTypeParameter('charset');
2873
			$this->mail_bo->fetchPartContents($uid, $calendar_part);
2874
			Api\Cache::setSession('calendar', 'ical', array(
2875
				'charset' => $charset ? $charset : 'utf-8',
2876
				'attachment' => $calendar_part->getContents(),
2877
				'method' => $calendar_part->getContentTypeParameter('method'),
2878
			));
2879
			$this->mail_bo->htmlOptions = $bufferHtmlOptions;
2880
			Api\Translation::add_app('calendar');
2881
			return ExecMethod('calendar.calendar_uiforms.meeting',
0 ignored issues
show
Deprecated Code introduced by
The function ExecMethod() has been deprecated with message: use autoloadable class-names, instanciate and call method or use static methods

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
2882
				array('event'=>null,'msg'=>'','useSession'=>true)
2883
			);
2884
		}
2885
		// Compose the content of the frame
2886
		$frameHtml =
2887
			$this->get_email_header($this->mail_bo->getStyles($bodyParts)).
2888
			$this->showBody($this->getdisplayableBody($bodyParts,true,false), false);
2889
		//IE10 eats away linebreaks preceeded by a whitespace in PRE sections
2890
		$frameHtml = str_replace(" \r\n","\r\n",$frameHtml);
2891
		$this->mail_bo->htmlOptions = $bufferHtmlOptions;
2892
2893
		return $frameHtml;
2894
	}
2895
2896
	static function get_email_header($additionalStyle='')
2897
	{
2898
		// egw_info[flags][css] already include <style> tags
2899
		$GLOBALS['egw_info']['flags']['css'] = preg_replace('|</?style[^>]*>|i', '', $additionalStyle);
2900
		$GLOBALS['egw_info']['flags']['nofooter']=true;
2901
		$GLOBALS['egw_info']['flags']['nonavbar']=true;
2902
		// do NOT include any default CSS
2903
		Framework::includeCSS('mail', 'preview', true, true);
2904
2905
		// load preview.js to activate mailto links
2906
		Framework::includeJS('/mail/js/preview.js');
2907
2908
		// send CSP and content-type header
2909
		return $GLOBALS['egw']->framework->header();
2910
	}
2911
2912
	function showBody(&$body, $print=true,$fullPageTags=true)
2913
	{
2914
		$BeginBody = '<div class="mailDisplayBody">
2915
<table width="100%" style="table-layout:fixed"><tr><td class="td_display">';
2916
2917
		$EndBody = '</td></tr></table></div>';
2918
		if ($fullPageTags) $EndBody .= "</body></html>";
2919
		if ($print)	{
2920
			print $BeginBody. $body .$EndBody;
2921
		} else {
2922
			return $BeginBody. $body .$EndBody;
2923
		}
2924
	}
2925
2926
	function &getdisplayableBody($_bodyParts,$modifyURI=true,$useTidy = true)
2927
	{
2928
		$bodyParts	= $_bodyParts;
2929
2930
		$nonDisplayAbleCharacters = array('[\016]','[\017]',
2931
				'[\020]','[\021]','[\022]','[\023]','[\024]','[\025]','[\026]','[\027]',
2932
				'[\030]','[\031]','[\032]','[\033]','[\034]','[\035]','[\036]','[\037]');
2933
2934
		$body = '';
2935
2936
		//error_log(__METHOD__.array2string($bodyParts)); //exit;
2937
		if (empty($bodyParts)) return "";
2938
		foreach((array)$bodyParts as $singleBodyPart) {
2939
			if (!isset($singleBodyPart['body'])) {
2940
				$singleBodyPart['body'] = $this->getdisplayableBody($singleBodyPart,$modifyURI,$useTidy);
2941
				$body .= $singleBodyPart['body'];
2942
				continue;
2943
			}
2944
			$bodyPartIsSet = strlen(trim($singleBodyPart['body']));
2945
			if (!$bodyPartIsSet)
2946
			{
2947
				$body .= '';
2948
				continue;
2949
			}
2950
			if(!empty($body)) {
2951
				$body .= '<hr style="border:dotted 1px silver;">';
2952
			}
2953
			//error_log($singleBodyPart['body']);
2954
			//error_log(__METHOD__.__LINE__.' CharSet:'.$singleBodyPart['charSet'].' mimeType:'.$singleBodyPart['mimeType']);
2955
			// some characterreplacements, as they fail to translate
2956
			$sar = array(
2957
				'@(\x84|\x93|\x94)@',
2958
				'@(\x96|\x97|\x1a)@',
2959
				'@(\x82|\x91|\x92)@',
2960
				'@(\x85)@',
2961
				'@(\x86)@',
2962
				'@(\x99)@',
2963
				'@(\xae)@',
2964
			);
2965
			$rar = array(
2966
				'"',
2967
				'-',
2968
				'\'',
2969
				'...',
2970
				'&',
2971
				'(TM)',
2972
				'(R)',
2973
			);
2974
2975
			if(($singleBodyPart['mimeType'] == 'text/html' || $singleBodyPart['mimeType'] == 'text/plain') &&
2976
				strtoupper($singleBodyPart['charSet']) != 'UTF-8')
2977
			{
2978
				$singleBodyPart['body'] = preg_replace($sar,$rar,$singleBodyPart['body']);
2979
			}
2980
			//error_log(__METHOD__.__LINE__.'reports:'.$singleBodyPart['charSet']);
2981
			if ($singleBodyPart['charSet']=='us-ascii')
2982
			{
2983
				$orgCharSet=$singleBodyPart['charSet'];
2984
				$singleBodyPart['charSet'] = Api\Translation::detect_encoding($singleBodyPart['body']);
2985
				error_log(__METHOD__.__LINE__.'reports:'.$orgCharSet.' but seems to be:'.$singleBodyPart['charSet']);
2986
			}
2987
			$singleBodyPart['body'] = Api\Translation::convert_jsonsafe($singleBodyPart['body'],$singleBodyPart['charSet']);
2988
			//error_log(__METHOD__.__LINE__.array2string($singleBodyPart));
2989
			if($singleBodyPart['mimeType'] == 'text/plain')
2990
			{
2991
				$newBody	= @htmlentities($singleBodyPart['body'],ENT_QUOTES, strtoupper(Mail::$displayCharset));
2992
				//error_log(__METHOD__.__LINE__.'..'.$newBody);
2993
				// if empty and charset is utf8 try sanitizing the string in question
2994
				if (empty($newBody) && strtolower($singleBodyPart['charSet'])=='utf-8') $newBody = @htmlentities(iconv('utf-8', 'utf-8', $singleBodyPart['body']),ENT_QUOTES, strtoupper(Mail::$displayCharset));
2995
				// if the conversion to htmlentities fails somehow, try without specifying the charset, which defaults to iso-
2996
				if (empty($newBody)) $newBody    = htmlentities($singleBodyPart['body'],ENT_QUOTES);
2997
2998
				// search http[s] links and make them as links available again
2999
				// to understand what's going on here, have a look at
3000
				// http://www.php.net/manual/en/function.preg-replace.php
3001
3002
				// create links for websites
3003
				if ($modifyURI) $newBody = Api\Html::activate_links($newBody);
3004
				//error_log(__METHOD__.__LINE__.'..'.$newBody);
3005
				// redirect links for websites if you use no cookies
3006
				#if (!($GLOBALS['egw_info']['server']['usecookies']))
3007
				#	$newBody = preg_replace("/href=(\"|\')((http(s?):\/\/)|(www\.))([\w,\-,\/,\?,\=,\.,&amp;,!\n,\%,@,\(,\),\*,#,:,~,\+]+)(\"|\')/ie",
3008
				#		"'href=\"$webserverURL/redirect.php?go='.@htmlentities(urlencode('http$4://$5$6'),ENT_QUOTES,\"Mail::$displayCharset\").'\"'", $newBody);
3009
3010
				// create links for email addresses
3011
				//TODO:if ($modifyURI) $this->parseEmail($newBody);
3012
				// create links for inline images
3013
				if ($modifyURI)
3014
				{
3015
					$newBody = self::resolve_inline_images($newBody, $this->mailbox, $this->uid, $this->partID, 'plain');
3016
				}
3017
3018
				//TODO:$newBody	= $this->highlightQuotes($newBody);
3019
				// to display a mailpart of mimetype plain/text, may be better taged as preformatted
3020
				#$newBody	= nl2br($newBody);
3021
				// since we do not display the message as HTML anymore we may want to insert good linebreaking (for visibility).
3022
				//error_log(__METHOD__.__LINE__.'..'.$newBody);
3023
				// dont break lines that start with > (&gt; as the text was processed with htmlentities before)
3024
				$newBody	= "<pre>".Mail::wordwrap($newBody,90,"\n",'&gt;')."</pre>";
3025
			}
3026
			else
3027
			{
3028
				$alreadyHtmlLawed=false;
3029
				$newBody	= $singleBodyPart['body'];
3030
				//TODO:$newBody	= $this->highlightQuotes($newBody);
3031
				#error_log(print_r($newBody,true));
3032 View Code Duplication
				if ($useTidy && extension_loaded('tidy'))
3033
				{
3034
					$tidy = new tidy();
3035
					$cleaned = $tidy->repairString($newBody, Mail::$tidy_config,'utf8');
3036
					// Found errors. Strip it all so there's some output
3037
					if($tidy->getStatus() == 2)
3038
					{
3039
						error_log(__METHOD__.' ('.__LINE__.') '.' ->'.$tidy->errorBuffer);
0 ignored issues
show
Bug introduced by
The property errorBuffer does not seem to exist in tidy.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
3040
					}
3041
					else
3042
					{
3043
						$newBody = $cleaned;
3044
					}
3045
					if (!$preserveHTML)	// ToDo KL: $preserveHTML is NOT initialised, so always if is dead code
3046
					{
3047
						// filter only the 'body', as we only want that part, if we throw away the Api\Html
3048
						preg_match('`(<htm.+?<body[^>]*>)(.+?)(</body>.*?</html>)`ims', $newBody, $matches=array());
0 ignored issues
show
Bug introduced by
$matches = array() cannot be passed to preg_match() as the parameter $matches expects a reference.
Loading history...
3049
						if ($matches[2])
3050
						{
3051
							$hasOther = true;
3052
							$newBody = $matches[2];
3053
						}
3054
					}
3055
				}
3056
				else
3057
				{
3058
					// htmLawed filter only the 'body'
3059
					preg_match('`(<htm.+?<body[^>]*>)(.+?)(</body>.*?</html>)`ims', $newBody, $matches=array());
0 ignored issues
show
Bug introduced by
$matches = array() cannot be passed to preg_match() as the parameter $matches expects a reference.
Loading history...
3060
					if ($matches[2])
3061
					{
3062
						$hasOther = true;
3063
						$newBody = $matches[2];
3064
					}
3065
					$htmLawed = new Api\Html\HtmLawed();
3066
					// the next line should not be needed, but produces better results on HTML 2 Text conversion,
3067
					// as we switched off HTMLaweds tidy functionality
3068
					$newBody = str_replace(array('&amp;amp;','<DIV><BR></DIV>',"<DIV>&nbsp;</DIV>",'<div>&nbsp;</div>'),array('&amp;','<BR>','<BR>','<BR>'),$newBody);
3069
					$newBody = $htmLawed->run($newBody,Mail::$htmLawed_config);
3070
					if ($hasOther && $preserveHTML) $newBody = $matches[1]. $newBody. $matches[3];
3071
					$alreadyHtmlLawed=true;
3072
				}
3073
				// do the cleanup, set for the use of purifier
3074
				//$newBodyBuff = $newBody;
3075
				/* if (!$alreadyHtmlLawed)*/ Mail::getCleanHTML($newBody);
3076
/*
3077
				// 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
3078
				if (strtoupper(Mail::$displayCharset) == 'UTF-8')
3079
				{
3080
					$test = @json_encode($newBody);
3081
					//error_log(__METHOD__.__LINE__.' ->'.strlen($singleBodyPart['body']).' Error:'.json_last_error().'<- BodyPart:#'.$test.'#');
3082
					if (($test=="null" || $test === false || is_null($test)) && strlen($newBody)>0)
3083
					{
3084
						$newBody = $newBodyBuff;
3085
						$tv = Mail::$htmLawed_config['tidy'];
3086
						Mail::$htmLawed_config['tidy'] = 0;
3087
						Mail::getCleanHTML($newBody);
3088
						Mail::$htmLawed_config['tidy'] = $tv;
3089
					}
3090
				}
3091
*/
3092
				// removes stuff between http and ?http
3093
				$Protocol = '(http:\/\/|(ftp:\/\/|https:\/\/))';    // only http:// gets removed, other protocolls are shown
3094
				$newBody = preg_replace('~'.$Protocol.'[^>]*\?'.$Protocol.'~sim','$1',$newBody); // removes stuff between http:// and ?http://
3095
				// TRANSFORM MAILTO LINKS TO EMAILADDRESS ONLY, WILL BE SUBSTITUTED BY parseEmail TO CLICKABLE LINK
3096
				$newBody = preg_replace('/(?<!"|href=|href\s=\s|href=\s|href\s=)'.'mailto:([a-z0-9._-]+)@([a-z0-9_-]+)\.([a-z0-9._-]+)/i',
3097
					"\\1@\\2.\\3",
3098
					$newBody);
3099
3100
				// redirect links for websites if you use no cookies
3101
				#if (!($GLOBALS['egw_info']['server']['usecookies'])) { //do it all the time, since it does mask the mailadresses in urls
3102
					//TODO:if ($modifyURI) $this->parseHREF($newBody);
3103
				#}
3104
				// create links for inline images
3105
				if ($modifyURI)
3106
				{
3107
					$newBody = self::resolve_inline_images ($newBody, $this->mailbox, $this->uid, $this->partID);
3108
				}
3109
				// email addresses / mailto links get now activated on client-side
3110
			}
3111
3112
			$body .= $newBody;
3113
		}
3114
		// create links for windows shares
3115
		// \\\\\\\\ == '\\' in real life!! :)
3116
		$body = preg_replace("/(\\\\\\\\)([\w,\\\\,-]+)/i",
3117
			"<a href=\"file:$1$2\" target=\"_blank\"><font color=\"blue\">$1$2</font></a>", $body);
3118
3119
		$body = preg_replace($nonDisplayAbleCharacters,'',$body);
3120
3121
		return $body;
3122
	}
3123
3124
	/**
3125
	 * Resolve inline images from CID to proper url
3126
	 *
3127
	 * @param string $_body message content
3128
	 * @param string $_mailbox mail folder
3129
	 * @param string $_uid uid
3130
	 * @param string $_partID part id
3131
	 * @param string $_messageType = 'html', message type is either html or plain
3132
	 * @return string message body including all CID images replaced
3133
	 */
3134
	public static function resolve_inline_images ($_body,$_mailbox, $_uid, $_partID, $_messageType = 'html')
3135
	{
3136
		if ($_messageType === 'plain')
3137
		{
3138
			return self::resolve_inline_image_byType($_body, $_mailbox, $_uid, $_partID, 'plain');
3139
		}
3140
		else
3141
		{
3142
			foreach(array('src','url','background') as $type)
3143
			{
3144
				$_body = self::resolve_inline_image_byType($_body, $_mailbox, $_uid, $_partID, $type);
3145
			}
3146
			return $_body;
3147
		}
3148
	}
3149
3150
	/**
3151
	 * Replace CID with proper type of content understandable by browser
3152
	 *
3153
	 * @param type $_body content of message
3154
	 * @param type $_mailbox mail box
3155
	 * @param type $_uid uid
3156
	 * @param type $_partID part id
3157
	 * @param type $_type = 'src' type of inline image that needs to be resolved and replaced
3158
	 *	- types: {plain|src|url|background}
3159
	 * @return string returns body content including all CID replacements
3160
	 */
3161
	public static function resolve_inline_image_byType ($_body,$_mailbox, $_uid, $_partID, $_type ='src')
3162
	{
3163
		/**
3164
		 * Callback for preg_replace_callback function
3165
		 * returns matched CID replacement string based on given type
3166
		 * @param array $matches
3167
		 * @param string $_mailbox
3168
		 * @param string $_uid
3169
		 * @param string $_partID
3170
		 * @param string $_type
3171
		 * @return string|boolean returns the replace
3172
		*/
3173
		$replace_callback = function ($matches) use ($_mailbox,$_uid, $_partID,  $_type)
3174
		{
3175
			if (!$_type)	return false;
3176
			$CID = '';
3177
			// Build up matches according to selected type
3178
			switch ($_type)
3179
			{
3180
				case "plain":
3181
					$CID = $matches[1];
3182
					break;
3183
				case "src":
3184
					// as src:cid contains some kind of url, it is likely to be urlencoded
3185
					$CID = urldecode($matches[2]);
3186
					break;
3187
				case "url":
3188
					$CID = $matches[1];
3189
					break;
3190
				case "background":
3191
					$CID = $matches[2];
3192
					break;
3193
			}
3194
3195
			static $cache = array();	// some caching, if mails containing the same image multiple times
3196
3197
			if (is_array($matches) && $CID)
3198
			{
3199
				$linkData = array (
3200
					'menuaction'    => 'mail.mail_ui.displayImage',
3201
					'uid'		=> $_uid,
3202
					'mailbox'	=> base64_encode($_mailbox),
3203
					'cid'		=> base64_encode($CID),
3204
					'partID'	=> $_partID,
3205
				);
3206
				$imageURL = Egw::link('/index.php', $linkData);
3207
				// to test without data uris, comment the if close incl. it's body
3208
				if (Api\Header\UserAgent::type() != 'msie' || Api\Header\UserAgent::version() >= 8)
3209
				{
3210
					if (!isset($cache[$imageURL]))
3211
					{
3212
						if ($_type !="background")
3213
						{
3214
							$bo = Mail::getInstance(false, mail_ui::$icServerID);
3215
							$attachment = $bo->getAttachmentByCID($_uid, $CID, $_partID);
3216
3217
							// only use data uri for "smaller" images, as otherwise the first display of the mail takes to long
3218
							if (($attachment instanceof Horde_Mime_Part) && $attachment->getBytes() < 8192)	// msie=8 allows max 32k data uris
0 ignored issues
show
Bug introduced by
The class Horde_Mime_Part does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
3219
							{
3220
								$bo->fetchPartContents($_uid, $attachment);
3221
								$cache[$imageURL] = 'data:'.$attachment->getType().';base64,'.base64_encode($attachment->getContents());
3222
							}
3223
							else
3224
							{
3225
								$cache[$imageURL] = $imageURL;
3226
							}
3227
						}
3228
						else
3229
						{
3230
							$cache[$imageURL] = $imageURL;
3231
						}
3232
					}
3233
					$imageURL = $cache[$imageURL];
3234
				}
3235
3236
				// Decides the final result of replacement according to the type
3237
				switch ($_type)
3238
				{
3239
					case "plain":
3240
						return '<img src="'.$imageURL.'" />';
3241
					case "src":
3242
						return 'src="'.$imageURL.'"';
3243
					case "url":
3244
						return 'url('.$imageURL.');';
3245
					case "background":
3246
						return 'background="'.$imageURL.'"';
3247
				}
3248
			}
3249
			return false;
3250
		};
3251
3252
		// return new body content base on chosen type
3253
		switch($_type)
3254
		{
3255
			case"plain":
3256
				return preg_replace_callback("/\[cid:(.*)\]/iU",$replace_callback,$_body);
3257
			case "src":
3258
				return preg_replace_callback("/src=(\"|\')cid:(.*)(\"|\')/iU",$replace_callback,$_body);
3259
			case "url":
3260
				return preg_replace_callback("/url\(cid:(.*)\);/iU",$replace_callback,$_body);
3261
			case "background":
3262
				return preg_replace_callback("/background=(\"|\')cid:(.*)(\"|\')/iU",$replace_callback,$_body);
3263
		}
3264
	}
3265
3266
	/**
3267
	 * importMessage
3268
	 * @param array $content = null an array of content
3269
	 */
3270
	function importMessage($content=null)
3271
	{
3272
		//error_log(__METHOD__.__LINE__.$this->mail_bo->getDraftFolder());
3273
3274
		if (!empty($content))
3275
		{
3276
			//error_log(__METHOD__.__LINE__.array2string($content));
3277
			if ($content['vfsfile'])
3278
			{
3279
				$file = $content['vfsfile'] = array(
3280
					'name' => Vfs::basename($content['vfsfile']),
3281
					'type' => Vfs::mime_content_type($content['vfsfile']),
3282
					'file' => Vfs::PREFIX.$content['vfsfile'],
3283
					'size' => filesize(Vfs::PREFIX.$content['vfsfile']),
3284
				);
3285
			}
3286
			else
3287
			{
3288
				$file = $content['uploadForImport'];
3289
			}
3290
			$destination = $content['FOLDER'][0];
3291
3292
			if (stripos($destination,self::$delimiter)!==false) list($icServerID,$destination) = explode(self::$delimiter,$destination,2);
3293
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
3294
			{
3295
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
3296
				$this->changeProfile($icServerID);
3297
			}
3298
			//error_log(__METHOD__.__LINE__.self::$delimiter.array2string($destination));
3299
			$importID = Mail::getRandomString();
3300
			$importFailed = false;
3301
			try
3302
			{
3303
				$messageUid = $this->importMessageToFolder($file,$destination,$importID);
3304
			    $linkData = array
3305
			    (
3306
					'id' => $this->createRowID($destination, $messageUid, true),
3307
			    );
3308
			}
3309
			catch (Api\Exception\WrongUserinput $e)
3310
			{
3311
					$importFailed=true;
3312
					$content['msg']		= $e->getMessage();
3313
			}
3314
			if (!$importFailed)
3315
			{
3316
				list($width, $height) = explode('x', Link::get_registry('mail', 'add_popup'));
3317
				if ($width > 0 && $height > 0) Api\Json\Response::get()->call('resizeTo', $width, $height);
3318
				ExecMethod2('mail.mail_ui.displayMessage',$linkData);
0 ignored issues
show
Deprecated Code introduced by
The function ExecMethod2() has been deprecated with message: use autoloadable class-names, instanciate and call method or use static methods

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
3319
				return;
3320
			}
3321
		}
3322
		if (!is_array($content)) $content = array();
3323
		if (empty($content['FOLDER'])) $content['FOLDER']=(array)$this->mail_bo->getDraftFolder();
3324
		if (!empty($content['FOLDER']))
3325
		{
3326
			$compose = new mail_compose();
3327
			$sel_options['FOLDER'] = $compose->ajax_searchFolder(0,true);
3328
		}
3329
3330
		$etpl = new Etemplate('mail.importMessage');
3331
		$etpl->setElementAttribute('uploadForImport','onFinish','app.mail.uploadForImport');
3332
		$etpl->exec('mail.mail_ui.importMessage',$content,$sel_options,array(),array(),2);
3333
	}
3334
3335
	/**
3336
	 * importMessageToFolder
3337
	 *
3338
	 * @param array $_formData Array with information of name, type, file and size
3339
	 * @param string $_folder (passed by reference) will set the folder used. must be set with a folder, but will hold modifications if
3340
	 *					folder is modified
3341
	 * @param string $importID ID for the imported message, used by attachments to identify them unambiguously
3342
	 * @return mixed $messageUID or exception
3343
	 */
3344
	function importMessageToFolder($_formData,&$_folder,$importID='')
3345
	{
3346
		$importfailed = false;
3347
		//error_log(__METHOD__.__LINE__.array2string($_formData));
3348
		if (empty($_formData['file'])) $_formData['file'] = $_formData['tmp_name'];
3349
		// check if formdata meets basic restrictions (in tmp dir, or vfs, mimetype, etc.)
3350
		try
3351
		{
3352
			$tmpFileName = Mail::checkFileBasics($_formData,$importID);
3353
		}
3354
		catch (Api\Exception\WrongUserinput $e)
3355
		{
3356
			$importfailed = true;
3357
			$alert_msg .= $e->getMessage();
3358
		}
3359
		// -----------------------------------------------------------------------
3360
		if ($importfailed === false)
3361
		{
3362
			$mailObject = new Api\Mailer();
3363
			try
3364
			{
3365
				$this->mail_bo->parseFileIntoMailObject($mailObject, $tmpFileName);
3366
			}
3367
			catch (Api\Exception\AssertionFailed $e)
3368
			{
3369
				$importfailed = true;
3370
				$alert_msg .= $e->getMessage();
3371
			}
3372
			$this->mail_bo->openConnection();
3373
			if (empty($_folder))
3374
			{
3375
				$importfailed = true;
3376
				$alert_msg .= lang("Import of message %1 failed. Destination Folder not set.",$_formData['name']);
3377
			}
3378
			$delimiter = $this->mail_bo->getHierarchyDelimiter();
3379
			if($_folder=='INBOX'.$delimiter) $_folder='INBOX';
3380
			if ($importfailed === false)
3381
			{
3382
				if ($this->mail_bo->folderExists($_folder,true)) {
3383
					try
3384
					{
3385
						$messageUid = $this->mail_bo->appendMessage($_folder,
3386
							$mailObject->getRaw(),
3387
							null,'\\Seen');
3388
					}
3389
					catch (Api\Exception\WrongUserinput $e)
3390
					{
3391
						$importfailed = true;
3392
						$alert_msg .= lang("Import of message %1 failed. Could not save message to folder %2 due to: %3",$_formData['name'],$_folder,$e->getMessage());
3393
					}
3394
				}
3395
				else
3396
				{
3397
					$importfailed = true;
3398
					$alert_msg .= lang("Import of message %1 failed. Destination Folder %2 does not exist.",$_formData['name'],$_folder);
3399
				}
3400
			}
3401
		}
3402
		// set the url to open when refreshing
3403
		if ($importfailed == true)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
3404
		{
3405
			throw new Api\Exception\WrongUserinput($alert_msg);
3406
		}
3407
		else
3408
		{
3409
			return $messageUid;
3410
		}
3411
	}
3412
3413
	/**
3414
	 * importMessageFromVFS2DraftAndEdit
3415
	 *
3416
	 * @param array $formData Array with information of name, type, file and size; file is required,
3417
	 *                               name, type and size may be set here to meet the requirements
3418
	 *						Example: $formData['name']	= 'a_email.eml';
3419
	 *								 $formData['type']	= 'message/rfc822';
3420
	 *								 $formData['file']	= 'vfs://default/home/leithoff/a_email.eml';
3421
	 *								 $formData['size']	= 2136;
3422
	 * @return void
3423
	 */
3424
	function importMessageFromVFS2DraftAndEdit($formData='')
3425
	{
3426
		$this->importMessageFromVFS2DraftAndDisplay($formData,'edit');
3427
	}
3428
3429
	/**
3430
	 * importMessageFromVFS2DraftAndDisplay
3431
	 *
3432
	 * @param array $formData Array with information of name, type, file and size; file is required,
3433
	 *                               name, type and size may be set here to meet the requirements
3434
	 *						Example: $formData['name']	= 'a_email.eml';
3435
	 *								 $formData['type']	= 'message/rfc822';
3436
	 *								 $formData['file']	= 'vfs://default/home/leithoff/a_email.eml';
3437
	 *								 $formData['size']	= 2136;
3438
	 * @param string $mode mode to open ImportedMessage display and edit are supported
3439
	 * @return void
3440
	 */
3441
	function importMessageFromVFS2DraftAndDisplay($formData='',$mode='display')
3442
	{
3443
		if (empty($formData)) if (isset($_REQUEST['formData'])) $formData = $_REQUEST['formData'];
3444
		//error_log(__METHOD__.__LINE__.':'.array2string($formData).' Mode:'.$mode.'->'.function_backtrace());
3445
		$draftFolder = $this->mail_bo->getDraftFolder(false);
3446
		$importID = Mail::getRandomString();
3447
3448
		// handling for mime-data hash
3449
		if (!empty($formData['data']))
3450
		{
3451
			$formData['file'] = 'egw-data://'.$formData['data'];
3452
		}
3453
		// name should be set to meet the requirements of checkFileBasics
3454
		if (parse_url($formData['file'],PHP_URL_SCHEME) == 'vfs' && empty($formData['name']))
3455
		{
3456
			$buff = explode('/',$formData['file']);
3457
			if (is_array($buff)) $formData['name'] = array_pop($buff); // take the last part as name
3458
		}
3459
		// type should be set to meet the requirements of checkFileBasics
3460
		if (parse_url($formData['file'],PHP_URL_SCHEME) == 'vfs' && empty($formData['type']))
3461
		{
3462
			$buff = explode('.',$formData['file']);
3463
			$suffix = '';
3464
			if (is_array($buff)) $suffix = array_pop($buff); // take the last extension to check with ext2mime
3465
			if (!empty($suffix)) $formData['type'] = Api\MimeMagic::ext2mime($suffix);
3466
		}
3467
		// size should be set to meet the requirements of checkFileBasics
3468
		if (parse_url($formData['file'],PHP_URL_SCHEME) == 'vfs' && !isset($formData['size']))
3469
		{
3470
			$formData['size'] = strlen($formData['file']); // set some size, to meet requirements of checkFileBasics
3471
		}
3472
		try
3473
		{
3474
			$messageUid = $this->importMessageToFolder($formData,$draftFolder,$importID);
3475
			$linkData = array
3476
			(
3477
		        'menuaction'    => ($mode=='display'?'mail.mail_ui.displayMessage':'mail.mail_compose.composeFromDraft'),
3478
				'id'		=> $this->createRowID($draftFolder,$messageUid,true),
3479
				'deleteDraftOnClose' => 1,
3480
			);
3481
			if ($mode!='display')
3482
			{
3483
				unset($linkData['deleteDraftOnClose']);
3484
				$linkData['method']	='importMessageToMergeAndSend';
3485
			}
3486
			else
3487
			{
3488
				$linkData['mode']=$mode;
3489
			}
3490
			Egw::redirect_link('/index.php',$linkData);
3491
		}
3492
		catch (Api\Exception\WrongUserinput $e)
3493
		{
3494
			Framework::window_close($e->getMessage());
3495
		}
3496
	}
3497
3498
	/**
3499
	 * loadEmailBody
3500
	 *
3501
	 * @param string _messageID UID
3502
	 *
3503
	 * @return xajax response
3504
	 */
3505
	function loadEmailBody($_messageID=null,$_partID=null,$_htmloptions=null)
3506
	{
3507
		//error_log(__METHOD__.__LINE__.array2string($_GET));
3508
		if (!$_messageID && !empty($_GET['_messageID'])) $_messageID = $_GET['_messageID'];
3509
		if (!$_partID && !empty($_GET['_partID'])) $_partID = $_GET['_partID'];
3510
		if (!$_htmloptions && !empty($_GET['_htmloptions'])) $_htmloptions = $_GET['_htmloptions'];
3511
		if(Mail::$debug) error_log(__METHOD__."->".print_r($_messageID,true).",$_partID,$_htmloptions");
3512
		if (empty($_messageID)) return "";
3513
		$uidA = self::splitRowID($_messageID);
3514
		$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
3515
		$messageID = $uidA['msgUID'];
3516
		$icServerID = $uidA['profileID'];
3517
		//something went wrong. there is a $_messageID but no $messageID: means $_messageID is crippeled
3518
		if (empty($messageID)) return "";
3519
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
3520
		{
3521
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
3522
			$this->changeProfile($icServerID);
3523
		}
3524
3525
		$bodyResponse = $this->get_load_email_data($messageID,$_partID,$folder,$_htmloptions);
3526
		Api\Session::cache_control(true);
3527
		//error_log(array2string($bodyResponse));
3528
		echo $bodyResponse;
3529
3530
	}
3531
3532
	/**
3533
	 * ajax_setFolderStatus - its called via json, so the function must start with ajax (or the class-name must contain ajax)
3534
	 * gets the counters and sets the text of a treenode if needed (unread Messages found)
3535
	 * @param array $_folder folders to refresh its unseen message counters
3536
	 * @return nothing
3537
	 */
3538
	function ajax_setFolderStatus($_folder)
3539
	{
3540
		Api\Translation::add_app('mail');
3541
		//error_log(__METHOD__.__LINE__.array2string($_folder));
3542
		if ($_folder)
3543
		{
3544
			$this->mail_bo->getHierarchyDelimiter(false);
3545
			$oA = array();
3546
			foreach ($_folder as $_folderName)
3547
			{
3548
				list($profileID,$folderName) = explode(self::$delimiter,$_folderName,2);
3549
				if (is_numeric($profileID))
3550
				{
3551
					if ($profileID != $this->mail_bo->profileID) continue; // only current connection
3552
					if ($folderName)
3553
					{
3554
						try
3555
						{
3556
							$fS = $this->mail_bo->getFolderStatus($folderName,false,false,false);
3557
						}
3558
						catch (Exception $e)
3559
						{
3560
							if (Mail::$debug) error_log(__METHOD__,' ()'.$e->getMessage ());
3561
							continue;
3562
						}
3563
						if (in_array($fS['shortDisplayName'],Mail::$autoFolders)) $fS['shortDisplayName']=lang($fS['shortDisplayName']);
3564
						//error_log(__METHOD__.__LINE__.array2string($fS));
3565
						if ($fS['unseen'])
3566
						{
3567
							$oA[$_folderName] = $fS['shortDisplayName'].' ('.$fS['unseen'].')';
3568
						}
3569
						if ($fS['unseen']==0 && $fS['shortDisplayName'])
3570
						{
3571
							$oA[$_folderName] = $fS['shortDisplayName'];
3572
						}
3573
					}
3574
				}
3575
			}
3576
			//error_log(__METHOD__.__LINE__.array2string($oA));
3577
			if ($oA)
3578
			{
3579
				$response = Api\Json\Response::get();
3580
				$response->call('app.mail.mail_setFolderStatus',$oA);
3581
			}
3582
		}
3583
	}
3584
3585
	/**
3586
	 * ajax_addFolder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
3587
	 * @param string $_parentFolderName folder to add a folder to
3588
	 * @param string $_newName new foldername
3589
	 * @return nothing
3590
	 */
3591
	function ajax_addFolder($_parentFolderName, $_newName)
3592
	{
3593
		//error_log(__METHOD__.__LINE__.' ParentFolderName:'.array2string($_parentFolderName).' NewName/Folder:'.array2string($_newName));
3594
		$errorMessage='';
3595
		if ($_parentFolderName)
3596
		{
3597
			$created = false;
3598
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_parentFolderName);
3599
			//the conversion is handeled by horde, frontend interaction is all utf-8
3600
			$_newName = $this->mail_bo->decodeEntityFolderName($_newName);
3601
			list($profileID,$parentFolderName) = explode(self::$delimiter,$decodedFolderName,2);
3602
			if (is_numeric($profileID))
3603
			{
3604
				if ($profileID != $this->mail_bo->profileID) return; // only current connection
3605
				$del = $this->mail_bo->getHierarchyDelimiter(false);
3606
				//$del = $prefix = '';
3607
				//$nameSpace = $this->mail_bo->_getNameSpaces();
3608
				//error_log(__METHOD__.__LINE__.array2string($nameSpace));
3609
				// we expect something like that: data may differ!
3610
				//$nameSpace = Array(
3611
				//	[0] => Array([prefix_present] => [prefix] => [delimiter] => /[type] => personal)
3612
				//	[1] => Array([prefix_present] => 1[prefix] => Other Users/[delimiter] => /[type] => others)
3613
				//	[2] => Array([prefix_present] => 1[prefix] => Shared Folders/[delimiter] => /[type] => shared)
3614
				//)
3615
				//
3616
				/*
3617
				foreach ($nameSpace as $nSp)
3618
				{
3619
					error_log(__METHOD__.__LINE__.array2string($nSp));
3620
					// personal is assumed to be the default
3621
					if ($nSp['type']=='personal')
3622
					{
3623
						$prefix = $nSp['prefix'];
3624
						$del = $nSp['delimiter'];
3625
					}
3626
					if ($parentFolderName && $nSp['prefix_present'] && stripos($parentFolderName,$nSp['prefix'])!==false && stripos($parentFolderName,$nSp['prefix'])<=strlen($nSp['delimiter']))
3627
					{
3628
						$prefix = $nSp['prefix'];
3629
						$del = $nSp['delimiter'];
3630
						break;
3631
					}
3632
					if (empty($parentFolderName) && !$nSp['prefix_present'])
3633
					{
3634
						$del = $nSp['delimiter'];
3635
						break;
3636
					}
3637
				}
3638
3639
				if (empty($del)) $del = $this->mail_bo->getHierarchyDelimiter(false);
3640
				*/
3641
				$nA = explode($del,$_newName);
3642
3643
				//error_log(__METHOD__.__LINE__."$folderName, $parentFolder, $_newName");
3644
				if (!!empty($parentFolderName)) $oldFolderInfo = $this->mail_bo->getFolderStatus($parentFolderName,false);
3645
				//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
3646
3647
				$this->mail_bo->reopen('INBOX');
3648
				$parentName = $parentFolderName;
3649
				// if newName has delimiter ($del) in it, we need to create the subtree
3650
				if (!empty($nA))
3651
				{
3652
					$c=0;
3653
					foreach($nA as $sTName)
3654
					{
3655
						$error=null;
3656
						if(($parentFolderName = $this->mail_bo->createFolder($parentFolderName, $sTName, $error)))
3657
						{
3658
							$c++;
3659
						}
3660
						else
3661
						{
3662
							$errorMessage .= $error;
3663
						}
3664
					}
3665
					if ($c==count($nA)) $created=true;
3666
				}
3667
				if (!empty($parentName)) $this->mail_bo->reopen($parentName);
3668
			}
3669
			//error_log(__METHOD__.__LINE__.array2string($oA));
3670
			if ($created===true)
3671
			{
3672
				$this->mail_bo->resetFolderObjectCache($profileID);
3673
				$response = Api\Json\Response::get();
3674
				if ( $oldFolderInfo['shortDisplayName'])
3675
				{
3676
					$nodeInfo = array($_parentFolderName=>$oldFolderInfo['shortDisplayName']);
3677
				}
3678
				else
3679
				{
3680
					$nodeInfo = array($profileID=>lang('INBOX'));
3681
				}
3682
				$response->call('app.mail.mail_reloadNode',$nodeInfo);
3683
			}
3684
			else
3685
			{
3686
				if ($errorMessage)
3687
				{
3688
					$response = Api\Json\Response::get();
3689
					$response->call('egw.message',$errorMessage);
3690
				}
3691
			}
3692
		}
3693
	}
3694
3695
	/**
3696
	 * ajax_renameFolder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
3697
	 * @param string $_folderName folder to rename and refresh
3698
	 * @param string $_newName new foldername
3699
	 * @return nothing
3700
	 */
3701
	function ajax_renameFolder($_folderName, $_newName)
3702
	{
3703
		if (Mail::$debug) error_log(__METHOD__.__LINE__.' OldFolderName:'.array2string($_folderName).' NewName:'.array2string($_newName));
3704
		if ($_folderName)
3705
		{
3706
			Api\Translation::add_app('mail');
3707
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
3708
			$_newName = $this->mail_bo->decodeEntityFolderName($_newName);
3709
			$del = $this->mail_bo->getHierarchyDelimiter(false);
3710
			$oA = array();
3711
			list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
3712
			$hasChildren = false;
3713
			if (is_numeric($profileID))
3714
			{
3715
				if ($profileID != $this->mail_bo->profileID) return; // only current connection
3716
				$pA = explode($del,$folderName);
3717
				array_pop($pA);
3718
				$parentFolder = implode($del,$pA);
3719
				if (strtoupper($folderName)!= 'INBOX')
3720
				{
3721
					//error_log(__METHOD__.__LINE__."$folderName, $parentFolder, $_newName");
3722
					$oldFolderInfo = $this->mail_bo->getFolderStatus($folderName,false);
3723
					//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
3724
					if (!empty($oldFolderInfo['attributes']) && stripos(array2string($oldFolderInfo['attributes']),'\hasnochildren')=== false)
3725
					{
3726
						$hasChildren=true; // translates to: hasChildren -> dynamicLoading
3727
						$delimiter = $this->mail_bo->getHierarchyDelimiter();
3728
						$nameSpace = $this->mail_bo->_getNameSpaces();
3729
						$prefix = $this->mail_bo->getFolderPrefixFromNamespace($nameSpace, $folderName);
3730
						//error_log(__METHOD__.__LINE__.'->'."$_folderName, $delimiter, $prefix");
3731
						$fragments = array();
3732
						$subFolders = $this->mail_bo->getMailBoxesRecursive($folderName, $delimiter, $prefix);
3733
						foreach ($subFolders as $k => $folder)
3734
						{
3735
							// we do not monitor failure or success on subfolders
3736
							if ($folder == $folderName)
3737
							{
3738
								unset($subFolders[$k]);
3739
							}
3740
							else
3741
							{
3742
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, false);
3743
								$fragments[$profileID.self::$delimiter.$folder] = substr($folder,strlen($folderName));
3744
							}
3745
						}
3746
						//error_log(__METHOD__.__LINE__.' Fetched Subfolders->'.array2string($fragments));
3747
					}
3748
3749
					$this->mail_bo->reopen('INBOX');
3750
					$success = false;
3751
					try
3752
					{
3753 View Code Duplication
						if(($newFolderName = $this->mail_bo->renameFolder($folderName, $parentFolder, $_newName)))
3754
						{
3755
							$this->mail_bo->resetFolderObjectCache($profileID);
3756
							//enforce the subscription to the newly named server, as it seems to fail for names with umlauts
3757
							$this->mail_bo->icServer->subscribeMailbox($newFolderName, true);
3758
							$this->mail_bo->icServer->subscribeMailbox($folderName, false);
3759
							$success = true;
3760
						}
3761
					}
3762
					catch (Exception $e)
3763
					{
3764
						$newFolderName=$folderName;
3765
						$msg = $e->getMessage();
3766
					}
3767
					$this->mail_bo->reopen($newFolderName);
3768
					$fS = $this->mail_bo->getFolderStatus($newFolderName,false);
3769
					//error_log(__METHOD__.__LINE__.array2string($fS));
3770 View Code Duplication
					if ($hasChildren)
3771
					{
3772
						$subFolders = $this->mail_bo->getMailBoxesRecursive($newFolderName, $delimiter, $prefix);
3773
						foreach ($subFolders as $k => $folder)
3774
						{
3775
							// we do not monitor failure or success on subfolders
3776
							if ($folder == $folderName)
3777
							{
3778
								unset($subFolders[$k]);
3779
							}
3780
							else
3781
							{
3782
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, true);
3783
							}
3784
						}
3785
						//error_log(__METHOD__.__LINE__.' Fetched Subfolders->'.array2string($subFolders));
3786
					}
3787
3788
					$oA[$_folderName]['id'] = $profileID.self::$delimiter.$newFolderName;
3789
					$oA[$_folderName]['olddesc'] = $oldFolderInfo['shortDisplayName'];
3790 View Code Duplication
					if ($fS['unseen'])
3791
					{
3792
						$oA[$_folderName]['desc'] = $fS['shortDisplayName'].' ('.$fS['unseen'].')';
3793
3794
					}
3795
					else
3796
					{
3797
						$oA[$_folderName]['desc'] = $fS['shortDisplayName'];
3798
					}
3799
					foreach($fragments as $oldFolderName => $fragment)
3800
					{
3801
						//error_log(__METHOD__.__LINE__.':'.$oldFolderName.'->'.$profileID.self::$delimiter.$newFolderName.$fragment);
3802
						$oA[$oldFolderName]['id'] = $profileID.self::$delimiter.$newFolderName.$fragment;
3803
						$oA[$oldFolderName]['olddesc'] = '#skip-user-interaction-message#';
3804
						$fS = $this->mail_bo->getFolderStatus($newFolderName.$fragment,false);
3805 View Code Duplication
						if ($fS['unseen'])
3806
						{
3807
							$oA[$oldFolderName]['desc'] = $fS['shortDisplayName'].' ('.$fS['unseen'].')';
3808
3809
						}
3810
						else
3811
						{
3812
							$oA[$oldFolderName]['desc'] = $fS['shortDisplayName'];
3813
						}
3814
					}
3815
				}
3816
			}
3817 View Code Duplication
			if ($folderName==$this->mail_bo->sessionData['mailbox'])
3818
			{
3819
				$this->mail_bo->sessionData['mailbox']=$newFolderName;
3820
				$this->mail_bo->saveSessionData();
3821
			}
3822
			//error_log(__METHOD__.__LINE__.array2string($oA));
3823
			$response = Api\Json\Response::get();
3824
			if ($oA && $success)
3825
			{
3826
				$response->call('app.mail.mail_setLeaf',$oA);
3827
			}
3828
			else
3829
			{
3830
				$response->call('egw.refresh',lang('failed to rename %1 ! Reason: %2',$oldFolderName,$msg),'mail');
0 ignored issues
show
Bug introduced by
The variable $oldFolderName seems to be defined by a foreach iteration on line 3799. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
3831
			}
3832
		}
3833
	}
3834
3835
	/**
3836
	 * reload node
3837
	 *
3838
	 * @param string _folderName  folder to reload
3839
	 * @param boolean $_subscribedOnly = true
3840
	 * @return void
3841
	 */
3842
	function ajax_reloadNode($_folderName,$_subscribedOnly=true)
3843
	{
3844
		Api\Translation::add_app('mail');
3845
		$oldPrefForSubscribedOnly = !$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'];
3846
3847
		// prefs are plain prefs; we discussed an approach to have user only prefs, and
3848
		// set them on rightclick action on foldertree
3849
		//error_log(__METHOD__.__LINE__.' showAllFoldersInFolderPane:'.$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'].'/'.$GLOBALS['egw_info']['user']['preferences']['mail']['showAllFoldersInFolderPane']);
3850
3851
		$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
3852
		$this->mail_bo->getHierarchyDelimiter(false);
3853
		list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
3854
		// if pref and required mode dont match -> reset the folderObject cache to ensure
3855
		// that we get what we request
3856
		if ($_subscribedOnly != $oldPrefForSubscribedOnly) $this->mail_bo->resetFolderObjectCache($profileID);
3857
		if ($profileID != $this->mail_bo->profileID) return; // only current connection
3858
		if (!empty($folderName))
3859
		{
3860
			$parentFolder=(!empty($folderName)?$folderName:'INBOX');
3861
			$folderInfo = $this->mail_bo->getFolderStatus($parentFolder,false,false,false);
3862
			if ($folderInfo['unseen'])
3863
			{
3864
				$folderInfo['shortDisplayName'] = $folderInfo['shortDisplayName'].' ('.$folderInfo['unseen'].')';
3865
			}
3866
			if ($folderInfo['unseen']==0 && $folderInfo['shortDisplayName'])
3867
			{
3868
				$folderInfo['shortDisplayName'] = $folderInfo['shortDisplayName'];
3869
			}
3870
3871
			$refreshData = array(
3872
				$profileID.self::$delimiter.$parentFolder=>$folderInfo['shortDisplayName']);
3873
		}
3874
		else
3875
		{
3876
			$refreshData = array(
3877
				$profileID=>lang('INBOX')//string with no meaning lateron
3878
			);
3879
		}
3880
		// Send full info back in the response
3881
		$response = Api\Json\Response::get();
3882
		foreach($refreshData as $folder => &$name)
3883
		{
3884
			$name = $this->mail_tree->getTree($folder,$profileID,1,false, $_subscribedOnly,true);
3885
		}
3886
		$response->call('app.mail.mail_reloadNode',$refreshData);
3887
3888
	}
3889
3890
	/**
3891
	 * ResolveWinmail fetches the encoded attachments
3892
	 * from winmail.dat and will response expected structure back
3893
	 * to client in order to display them.
3894
	 *
3895
	 * Note: this ajax function should only be called via
3896
	 * nm mail selection as it does not support profile change
3897
	 * and uses the current available ic_server connection.
3898
	 *
3899
	 * @param type $_rowid row id from nm
3900
	 *
3901
	 */
3902
	function ajax_resolveWinmail ($_rowid)
3903
	{
3904
		$response = Api\Json\Response::get();
3905
3906
		$idParts = self::splitRowID($_rowid);
3907
		$uid = $idParts['msgUID'];
3908
		$mbox = $idParts['folder'];
3909
3910
		$attachments = $this->mail_bo->getMessageAttachments($uid, null, null, false,true,true,$mbox);
3911
		if (is_array($attachments))
3912
		{
3913
			$attachments = $this->createAttachmentBlock($attachments, $_rowid, $uid, $mbox, false);
3914
			$response->data($attachments);
3915
		}
3916
		else
3917
		{
3918
			$response->call('egw.message', lang('Can not resolve the winmail.dat attachment!'));
3919
		}
3920
	}
3921
3922
	/**
3923
	 * move folder
3924
	 *
3925
	 * @param string _folderName  folder to vove
3926
	 * @param string _target target folder
3927
	 *
3928
	 * @return void
3929
	 */
3930
	function ajax_MoveFolder($_folderName, $_target)
3931
	{
3932
		if (Mail::$debug) error_log(__METHOD__.__LINE__."Move Folder: $_folderName to Target: $_target");
3933
		if ($_folderName)
3934
		{
3935
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
3936
			$_newLocation2 = $this->mail_bo->decodeEntityFolderName($_target);
3937
			$del = $this->mail_bo->getHierarchyDelimiter(false);
3938
			list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
3939
			list($newProfileID,$_newLocation) = explode(self::$delimiter,$_newLocation2,2);
3940
			$hasChildren = false;
3941
			if (is_numeric($profileID))
3942
			{
3943
				if ($profileID != $this->mail_bo->profileID || $profileID != $newProfileID) return; // only current connection
3944
				$pA = explode($del,$folderName);
3945
				$namePart = array_pop($pA);
3946
				$_newName = $namePart;
3947
				$oldParentFolder = implode($del,$pA);
3948
				$parentFolder = $_newLocation;
3949
3950
				if (strtoupper($folderName)!= 'INBOX' &&
3951
					(($oldParentFolder === $parentFolder) || //$oldParentFolder == $parentFolder means move on same level
3952
					(($oldParentFolder != $parentFolder &&
3953
					strlen($parentFolder)>0 && strlen($folderName)>0 &&
3954
					strpos($parentFolder,$folderName)===false)))) // indicates that we move the older up the tree within its own branch
3955
				{
3956
					//error_log(__METHOD__.__LINE__."$folderName, $parentFolder, $_newName");
3957
					$oldFolderInfo = $this->mail_bo->getFolderStatus($folderName,false,false,false);
3958
					//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
3959
					if (!empty($oldFolderInfo['attributes']) && stripos(array2string($oldFolderInfo['attributes']),'\hasnochildren')=== false)
3960
					{
3961
						$hasChildren=true; // translates to: hasChildren -> dynamicLoading
3962
						$delimiter = $this->mail_bo->getHierarchyDelimiter();
3963
						$nameSpace = $this->mail_bo->_getNameSpaces();
3964
						$prefix = $this->mail_bo->getFolderPrefixFromNamespace($nameSpace, $folderName);
3965
						//error_log(__METHOD__.__LINE__.'->'."$_folderName, $delimiter, $prefix");
3966
3967
						$subFolders = $this->mail_bo->getMailBoxesRecursive($folderName, $delimiter, $prefix);
3968
						foreach ($subFolders as $k => $folder)
3969
						{
3970
							// we do not monitor failure or success on subfolders
3971
							if ($folder == $folderName)
3972
							{
3973
								unset($subFolders[$k]);
3974
							}
3975
							else
3976
							{
3977
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, false);
3978
							}
3979
						}
3980
					}
3981
3982
					$this->mail_bo->reopen('INBOX');
3983
					$success = false;
3984
					try
3985
					{
3986 View Code Duplication
						if(($newFolderName = $this->mail_bo->renameFolder($folderName, $parentFolder, $_newName)))
3987
						{
3988
							$this->mail_bo->resetFolderObjectCache($profileID);
3989
							//enforce the subscription to the newly named server, as it seems to fail for names with umlauts
3990
							$this->mail_bo->icServer->subscribeMailbox($newFolderName, true);
3991
							$this->mail_bo->icServer->subscribeMailbox($folderName, false);
3992
							$this->mail_bo->resetFolderObjectCache($profileID);
3993
							$success = true;
3994
						}
3995
					}
3996
					catch (Exception $e)
3997
					{
3998
						$newFolderName=$folderName;
3999
						$msg = $e->getMessage();
4000
					}
4001
					$this->mail_bo->reopen($parentFolder);
4002
					$this->mail_bo->getFolderStatus($parentFolder,false,false,false);
4003
					//error_log(__METHOD__.__LINE__.array2string($fS));
4004 View Code Duplication
					if ($hasChildren)
4005
					{
4006
						$subFolders = $this->mail_bo->getMailBoxesRecursive($parentFolder, $delimiter, $prefix);
4007
						foreach ($subFolders as $k => $folder)
4008
						{
4009
							// we do not monitor failure or success on subfolders
4010
							if ($folder == $folderName)
4011
							{
4012
								unset($subFolders[$k]);
4013
							}
4014
							else
4015
							{
4016
								$rv = $this->mail_bo->icServer->subscribeMailbox($folder, true);
4017
							}
4018
						}
4019
						//error_log(__METHOD__.__LINE__.' Fetched Subfolders->'.array2string($subFolders));
4020
					}
4021
				}
4022
			}
4023 View Code Duplication
			if ($folderName==$this->mail_bo->sessionData['mailbox'])
4024
			{
4025
				$this->mail_bo->sessionData['mailbox']=$newFolderName;
4026
				$this->mail_bo->saveSessionData();
4027
			}
4028
			//error_log(__METHOD__.__LINE__.array2string($oA));
4029
			$response = Api\Json\Response::get();
4030
			if ($success)
4031
			{
4032
				Api\Translation::add_app('mail');
4033
4034
				$oldFolderInfo = $this->mail_bo->getFolderStatus($oldParentFolder,false,false,false);
4035
				$folderInfo = $this->mail_bo->getFolderStatus($parentFolder,false,false,false);
4036
				$refreshData = array(
4037
					$profileID.self::$delimiter.$oldParentFolder=>$oldFolderInfo['shortDisplayName'],
4038
					$profileID.self::$delimiter.$parentFolder=>$folderInfo['shortDisplayName']);
4039
				// if we move the folder within the same parent-branch of the tree, there is no need no refresh the upper part
4040 View Code Duplication
				if (strlen($parentFolder)>strlen($oldParentFolder) && strpos($parentFolder,$oldParentFolder)!==false) unset($refreshData[$profileID.self::$delimiter.$parentFolder]);
4041 View Code Duplication
				if (count($refreshData)>1 && strlen($oldParentFolder)>strlen($parentFolder) && strpos($oldParentFolder,$parentFolder)!==false) unset($refreshData[$profileID.self::$delimiter.$oldParentFolder]);
4042
4043
				// Send full info back in the response
4044
				foreach($refreshData as $folder => &$name)
4045
				{
4046
					$name = $this->mail_tree->getTree($folder,$profileID,1,false,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'],true);
4047
				}
4048
				$response->call('app.mail.mail_reloadNode',$refreshData);
4049
4050
			}
4051
			else
4052
			{
4053
				$response->call('egw.refresh',lang('failed to move %1 ! Reason: %2',$folderName,$msg),'mail');
4054
			}
4055
		}
4056
	}
4057
4058
	/**
4059
	 * ajax_deleteFolder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4060
	 * @param string $_folderName folder to delete
4061
	 * @param boolean $_return = false wheter return the success value (true) or send response to client (false)
4062
	 * @return nothing
4063
	 */
4064
	function ajax_deleteFolder($_folderName, $_return = false)
4065
	{
4066
		//error_log(__METHOD__.__LINE__.' OldFolderName:'.array2string($_folderName));
4067
		$success = false;
4068
		if ($_folderName)
4069
		{
4070
			$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
4071
			$del = $this->mail_bo->getHierarchyDelimiter(false);
4072
			$oA = array();
4073
			list($profileID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
4074
			$hasChildren = false;
4075
			if (is_numeric($profileID))
4076
			{
4077
				if ($profileID != $this->mail_bo->profileID) return; // only current connection
4078
				$pA = explode($del,$folderName);
4079
				array_pop($pA);
4080
				if (strtoupper($folderName)!= 'INBOX')
4081
				{
4082
					//error_log(__METHOD__.__LINE__."$folderName,  implode($del,$pA), $_newName");
4083
					$oA = array();
4084
					$subFolders = array();
4085
					$oldFolderInfo = $this->mail_bo->getFolderStatus($folderName,false,false,false);
4086
					//error_log(__METHOD__.__LINE__.array2string($oldFolderInfo));
4087
					if (!empty($oldFolderInfo['attributes']) && stripos(array2string($oldFolderInfo['attributes']),'\hasnochildren')=== false)
4088
					{
4089
						$hasChildren=true; // translates to: hasChildren -> dynamicLoading
4090
						$ftD = array();
4091
						$delimiter = $this->mail_bo->getHierarchyDelimiter();
4092
						$nameSpace = $this->mail_bo->_getNameSpaces();
4093
						$prefix = $this->mail_bo->getFolderPrefixFromNamespace($nameSpace, $folderName);
4094
						//error_log(__METHOD__.__LINE__.'->'."$_folderName, $delimiter, $prefix");
4095
						$subFolders = $this->mail_bo->getMailBoxesRecursive($folderName, $delimiter, $prefix);
4096
						//error_log(__METHOD__.__LINE__.'->'."$folderName, $delimiter, $prefix");
4097
						foreach ($subFolders as $k => $f)
4098
						{
4099
							$ftD[substr_count($f,$delimiter)][]=$f;
4100
						}
4101
						krsort($ftD,SORT_NUMERIC);//sort per level
4102
						//we iterate per level of depth of the subtree, deepest nesting is to be deleted first, and then up the tree
4103
						foreach($ftD as $k => $lc)//collection per level
4104
						{
4105
							foreach($lc as $f)//folders contained in that level
4106
							{
4107
								try
4108
								{
4109
									//error_log(__METHOD__.__LINE__.array2string($f).'<->'.$folderName);
4110
									$this->mail_bo->deleteFolder($f);
4111
									$success = true;
4112
									if ($f==$folderName) $oA[$_folderName] = $oldFolderInfo['shortDisplayName'];
4113
								}
4114
								catch (Exception $e)
4115
								{
4116
									$msg .= ($msg?' ':'').lang("Failed to delete %1. Server responded:",$f).$e->getMessage();
4117
									$success = false;
4118
								}
4119
							}
4120
						}
4121
					}
4122
					else
4123
					{
4124
						try
4125
						{
4126
							$this->mail_bo->deleteFolder($folderName);
4127
							$success = true;
4128
							$oA[$_folderName] = $oldFolderInfo['shortDisplayName'];
4129
						}
4130
						catch (Exception $e)
4131
						{
4132
							$msg = $e->getMessage();
4133
							$success = false;
4134
						}
4135
					}
4136
				}
4137
				else
4138
				{
4139
					$msg = lang("refused to delete folder INBOX");
4140
				}
4141
			}
4142
			if ($_return) return $success;
4143
			$response = Api\Json\Response::get();
4144
			if ($success)
4145
			{
4146
				//error_log(__METHOD__.__LINE__.array2string($oA));
4147
				$response->call('app.mail.mail_removeLeaf',$oA);
4148
			}
4149
			else
4150
			{
4151
				$response->call('egw.refresh',lang('failed to delete %1 ! Reason: %2',$oldFolderInfo['shortDisplayName'],$msg),'mail');
4152
			}
4153
		}
4154
	}
4155
4156
	/**
4157
	 * empty changeProfile - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4158
	 *
4159
	 * Made static to NOT call __construct, as it would connect to old server, before going to new one
4160
	 *
4161
	 * @param int $icServerID New profile / server ID
4162
	 * @param bool $getFolders The client needs the folders for the profile
4163
	 * @return nothing
4164
	 */
4165
	public static function ajax_changeProfile($icServerID, $getFolders = true, $exec_id=null)
4166
	{
4167
		$response = Api\Json\Response::get();
4168
4169
		$previous_id = $GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'];
4170
4171
		if ($icServerID && $icServerID != $previous_id)
4172
		{
4173
			$mail_ui = new mail_ui(false);	// do NOT run constructor, as we call changeProfile anyway
4174
			try
4175
			{
4176
				$mail_ui->changeProfile($icServerID);
4177
				// if we have an eTemplate exec_id, also send changed actions
4178
				if ($exec_id && ($actions = $mail_ui->get_actions()))
4179
				{
4180
					$response->generic('assign', array(
4181
						'etemplate_exec_id' => $exec_id,
4182
						'id' => 'nm',
4183
						'key' => 'actions',
4184
						'value' => $actions,
4185
					));
4186
				}
4187
			}
4188
			catch (Exception $e) {
4189
				self::callWizard($e->getMessage(),true, 'error');
4190
			}
4191
		}
4192
		else
4193
		{
4194
			$mail_ui = new mail_ui(true);	// run constructor
4195
		}
4196
4197
		// Send full info back in the response
4198
		if($getFolders)
4199
		{
4200
			Api\Translation::add_app('mail');
4201
4202
			$refreshData = array(
4203
				$icServerID => $mail_ui->mail_tree->getTree(null,$icServerID,1,false,!$mail_ui->mail_bo->mailPreferences['showAllFoldersInFolderPane'],!$mail_ui->mail_bo->mailPreferences['showAllFoldersInFolderPane'])
4204
			);
4205
			$response->call('app.mail.mail_reloadNode',$refreshData);
4206
		}
4207
	}
4208
4209
	/**
4210
	 * ajax_refreshVacationNotice - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4211
	 *	Note: only the activeProfile VacationNotice is refreshed
4212
	 * @param int $icServerID profileId / server ID to work on; may be empty -> then activeProfile is used
4213
	 *						if other than active profile; nothing is done!
4214
	 * @return nothing
4215
	 */
4216
	public static function ajax_refreshVacationNotice($icServerID=null)
4217
	{
4218
		//Get vacation from cache if it's available
4219
		$cachedVacations = Api\Cache::getCache(Api\Cache::INSTANCE, 'email', 'vacationNotice'.$GLOBALS['egw_info']['user']['account_lid']);
4220
		$vacation = $cachedVacations[$icServerID];
4221
4222
		if (!$vacation)
4223
		{
4224
			try
4225
			{
4226
				// Create mail app object
4227
				$mail = new mail_ui();
4228
4229
				if (empty($icServerID)) $icServerID = $mail->Mail->profileID;
4230
				if ($icServerID != $mail->Mail->profileID) return;
4231
4232
				$vacation = $mail->gatherVacation($cachedVacations);
4233
			} catch (Exception $e) {
4234
				$vacation=false;
4235
				error_log(__METHOD__.__LINE__." ".$e->getMessage());
4236
				unset($e);
4237
			}
4238
		}
4239
4240
		if($vacation) {
4241
			if (is_array($vacation) && ($vacation['status'] == 'on' || $vacation['status']=='by_date'))
4242
			{
4243
				$dtfrmt = $GLOBALS['egw_info']['user']['preferences']['common']['dateformat'];
4244
				$refreshData['vacationnotice'] = lang('Vacation notice is active');
4245
				$refreshData['vacationrange'] = ($vacation['status']=='by_date'? Api\DateTime::server2user($vacation['start_date'],$dtfrmt,true).($vacation['end_date']>$vacation['start_date']?'->'.Api\DateTime::server2user($vacation['end_date']+ 24*3600-1,$dtfrmt,true):''):'');
0 ignored issues
show
Unused Code introduced by
The call to DateTime::server2user() has too many arguments starting with true.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
4246
				if ($vacation['status'] == 'by_date' && $vacation['end_date']+ 24*3600 < time())$refreshData = '';
4247
			}
4248
		}
4249
		if ($vacation==false)
4250
		{
4251
			$refreshData['vacationnotice'] =  '';
4252
			$refreshData['vacationrange'] =  '';
4253
		}
4254
		$response = Api\Json\Response::get();
4255
		$response->call('app.mail.mail_refreshVacationNotice',$refreshData);
4256
	}
4257
4258
	/**
4259
	 * ajax_refreshFilters - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4260
	 *	Note: only the activeProfile Filters are refreshed
4261
	 * @param int $icServerID profileId / server ID to work on; may be empty -> then activeProfile is used
4262
	 *						if other than active profile; nothing is done!
4263
	 * @return nothing
4264
	 */
4265
	function ajax_refreshFilters($icServerID=null)
4266
	{
4267
		//error_log(__METHOD__.__LINE__.array2string($icServerId));
4268
		if (empty($icServerID)) $icServerID = $this->mail_bo->profileID;
4269
		if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
4270
		{
4271
			Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
4272
			if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
4273
		}
4274 View Code Duplication
		if (!Mail::$supportsORinQuery[$this->mail_bo->profileID])
4275
		{
4276
			unset($this->searchTypes['quick']);
4277
			unset($this->searchTypes['quickwithcc']);
4278
		}
4279 View Code Duplication
		if ( $this->mail_bo->icServer->hasCapability('SUPPORTS_KEYWORDS'))
4280
		{
4281
			$this->statusTypes = array_merge($this->statusTypes,array(
4282
				'keyword1'	=> 'important',//lang('important'),
4283
				'keyword2'	=> 'job',	//lang('job'),
4284
				'keyword3'	=> 'personal',//lang('personal'),
4285
				'keyword4'	=> 'to do',	//lang('to do'),
4286
				'keyword5'	=> 'later',	//lang('later'),
4287
			));
4288
		}
4289
		else
4290
		{
4291
			$keywords = array('keyword1','keyword2','keyword3','keyword4','keyword5');
4292
			foreach($keywords as &$k)
4293
			{
4294
				if (array_key_exists($k,$this->statusTypes)) unset($this->statusTypes[$k]);
4295
			}
4296
		}
4297
4298
		$response = Api\Json\Response::get();
4299
		$response->call('app.mail.mail_refreshCatIdOptions',$this->searchTypes);
4300
		$response->call('app.mail.mail_refreshFilterOptions',$this->statusTypes);
4301
		$response->call('app.mail.mail_refreshFilter2Options',array(''=>lang('No Sneak Preview in list'),1=>lang('Sneak Preview in list')));
4302
4303
	}
4304
4305
	/**
4306
	 * This function asks quota from IMAP server and makes the
4307
	 * result as JSON response to send it to mail_sendQuotaDisplay
4308
	 * function in client side.
4309
	 *
4310
	 * @param string $icServerID = null
4311
	 *
4312
	 */
4313
	function ajax_refreshQuotaDisplay($icServerID=null)
4314
	{
4315
		Api\Translation::add_app('mail');
4316
		if (is_null($icServerID)) $icServerID = $this->mail_bo->profileID;
4317
		$rememberServerID = $this->mail_bo->profileID;
4318
		try
4319
		{
4320
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
4321
			{
4322
				$this->changeProfile($icServerID);
4323
			}
4324
			$quota = $this->mail_bo->getQuotaRoot();
4325
		} catch (Exception $e) {
4326
			$quota['limit'] = 'NOT SET';
4327
			error_log(__METHOD__.__LINE__." ".$e->getMessage());
4328
			unset($e);
4329
		}
4330
4331
		if($quota !== false && $quota['limit'] != 'NOT SET') {
4332
			$quotainfo = $this->quotaDisplay($quota['usage'], $quota['limit']);
4333
			$quotaMin = $quotainfo['freespace']/pow(1024, 2);
4334
			$content = array (
4335
				'quota'				=> $quotainfo['text'],
4336
				'quotainpercent'	=> (string)$quotainfo['percent'],
4337
				'quotaclass'		=> $quotainfo['class'],
4338
				'quotanotsupported'	=> "",
4339
				'profileid'			=> $icServerID,
4340
				'quotawarning'		=> $quotaMin < 50 ? true : false
4341
			);
4342
		}
4343
		else
4344
		{
4345
			$content = array (
4346
				'quota'				=> lang("Quota not provided by server"),
4347
				'quotaclass'		=> "mail_DisplayNone",
4348
				'quotanotsupported'	=> "mail_DisplayNone"
4349
			);
4350
		}
4351
		if ($rememberServerID != $this->mail_bo->profileID)
4352
		{
4353
			try
4354
			{
4355
				$this->changeProfile($rememberServerID);
4356
			} catch (Exception $e) {
4357
				unset($e);
4358
			}
4359
		}
4360
		$response = Api\Json\Response::get();
4361
		$response->call('app.mail.mail_setQuotaDisplay',array('data'=>$content));
4362
	}
4363
4364
	/**
4365
	 * Empty spam/junk folder
4366
	 *
4367
	 * @param string $icServerID id of the server to empty its junkFolder
4368
	 * @param string $selectedFolder seleted(active) folder by nm filter
4369
	 * @return nothing
4370
	 */
4371 View Code Duplication
	function ajax_emptySpam($icServerID, $selectedFolder)
4372
	{
4373
		//error_log(__METHOD__.__LINE__.' '.$icServerID);
4374
		Api\Translation::add_app('mail');
4375
		$response = Api\Json\Response::get();
4376
		$rememberServerID = $this->mail_bo->profileID;
4377
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
4378
		{
4379
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
4380
			$this->changeProfile($icServerID);
4381
		}
4382
		$junkFolder = $this->mail_bo->getJunkFolder();
4383
		if(!empty($junkFolder)) {
4384
			if ($selectedFolder == $icServerID.self::$delimiter.$junkFolder)
4385
			{
4386
				// Lock the tree if the active folder is junk folder
4387
				$response->call('app.mail.lock_tree');
4388
			}
4389
			$this->mail_bo->deleteMessages('all',$junkFolder,'remove_immediately');
4390
4391
			$heirarchyDelimeter = $this->mail_bo->getHierarchyDelimiter(true);
4392
			$fShortName =  array_pop(explode($heirarchyDelimeter, $junkFolder));
0 ignored issues
show
Bug introduced by
explode($heirarchyDelimeter, $junkFolder) cannot be passed to array_pop() as the parameter $array expects a reference.
Loading history...
4393
			$fStatus = array(
4394
				$icServerID.self::$delimiter.$junkFolder => lang($fShortName)
4395
			);
4396
			//Call to reset folder status counter, after junkFolder triggered not from Junk folder
4397
			//-as we don't have junk folder specific information available on client-side we need to deal with it on server
4398
			$response->call('app.mail.mail_setFolderStatus',$fStatus);
4399
		}
4400
		if ($rememberServerID != $this->mail_bo->profileID)
4401
		{
4402
			$oldFolderInfo = $this->mail_bo->getFolderStatus($junkFolder,false,false,false);
4403
			$response->call('egw.message',lang('empty junk'));
4404
			$response->call('app.mail.mail_reloadNode',array($icServerID.self::$delimiter.$junkFolder=>$oldFolderInfo['shortDisplayName']));
4405
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$rememberServerID);
4406
			$this->changeProfile($rememberServerID);
4407
		}
4408
		else if ($selectedFolder == $icServerID.self::$delimiter.$junkFolder)
4409
		{
4410
			$response->call('egw.refresh',lang('empty junk'),'mail');
4411
		}
4412
	}
4413
4414
	/**
4415
	 * Empty trash folder
4416
	 *
4417
	 * @param string $icServerID id of the server to empty its trashFolder
4418
	 * @param string $selectedFolder seleted(active) folder by nm filter
4419
	 * @return nothing
4420
	 */
4421 View Code Duplication
	function ajax_emptyTrash($icServerID, $selectedFolder)
4422
	{
4423
		//error_log(__METHOD__.__LINE__.' '.$icServerID);
4424
		Api\Translation::add_app('mail');
4425
		$response = Api\Json\Response::get();
4426
		$rememberServerID = $this->mail_bo->profileID;
4427
		if ($icServerID && $icServerID != $this->mail_bo->profileID)
4428
		{
4429
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
4430
			$this->changeProfile($icServerID);
4431
		}
4432
		$trashFolder = $this->mail_bo->getTrashFolder();
4433
		if(!empty($trashFolder)) {
4434
			if ($selectedFolder == $icServerID.self::$delimiter.$trashFolder)
4435
			{
4436
				// Lock the tree if the active folder is Trash folder
4437
				$response->call('app.mail.lock_tree');
4438
			}
4439
			$this->mail_bo->compressFolder($trashFolder);
4440
4441
			$heirarchyDelimeter = $this->mail_bo->getHierarchyDelimiter(true);
4442
			$fShortName =  array_pop(explode($heirarchyDelimeter, $trashFolder));
0 ignored issues
show
Bug introduced by
explode($heirarchyDelimeter, $trashFolder) cannot be passed to array_pop() as the parameter $array expects a reference.
Loading history...
4443
			$fStatus = array(
4444
				$icServerID.self::$delimiter.$trashFolder => lang($fShortName)
4445
			);
4446
			//Call to reset folder status counter, after emptyTrash triggered not from Trash folder
4447
			//-as we don't have trash folder specific information available on client-side we need to deal with it on server
4448
			$response->call('app.mail.mail_setFolderStatus',$fStatus);
4449
		}
4450
		if ($rememberServerID != $this->mail_bo->profileID)
4451
		{
4452
			$oldFolderInfo = $this->mail_bo->getFolderStatus($trashFolder,false,false,false);
4453
			$response->call('egw.message',lang('empty trash'));
4454
			$response->call('app.mail.mail_reloadNode',array($icServerID.self::$delimiter.$trashFolder=>$oldFolderInfo['shortDisplayName']));
4455
			//error_log(__METHOD__.__LINE__.' change Profile to ->'.$rememberServerID);
4456
			$this->changeProfile($rememberServerID);
4457
		}
4458
		else if ($selectedFolder == $icServerID.self::$delimiter.$trashFolder)
4459
		{
4460
			$response->call('egw.refresh',lang('empty trash'),'mail');
4461
		}
4462
	}
4463
4464
	/**
4465
	 * compress folder - its called via json, so the function must start with ajax (or the class-name must contain ajax)
4466
	 * fetches the current folder from session and compresses it
4467
	 * @param string $_folderName id of the folder to compress
4468
	 * @return nothing
4469
	 */
4470
	function ajax_compressFolder($_folderName)
4471
	{
4472
		//error_log(__METHOD__.__LINE__.' '.$_folderName);
4473
		Api\Translation::add_app('mail');
4474
4475
		$this->mail_bo->restoreSessionData();
4476
		$decodedFolderName = $this->mail_bo->decodeEntityFolderName($_folderName);
4477
		list($icServerID,$folderName) = explode(self::$delimiter,$decodedFolderName,2);
4478
4479
		if (empty($folderName)) $folderName = $this->mail_bo->sessionData['mailbox'];
4480
		if ($this->mail_bo->folderExists($folderName))
4481
		{
4482
			$rememberServerID = $this->mail_bo->profileID;
4483
			if ($icServerID && $icServerID != $this->mail_bo->profileID)
4484
			{
4485
				//error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID);
4486
				$this->changeProfile($icServerID);
4487
			}
4488
			if(!empty($_folderName)) {
4489
				$this->mail_bo->compressFolder($folderName);
4490
			}
4491
			if ($rememberServerID != $this->mail_bo->profileID)
4492
			{
4493
				//error_log(__METHOD__.__LINE__.' change Profile back to where we came from ->'.$rememberServerID);
4494
				$this->changeProfile($rememberServerID);
4495
			}
4496
			$response = Api\Json\Response::get();
4497
			$response->call('egw.refresh',lang('compress folder').': '.$folderName,'mail');
4498
		}
4499
	}
4500
4501
	/**
4502
	 * sendMDN, ...
4503
	 *
4504
	 * @param array _messageList list of UID's
4505
	 *
4506
	 * @return nothing
4507
	 */
4508
	function ajax_sendMDN($_messageList)
4509
	{
4510
		if(Mail::$debug) error_log(__METHOD__."->".array2string($_messageList));
4511
		$uidA = self::splitRowID($_messageList['msg'][0]);
4512
		$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4513
		$this->mail_bo->sendMDN($uidA['msgUID'],$folder);
4514
	}
4515
4516
	/**
4517
	 * flag messages as read, unread, flagged, ...
4518
	 *
4519
	 * @param string _flag name of the flag
4520
	 * @param array _messageList list of UID's
4521
	 * @param bool _sendJsonResponse tell fuction to send the JsonResponse
4522
	 *
4523
	 * @return xajax response
4524
	 */
4525
	function ajax_flagMessages($_flag, $_messageList, $_sendJsonResponse=true)
4526
	{
4527
		if(Mail::$debug) error_log(__METHOD__."->".$_flag.':'.array2string($_messageList));
4528
		Api\Translation::add_app('mail');
4529
		$alreadyFlagged=false;
4530
		$flag2check='';
4531
		$filter2toggle = $query = array();
4532
		if ($_messageList=='all' || !empty($_messageList['msg']))
4533
		{
4534
			if (isset($_messageList['all']) && $_messageList['all'])
4535
			{
4536
				// we have both messageIds AND allFlag folder information
4537
				$uidA = self::splitRowID($_messageList['msg'][0]);
4538
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4539
				if (isset($_messageList['activeFilters']) && $_messageList['activeFilters'])
4540
				{
4541
					$query = $_messageList['activeFilters'];
4542
					if (!empty($query['search']) || !empty($query['filter'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
4543
					{
4544
						//([filterName] => Schnellsuche[type] => quick[string] => ebay[status] => any
4545
						if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
4546
						{
4547
							Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
4548
							if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
4549
						}
4550
						//error_log(__METHOD__.__LINE__.' Startdate:'.$query['startdate'].' Enddate'.$query['enddate']);
4551
						$cutoffdate = $cutoffdate2 = null;
4552
						if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
4553
						if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
4554
						//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
4555
						$filter = array(
4556
							'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
4557
							'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
4558
							'string' => $query['search'],
4559
							'status' => 'any',//this is a status change. status will be manipulated later on
4560
							//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
4561
						);
4562
						if ($query['enddate']||$query['startdate']) {
4563
							$filter['range'] = "BETWEEN";
4564
							if ($cutoffdate) {
4565
								$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
4566
								if (empty($cutoffdate2)) $filter['range'] = "SINCE";
4567
							}
4568
							if ($cutoffdate2) {
4569
								$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
4570
								if (empty($cutoffdate)) $filter['range'] = "BEFORE";
4571
							}
4572
						}
4573
						$filter2toggle = $filter;
4574
					}
4575
					else
4576
					{
4577
						$filter = $filter2toggle = array();
4578
					}
4579
					// flags read,flagged,label1,label2,label3,label4,label5 can be toggled: handle this when all mails in a folder
4580
					// should be affected serverside. here.
4581
					$messageList = $messageListForToggle = array();
4582
					$flag2check = ($_flag=='read'?'seen':$_flag);
4583
					if (in_array($_flag,array('read','flagged','label1','label2','label3','label4','label5')) &&
4584
						!($flag2check==$query['filter'] || stripos($query['filter'],$flag2check)!==false))
4585
					{
4586
						$filter2toggle['status'] = array('un'.$_flag);
4587 View Code Duplication
						if ($query['filter'] && $query['filter']!='any')
4588
						{
4589
							$filter2toggle['status'][] = $query['filter'];
4590
						}
4591
						$rByUid = true;
4592
						$reverse = 1;
4593
						$_sRt = $this->mail_bo->getSortedList(
4594
							$folder,
4595
							$sort=0,
4596
							$reverse,
4597
							$filter2toggle,
4598
							$rByUid,
4599
							false
4600
						);
4601
						$messageListForToggle = $_sRt['match']->ids;
4602
						$filter['status'] = array($_flag);
4603 View Code Duplication
						if ($query['filter'] && $query['filter'] !='any')
4604
						{
4605
							$filter['status'][] = $query['filter'];
4606
						}
4607
						$rByUid=true;
4608
						$reverse = 1;
4609
						$_sR = $this->mail_bo->getSortedList(
4610
							$folder,
4611
							$sort=0,
4612
							$reverse,
4613
							$filter,
4614
							$rByUid,
4615
							false
4616
						);
4617
						$messageList = $_sR['match']->ids;
4618 View Code Duplication
						if (count($messageListForToggle)>0)
4619
						{
4620
							$flag2set = (strtolower($_flag));
4621
							if(Mail::$debug) error_log(__METHOD__.__LINE__." toggle un$_flag -> $flag2set ".array2string($filter2toggle).array2string($messageListForToggle));
4622
							$this->mail_bo->flagMessages($flag2set, $messageListForToggle,$folder);
4623
						}
4624 View Code Duplication
						if (count($messageList)>0)
4625
						{
4626
							$flag2set = 'un'.$_flag;
4627
							if(Mail::$debug) error_log(__METHOD__.__LINE__." $_flag -> $flag2set ".array2string($filter).array2string($messageList));
4628
							$this->mail_bo->flagMessages($flag2set, $messageList,$folder);
4629
						}
4630
						$alreadyFlagged=true;
4631
					}
4632
					elseif (!empty($filter) &&
4633
						(!in_array($_flag,array('read','flagged','label1','label2','label3','label4','label5')) ||
4634
						(in_array($_flag,array('read','flagged','label1','label2','label3','label4','label5')) &&
4635
						($flag2check==$query['filter'] || stripos($query['filter'],$flag2check)!==false))))
4636
					{
4637
						if ($query['filter'] && $query['filter'] !='any')
4638
						{
4639
							$filter['status'] = $query['filter'];
4640
							// since we toggle and we toggle by the filtered flag we must must change _flag
4641
							$_flag = ($query['filter']=='unseen' && $_flag=='read' ? 'read' : ($query['filter']=='seen'&& $_flag=='read'?'unread':($_flag==$query['filter']?'un'.$_flag:$_flag)));
4642
						}
4643
						if(Mail::$debug) error_log(__METHOD__.__LINE__." flag all with $_flag on filter used:".array2string($filter));
4644
						$rByUid = true;
4645
						$reverse = 1;
4646
						$_sR = $this->mail_bo->getSortedList(
4647
							$folder,
4648
							$sort=0,
4649
							$reverse,
4650
							$filter,
4651
							$rByUid,
4652
							false
4653
						);
4654
						$messageList = $_sR['match']->ids;
4655
						unset($_messageList['all']);
4656
						$_messageList['msg'] = array();
4657
					}
4658
					else
4659
					{
4660
						if(Mail::$debug) error_log(__METHOD__.__LINE__." $_flag all ".array2string($filter));
4661
						$alreadyFlagged=true;
4662
						$uidA = self::splitRowID($_messageList['msg'][0]);
4663
						$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4664
						$this->mail_bo->flagMessages($_flag, 'all', $folder);
4665
					}
4666
				}
4667
			}
4668
			else
4669
			{
4670
				$uidA = self::splitRowID($_messageList['msg'][0]);
4671
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4672
			}
4673
			if (!$alreadyFlagged)
4674
			{
4675 View Code Duplication
				foreach($_messageList['msg'] as $rowID)
4676
				{
4677
					$hA = self::splitRowID($rowID);
4678
					$messageList[] = $hA['msgUID'];
4679
				}
4680
				if(Mail::$debug) error_log(__METHOD__.__LINE__." $_flag in $folder:".array2string(((isset($_messageList['all']) && $_messageList['all']) ? 'all':$messageList)));
4681
				$this->mail_bo->flagMessages($_flag, ((isset($_messageList['all']) && $_messageList['all']) ? 'all':$messageList),$folder);
4682
			}
4683
		}
4684
		else
4685
		{
4686
			if(Mail::$debug) error_log(__METHOD__."-> No messages selected.");
4687
		}
4688
4689
		if ($_sendJsonResponse)
4690
		{
4691
			$flag=array(
4692
				'label1'	=> 'important',//lang('important'),
4693
				'label2'	=> 'job',	//lang('job'),
4694
				'label3'	=> 'personal',//lang('personal'),
4695
				'label4'	=> 'to do',	//lang('to do'),
4696
				'label5'	=> 'later',	//lang('later'),
4697
			);
4698
			$response = Api\Json\Response::get();
4699
			if (isset($_messageList['msg']) && $_messageList['popup'])
4700
			{
4701
				$response->call('egw.refresh',lang('flagged %1 messages as %2 in %3',$_messageList['msg'],lang(($flag[$_flag]?$flag[$_flag]:$_flag)),$folder),'mail', $_messageList['msg'], 'update');
4702
			}
4703
			else if ((isset($_messageList['all']) && $_messageList['all']) || ($query['filter'] && ($flag2check==$query['filter'] || stripos($query['filter'],$flag2check)!==false)))
4704
			{
4705
				$response->call('egw.refresh',lang('flagged %1 messages as %2 in %3',(isset($_messageList['all']) && $_messageList['all']?lang('all'):count($_messageList['msg'])),lang(($flag[$_flag]?$flag[$_flag]:$_flag)),$folder),'mail');
4706
			}
4707
			else
4708
			{
4709
				$response->call('egw.message',lang('flagged %1 messages as %2 in %3',(isset($_messageList['all']) && $_messageList['all']?lang('all'):count($_messageList['msg'])),lang(($flag[$_flag]?$flag[$_flag]:$_flag)),$folder));
4710
			}
4711
		}
4712
	}
4713
4714
	/**
4715
	 * delete messages
4716
	 *
4717
	 * @param array _messageList list of UID's
4718
	 * @param string _forceDeleteMethod - method of deletion to be enforced
4719
	 * @return xajax response
4720
	 */
4721
	function ajax_deleteMessages($_messageList,$_forceDeleteMethod=null)
4722
	{
4723
		if(Mail::$debug) error_log(__METHOD__."->".print_r($_messageList,true).' Method:'.$_forceDeleteMethod);
4724
		$error = null;
4725
		$filtered =  false;
4726
		if ($_messageList=='all' || !empty($_messageList['msg']))
4727
		{
4728
			if (isset($_messageList['all']) && $_messageList['all'])
4729
			{
4730
				// we have both messageIds AND allFlag folder information
4731
				$uidA = self::splitRowID($_messageList['msg'][0]);
4732
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4733
				if (isset($_messageList['activeFilters']) && $_messageList['activeFilters'])
4734
				{
4735
					$query = $_messageList['activeFilters'];
4736 View Code Duplication
					if (!empty($query['search']) || !empty($query['filter'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
4737
					{
4738
						//([filterName] => Schnellsuche[type] => quick[string] => ebay[status] => any
4739
						if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
4740
						{
4741
							Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
4742
							if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
4743
						}
4744
						$filtered =  true;
4745
						$cutoffdate = $cutoffdate2 = null;
4746
						if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
4747
						if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
4748
						//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
4749
						$filter = array(
4750
							'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
4751
							'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
4752
							'string' => $query['search'],
4753
							'status' => (!empty($query['filter'])?$query['filter']:'any'),
4754
							//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
4755
						);
4756
						if ($query['enddate']||$query['startdate']) {
4757
							$filter['range'] = "BETWEEN";
4758
							if ($cutoffdate) {
4759
								$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
4760
								if (empty($cutoffdate2)) $filter['range'] = "SINCE";
4761
							}
4762
							if ($cutoffdate2) {
4763
								$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
4764
								if (empty($cutoffdate)) $filter['range'] = "BEFORE";
4765
							}
4766
						}
4767
					}
4768
					else
4769
					{
4770
						$filter = array();
4771
					}
4772
					//error_log(__METHOD__.__LINE__."->".print_r($filter,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod);
4773
					$reverse = 1;
4774
					$rByUid = true;
4775
					$_sR = $this->mail_bo->getSortedList(
4776
						$folder,
4777
						$sort=0,
4778
						$reverse,
4779
						$filter,
4780
						$rByUid,
4781
						false
4782
					);
4783
					$messageList = $_sR['match']->ids;
4784
				}
4785
				else
4786
				{
4787
					$messageList='all';
4788
				}
4789
				try
4790
				{
4791
					//error_log(__METHOD__.__LINE__."->".print_r($messageList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod);
4792
					$this->mail_bo->deleteMessages(($messageList=='all' ? 'all':$messageList),$folder,(empty($_forceDeleteMethod)?'no':$_forceDeleteMethod));
4793
				}
4794
				catch (Api\Exception $e)
4795
				{
4796
					$error = str_replace('"',"'",$e->getMessage());
4797
				}
4798
			}
4799
			else
4800
			{
4801
				$uidA = self::splitRowID($_messageList['msg'][0]);
4802
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4803 View Code Duplication
				foreach($_messageList['msg'] as $rowID)
4804
				{
4805
					$hA = self::splitRowID($rowID);
4806
					$messageList[] = $hA['msgUID'];
4807
				}
4808
				try
4809
				{
4810
					//error_log(__METHOD__.__LINE__."->".print_r($messageList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod);
4811
					$this->mail_bo->deleteMessages($messageList,$folder,(empty($_forceDeleteMethod)?'no':$_forceDeleteMethod));
4812
				}
4813
				catch (Api\Exception $e)
4814
				{
4815
					$error = str_replace('"',"'",$e->getMessage());
4816
				}
4817
			}
4818
			$response = Api\Json\Response::get();
4819
			if (empty($error))
4820
			{
4821
				$response->call('app.mail.mail_deleteMessagesShowResult',array('egw_message'=>lang('deleted %1 messages in %2',($messageList=='all'||$_messageList['all']?($filtered?lang('all filtered'):lang('all')):count($_messageList['msg'])),$folder),'msg'=>$_messageList['msg']));
4822
			}
4823
			else
4824
			{
4825
				$error = str_replace('\n',"\n",lang('mailserver reported:\n%1 \ndo you want to proceed by deleting the selected messages immediately (click ok)?\nif not, please try to empty your trashfolder before continuing. (click cancel)',$error));
4826
				$response->call('app.mail.mail_retryForcedDelete',array('response'=>$error,'messageList'=>$_messageList));
4827
			}
4828
		}
4829
		else
4830
		{
4831
			if(Mail::$debug) error_log(__METHOD__."-> No messages selected.");
4832
		}
4833
	}
4834
4835
	/**
4836
	 * copy messages
4837
	 *
4838
	 * @param array _folderName target folder
4839
	 * @param array _messageList list of UID's
4840
	 * @param string _copyOrMove method to use copy or move allowed
4841
	 * @param string _move2ArchiveMarker marker to indicate if a move 2 archive was triggered
4842
	 *
4843
	 * @return xajax response
4844
	 */
4845
	function ajax_copyMessages($_folderName, $_messageList, $_copyOrMove='copy', $_move2ArchiveMarker='_')
4846
	{
4847
		if(Mail::$debug) error_log(__METHOD__."->".$_folderName.':'.print_r($_messageList,true).' Method:'.$_copyOrMove.' ArchiveMarker:'.$_move2ArchiveMarker);
4848
		Api\Translation::add_app('mail');
4849
		$folderName = $this->mail_bo->decodeEntityFolderName($_folderName);
4850
		// only copy or move are supported as method
4851
		if (!($_copyOrMove=='copy' || $_copyOrMove=='move')) $_copyOrMove='copy';
4852
		list($targetProfileID,$targetFolder) = explode(self::$delimiter,$folderName,2);
4853
		// check if move2archive was called with the correct archiveFolder
4854
		$archiveFolder = $this->mail_bo->getArchiveFolder();
4855
		if ($_move2ArchiveMarker=='2' && $targetFolder != $archiveFolder)
4856
		{
4857
			error_log(__METHOD__.__LINE__."#Move to Archive called with:"."$targetProfileID,$targetFolder");
4858
			$targetProfileID = $this->mail_bo->profileID;
4859
			$targetFolder = $archiveFolder;
4860
			error_log(__METHOD__.__LINE__."#Fixed ArchiveFolder:"."$targetProfileID,$targetFolder");
4861
		}
4862
		$lastFoldersUsedForMoveCont = Api\Cache::getCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),null,array(),$expiration=60*60*1);
4863
		$changeFolderActions = false;
4864
		//error_log(__METHOD__.__LINE__."#"."$targetProfileID,$targetFolder");
4865
		//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont));
4866
		if (!isset($lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]))
4867
		{
4868
			//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]));
4869
			if ($lastFoldersUsedForMoveCont[$targetProfileID] && count($lastFoldersUsedForMoveCont[$targetProfileID])>3)
4870
			{
4871
				$keys = array_keys($lastFoldersUsedForMoveCont[$targetProfileID]);
4872
				foreach( $keys as &$f)
4873
				{
4874
					if (count($lastFoldersUsedForMoveCont[$targetProfileID])>9) unset($lastFoldersUsedForMoveCont[$targetProfileID][$f]);
4875
					else break;
4876
				}
4877
				//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont[$targetProfileID]));
4878
			}
4879
			//error_log(__METHOD__.__LINE__."#"."$targetProfileID,$targetFolder = $_folderName");
4880
			$lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]=$folderName;
4881
			$changeFolderActions = true;
4882
		}
4883
		$filtered = false;
4884
		if ($_messageList=='all' || !empty($_messageList['msg']))
4885
		{
4886
			$error=false;
4887
			if (isset($_messageList['all']) && $_messageList['all'])
4888
			{
4889
				// we have both messageIds AND allFlag folder information
4890
				$uidA = self::splitRowID($_messageList['msg'][0]);
4891
				$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4892
				$sourceProfileID = $uidA['profileID'];
4893
				if (isset($_messageList['activeFilters']) && $_messageList['activeFilters'])
4894
				{
4895
					$query = $_messageList['activeFilters'];
4896 View Code Duplication
					if (!empty($query['search']) || !empty($query['filter'])||($query['cat_id']=='bydate' && (!empty($query['startdate'])||!empty($query['enddate']))))
4897
					{
4898
						//([filterName] => Schnellsuche[type] => quick[string] => ebay[status] => any
4899
						if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[$this->mail_bo->profileID]))
4900
						{
4901
							Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']), null, array(), 60*60*10);
4902
							if (!isset(Mail::$supportsORinQuery[$this->mail_bo->profileID])) Mail::$supportsORinQuery[$this->mail_bo->profileID]=true;
4903
						}
4904
						$filtered = true;
4905
						$cutoffdate = $cutoffdate2 = null;
4906
						if ($query['startdate']) $cutoffdate = Api\DateTime::to($query['startdate'],'ts');//SINCE, enddate
4907
						if ($query['enddate']) $cutoffdate2 = Api\DateTime::to($query['enddate'],'ts');//BEFORE, startdate
4908
						//error_log(__METHOD__.__LINE__.' Startdate:'.$cutoffdate2.' Enddate'.$cutoffdate);
4909
						$filter = array(
4910
							'filterName' => (Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?lang('quicksearch'):lang('subject')),
4911
							'type' => ($query['cat_id']?$query['cat_id']:(Mail::$supportsORinQuery[$mail_ui->mail_bo->profileID]?'quick':'subject')),
4912
							'string' => $query['search'],
4913
							'status' => (!empty($query['filter'])?$query['filter']:'any'),
4914
							//'range'=>"BETWEEN",'since'=> date("d-M-Y", $cutoffdate),'before'=> date("d-M-Y", $cutoffdate2)
4915
						);
4916
						if ($query['enddate']||$query['startdate']) {
4917
							$filter['range'] = "BETWEEN";
4918
							if ($cutoffdate) {
4919
								$filter[(empty($cutoffdate2)?'date':'since')] =  date("d-M-Y", $cutoffdate);
4920
								if (empty($cutoffdate2)) $filter['range'] = "SINCE";
4921
							}
4922
							if ($cutoffdate2) {
4923
								$filter[(empty($cutoffdate)?'date':'before')] =  date("d-M-Y", $cutoffdate2);
4924
								if (empty($cutoffdate)) $filter['range'] = "BEFORE";
4925
							}
4926
						}
4927
					}
4928
					else
4929
					{
4930
						$filter = array();
4931
					}
4932
					$reverse = 1;
4933
					$rByUid = true;
4934
					$_sR = $this->mail_bo->getSortedList(
4935
						$folder,
4936
						$sort=0,
4937
						$reverse,
4938
						$filter,
4939
						$rByUid=true,
0 ignored issues
show
Bug introduced by
$rByUid = true cannot be passed to getsortedlist() as the parameter $resultByUid expects a reference.
Loading history...
4940
						false
4941
					);
4942
					$messageList = $_sR['match']->ids;
4943
					foreach($messageList as $uID)
4944
					{
4945
						//error_log(__METHOD__.__LINE__.$uID);
4946
						if ($_copyOrMove=='move')
4947
						{
4948
							$messageListForRefresh[] = self::generateRowID($sourceProfileID, $folderName, $uID, $_prependApp=false);
4949
						}
4950
					}
4951
				}
4952
				else
4953
				{
4954
					$messageList='all';
4955
				}
4956
				try
4957
				{
4958
					//error_log(__METHOD__.__LINE__."->".print_r($messageList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod.' '.$targetProfileID.'/'.$sourceProfileID);
4959
					$this->mail_bo->moveMessages($targetFolder,$messageList,($_copyOrMove=='copy'?false:true),$folder,false,$sourceProfileID,($targetProfileID!=$sourceProfileID?$targetProfileID:null));
4960
				}
4961
				catch (Api\Exception $e)
4962
				{
4963
					$error = str_replace('"',"'",$e->getMessage());
4964
				}
4965
			}
4966
			else
4967
			{
4968
				$messageList = array();
4969
				while(count($_messageList['msg']) > 0)
4970
				{
4971
					$uidA = self::splitRowID($_messageList['msg'][0]);
4972
					$folder = $uidA['folder']; // all messages in one set are supposed to be within the same folder
4973
					$sourceProfileID = $uidA['profileID'];
4974
					$moveList = array();
4975
					foreach($_messageList['msg'] as $rowID)
4976
					{
4977
						$hA = self::splitRowID($rowID);
4978
4979
						// If folder changes, stop and move what we've got
4980
						if($hA['folder'] != $folder) break;
4981
4982
						array_shift($_messageList['msg']);
4983
						$messageList[] = $hA['msgUID'];
4984
						$moveList[] = $hA['msgUID'];
4985
						if ($_copyOrMove=='move')
4986
						{
4987
							$helpvar = explode(self::$delimiter,$rowID);
4988
							array_shift($helpvar);
4989
							$messageListForRefresh[]= implode(self::$delimiter,$helpvar);
4990
						}
4991
					}
4992
					try
4993
					{
4994
						//error_log(__METHOD__.__LINE__."->".print_r($moveList,true).' folder:'.$folder.' Method:'.$_forceDeleteMethod.' '.$targetProfileID.'/'.$sourceProfileID);
4995
						$this->mail_bo->moveMessages($targetFolder,$moveList,($_copyOrMove=='copy'?false:true),$folder,false,$sourceProfileID,($targetProfileID!=$sourceProfileID?$targetProfileID:null));
4996
					}
4997
					catch (Api\Exception $e)
4998
					{
4999
						$error = str_replace('"',"'",$e->getMessage());
5000
					}
5001
				}
5002
			}
5003
5004
			$response = Api\Json\Response::get();
5005
			if ($error)
0 ignored issues
show
Bug Best Practice introduced by
The expression $error of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
5006
			{
5007
				if ($changeFolderActions == false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
5008
				{
5009
					unset($lastFoldersUsedForMoveCont[$targetProfileID][$targetFolder]);
5010
					$changeFolderActions = true;
5011
				}
5012
				$response->call('egw.message',$error,"error");
5013
			}
5014
			else
5015
			{
5016
				if ($_copyOrMove=='copy')
5017
				{
5018
					$response->call('egw.message',lang('copied %1 message(s) from %2 to %3',($messageList=='all'||$_messageList['all']?($filtered?lang('all filtered'):lang('all')):count($messageList)),$folder,$targetFolder));
5019
				}
5020
				else
5021
				{
5022
					$response->call('egw.refresh',lang('moved %1 message(s) from %2 to %3',($messageList=='all'||$_messageList['all']?($filtered?lang('all filtered'):lang('all')):count($messageList)),$folder,$targetFolder),'mail',$messageListForRefresh,'delete');
5023
				}
5024
			}
5025
			if ($changeFolderActions == true)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
5026
			{
5027
				//error_log(__METHOD__.__LINE__.array2string($lastFoldersUsedForMoveCont));
5028
				Api\Cache::setCache(Api\Cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),$lastFoldersUsedForMoveCont, $expiration=60*60*1);
5029
				$actionsnew = Etemplate\Widget\Nextmatch::egw_actions(self::get_actions());
5030
				$response->call('app.mail.mail_rebuildActionsOnList',$actionsnew);
5031
			}
5032
		}
5033
		else
5034
		{
5035
			if(Mail::$debug) error_log(__METHOD__."-> No messages selected.");
5036
		}
5037
	}
5038
5039
	/**
5040
	 * Autoloading function to load branches of tree node
5041
	 * of management folder tree
5042
	 *
5043
	 * @param type $_id
5044
	 */
5045
	function ajax_folderMgmtTree_autoloading ($_id = null)
5046
	{
5047
		$mail_ui = new mail_ui();
5048
		$id = $_id? $_id : $_GET['id'];
5049
		Etemplate\Widget\Tree::send_quote_json($mail_ui->mail_tree->getTree($id,'',1,true,false,false,false));
5050
	}
5051
5052
	/**
5053
	 * Main function to handle folder management dialog
5054
	 *
5055
	 * @param array $content content of dialog
5056
	 */
5057
	function folderManagement (array $content = null)
5058
	{
5059
		$dtmpl = new Etemplate('mail.folder_management');
5060
		$profileID = $_GET['acc_id']? $_GET['acc_id']: $content['acc_id'];
5061
		$sel_options['tree'] = $this->mail_tree->getTree(null,$profileID, 1, true, false, false);
5062
5063
		if (!is_array($content))
5064
		{
5065
			$content = array ('acc_id' => $profileID);
5066
		}
5067
5068
		$readonlys = array();
5069
		// Preserv
5070
		$preserv = array(
5071
			'acc_id' => $content['acc_id'] // preserve acc id to be used in client-side
5072
		);
5073
		$dtmpl->exec('mail.mail_ui.folderManagement', $content,$sel_options,$readonlys,$preserv,2);
5074
	}
5075
5076
	/**
5077
	 * Function to delete folder for management longTask dialog
5078
	 * it sends successfully deleted folder as response to be
5079
	 * used in long task response handler.
5080
	 *
5081
	 * @param type $_folderName
5082
	 */
5083
	function ajax_folderMgmt_delete ($_folderName)
5084
	{
5085
		if ($_folderName)
5086
		{
5087
			$success = $this->ajax_deleteFolder($_folderName,true);
5088
			$response = Api\Json\Response::get();
5089
			list(,$folderName) = explode(self::$delimiter, $_folderName);
5090
			if ($success)
5091
			{
5092
				$res = $folderName;
5093
			}
5094
			else
5095
			{
5096
				$res = lang("Failed to delete %1",$folderName);
5097
			}
5098
			$response->data($res);
5099
		}
5100
	}
5101
}