diff --git a/admin/inc/class.admin_mail.inc.php b/admin/inc/class.admin_mail.inc.php
index 4f47d7d9bdb..add619e5fe3 100644
--- a/admin/inc/class.admin_mail.inc.php
+++ b/admin/inc/class.admin_mail.inc.php
@@ -80,7 +80,8 @@ class admin_mail
public $public_functions = array(
'add' => true,
'edit' => true,
- 'ajax_activeAccounts' => true
+ 'ajax_activeAccounts' => true,
+ 'ews_custom_permissions' => true
);
/**
@@ -196,6 +197,11 @@ public function add(array $content=array(), $msg='', $msg_type='success')
'acc_imap_port' => 993,
'manual_class' => 'emailadmin_manual',
);
+ // Select Options
+ $sel_options['acc_imap_ssl'] = self::$ssl_types;
+ // Not listing other server types, since this could be single account
+ // EGroupware only allows different types for multiple accounts
+ $sel_options['acc_imap_type'] = Mail\Types::getIMAPServerTypes(false);
Framework::message($msg ? $msg : (string)$_GET['msg'], $msg_type);
if (!empty($content['acc_imap_host']) || !empty($content['acc_imap_username']))
@@ -203,9 +209,7 @@ public function add(array $content=array(), $msg='', $msg_type='success')
$readonlys['button[manual]'] = true;
unset($content['manual_class']);
}
- $tpl->exec(static::APP_CLASS.'autoconfig', $content, array(
- 'acc_imap_ssl' => self::$ssl_types,
- ), $readonlys, $content, 2);
+ $tpl->exec(static::APP_CLASS.'autoconfig', $content, $sel_options, $readonlys, $content, 2);
}
/**
@@ -285,17 +289,17 @@ public function autoconfig(array $content)
$e = null;
try {
- $content['output'] .= "\n".Api\DateTime::to('now', 'H:i:s').": Trying $ssl connection to $host:$port ...\n";
+ $content['output'] .= "\n" . Api\DateTime::to('now', 'H:i:s') . ": Trying $ssl connection to $host:$port ...\n";
$content['acc_imap_port'] = $port;
$imap = self::imap_client($content, self::TIMEOUT);
//$content['output'] .= array2string($imap->capability());
$imap->login();
- $content['output'] .= "\n".lang('Successful connected to %1 server%2.', 'IMAP', ' '.lang('and logged in'))."\n";
+ $content['output'] .= "\n" . lang('Successful connected to %1 server%2.', 'IMAP', ' ' . lang('and logged in')) . "\n";
if (!$imap->isSecureConnection())
{
- $content['output'] .= lang('Connection is NOT secure! Everyone can read eg. your credentials.')."\n";
+ $content['output'] .= lang('Connection is NOT secure! Everyone can read eg. your credentials.') . "\n";
$content['acc_imap_ssl'] = 'no';
}
//$content['output'] .= "\n\n".array2string($imap->capability());
@@ -331,8 +335,13 @@ public function autoconfig(array $content)
if ($connected) // continue with next wizard step: define folders
{
unset($content['button']);
- return $this->folder($content, lang('Successful connected to %1 server%2.', 'IMAP', ' '.lang('and logged in')).
- ($imap->isSecureConnection() ? '' : "\n".lang('Connection is NOT secure! Everyone can read eg. your credentials.')));
+ //EWS: skip steps
+ if (Mail\Account::is_ews_type($content['acc_imap_type']))
+ return $this->smtp($content, lang('Successful connected to %1 server%2.', 'EWS', ' ' . lang('and logged in')) .
+ ($imap->isSecureConnection() ? '' : "\n" . lang('Connection is NOT secure! Everyone can read eg. your credentials.')));
+ else
+ return $this->folder($content, lang('Successful connected to %1 server%2.', 'IMAP', ' ' . lang('and logged in')) .
+ ($imap->isSecureConnection() ? '' : "\n".lang('Connection is NOT secure! Everyone can read eg. your credentials.')));
}
// add validation error, if we can identify a field
if (!$connected && $e instanceof Horde_Imap_Client_Exception)
@@ -352,6 +361,9 @@ public function autoconfig(array $content)
$readonlys['button[manual]'] = true;
unset($content['manual_class']);
$sel_options['acc_imap_ssl'] = self::$ssl_types;
+ // Not listing other server types, since this could be single account
+ // EGroupware only allows different types for multiple accounts
+ $sel_options['acc_imap_type'] = Mail\Types::getIMAPServerTypes(false);
$tpl = new Etemplate('admin.mailwizard');
$tpl->exec(static::APP_CLASS.'autoconfig', $content, $sel_options, $readonlys, $content, 2);
}
@@ -630,7 +642,10 @@ public function smtp(array $content, $msg='')
switch($button)
{
case 'back':
- return $this->sieve($content);
+ if (Mail\Account::is_ews_type($content['acc_imap_type']))
+ return $this->add($content);
+ else
+ return $this->sieve($content);
}
}
// first try: hide manual config
@@ -824,6 +839,7 @@ public function smtp(array $content, $msg='')
*/
public function edit(array $content=null, $msg='', $msg_type='success')
{
+ unset($content['manual_class']);
// app is trying to tell something, while redirecting to wizard
if (empty($content) && $_GET['acc_id'] && empty($msg) && !empty( $_GET['msg']))
{
@@ -893,6 +909,7 @@ public function edit(array $content=null, $msg='', $msg_type='success')
{
if (!$content['acc_'.$type.'_ssl']) $content['acc_'.$type.'_ssl'] = 'no';
}
+
}
catch(Api\Exception\NotFound $e) {
if (self::$debug) _egw_log_exception($e);
@@ -933,8 +950,9 @@ public function edit(array $content=null, $msg='', $msg_type='success')
'acc_smtp_type' => true, 'acc_smtp_auth_session' => true,
);
}
+
// ensure correct values for single user mail accounts (we only hide them client-side)
- if (!($is_multiple = Mail\Account::is_multiple($content)))
+ if (!($is_multiple = Mail\Account::is_multiple($content)) && $content['acc_imap_type'] != 'EGroupware\Api\Mail\Imap' && !Mail\Account::is_ews_type( $content['acc_imap_type'] ) )
{
$content['acc_imap_type'] = 'EGroupware\\Api\\Mail\\Imap';
unset($content['acc_imap_login_type']);
@@ -1086,6 +1104,15 @@ public function edit(array $content=null, $msg='', $msg_type='success')
}
$content['accounts'][$content['acc_id']] = Mail\Account::identity_name($content, false);
}
+ if ($content['acc_imap_type'] && Mail\Account::is_ews_type($content['acc_imap_type']))
+ {
+ if ($content['clear_grid'])
+ {
+ $content['ews_permissions'] = array();
+ $content['clear_grid'] = false;
+ Api\Mail_EWS::storeFolderPermissions($content['ews_permissions'], $content['acc_id']);
+ }
+ }
}
else
{
@@ -1220,6 +1247,24 @@ public function edit(array $content=null, $msg='', $msg_type='success')
$tpl->setElementAttribute($folder, 'allowFreeEntries', true);
}
}
+ elseif (Mail\Account::is_ews_type($content['acc_imap_type']))
+ {
+ try {
+
+ $sel_options['acc_folder_sent'] = $sel_options['acc_folder_trash'] =
+ $sel_options['acc_folder_draft'] = $sel_options['acc_folder_template'] =
+ $sel_options['acc_folder_junk'] = $sel_options['acc_folder_archive'] =
+ $sel_options['notify_folders'] = $sel_options['acc_folder_ham'] =
+ Api\Mail\EWS\Lib::getFoldersSelOptions($content['acc_id'], true);
+ }
+ catch (Exception $e)
+ {
+ if (self::$debug) _egw_log_exception($e);
+ // let user know what the problem is and that he can fix it using wizard or deleting
+ $msg = lang($e->getMessage()) . "\n\n" . lang('You can use wizard to fix account settings or delete account.');
+ $msg_type = 'error';
+ }
+ }
else
{
try {
@@ -1253,6 +1298,13 @@ public function edit(array $content=null, $msg='', $msg_type='success')
$sel_options['ident_id'] = $content['identities'];
$sel_options['acc_id'] = $content['accounts'];
$sel_options['acc_further_identities'] = self::$further_identities;
+ $sel_options['acc_ews_type'] = array(
+ 'inbox' => 'Inbox',
+ 'public_folders' => 'Public Folders'
+ );
+
+ // Disable permissions if inbox
+ $content['isInbox'] = ( $content['acc_ews_type'] == 'inbox' );
// user is allowed to create or edit further identities
if ($edit_access || $content['acc_further_identities'])
@@ -1334,6 +1386,12 @@ public function edit(array $content=null, $msg='', $msg_type='success')
}
}
+ // Disable EWS tab for other types
+ if ($content['acc_imap_type'] && !Mail\Account::is_ews_type($content['acc_imap_type']))
+ {
+ $readonlys['tabs']['admin.mailaccount.ews'] = true;
+ }
+
// account allows users to change forwards
if (!$edit_access && !$readonlys['tabs']['admin.mailaccount.aliases'] && $content['acc_user_forward'])
{
@@ -1371,6 +1429,23 @@ public function edit(array $content=null, $msg='', $msg_type='success')
}
$content['admin_actions'] = (bool)$admin_actions;
+ if ($content['acc_imap_type'] && Mail\Account::is_ews_type($content['acc_imap_type']))
+ {
+ try
+ {
+ $content['acc_ews_apply_permissions'] = (int) $content['acc_ews_apply_permissions'];
+ } catch (Exception $e)
+ {
+ if (self::$debug)
+ {
+ _egw_log_exception($e);
+ }
+ // let user know what the problem is and that he can fix it using wizard or deleting
+ $msg = lang($e->getMessage()) . "\n\n" . lang('You can use wizard to fix account settings or delete account.');
+ $msg_type = 'error';
+ }
+ }
+
//try to fix identities with no domain part set e.g. alias as identity
if (!strpos($content['ident_email'], '@'))
{
@@ -1380,6 +1455,31 @@ public function edit(array $content=null, $msg='', $msg_type='success')
$tpl->exec(static::APP_CLASS.'edit', $content, $sel_options, $readonlys, $content, 2);
}
+ public function ews_custom_permissions( $content = array() )
+ {
+ $dtmpl = new Etemplate('admin.mailaccount.permissions');
+ $acc_id = $_GET['acc_id']? $_GET['acc_id']: $content['acc_id'];
+ $content['acc_id'] = $acc_id;
+
+ $sel_options['ews_permissions'] = Api\Mail_EWS::getFolderPermissionsSelOptions( $content['acc_id'] );
+ $names = array_values( $sel_options['ews_permissions'][1]['ews_move_to'] );
+ $sel_options['mailbox'] = array_combine( $names, $names );
+
+ if ( $content['save'] || $content['apply'] ) {
+ $res = Api\Mail_EWS::storeFolderPermissions( $content['ews_permissions'], $content['acc_id'] );
+ $msg = lang('Operation Successful');
+ if ( $res && $content['save'] ) {
+ Framework::message( $msg );
+ Framework::window_close();
+ }
+ $content['msg'] = $msg;
+ }
+
+ $content['ews_permissions'] = Api\Mail_EWS::getFolderPermissions( $content['acc_id'] );
+ $readonlys = array();
+ $dtmpl->exec('admin.admin_mail.ews_custom_permissions', $content,$sel_options,$readonlys,$content,2);
+ }
+
/**
* Replace 0 with '' or back
*
@@ -1407,17 +1507,25 @@ private static function fix_account_id_0(&$account_id=null, $back=false)
* @param int $timeout =null default use value returned by Mail\Imap::getTimeOut()
* @return Horde_Imap_Client_Socket
*/
- protected static function imap_client(array $content, $timeout=null)
- {
- return new Horde_Imap_Client_Socket(array(
- 'username' => $content['acc_imap_username'],
- 'password' => $content['acc_imap_password'],
- 'hostspec' => $content['acc_imap_host'],
- 'port' => $content['acc_imap_port'],
- 'secure' => self::$ssl2secure[(string)array_search($content['acc_imap_ssl'], self::$ssl2type)],
- 'timeout' => $timeout > 0 ? $timeout : Mail\Imap::getTimeOut(),
- 'debug' => self::DEBUG_LOG,
- ));
+ protected static function imap_client(array $content, $timeout = null) {
+ //EWS: Instantiate different object
+ if (Mail\Account::is_ews_type($content['acc_imap_type']))
+ {
+ $class = $content['acc_imap_type'];
+ return new $class($content);
+ }
+ else
+ {
+ return new Horde_Imap_Client_Socket(array(
+ 'username' => $content['acc_imap_username'],
+ 'password' => $content['acc_imap_password'],
+ 'hostspec' => $content['acc_imap_host'],
+ 'port' => $content['acc_imap_port'],
+ 'secure' => self::$ssl2secure[(string) array_search($content['acc_imap_ssl'], self::$ssl2type)],
+ 'timeout' => $timeout > 0 ? $timeout : Mail\Imap::getTimeOut(),
+ 'debug' => self::DEBUG_LOG,
+ ));
+ }
}
/**
diff --git a/admin/js/app.js b/admin/js/app.js
index 0c143e7f161..1b6d58ae817 100644
--- a/admin/js/app.js
+++ b/admin/js/app.js
@@ -47,6 +47,11 @@ app.classes.admin = AppJS.extend(
*/
acl_dialog: null,
+ /**
+ * Keep widgets from triggering again
+ */
+ widget_active: false,
+
/**
* Constructor
*
@@ -117,6 +122,9 @@ app.classes.admin = AppJS.extend(
case 'admin.mailaccount':
this.account_hide_not_applying();
break;
+ case 'admin.mailaccount.permissions':
+ this.ews_switch_folder();
+ break;
}
},
@@ -1273,5 +1281,51 @@ app.classes.admin = AppJS.extend(
resizable: false,
position: 'left top'
}, et2_dialog._create_parent('mail'));
+ },
+ /**
+ * Switch EWS Type
+ *
+ * When switching, we want to immediately save and change the array that's being loaded
+ *
+ */
+ ews_switch_type: function (_element, _widget)
+ {
+ var option1 = 'inbox';
+ var option2 = 'public_folders';
+ var clear = this.et2.getWidgetById('clear_grid');
+ var apply = this.et2.getWidgetById('button[apply]');
+ var that = this;
+ if (!that.widget_active) {
+ that.widget_active = true;
+ et2_dialog.show_dialog(function (_button) {
+ if (_button == 2) {
+ clear.set_value(true);
+ apply.getInstanceManager().submit(apply, false, apply.options.novalidate);
+ } else {
+ if (_widget.getValue() == option1)
+ _widget.set_value(option2);
+ else
+ _widget.set_value(option1);
+ that.widget_active = false;
+ }
+ }, egw.lang('Changing Account Type will delete all your current settings. Are you sure you want to continue?'),
+ egw.lang('Change Type'), null, et2_dialog.BUTTON_YES_NO, et2_dialog.WARNING_MESSAGE, undefined, egw);
+ }
+ },
+
+ /**
+ * Switch Folder in ews permissions
+ *
+ */
+ ews_switch_folder: function()
+ {
+ var folder = this.et2.getWidgetById('mailbox');
+ jQuery('#admin-mailaccount-permissions_ews_permissions tr').each( function() {
+ if ( jQuery( this ).hasClass('th') ) return;
+ if ( jQuery( this ).find('input[name*=ews_name]').val() == folder.getValue() )
+ jQuery( this ).show();
+ else
+ jQuery( this ).hide();
+ });
}
});
diff --git a/admin/templates/default/app.css b/admin/templates/default/app.css
index 4048399ca3e..8ebb7229883 100644
--- a/admin/templates/default/app.css
+++ b/admin/templates/default/app.css
@@ -177,3 +177,14 @@ select#admin-mailaccount_ident_id {
font-size: 110%;
}
#admin-acl {width:99%;}
+
+#admin-mailaccount-permissions_ews_tabs .th {
+ border-bottom: none;
+}
+#admin-mailaccount-permissions_ews_tabs .et2_tabheader {
+ display:none;
+}
+#admin-mailaccount-permissions_ews_tabs #admin-mailaccount-permissions_ews_permissions .th {
+ visibility: collapse;
+}
+
diff --git a/admin/templates/default/mailaccount.permissions.xet b/admin/templates/default/mailaccount.permissions.xet
new file mode 100644
index 00000000000..52a2287e0c5
--- /dev/null
+++ b/admin/templates/default/mailaccount.permissions.xet
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/admin/templates/default/mailaccount.xet b/admin/templates/default/mailaccount.xet
index 21c9f58b46b..338d857ec97 100644
--- a/admin/templates/default/mailaccount.xet
+++ b/admin/templates/default/mailaccount.xet
@@ -53,7 +53,7 @@
-
+
@@ -322,6 +322,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -335,7 +361,6 @@
-
diff --git a/admin/templates/default/mailwizard.xet b/admin/templates/default/mailwizard.xet
index c5a192467d6..3cde96e5181 100644
--- a/admin/templates/default/mailwizard.xet
+++ b/admin/templates/default/mailwizard.xet
@@ -10,7 +10,7 @@
@@ -26,7 +26,13 @@
-
+
+
+
+
+
+
+
diff --git a/admin/templates/pixelegg/app.css b/admin/templates/pixelegg/app.css
index 3c839f1ba92..0dec7c221d3 100755
--- a/admin/templates/pixelegg/app.css
+++ b/admin/templates/pixelegg/app.css
@@ -184,6 +184,15 @@ select#admin-mailaccount_ident_id {
#admin-acl {
width: 99%;
}
+#admin-mailaccount-permissions_ews_tabs .th {
+ border-bottom: none;
+}
+#admin-mailaccount-permissions_ews_tabs .et2_tabheader {
+ display: none;
+}
+#admin-mailaccount-permissions_ews_tabs #admin-mailaccount-permissions_ews_permissions .th {
+ visibility: collapse;
+}
@media all {
div.dhtmlxTree td.standartTreeRow span.selectedTreeRow {
background-color: #ffdd73;
diff --git a/api/setup/setup.inc.php b/api/setup/setup.inc.php
index 6b1a43c1e12..1ff196233b2 100644
--- a/api/setup/setup.inc.php
+++ b/api/setup/setup.inc.php
@@ -11,7 +11,7 @@
/* Basic information about this app */
$setup_info['api']['name'] = 'api';
$setup_info['api']['title'] = 'EGroupware API';
-$setup_info['api']['version'] = '17.1';
+$setup_info['api']['version'] = '17.1.001';
$setup_info['api']['versions']['current_header'] = '1.29';
// maintenance release in sync with changelog in doc/rpm-build/debian.changes
$setup_info['api']['versions']['maintenance_release'] = '17.1.20171115';
@@ -52,6 +52,7 @@
$setup_info['api']['tables'][] = 'egw_ea_identities';
$setup_info['api']['tables'][] = 'egw_ea_valid';
$setup_info['api']['tables'][] = 'egw_ea_notifications';
+$setup_info['api']['tables'][] = 'egw_ea_ews';
// hooks used by vfs_home_hooks to manage user- and group-directories for the new stream based VFS
$setup_info['api']['hooks']['addaccount'] = array('EGroupware\\Api\\Vfs\\Hooks::addAccount', 'EGroupware\\Api\\Mail\\Hooks::addaccount');
diff --git a/api/setup/tables_current.inc.php b/api/setup/tables_current.inc.php
index 4353f837c8b..c52de985121 100644
--- a/api/setup/tables_current.inc.php
+++ b/api/setup/tables_current.inc.php
@@ -451,7 +451,9 @@
'acc_user_forward' => array('type' => 'bool','default' => '0','comment' => 'allow user to define forwards'),
'acc_further_identities' => array('type' => 'int','precision' => '1','nullable' => False,'default' => '1','comment' => '0=no, 1=yes, 2=only matching aliases'),
'acc_folder_ham' => array('type' => 'varchar','precision' => '128','comment' => 'ham folder'),
- 'acc_spam_api' => array('type' => 'varchar','precision' => '128','comment' => 'SpamTitan API URL')
+ 'acc_spam_api' => array('type' => 'varchar','precision' => '128','comment' => 'SpamTitan API URL'),
+ 'acc_ews_type' => array('type' => 'varchar','precision' => '128','default' => 'inbox','comment' => 'inbox/public_folders'),
+ 'acc_ews_apply_permissions' => array('type' => 'bool','comment' => 'Always apply permissions ')
),
'pk' => array('acc_id'),
'fk' => array(),
@@ -510,5 +512,22 @@
'fk' => array(),
'ix' => array(array('account_id','acc_id')),
'uc' => array()
+ ),
+ 'egw_ea_ews' => array(
+ 'fd' => array(
+ 'ews_profile' => array('type' => 'int','precision' => '11','nullable' => False,'comment' => 'ewg_ea_account, acc_id'),
+ 'ews_folder' => array('type' => 'varchar','precision' => '255','nullable' => False,'comment' => 'Exchange Folder ID'),
+ 'ews_name' => array('type' => 'varchar','precision' => '100','nullable' => False,'comment' => 'Exchange Folder Name'),
+ 'ews_is_default' => array('type' => 'bool','comment' => 'Default folder'),
+ 'ews_order' => array('type' => 'int','precision' => '5','comment' => 'Order to display in tree'),
+ 'ews_move_anywhere' => array('type' => 'bool','comment' => 'Permission to move emails between folders'),
+ 'ews_move_to' => array('type' => 'text','comment' => 'Array with only folders allowed to move emails to'),
+ 'ews_permissions' => array('type' => 'text','comment' => 'Array with folder permissions'),
+ 'ews_apply_permissions' => array('type' => 'bool','comment' => 'Whether to apply extra permissions or not')
+ ),
+ 'pk' => array('ews_profile','ews_folder'),
+ 'fk' => array('ews_profile' => 'egw_ea_account.acc_id'),
+ 'ix' => array(),
+ 'uc' => array()
)
);
diff --git a/api/setup/tables_update.inc.php b/api/setup/tables_update.inc.php
index 897c67dc7f1..68797ade21e 100644
--- a/api/setup/tables_update.inc.php
+++ b/api/setup/tables_update.inc.php
@@ -407,3 +407,49 @@ function api_upgrade16_9_004()
{
return $GLOBALS['setup_info']['api']['currentver'] = '17.1';
}
+
+function api_upgrade17_1()
+{
+ $GLOBALS['egw_setup']->oProc->AddColumn('egw_ea_accounts','acc_ews_type',array(
+ 'type' => 'varchar',
+ 'precision' => '128',
+ 'default' => 'inbox',
+ 'comment' => 'inbox/public_folders'
+ ));
+
+{
+ $GLOBALS['egw_setup']->oProc->CreateTable('egw_ea_ews',array(
+ 'fd' => array(
+ 'ews_profile' => array('type' => 'int','precision' => '11','nullable' => False,'comment' => 'ewg_ea_account, acc_id'),
+ 'ews_folder' => array('type' => 'varchar','precision' => '255','nullable' => False,'comment' => 'Exchange Folder ID'),
+ 'ews_name' => array('type' => 'varchar','precision' => '100','nullable' => False,'comment' => 'Exchange Folder Name'),
+ 'ews_is_default' => array('type' => 'bool','comment' => 'Default folder'),
+ 'ews_order' => array('type' => 'int','precision' => '5','comment' => 'Order to display in tree'),
+ 'ews_move_anywhere' => array('type' => 'bool','comment' => 'Permission to move emails between folders'),
+ 'ews_move_to' => array('type' => 'text','comment' => 'Array with only folders allowed to move emails to')
+ ),
+ 'pk' => array('ews_profile','ews_folder'),
+ 'fk' => array('ews_profile' => 'egw_ea_account.acc_id'),
+ 'ix' => array(),
+ 'uc' => array()
+ ));
+
+ $GLOBALS['egw_setup']->oProc->AddColumn('egw_ea_ews','ews_permissions',array(
+ 'type' => 'text',
+ 'comment' => 'Array with folder permissions'
+ ));
+
+ $GLOBALS['egw_setup']->oProc->AddColumn('egw_ea_ews','ews_apply_permissions',array(
+ 'type' => 'bool',
+ 'comment' => 'Whether to apply extra permissions or not'
+ ));
+
+ $GLOBALS['egw_setup']->oProc->AddColumn('egw_ea_accounts','acc_ews_apply_permissions',array(
+ 'type' => 'bool',
+ 'comment' => 'Always apply permissions '
+ ));
+
+ $GLOBALS['egw_setup']->db->query( 'alter table egw_ea_ews modify ews_folder varchar(255) collate utf8_bin' );
+
+ return $GLOBALS['setup_info']['api']['currentver'] = '17.1.001';
+}
diff --git a/api/src/Mail.php b/api/src/Mail.php
index 67811ddd63c..6fdd890d89e 100644
--- a/api/src/Mail.php
+++ b/api/src/Mail.php
@@ -263,7 +263,11 @@ public static function getInstance($_restoreSession=true, &$_profileID=0, $_vali
//error_log(__METHOD__.' ('.__LINE__.') '.' RestoreSession:'.$_restoreSession.' ProfileId:'.$_profileID.' called from:'.function_backtrace());
if ($_profileID && (!isset(self::$instances[$_profileID]) || $_restoreSession===false))
{
- self::$instances[$_profileID] = new Mail('utf-8',$_restoreSession,$_profileID,false,$_reuseCache);
+ $account = Mail\Account::read($_profileID);
+ if ($account->is_ews())
+ self::$instances[$_profileID] = new Mail_EWS('utf-8', $_restoreSession, $_profileID, false, $_reuseCache);
+ else
+ self::$instances[$_profileID] = new Mail('utf-8',$_restoreSession,$_profileID,false,$_reuseCache);
}
else
{
@@ -800,6 +804,11 @@ function getDefaultIdentity()
}
}
+ function getDefaultFolder()
+ {
+ return $this->profileID . self::DELIMITER . 'INBOX';
+ }
+
/**
* getIdentitiesWithAccounts
*
diff --git a/api/src/Mail/Account.php b/api/src/Mail/Account.php
index 0849653c83c..1414d4b45ac 100644
--- a/api/src/Mail/Account.php
+++ b/api/src/Mail/Account.php
@@ -391,6 +391,30 @@ public function imapServer($_adminConnection=false, $_timeout=null)
return $this->imapServer;
}
+ /**
+ * Check if account is an ews account
+ *
+ * @return boolean
+ */
+ public function is_ews()
+ {
+ if (empty($this->acc_imap_host) || ( empty($this->acc_imap_username) && empty($this->acc_imap_password) ) )
+ {
+ return false; // no host or credentials
+ }
+
+ if ( strpos( $this->acc_imap_type, __NAMESPACE__.'\EWS' ) !== FALSE )
+ return true;
+
+ return false;
+ }
+ static function is_ews_type( $type )
+ {
+ $full = (strpos( $type, __NAMESPACE__.'\EWS' ) !== FALSE );
+ $short = (substr( $type, 0, 3 ) == 'EWS' );
+ return $full || $short;
+ }
+
/**
* Check if account is an imap account
*
@@ -1066,7 +1090,7 @@ protected static function db2data(array $data)
// convert old plugin names and readd namespace
if ($data['acc_imap_type'])
{
- if (substr($data['acc_imap_type'], 0, 4) == 'Imap')
+ if (substr($data['acc_imap_type'], 0, 4) == 'Imap' || substr($data['acc_imap_type'], 0, 3) == 'EWS' )
{
$data['acc_imap_type'] = __NAMESPACE__.'\\'.$data['acc_imap_type'];
}
diff --git a/api/src/Mail/EWS.php b/api/src/Mail/EWS.php
new file mode 100644
index 00000000000..c6eaac8fc01
--- /dev/null
+++ b/api/src/Mail/EWS.php
@@ -0,0 +1,136 @@
+
+ * @author Stylite AG
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @version $Id$
+ */
+
+namespace EGroupware\Api\Mail;
+use EGroupware\Api\Mail;
+use EGroupware\Api\Mail\EWS\Lib;
+
+/**
+ * This class holds all information about the imap connection.
+ * This is the base class for all other imap classes.
+ *
+ * Also proxies Sieve calls to Mail\Sieve (eg. it behaves like the former felamimail bosieve),
+ * to allow IMAP plugins to also manage Sieve connection.
+ */
+class EWS
+{
+ const DESCRIPTION = 'Microsoft Exchange (EWS)';
+ const VERSION = 'Exchange_2007';
+ var $ImapServerId;
+ var $params;
+
+ function __construct(array $params, $_timeout=null)
+ {
+ $this->ImapServerId = $params['acc_id'];
+ $this->params = $params;
+ $this->params['version'] = static::VERSION;
+ }
+ function getCurrentMailbox()
+ {
+ return Lib::getDefaultFolder( $this->ImapServerId );
+ }
+ public function __call($name,array $params=null)
+ {
+ if ($this->debug) error_log(__METHOD__.'->'.$name.' with params:'.array2string($params));
+ switch($name)
+ {
+ case 'installScript':
+ case 'getScript':
+ case 'setActive':
+ case 'setEmailNotification':
+ case 'getEmailNotification':
+ case 'setRules':
+ case 'getRules':
+ case 'retrieveRules':
+ case 'getVacation':
+ case 'setVacation':
+ if (is_null($this->sieve))
+ {
+ $this->sieve = new Sieve($this);
+ $this->error =& $this->sieve->error;
+ }
+ $ret = call_user_func_array(array($this->sieve,$name),$params);
+ //error_log(__CLASS__.'->'.$name.'('.array2string($params).') returns '.array2string($ret));
+ return $ret;
+ break;
+ case 'subscribeMailbox':
+ case 'search':
+ case 'listSubscribedMailboxes':
+ return;
+ break;
+ }
+ throw new Api\Exception\WrongParameter("No method '$name' implemented!");
+ }
+ function hasCapability($capability)
+ {
+ switch( $capability ) {
+ case 'SUPPORTS_FLAGS':
+ case 'SUPPORTS_KEYWORDS':
+ return false;
+ break;
+ default:
+ return true;
+ break;
+ }
+ return true;
+ }
+ function mailboxExist($mailbox)
+ {
+ return true;
+ }
+
+ static function description() {
+ return static::DESCRIPTION;
+ }
+ function isSecureConnection() {
+ // if ssl=0, then connection insecure
+ return ( $this->params['acc_imap_ssl'] );
+ }
+ function login() {
+ EWS\Lib::login( $this->params );
+ return true;
+ }
+ function setUserData($_username, $_quota)
+ {
+ unset($_username, $_quota); // not used
+ return true;
+ }
+ static function getUIreadonlys() {
+ return array();
+ }
+ public function runOnLogin($func, array $params=array())
+ {
+ $this->run_on_login[] = array($func, $params);
+ }
+ function examineMailbox($mailbox, $flags=null)
+ {
+ return false;
+ }
+ function getDelimiter($_type=1)
+ {
+ return "/";
+ }
+ function openMailbox($mailbox)
+ {
+ return true;
+ }
+ function getNameSpaceArray()
+ {
+ return array();
+ }
+ function getStorageQuotaRoot($mailboxName)
+ {
+ return false;
+ }
+
+}
diff --git a/api/src/Mail/EWS/Attachment.php b/api/src/Mail/EWS/Attachment.php
new file mode 100644
index 00000000000..f59cb98146c
--- /dev/null
+++ b/api/src/Mail/EWS/Attachment.php
@@ -0,0 +1,62 @@
+type = $params['type'];
+ $this->filename = $params['filename'];
+ $this->contents = $params['attachment'];
+ }
+ function getType() {
+ return $this->type;
+ }
+ function getDispositionParameter( $var ) {
+ if ( $var == 'filename' )
+ return $this->filename;
+
+ return '';
+ }
+ function getContents( $options = array()) {
+ if ( $options['stream'] )
+ return $this->contents;
+ else
+ return $this->_readStream( $this->contents );
+ }
+ protected function _readStream($fp, $close = false)
+ {
+ $out = '';
+
+ if (!is_resource($fp)) {
+ return $out;
+ }
+
+ rewind($fp);
+ while (!feof($fp)) {
+ $out .= fread($fp, 8192);
+ }
+
+ if ($close) {
+ fclose($fp);
+ }
+
+ return $out;
+ }
+ public function getBytes($approx = false)
+ {
+ $bytes = 0;
+ if ($this->contents) {
+ fseek($this->_contents, 0, SEEK_END);
+ $bytes = ftell($this->_contents);
+ }
+
+ return intval($bytes);
+ }
+
+}
diff --git a/api/src/Mail/EWS/EWS_07.php b/api/src/Mail/EWS/EWS_07.php
new file mode 100644
index 00000000000..01ff774ccbc
--- /dev/null
+++ b/api/src/Mail/EWS/EWS_07.php
@@ -0,0 +1,11 @@
+params['acc_imap_type'];
+ $version = 'Exchange_2007';
+ if ( class_exists( $type ) ) {
+ $obj = new $type( $account->params );
+ $version = $obj::VERSION;
+ }
+
+ $info = array(
+ 'exchange_user' => $account->params['acc_imap_username'],
+ 'exchange_host' => $account->params['acc_imap_host'],
+ 'exchange_password' => $account->params['acc_imap_password'],
+ 'exchange_version' => $version,
+ );
+
+ self::$info = $info;
+ }
+
+ extract( self::$info );
+
+ return new EwsConnection(
+ $exchange_host,
+ $exchange_user,
+ $exchange_password,
+ $exchange_version
+ );
+ }
+
+ static function setRead( $profile, $mailID, $changeKey, $read = true ) {
+ $ews = self::init( $profile );
+
+ $request = new DT\UpdateItemType();
+ $request->ConflictResolution = "AutoResolve";
+ $request->MessageDisposition = "SaveOnly";
+
+ $request->ItemChanges = new DT\NonEmptyArrayOfItemChangesType();
+
+ $itemChange = new DT\ItemChangeType();
+ $itemChange->ItemId = new DT\ItemIdType();
+ $itemChange->ItemId->Id = $mailID;
+ $itemChange->ItemId->ChangeKey = $changeKey;
+ $itemChange->Updates = new DT\NonEmptyArrayOfItemChangeDescriptionsType();
+
+ $set = new DT\SetItemFieldType();
+ $set->FieldURI = new DT\PathToUnindexedFieldType;
+ $set->FieldURI->FieldURI = 'message:IsRead';
+ $set->Message = new DT\MessageType();
+ $set->Message->IsRead = $read;
+ $itemChange->Updates->SetItemField = array( $set );
+
+ $request->ItemChanges->ItemChange = array( $itemChange );
+
+ $response = $ews->UpdateItem($request);
+
+ return $response->ResponseMessages->UpdateItemResponseMessage->ResponseClass == 'Success';
+ }
+
+ static function createMail( $profile, $folderID, $mime ) {
+ $ews = self::init( $profile );
+
+ $folderID = self::getActualFolderId( $profile, $folderID );
+
+ $request = new DT\CreateItemType();
+ $request->MessageDisposition = 'SaveOnly';
+
+ $request->SavedItemFolderId = new DT\TargetFolderIdType();
+ $request->SavedItemFolderId->FolderId = new DT\FolderIdType();
+ $request->SavedItemFolderId->FolderId->Id = $folderID;
+
+ $message = new DT\MessageType();
+ $message->MimeContent = $mime;
+
+ $request->Items = new DT\NonEmptyArrayOfAllItemsType();
+ $request->Items->Message[] = $message;
+
+ $response = $ews->CreateItem($request);
+
+ $result = false;
+ if ( $response->ResponseMessages->CreateItemResponseMessage->ResponseClass == 'Success' )
+ $result = $response->ResponseMessages->CreateItemResponseMessage->Items->Message->ItemId->Id;
+
+ return $result;
+ }
+
+ static function getAttachment( $profile, $attachmentID ) {
+ $attachmentID = urldecode( $attachmentID );
+ $attachmentID = str_replace(' ','+', $attachmentID );
+ $ews = self::init( $profile );
+
+ $request = new DT\GetAttachmentType();
+ $request->AttachmentIds = new DT\NonEmptyArrayOfRequestAttachmentIdsType();
+ $request->AttachmentIds->AttachmentId = new DT\RequestAttachmentIdType();
+ $request->AttachmentIds->AttachmentId->Id = $attachmentID;
+
+ // Include MimeContent (raw) in case attachment is email
+ $request->AttachmentShape = new DT\AttachmentResponseShapeType();
+ $request->AttachmentShape->AdditionalProperties = new DT\NonEmptyArrayOfPathsToElementType();
+ $prop = new DT\PathToUnindexedFieldType();
+ $prop->FieldURI = 'item:MimeContent';
+ $request->AttachmentShape->AdditionalProperties->FieldURI = array( $prop );
+
+ $response = $ews->GetAttachment($request);
+
+ if ( $response->ResponseMessages->GetAttachmentResponseMessage->ResponseClass == 'Error' )
+ throw new \Exception( $response->ResponseMessages->GetAttachmentResponseMessage->MessageText );
+
+ $attachment = $response->ResponseMessages->GetAttachmentResponseMessage->Attachments->FileAttachment;
+ if ( !$attachment )
+ $attachment = $response->ResponseMessages->GetAttachmentResponseMessage->Attachments->ItemAttachment;
+
+ return $attachment;
+ }
+
+ static function getMail( $profile, $emailID ) {
+ $ews = self::init( $profile );
+
+ $request = new DT\GetItemType();
+ $request->ItemShape = new DT\ItemResponseShapeType();
+ $request->ItemShape->BaseShape = DT\DefaultShapeNamesType::ALL_PROPERTIES;
+ $request->ItemIds = new DT\NonEmptyArrayOfBaseItemIdsType();
+ $request->ItemIds->ItemId = array();
+
+ $message_item = new DT\ItemIdType();
+ $message_item->Id = $emailID;
+ $request->ItemIds->ItemId[] = $message_item;
+
+ try {
+ $response = $ews->GetItem($request);
+ }
+ catch (\Exception $e) {
+ // Unknown error with some newsletters. Fall back to text
+ if ( $e->getMessage() == 'looks like we got no XML document') {
+ $request->ItemShape->BodyType = DT\BodyTypeResponseType::TEXT;
+ try {
+ $response = $ews->GetItem($request);
+ }
+ catch (\Exception $e) {
+ return self::getMailPart( $profile, $emailID, 'nobody' );
+ }
+ }
+ }
+
+
+ $msg = $response->ResponseMessages->GetItemResponseMessage->Items->Message;
+
+ return $msg;
+ }
+ /**
+ * getMailPart: Getting only selected parts of the email
+ *
+ * @param string $profile
+ * @param string $emailID
+ * @param string $part comma-separated fields to include
+ * @return object
+ */
+ static function getMailPart( $profile, $emailID, $part) {
+ $ews = self::init( $profile );
+
+ $request = new DT\GetItemType();
+ $request->ItemShape = new DT\ItemResponseShapeType();
+ $request->ItemShape->BaseShape = DT\DefaultShapeNamesType::ID_ONLY;
+
+ $properties = array();
+ $parts = explode( ',', $part );
+ foreach( $parts as $prop ) {
+ switch( $prop ) {
+ case 'addresses':
+ $properties[] = 'message:From';
+ $properties[] = 'message:ToRecipients';
+ $properties[] = 'message:CcRecipients';
+ break;
+ case 'attachments':
+ $properties[] = 'item:Attachments';
+ break;
+ case 'body':
+ $properties[] = 'item:Body';
+ break;
+ case 'nobody':
+ $properties[] = 'message:From';
+ $properties[] = 'message:Sender';
+ $properties[] = 'message:ToRecipients';
+ $properties[] = 'message:CcRecipients';
+ $properties[] = 'message:InternetMessageId';
+ $properties[] = 'item:DateTimeReceived';
+ $properties[] = 'item:Subject';
+ $properties[] = 'item:Importance';
+ break;
+ }
+ }
+ if ( $properties ) {
+ $request->ItemShape->AdditionalProperties = new DT\NonEmptyArrayOfPathsToElementType();
+ $additional = array();
+ foreach ( $properties as $fieldURI ) {
+ $body_property = new DT\PathToUnindexedFieldType();
+ $body_property->FieldURI = $fieldURI;
+ $additional[] = $body_property;
+ }
+ $request->ItemShape->AdditionalProperties->FieldURI = $additional;
+ }
+ $request->ItemIds = new DT\NonEmptyArrayOfBaseItemIdsType();
+ $request->ItemIds->ItemId = array();
+
+ $message_item = new DT\ItemIdType();
+ $message_item->Id = $emailID;
+ $request->ItemIds->ItemId[] = $message_item;
+
+ try {
+ $response = $ews->GetItem($request);
+ }
+ catch (\Exception $e) {
+ // Unknown error with some newsletters. Fall back to text
+ if ( $e->getMessage() == 'looks like we got no XML document') {
+ $request->ItemShape->BodyType = DT\BodyTypeResponseType::TEXT;
+ try {
+ $response = $ews->GetItem($request);
+ }
+ catch (\Exception $e) {
+ error_log( $e->getMessage() );
+ error_log( "Error caused by Mail Id: $emailID" );
+ return false;
+ }
+ }
+ }
+
+
+ $msg = $response->ResponseMessages->GetItemResponseMessage->Items->Message;
+
+ return $msg;
+ }
+
+ static function getMailRaw( $profile, $emailID ) {
+ $ews = self::init( $profile );
+
+ $request = new DT\GetItemType();
+ $request->ItemShape = new DT\ItemResponseShapeType();
+ $request->ItemShape->BaseShape = DT\DefaultShapeNamesType::ALL_PROPERTIES;
+ $request->ItemShape->IncludeMimeContent = true;
+
+ $request->ItemIds = new DT\NonEmptyArrayOfBaseItemIdsType();
+ $request->ItemIds->ItemId = new DT\ItemIdType();
+ $request->ItemIds->ItemId->Id = $emailID;
+
+ try {
+ $response = $ews->GetItem($request);
+ }
+ catch (\Exception $e) {
+ // Unknown error with some newsletters. Fall back to text
+ if ( $e->getMessage() == 'looks like we got no XML document') {
+ $request->ItemShape->BodyType = DT\BodyTypeResponseType::TEXT;
+ try {
+ $response = $ews->GetItem($request);
+ }
+ catch (\Exception $e) {
+ }
+ }
+ }
+
+ $response = $response->ResponseMessages->GetItemResponseMessage;
+ if ($response->ResponseCode == 'NoError' &&
+ $response->ResponseClass == 'Success') {
+ return base64_decode( $response->Items->Message->MimeContent->_ );
+ }
+ else
+ return '';
+ }
+
+ static function getMails( $profile, $folderID, $start, $num_rows, $sort, $filter, $id_only = false ) {
+ $ews = self::init( $profile );
+
+ $request = new DT\FindItemType();
+ $request->ItemShape = new DT\ItemResponseShapeType();
+ if ( $id_only )
+ $request->ItemShape->BaseShape = DT\DefaultShapeNamesType::ID_ONLY;
+ else
+ $request->ItemShape->BaseShape = DT\DefaultShapeNamesType::ALL_PROPERTIES;
+ $request->Traversal = DT\ItemQueryTraversalType::SHALLOW;
+
+ /* $offset = $limit * ($page - 1); */
+ $request->IndexedPageItemView = new DT\IndexedPageViewType();
+ $request->IndexedPageItemView->BasePoint = "Beginning";
+ $request->IndexedPageItemView->Offset = $start; // Item number you want to start at
+ $request->IndexedPageItemView->MaxEntriesReturned = $num_rows; // Numer of items to return in total
+
+ $request->ParentFolderIds = new DT\NonEmptyArrayOfBaseFolderIdsType();
+ $request->ParentFolderIds->FolderId = new DT\FolderIdType();
+ $request->ParentFolderIds->FolderId->Id = $folderID;
+
+ $request->SortOrder = new DT\NonEmptyArrayOfFieldOrdersType();
+ $request->SortOrder->FieldOrder = array();
+
+ $order = new DT\FieldOrderType();
+ $order->FieldURI = '';
+ @$order->FieldURI->FieldURI = $sort['order']; // @ symbol stops the creating default object from empty value error
+ $order->Order = $sort['sort'];
+ $request->SortOrder->FieldOrder[] = $order;
+
+ if ( $res = self::setRestriction( $filter ) )
+ $request->Restriction = $res;
+
+ $response = $ews->FindItem($request);
+
+ //Format
+ $emails = $response->ResponseMessages->FindItemResponseMessage->RootFolder->Items->Message;
+ if ( $emails && is_array($emails) === FALSE ) {
+ $emails = array( $emails );
+ }
+
+ return array( 'rows' => $emails, 'count' => $response->ResponseMessages->FindItemResponseMessage->RootFolder->TotalItemsInView );
+
+ }
+
+ // Get all mail ids inside a specific folder
+ static function getMailIds( $profile, $folderID ) {
+ $ews = self::init( $profile );
+
+ $request = new DT\FindItemType();
+ $request->ItemShape = new DT\ItemResponseShapeType();
+ $request->ItemShape->BaseShape = DT\DefaultShapeNamesType::ID_ONLY;
+ $request->Traversal = DT\ItemQueryTraversalType::SHALLOW;
+
+ $request->ParentFolderIds = new DT\NonEmptyArrayOfBaseFolderIdsType();
+ $request->ParentFolderIds->FolderId = new DT\FolderIdType();
+ $request->ParentFolderIds->FolderId->Id = $folderID;
+
+ $response = $ews->FindItem($request);
+
+ //Format
+ $emails = $response->ResponseMessages->FindItemResponseMessage->RootFolder->Items->Message;
+ if ( is_array($emails) === FALSE ) {
+ $array = array( $emails );
+ }
+ else {
+ $array = $emails;
+ }
+ $ids = array();
+ foreach ( $array as $email ) {
+ $ids[] = $email->ItemId->Id;
+ }
+
+ return $ids;
+
+ }
+ static function getMailIdsPaginated( $profile, $folderID, $page, $limit) {
+ $ews = self::init( $profile );
+
+ $request = new DT\FindItemType();
+ $request->ItemShape = new DT\ItemResponseShapeType();
+ $request->ItemShape->BaseShape = DT\DefaultShapeNamesType::ID_ONLY;
+ $request->Traversal = DT\ItemQueryTraversalType::SHALLOW;
+
+ $offset = $limit * ($page - 1);
+ $request->IndexedPageItemView = new DT\IndexedPageViewType();
+ $request->IndexedPageItemView->BasePoint = "Beginning";
+ $request->IndexedPageItemView->Offset = $offset; // Item number you want to start at
+ $request->IndexedPageItemView->MaxEntriesReturned = $limit; // Numer of items to return in total
+
+ $request->ParentFolderIds = new DT\NonEmptyArrayOfBaseFolderIdsType();
+ $request->ParentFolderIds->FolderId = new DT\FolderIdType();
+ $request->ParentFolderIds->FolderId->Id = $folderID;
+
+ $request->SortOrder = new DT\NonEmptyArrayOfFieldOrdersType();
+ $request->SortOrder->FieldOrder = array();
+ $order = new DT\FieldOrderType();
+
+ $order->FieldURI = '';
+ @$order->FieldURI->FieldURI = 'item:DateTimeSent'; // @ symbol stops the creating default object from empty value error
+ $order->Order = 'Descending';
+ $request->SortOrder->FieldOrder[] = $order;
+
+ $response = $ews->FindItem($request);
+
+ //Format
+ $emails = $response->ResponseMessages->FindItemResponseMessage->RootFolder->Items->Message;
+ if ( is_array($emails) === FALSE ) {
+ $array = array( $emails );
+ }
+ else {
+ $array = $emails;
+ }
+ $ids = array();
+ foreach ( $array as $email ) {
+ $ids[] = $email->ItemId->Id;
+ }
+
+ return $ids;
+
+ }
+ static function getMailIdsAfterDate( $profile, $folderID, $date ) {
+ $ews = self::init( $profile );
+
+ $request = new DT\FindItemType();
+ $request->ItemShape = new DT\ItemResponseShapeType();
+ $request->ItemShape->BaseShape = DT\DefaultShapeNamesType::ID_ONLY;
+ $request->Traversal = DT\ItemQueryTraversalType::SHALLOW;
+
+ $request->ParentFolderIds = new DT\NonEmptyArrayOfBaseFolderIdsType();
+ $request->ParentFolderIds->FolderId = new DT\FolderIdType();
+ $request->ParentFolderIds->FolderId->Id = $folderID;
+
+ $request->SortOrder = new DT\NonEmptyArrayOfFieldOrdersType();
+ $request->SortOrder->FieldOrder = array();
+ $order = new DT\FieldOrderType();
+
+ $request->Restriction = new DT\RestrictionType();
+ $request->Restriction->IsGreaterThan = new DT\IsGreaterThanType();
+ $request->Restriction->IsGreaterThan->FieldURI = new DT\PathToUnindexedFieldType();
+ $request->Restriction->IsGreaterThan->FieldURI->FieldURI = 'item:DateTimeReceived';
+ $request->Restriction->IsGreaterThan->FieldURIOrConstant = new DT\FieldURIOrConstantType();
+ $request->Restriction->IsGreaterThan->FieldURIOrConstant->Constant = new DT\ConstantValueType();
+ $request->Restriction->IsGreaterThan->FieldURIOrConstant->Constant->Value = $date;
+
+ $order->FieldURI = '';
+ @$order->FieldURI->FieldURI = 'item:DateTimeReceived';
+ $order->Order = 'Ascending';
+ $request->SortOrder->FieldOrder[] = $order;
+
+ $response = $ews->FindItem($request);
+
+ if ( $emails = $response->ResponseMessages->FindItemResponseMessage->RootFolder->Items->Message ) {
+ //Format
+ if ( is_array($emails) === FALSE ) {
+ $array = array( $emails );
+ }
+ else {
+ $array = $emails;
+ }
+ $ids = array();
+ foreach ( $array as $email ) {
+ $ids[] = $email->ItemId->Id;
+ }
+ }
+ else
+ $ids = array();
+
+ return $ids;
+
+ }
+
+ static function getFolder( $profile, $folderID, $id_only = false ) {
+ $ews = self::init( $profile );
+
+ $request = new DT\GetFolderType();
+ $request->FolderShape = new DT\FolderResponseShapeType();
+ $request->FolderShape->BaseShape = DT\DefaultShapeNamesType::DEFAULT_PROPERTIES;
+ if ( $id_only )
+ $request->FolderShape->BaseShape = DT\DefaultShapeNamesType::ID_ONLY;
+
+ $request->FolderIds = new DT\NonEmptyArrayOfBaseFolderIdsType();
+ $request->FolderIds->FolderId = new DT\FolderIdType();
+ $request->FolderIds->FolderId->Id = $folderID;
+
+ $response = $ews->GetFolder($request);
+
+ $folder = $response->ResponseMessages->GetFolderResponseMessage->Folders->Folder;
+
+ return $folder;
+ }
+
+ /**
+ * getTreeFolders: Return only folders allowed by permissions
+ *
+ * @param string $profile
+ * @return array id => folder_id, name => folder_name
+ */
+ static function getTreeFolders( $profile ) {
+
+ // From Db
+ $used = array();
+ $forbidden = array();
+ $final = array();
+ $rows = self::getDBFolders( $profile );
+ foreach( $rows as $row ) {
+ if ( !self::is_allowed( $profile, $row['ews_folder'], 'read' ) ) {
+ $forbidden[] = $row['ews_folder'];
+ continue;
+ }
+
+ $final[] = array(
+ 'id' => $row['ews_folder'],
+ 'name' => $row['ews_name'],
+ );
+ $used[] = $row['ews_folder'];
+ }
+
+ // Fill in the rest
+ $folders = self::getSettingsFolders( $profile );
+
+ foreach ( $folders as $idx => $folder ) {
+ if ( !in_array( $folder['id'], $used ) && !in_array( $folder['id'], $forbidden ) )
+ $final[] = $folder;
+ }
+
+ return $final;
+ }
+
+ /**
+ * getFolders: Recursive function that gets all folders for a profile from EWS
+ *
+ * @param string $profile
+ * @param string $node
+ * @param string $node_name
+ * @return array folder_id => folder_name
+ */
+ static function getFolders( $profile, $node = null, $node_name = null) {
+ $ews = self::init( $profile );
+
+ $request = new DT\FindFolderType();
+ $request->Traversal = DT\FolderQueryTraversalType::SHALLOW;
+ $request->FolderShape = new DT\FolderResponseShapeType();
+ $request->FolderShape->BaseShape = DT\DefaultShapeNamesType::DEFAULT_PROPERTIES;
+
+ $request->IndexedPageFolderView = new DT\IndexedPageViewType();
+ $request->IndexedPageFolderView->BasePoint = 'Beginning';
+ $request->IndexedPageFolderView->Offset = 0;
+ $request->ParentFolderIds = new DT\NonEmptyArrayOfBaseFolderIdsType();
+
+ if ( $node ) {
+ $request->ParentFolderIds->FolderId = new DT\FolderIdType();
+ $request->ParentFolderIds->FolderId->Id = $node;
+ }
+ else {
+ $request->ParentFolderIds->DistinguishedFolderId = new DT\DistinguishedFolderIdType();
+ $request->ParentFolderIds->DistinguishedFolderId->Id = self::getRootFolder( $profile );
+ }
+
+ $response = $ews->FindFolder($request);
+
+ $folders = array();
+ $array = $response->ResponseMessages->FindFolderResponseMessage->RootFolder->Folders->Folder;
+ if ( !is_array( $array ) ) $array = array( $array );
+ foreach ( $array as $folder ) {
+ $name = $folder->DisplayName;
+ if ( $node_name )
+ $name = "$node_name/$name";
+ $folders[ $folder->FolderId->Id ] = $name;
+
+ if ( $folder->ChildFolderCount )
+ $folders += self::getFolders( $profile, $folder->FolderId->Id, $name );
+ }
+
+ return $folders;
+ }
+
+ /**
+ * getSettingsFolders: Get all folders for a profile from exchange
+ *
+ * @param string $profile
+ * @return array id => folder_id, name => folder_name
+ */
+ static function getSettingsFolders( $profile ) {
+ $folders = array();
+
+ $array = self::getFolders( $profile );
+
+ foreach ( $array as $id => $folder ) {
+ $folders[] = array(
+ 'id' => $id,
+ 'name' => $folder,
+ );
+ }
+
+ return $folders;
+ }
+ static function getFoldersSelOptions( $profile, $names_only = false ) {
+ if ( !$profile ) return array();
+
+ $folders = self::getSettingsFolders( $profile );
+ $final = array();
+ foreach ( $folders as $folder ) {
+ if ( !$names_only )
+ $final[ $folder['id'] ] = $folder['name'];
+ else
+ $final[ $folder['name'] ] = $folder['name'];
+ }
+
+ return $final;
+ }
+
+ static function moveMail( $profile, $Id, $ChangeKey, $toFolder, $move = true ) {
+ $ews = self::init( $profile );
+
+ $toFolder = self::getActualFolderId( $profile, $toFolder );
+
+ $request = new DT\BaseMoveCopyItemType();
+ $request->ToFolderId = new DT\TargetFolderIdType();
+ $request->ToFolderId->FolderId = new DT\FolderIdType();
+ $request->ToFolderId->FolderId->Id =$toFolder;
+ $request->ItemIds = new DT\NonEmptyArrayOfBaseItemIdsType();
+ $request->ItemIds->ItemId = array();
+ $message_item = new DT\ItemIdType();
+ $message_item->Id = $Id;
+ $message_item->ChangeKey = $ChangeKey;
+ $request->ItemIds->ItemId[] = $message_item;
+ if ( $move ) {
+ $response = $ews->MoveItem($request);
+ $status = ( $response->ResponseMessages->MoveItemResponseMessage->ResponseClass == 'Success' );
+ $msg = 'Exchange: '.$response->ResponseMessages->MoveItemResponseMessage->MessageText;
+ }
+ else {
+ $response = $ews->CopyItem($request);
+ $status = ( $response->ResponseMessages->CopyItemResponseMessage->ResponseClass == 'Success' );
+ $msg = 'Exchange: '.$response->ResponseMessages->CopyItemResponseMessage->MessageText;
+ }
+
+ if ( !$status )
+ throw new \Exception( $msg );
+
+ return $msg;
+ }
+
+ static function DeleteMail( $profile, $Id, $deleteType ) {
+ $ews = self::init( $profile );
+
+ $request = new DT\DeleteItemType();
+ $request->DeleteType = $deleteType;
+ $request->ItemIds = new DT\NonEmptyArrayOfBaseItemIdsType();
+ $request->ItemIds->ItemId = array();
+ $message_item = new DT\ItemIdType();
+ $message_item->Id = $Id;
+ $request->ItemIds->ItemId[] = $message_item;
+ $response = $ews->DeleteItem($request);
+
+ $msg = 'Exchange:'.$response->ResponseMessages->DeleteItemResponseMessage->MessageText;
+ $status = ( $response->ResponseMessages->DeleteItemResponseMessage->ResponseClass == 'Success' );
+ if ( !$status )
+ throw new \Exception( $msg );
+
+
+ return array('status' => $status, 'msg' => $msg);
+ }
+
+ static function getInbox( $ews ) {
+
+ $request = new DT\GetFolderType();
+ $request->FolderShape = new DT\FolderResponseShapeType();
+ $request->FolderShape->BaseShape = DT\DefaultShapeNamesType::DEFAULT_PROPERTIES;
+
+ $request->FolderIds = new DT\NonEmptyArrayOfBaseFolderIdsType();
+ $request->FolderIds->DistinguishedFolderId = new DT\DistinguishedFolderIdType();
+ $request->FolderIds->DistinguishedFolderId->Id = DT\DistinguishedFolderIdNameType::MESSAGE_FOLDER_ROOT;
+
+ $response = $ews->GetFolder($request);
+
+ if ( $response->ResponseMessages->GetFolderResponseMessage->ResponseClass == 'Error' )
+ throw new \Exception( $response->ResponseMessages->GetFolderResponseMessage->MessageText );
+
+ return true;
+
+ }
+
+ static function getDefaultFolder( $profile ) {
+ // Get From Db
+ $db = clone($GLOBALS['egw']->db);
+ $sql = "SELECT * FROM egw_ea_ews WHERE ews_profile= $profile and ews_is_default=1";
+ $db->query($sql);
+ $row = $db->row( true );
+ if ( $row )
+ return $row['ews_name'];
+
+ $folders = self::getTreeFolders( $profile );
+ return $folders[0]['name'];
+ }
+
+ static function createFolder( $profile, $parentID, $name ) {
+ $ews = self::init( $profile );
+
+ // Build the request object.
+ $request = new DT\CreateFolderType();
+ $request->Folders = new DT\ArrayOfFoldersType();
+
+ $parent = new DT\TargetFolderIdType();
+ if ( !$parentID ) {
+ $parent->DistinguishedFolderId = new DT\DistinguishedFolderIdType();
+ $parent->DistinguishedFolderId->Id = self::getRootFolder( $profile );
+ }
+ else {
+ $parent->FolderId = new DT\FolderIdType();
+ $parent->FolderId->Id = $parentID;
+ }
+
+ $request->ParentFolderId = $parent;
+
+ $folder = new DT\FolderType();
+ $folder->DisplayName = $name;
+ $request->Folders->Folder = array( $folder );
+ $response = $ews->CreateFolder($request);
+
+ if ( $response->ResponseMessages->CreateFolderResponseMessage->ResponseClass == 'Error' )
+ throw new \Exception( $response->ResponseMessages->CreateFolderResponseMessage->MessageText );
+
+ return true;
+ }
+ static function deleteFolder( $profile, $folderID ) {
+ $ews = self::init( $profile );
+
+ // Build the request object.
+ $request = new DT\DeleteFolderType();
+ $request->DeleteType = 'HardDelete';
+ $request->FolderIds = new DT\NonEmptyArrayOfBaseFolderIdsType();
+
+ $request->FolderIds = new DT\NonEmptyArrayOfBaseFolderIdsType();
+ $request->FolderIds->FolderId = new DT\FolderIdType();
+ $request->FolderIds->FolderId->Id = $folderID;
+
+ $response = $ews->DeleteFolder($request);
+
+ if ( $response->ResponseMessages->DeleteFolderResponseMessage->ResponseClass == 'Error' )
+ throw new \Exception( $response->ResponseMessages->DeleteFolderResponseMessage->MessageText );
+
+ return true;
+ }
+ static function moveFolder( $profile, $folder, $parent ) {
+ $ews = self::init( $profile );
+
+ // Build the request object.
+ $request = new DT\BaseMoveCopyFolderType();
+
+ $request->ToFolderId = new DT\TargetFolderIdType();
+ $request->ToFolderId->FolderId = new DT\FolderIdType();
+ $request->ToFolderId->FolderId->Id = $parent;
+
+ // Set the parent folder for the newly DT\created folder.
+ $request->FolderIds = new DT\NonEmptyArrayOfBaseFolderIdsType();
+ $request->FolderIds->FolderId = new DT\FolderIdType();
+ $request->FolderIds->FolderId->Id = $folder;
+
+ $response = $ews->MoveFolder($request);
+ if ( $response->ResponseMessages->MoveFolderResponseMessage->ResponseClass == 'Error' )
+ throw new \Exception( $response->ResponseMessages->MoveFolderResponseMessage->MessageText );
+
+ return true;
+ }
+
+ static function renameFolder( $profile, $folder, $changeKey, $name ) {
+ $ews = self::init( $profile );
+
+ // Build the request object.
+ $request = new DT\UpdateFolderType();
+
+ $set = new DT\SetFolderFieldType();
+ $set->FieldURI = new DT\PathToUnindexedFieldType;
+ $set->FieldURI->FieldURI = 'folder:DisplayName';
+ $set->Folder = new DT\FolderType();
+ $set->Folder->DisplayName = $name;
+
+ $change = new DT\FolderChangeType();
+ $change->FolderId = new DT\FolderIdType();
+ $change->FolderId->Id = $folder;
+ $change->FolderId->ChangeKey = $changeKey;
+ $change->Updates = new DT\NonEmptyArrayOfFolderChangeDescriptionsType();
+ $change->Updates->SetFolderField = array( $set );
+
+ $request->FolderChanges = new DT\NonEmptyArrayOfFolderChangesType();
+ $request->FolderChanges->FolderChange = array( $change );
+
+ $response = $ews->UpdateFolder($request);
+
+ if ( $response->ResponseMessages->UpdateFolderResponseMessage->ResponseClass == 'Error' )
+ throw new \Exception( $response->ResponseMessages->UpdateFolderResponseMessage->MessageText );
+
+ return true;
+ }
+ static function getType( $object ) {
+ $type = get_class( $object );
+ list(,,$datatype) = explode('\\', $type);
+ $final = str_replace('Type','', $datatype );
+ if ( $final == 'ContainsExpression' )
+ $final = 'Contains';
+ return $final;
+ }
+ static function setRestriction( $filter ) {
+
+ $restrictions = array();
+ if ( $tmp = self::buildSearchRestrictions( $filter ) )
+ $restrictions[] = $tmp;
+ if ( $tmp = self::buildDateRestrictions( $filter ) )
+ $restrictions[] = $tmp;
+ if ( $tmp = self::buildStatusRestrictions( $filter ) )
+ $restrictions[] = $tmp;
+
+ if ( !$restrictions ) return null;
+
+ if ( count( $restrictions ) > 1 ) {
+ // Concatenate Ands
+ $and = new DT\AndType();
+ $one = array_pop( $restrictions );
+ $two = array_pop( $restrictions );
+ $type1 = self::getType( $one );
+ $type2 = self::getType( $two );
+ $and->$type1 = $one;
+ $and->$type2 = $two;
+ while( $curr = array_pop( $restrictions ) ) {
+ $new = new DT\AndType();
+ $type = self::getType( $curr );
+ $new->$type = $curr;
+ $new->And = $and;
+ $and = $new;
+ }
+
+ $final = $and;
+ }
+ else
+ $final = $restrictions[0];
+
+ $restriction = new DT\RestrictionType();
+ $type = self::getType( $final );
+ $restriction->$type = $final;
+
+ return $restriction;
+ }
+ static function buildSearchRestrictions( $filter ) {
+ if ( !$filter['string'] ) return null;
+
+ $fields = array(
+ 'cc' => 'message:CcRecipients',
+ 'to' => 'message:ToRecipients',
+ 'from' => 'message:From',
+ 'subject' => 'item:Subject',
+ 'body' => 'item:Body',
+ );
+
+ // Prepare restrictions
+ $restr = array();
+ foreach( $fields as $field => $value ) {
+ $tmp = new DT\ContainsExpressionType();
+ $tmp->ContainmentMode = 'Substring';
+ $tmp->ContainmentComparison = 'IgnoreCase';
+ $tmp->FieldURI = new DT\PathToUnindexedFieldType();
+ $tmp->FieldURI->FieldURI = $value;
+ $tmp->Constant = new DT\ConstantValueType();
+ $tmp->Constant->Value = $filter['string'];
+ $restr[ $field ] = $tmp;
+ }
+
+ // Apply appropriate restrictions to each scenario
+ $ors = array();
+ switch( $filter['type'] ) {
+ case 'quickwithcc':
+ $ors[] = $restr['cc'];
+ case 'bydate':
+ case 'quick':
+ $ors[] = $restr['to'];
+ $ors[] = $restr['from'];
+ $ors[] = $restr['subject'];
+ break;
+ case 'subject':
+ case 'body':
+ case 'from':
+ case 'to':
+ case 'cc':
+ $ors[] = $restr[ $filter['type'] ];
+ break;
+ case 'text':
+ foreach( $fields as $field => $value )
+ $ors[] = $restr[ $field ];
+ break;
+ }
+
+ $restriction = null;
+ if ( count( $ors ) > 1 ) {
+ // Concatenate ORs
+ $or = new DT\OrType();
+ $one = array_pop( $ors );
+ $two = array_pop( $ors );
+ $or->Contains = array( $one, $two );
+ while( $curr = array_pop( $ors ) ) {
+ $new = new DT\OrType();
+ $new->Contains = $curr;
+ $new->Or = $or;
+ $or = $new;
+ }
+
+ $restriction = $or;
+ }
+ else
+ $restriction = $ors[0];
+
+ return $restriction;
+ }
+ static function buildDateRestrictions( $filter ) {
+ if ( !$filter['range'] ) return null;
+
+ $greater = null;
+ if ( $filter['range'] == 'SINCE' || $filter['range'] == 'BETWEEN' ) {
+ $date = ( $filter['range'] == 'SINCE' ? $filter['date'] : $filter['since'] );
+ $tz = new \DateTimeZone('UTC');
+ $dt = \DateTime::createFromFormat( 'd-M-Y H:i:s', "$date 00:00:00", $tz );
+ $greater = new DT\IsGreaterThanOrEqualToType();
+ $greater->FieldURI = new DT\PathToUnindexedFieldType();
+ $greater->FieldURI->FieldURI = 'item:DateTimeReceived';
+ $greater->FieldURIOrConstant = new DT\FieldURIOrConstantType();
+ $greater->FieldURIOrConstant->Constant = new DT\ConstantValueType();
+ $greater->FieldURIOrConstant->Constant->Value = $dt->format("Y-m-d\TH:i:s\Z");
+ }
+
+ $less = null;
+ if ( $filter['range'] == 'BEFORE' || $filter['range'] == 'BETWEEN' ) {
+ $date = ( $filter['range'] == 'BEFORE' ? $filter['date'] : $filter['before'] );
+ $tz = new \DateTimeZone('UTC');
+ $dt = \DateTime::createFromFormat( 'd-M-Y H:i:s', "$date 23:59:59", $tz );
+ $less = new DT\IsLessThanOrEqualToType();
+ $less->FieldURI = new DT\PathToUnindexedFieldType();
+ $less->FieldURI->FieldURI = 'item:DateTimeReceived';
+ $less->FieldURIOrConstant = new DT\FieldURIOrConstantType();
+ $less->FieldURIOrConstant->Constant = new DT\ConstantValueType();
+ $less->FieldURIOrConstant->Constant->Value = $dt->format("Y-m-d\TH:i:s\Z");
+ }
+
+ $restriction = null;
+ if ( $greater && $less ) {
+ $restriction = new DT\AndType();
+ $restriction->IsGreaterThanOrEqualTo = $greater;
+ $restriction->IsLessThanOrEqualTo = $less;
+ }
+ else if ( $greater )
+ $restriction = $greater;
+ else
+ $restriction = $less;
+
+ return $restriction;
+ }
+ static function buildStatusRestrictions( $filter ) {
+ if ( !$filter['status'] ) return null;
+ $status = ( is_array( $filter['status'] ) ? $filter['status'] : array( $filter['status'] ) );
+ $ands = array();
+ foreach( $status as $current ) {
+ $stat = strtolower( $current );
+ switch( $stat ) {
+ case 'seen':
+ case 'unseen':
+ $equal = new DT\IsEqualToType();
+ $equal->FieldURI = new DT\PathToUnindexedFieldType();
+ $equal->FieldURI->FieldURI = 'message:IsRead';
+ $equal->FieldURIOrConstant = new DT\FieldURIOrConstantType();
+ $equal->FieldURIOrConstant->Constant = new DT\ConstantValueType();
+ $equal->FieldURIOrConstant->Constant->Value = ( $stat == 'seen' ? 1: 0);
+ $ands[] = $equal;
+ break;
+ }
+ }
+ if ( !$ands ) return null;
+
+ if ( count( $ands ) > 1 ) {
+ // Concatenate ORs
+ $and = new DT\AndType();
+ $one = array_pop( $ands );
+ $two = array_pop( $ands );
+ $and->IsEqualTo = array( $one, $two );
+ while( $curr = array_pop( $ands ) ) {
+ $new = new DT\AndType();
+ $new->IsEqualTo = $curr;
+ $new->And = $and;
+ $and = $new;
+ }
+
+ $restriction = $or;
+ }
+ else
+ $restriction = $ands[0];
+
+ return $restriction;
+ }
+
+ /**
+ * getDBFolders
+ *
+ * @param mixed $profile
+ * @return void
+ */
+ static function getDBFolders( $profile ) {
+ $db = clone( $GLOBALS['egw']->db );
+
+ $db->query("SELECT * FROM egw_ea_ews WHERE ews_profile=$profile ORDER BY ews_order,ews_name");
+ $rows = array();
+ while( $row = $db->row( true ) )
+ $rows[] = $row;
+
+ return $rows;
+ }
+ static function getWriteFolders( $profile, $from ) {
+ $db = clone( $GLOBALS['egw']->db );
+ $account = Mail\Account::read( $profile );
+
+ if ( $account->params['acc_ews_type'] == 'inbox' )
+ return self::getFolders( $profile );
+
+ // Can move FROM folder
+ $sql = "SELECT ifnull(ews_move_to,0) as ews_move_to, ews_move_anywhere FROM egw_ea_ews WHERE ews_profile= $profile and ews_folder='$from'";
+ $db->query($sql);
+ $row = $db->row( true );
+ $acc = Mail\Account::read( $profile );
+ $move_anywhere = $row['ews_move_anywhere'];
+ $move_to = explode(',', $row['ews_move_to'] );
+
+ $db->query("SELECT * FROM egw_ea_ews WHERE ews_profile=$profile ORDER BY ews_order,ews_name");
+ $rows = array();
+
+ while( $row = $db->row( true ) ) {
+ if ( $row['ews_apply_permissions'] || $acc['acc_ews_apply_permissions']) {
+ if ( !$move_anywhere )
+ $allow_from = in_array( $row['ews_folder'], $move_to );
+ else
+ $allow_from = true;
+ }
+ else
+ $allow_from = true;
+
+ if ( $row['ews_apply_permissions'] || $acc['acc_ews_apply_permissions']) {
+ $permissions = unserialize( $row['ews_permissions'] );
+ $allow_to = ( $permissions['write'] );
+ }
+ else
+ $allow_to = true;
+
+ if ( $allow_to && $allow_from )
+ $rows[ $row['ews_folder'] ] = $row['ews_name'];
+ }
+
+ return $rows;
+ }
+ static function is_allowed( $profile, $folder, $action ) {
+ $allowed = false;
+ $account = Mail\Account::read( $profile );
+
+ if ( $account->params['acc_ews_type'] == 'inbox' ) return true;
+
+ $db = clone($GLOBALS['egw']->db);
+ $sql = "SELECT ews_apply_permissions, ews_permissions FROM egw_ea_ews WHERE ews_profile=$profile AND ews_folder='$folder' ORDER BY ews_order,ews_name";
+ $db->query($sql);
+ $row = $db->row( true );
+
+ $acc = Mail\Account::read( $profile );
+
+ if ( $row['ews_apply_permissions'] || $acc['acc_ews_apply_permissions']) {
+ $permissions = unserialize( $row['ews_permissions'] );
+ $allow = ( $permissions[ $action ] );
+ }
+ else
+ $allow = true;
+
+ return $allow;
+ }
+ static function can_delete( $profile, $folder ) {
+ return self::is_allowed( $profile, $folder, 'delete' );
+ }
+ static function can_move( $profile, $from, $to ) {
+ $db = clone($GLOBALS['egw']->db);
+
+ // Can move FROM->TO folder
+ $sql = "SELECT ifnull(ews_move_to,0) as ews_move_to, ews_move_anywhere FROM egw_ea_ews WHERE ews_profile= $profile and ews_folder='$from'";
+ $db->query($sql);
+ $row = $db->row( true );
+ $acc = Mail\Account::read( $profile );
+ if ( $row['ews_apply_permissions'] || $acc['acc_ews_apply_permissions'])
+ $allowed_from = ( $row['ews_move_anywhere'] || in_array( $to, explode(',', $row['ews_move_to'] )));
+ else
+ $allowed_from = true;
+
+ // Can write in TO folder
+ $allowed_to = self::is_allowed( $profile, $to, 'write' );
+
+ return $allowed_from && $allowed_to;
+ }
+ static function can_manage_folder( $profile, $folder ) {
+ return self::is_allowed( $profile, $folder, 'manage_folder' );
+ }
+ static function getRootFolder( $profile ) {
+ if ( $profile )
+ $account = Mail\Account::read( $profile );
+
+ if ( !$profile || $account->params['acc_ews_type'] == 'inbox' )
+ return DT\DistinguishedFolderIdNameType::MESSAGE_FOLDER_ROOT;
+ else
+ return DT\DistinguishedFolderIdNameType::PUBLIC_FOLDERS_ROOT;
+ }
+
+ static function formatAddresses( $obj ) {
+ if ( !$obj || !$obj->Mailbox ) return '';
+ $mailbox = $obj->Mailbox;
+ $mailboxes = (is_array( $mailbox ) ? $mailbox : array( $mailbox ));
+
+ $addresses = array();
+ foreach( $mailboxes as $mail) {
+ if ( $mail->Name && $mail->Name != $mail->EmailAddress )
+ $addresses[] = $mail->Name.' <'.$mail->EmailAddress.'>';
+ else
+ $addresses[] = $mail->EmailAddress;
+ }
+
+ return implode(',',$addresses );
+ }
+
+ static function getActualFolderId( $profile, $folder ) {
+ if ( strlen( $folder ) < 50 ) {
+ $folderName = $folder;
+ $folders = self::getFolders( $profile );
+ $folder = array_search( $folderName, $folders);
+ if ( $folder === FALSE)
+ throw new \Exception( "Specified folder $folderName was not found in your profile" );
+ }
+ return $folder;
+ }
+
+ static function renameFolderDB( $profile, $folderID, $name ) {
+ $db = clone($GLOBALS['egw']->db);
+ $sql = "UPDATE egw_ea_ews set ews_name='$name' WHERE ews_profile= $profile and ews_folder='$folderID'";
+ $db->query($sql);
+ return true;
+ }
+}
diff --git a/api/src/Mail/Imap.php b/api/src/Mail/Imap.php
index a32a041756a..557f2bba227 100644
--- a/api/src/Mail/Imap.php
+++ b/api/src/Mail/Imap.php
@@ -876,6 +876,9 @@ function getCapabilities()
*/
function hasCapability($capability)
{
+ if ($capability=='SUPPORTS_FLAGS')
+ return true;
+
if ($capability=='SUPPORTS_KEYWORDS')
{
// if pseudo-flag is not set, call examineMailbox now to set it (no STATUS_ALL = counters necessary)
diff --git a/api/src/Mail/Types.php b/api/src/Mail/Types.php
index 9d031b8b151..bf12701110f 100644
--- a/api/src/Mail/Types.php
+++ b/api/src/Mail/Types.php
@@ -112,7 +112,22 @@ protected static function server_types($imap=true, $extended=false)
$types[$class_name] = $extended ? $type : $type['description'];
}
- //error_log(__METHOD__."(".array2string($data).") returning ".array2string($types));
+ // Add EWS
+ if ( $imap ) {
+ foreach( scandir($dir=__DIR__.'/EWS') as $file )
+ {
+ if (substr($file, -4) == '.php' && substr($file,0,3) == 'EWS') {
+ $class_name = __NAMESPACE__.'\\EWS\\'.substr($file, 0, -4);
+ $type = array(
+ 'classname' => $class_name,
+ 'description' => is_callable($function=$class_name.'::description') ? call_user_func($function) : $class_name,
+ );
+ $type['protocol'] = 'imap';
+
+ $types[$class_name] = $extended ? $type : $type['description'];
+ }
+ }
+ }
return $types;
}
-}
\ No newline at end of file
+}
diff --git a/api/src/Mail_EWS.php b/api/src/Mail_EWS.php
new file mode 100644
index 00000000000..6a701615103
--- /dev/null
+++ b/api/src/Mail_EWS.php
@@ -0,0 +1,870 @@
+
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @version $Id$
+ */
+
+namespace EGroupware\Api;
+
+use Horde_Imap_Client;
+use Horde_Imap_Client_Ids;
+use Horde_Imap_Client_Fetch_Query;
+use Horde_Imap_Client_Data_Fetch;
+use Horde_Mime_Part;
+use Horde_Imap_Client_Search_Query;
+use Horde_Idna;
+use Horde_Imap_Client_DateTime;
+use Horde_Mime_Headers;
+use Horde_Compress;
+use Horde_Mime_Magic;
+use Horde_Mail_Rfc822;
+use Horde_Mail_Rfc822_List;
+use Horde_Mime_Mdn;
+use EGroupware\Api;
+use EGroupware\Api\Mail;
+use EGroupware\Api\Mail\EWS;
+use EGroupware\Api\Mail\EWS\Lib;
+
+use tidy;
+
+/**
+ * Mail worker class
+ * -provides backend functionality for all classes in Mail
+ * -provides classes that may be used by other apps too
+ *
+ * @link https://github.com/horde/horde/blob/master/imp/lib/Contents.php
+ */
+class Mail_EWS extends Mail
+{
+ protected function __construct($_displayCharset='utf-8',$_restoreSession=true, $_profileID=0, $_oldImapServerObject=false, $_reuseCache=null)
+ {
+ if (is_null($_reuseCache)) $_reuseCache = $_restoreSession;
+ if (!empty($_displayCharset)) self::$displayCharset = $_displayCharset;
+ // not nummeric, we assume we only want an empty class object
+ if (!is_numeric($_profileID)) return true;
+ if ($_restoreSession)
+ {
+ $this->restoreSessionData();
+ $lv_mailbox = $this->sessionData['mailbox'];
+ $firstMessage = $this->sessionData['previewMessage'];
+ }
+ else
+ {
+ $this->restoreSessionData();
+ $lv_mailbox = $this->sessionData['mailbox'];
+ $firstMessage = $this->sessionData['previewMessage'];
+ $this->sessionData = array();
+ }
+ if (!$_reuseCache) $this->forcePrefReload($_profileID,!$_reuseCache);
+ try
+ {
+ $this->profileID = self::validateProfileID($_profileID);
+ $this->accountid = $GLOBALS['egw_info']['user']['account_id'];
+
+ $acc = Mail\Account::read($this->profileID);
+ }
+ catch (\Exception $e)
+ {
+ throw new Exception(__METHOD__." failed to instanciate Mail for $_profileID / ".$this->profileID." with error:".$e->getMessage());
+ }
+ $this->icServer = ($_oldImapServerObject?$acc->oldImapServer():$acc->imapServer());
+ $this->ogServer = $acc->smtpServer();
+ // TODO: merge mailprefs into userprefs, for easy treatment
+ $this->mailPreferences = $GLOBALS['egw_info']['user']['preferences']['mail'];
+ $this->htmlOptions = $this->mailPreferences['htmlOptions'];
+ if (isset($this->icServer->ImapServerId) && !empty($this->icServer->ImapServerId))
+ {
+ $_profileID = $this->profileID = $this->icServer->ImapServerId;
+ }
+
+ if (is_null(self::$mailConfig)) self::$mailConfig = Config::read('mail');
+ }
+ function appendMessage($_folderId, $_header, $_body, $_flags='\\Recent')
+ {
+ $folderId = $this->getFolderId( $_folderId );
+
+ // After Message is Sent, store mail (contained as stream in _header) to 'Sent Folder' (from config) _folderId
+ $raw = stream_get_contents( $_header );
+ $mime = base64_encode( $raw );
+
+ return Lib::createMail( $this->profileID, $folderId, $mime );
+ }
+ function deleteMessages($_messageUID, $_folder=NULL, $_forceDeleteMethod='no') {
+ // Delete messages after checking for specitic folder permissions
+ if ( !$_folder || !$_messageUID ) return;
+
+
+ $folderID = $this->getFolderId( $_folder );
+ $methodMap = array(
+ 'no' => 'MoveToDeletedItems',
+ 'mark_as_deleted' => 'SoftDelete',
+ 'remove_immediately' => 'HardDelete',
+ 'move_to_trash' => 'MoveToDeletedItems',
+ );
+
+ if ( $_messageUID == 'all' )
+ $_messageUID = Lib::getMailIds( $this->profileID, $folderID );
+
+ if ( !is_array( $_messageUID ) )
+ $_messageUID = array( $_messageUID );
+
+ $messages = '';
+ foreach( $_messageUID as $message ) {
+ list($mailID, $changeKey) = explode( '||', $message );
+ $allowed = Lib::can_delete( $this->profileID, $folderID );
+ if ( !$allowed )
+ $messages .= "No Permissions";
+
+
+ // Check for external actions before deleting email
+ $validations = Api\Hooks::process(array(
+ 'location' => 'mail_before_delete',
+ 'profile' => $this->profileID,
+ 'folder' => $folderID,
+ 'message' => $mailID,
+ ));
+ if ( is_array( $validations ) ) {
+ foreach ( $validations as $app => $validation) {
+ $allowed = $allowed && $validation['allowed'];
+ if ( !$allowed )
+ $messages .= $validation['messages'];
+ }
+ }
+ $method = $methodMap[ $_forceDeleteMethod ];
+
+ // If in Trash folder, hard Delete
+ if ( $_folder == $this->getTrashFolder() )
+ $method = 'HardDelete';
+
+ if ( !$allowed )
+ throw new Exception("Deleting Mail failed! $messages");
+
+ try {
+ if ( $method == 'MoveToDeletedItems' && $this->getTrashFolder() )
+ Lib::moveMail( $this->profileID, $mailID, $changeKey, $this->getTrashFolder() );
+ else
+ Lib::DeleteMail( $this->profileID, $mailID, $method );
+ }
+ catch (\Exception $e ) {
+ throw new Exception("Deleting Mail failed! Error:".$e->getMessage());
+ }
+
+ }
+
+ return true;
+
+ }
+ function flagMessages($_flag, $_messageUID,$_folder=NULL) {
+ // Flag messages as read/unread. Other flags are not implemented
+ $messages = '';
+ if ( !is_array( $_messageUID ) ) $_messageUID = array( $_messageUID );
+ foreach( $_messageUID as $message ) {
+ list($mailID, $changeKey) = explode( '||', $message );
+ if ( $_flag == 'read' )
+ $messages .= Lib::setRead( $this->profileID, $mailID, $changeKey, true );
+ else if ( $_flag == 'unread' )
+ $messages .= Lib::setRead( $this->profileID, $mailID, $changeKey, false );
+ /* else */
+ /* throw new Exception("Operation '$_flag' not supported for EWS"); */
+ }
+
+ return $messages;
+ }
+ function getAttachments($mailID )
+ {
+ // Get attachments for mailID excluding Embedded images
+ $attachments = $this->getAllAttachments( $mailID );
+ $only = array();
+ foreach ( $attachments as $attachment )
+ if ( !$attachment['cid'] ) $only[] = $attachment;
+
+ return $only;
+ }
+ function getAllAttachments( $mailID ) {
+ // Get All attachments for mailID, including Embedded images
+ $mailID = str_replace(' ','+', $mailID );
+ $email = Lib::getMailPart( $this->profileID, $mailID, 'attachments,body' );
+
+ return $this->formatAttachments( $email );
+ }
+ function getAttachment($_uid, $_partID, $_winmail_nr=0, $_returnPart=true, $_stream=false, $_folder=null)
+ {
+ // Get attachment and its contents, return as data or stream
+ $attachment = Lib::getAttachment( $this->profileID, $_partID );
+ $att_content = $attachment->Content;
+
+ // If attachment fetched is email, get raw content
+ if ( !$att_content && $attachment->Message )
+ $att_content = base64_decode( $attachment->Message->MimeContent->_ );
+
+ if ( $_stream ) {
+ $tmp = fopen('php://temp', 'w+');
+
+ if (!is_null($att_content)) {
+ fwrite($tmp, $att_content);
+ rewind($tmp);
+ }
+ $content = $tmp;
+ }
+ else {
+ $content = $att_content;
+ }
+ $type = $attachment->ContentType;
+ if ( !$type )
+ $type = Api\MimeMagic::ext2mime( pathinfo( $attachment->Name, PATHINFO_EXTENSION ) );
+
+ return array(
+ 'type' => $type,
+ 'mimeType' => $type,
+ 'charset' => '',
+ 'filename' => $attachment->Name,
+ 'attachment' => $content,
+ );
+ }
+ function getAttachmentByCID($_uid, $_cid, $_part, $_stream=null) {
+ // Get embedded
+ list($mailID,$changeKey) = explode( '||', $_uid );
+ $mailID = str_replace(' ','+', $mailID );
+ $attachments = self::getAllAttachments( $mailID );
+
+ $final = new EWS\Attachment();
+ foreach( $attachments as $attachment ) {
+ if ( $attachment['cid'] == $_cid ) {
+ $full_attach = self::getAttachment( $_uid, $attachment['partID'], 0, true, $_stream );
+ $final->loadAttachment( $full_attach );
+ }
+ }
+
+ return $final;
+ }
+ function getFolderArrays ($_nodePath = null, $_onlyTopLevel = false, $_search= 2, $_subscribedOnly = false, $_getCounter = false) {
+ // Get Folder Tree to display. Used in mail_tree
+ $efolders = Lib::getTreeFolders( $this->profileID );
+ $foldersList = array();
+ foreach ( $efolders as $folder ) {
+ $foldersList[ $folder['name'] ] = array(
+ 'MAILBOX' => $folder['name'] ,
+ 'ATTRIBUTES' => array(
+ '\\hasChildren', '\\subscribed',
+ ),
+ 'CAN_DELETE' => $folder['delete'],
+ 'delimiter' => '/',
+ 'SUBSCRIBED' => 1,
+ 'counter' => array(
+ 'MESSAGES' => '1',
+ 'RECENT' => '0',
+ 'UIDNEXT' => '2',
+ 'UIDVALIDITY' => '1465840832',
+ 'UNSEEN' => '0',
+ )
+ );
+ }
+
+ $ids = array();
+ $all = Lib::getSettingsFolders( $this->profileID );
+ foreach( $all as $folder )
+ $ids[ $folder['name'] ] = $folder['id'];
+
+ Api\Cache::setSession('mail', $this->profileID.'::ews_folder_ids', $ids );
+ return $foldersList;
+ }
+ function getFolderStatus($_folderName,$ignoreStatusCache=false,$basicInfoOnly=false,$fetchSubscribedInfo=true) {
+ // Get Folder status (read/unread)
+ $folderId = $this->getFolderId( $_folderName );
+ $folder = Lib::getFolder( $this->profileID, $folderId );
+
+ return array(
+ 'displayName' => $folder->DisplayName,
+ 'shortName' => $folder->DisplayName,
+ 'shortDisplayName' => $folder->DisplayName,
+ 'messages' => $folder->TotalCount,
+ 'unseen' => $folder->UnreadCount,
+ /* 'uidnext' => 129, */
+ /* 'uidvalidity' => 1, */
+ /* 'recent' => 0, */
+ /* 'subscribed' => '', */
+ /* 'delimiter' => '/', */
+ );
+ }
+ function getHeaders($_folderName, $_startMessage, $_numberOfMessages, $_sort, $_reverse, $_filter, $_thisUIDOnly=null, $_cacheResult=true, $_fetchPreviews=false)
+ {
+ // Get All mails in specific folder
+
+ // Get default folder if none
+ if ( !$_folderName )
+ $_folderName = Lib::getDefaultFolder( $this->profileID );
+
+ $folderID = $this->getFolderId( $_folderName );
+
+ if ( !$_sort )
+ $_sort = 'date';
+
+ $sort_map = array(
+ 'subject' => 'item:Subject',
+ 'size' => 'item:Size',
+ 'date' => 'item:DateTimeSent',
+ 'arrival' => 'item:DateTimeCreated',
+ 'uid' => 'item:ItemId',
+ 'attachments' => 'item:HasAttachments',
+ 'seen' => 'item:IsRead',
+ 'toaddress' => 'item:DisplayTo',
+ 'fromaddress' => 'message:From',
+ 'address' => 'message:From',
+ );
+ $sort = array(
+ 'order' => $sort_map[ $_sort ],
+ 'sort' => 'Descending',
+ );
+ $_startMessage--;
+
+ $array = Lib::getMails( $this->profileID, $folderID, $_startMessage, $_numberOfMessages, $sort, $_filter );
+ if ( empty( $array['rows'] ) )
+ return array();
+
+ $emails = array();
+ foreach ( $array['rows'] as $index => &$email ) {
+ // Set fields
+ // Appended ChangeKey to ID with default delimiter. This means that uid is still recognized since array is exploded,
+ // and in specific cases ChangeKey is available for use (eg. set read/unread )
+
+ // Convert datetimes
+ $properties = array( 'DateTimeSent', 'DateTimeCreated');
+ $zone = new \DateTimeZone( $GLOBALS['egw_info']['server']['server_timezone'] );
+ foreach ( $properties as $property ) {
+ $date = new \DateTime($email->$property);
+ $date->setTimezone($zone);
+ $email->$property = $date->format('c');
+ }
+
+ $parts = 'addresses';
+
+ // Call each mail individually to get extra data, like addresses and attachment list
+ if ( $email->HasAttachments ) $parts .= ',attachments,body';
+ $email_extra = Lib::getMailPart($this->profileID, $email->ItemId->Id, $parts);
+ $attachments = ( $email->HasAttachments ? $this->formatAttachments( $email_extra, true) : array() );
+ $from = Lib::formatAddresses( $email_extra->From );
+ $to = Lib::formatAddresses( $email_extra->ToRecipients );
+ $cc = Lib::formatAddresses( $email_extra->CcRecipients );
+
+ $emails[] = array(
+ 'subject' => $email->Subject,
+ 'size' => $email->Size,
+ 'date' => $email->DateTimeSent,
+ 'internaldate' => $email->DateTimeCreated,
+ 'mimetype' => 'multipart/alternative',
+ 'uid' => $email->ItemId->Id .'||'. $email->ItemId->ChangeKey,
+ 'attachments' => $attachments,
+ // 'uid' => "e$index",
+ 'priority' => 3,
+ 'recent' => '',
+ 'flagged' => '',
+ 'answered' => '',
+ 'forwarded' => '',
+ 'deleted' => '',
+ 'seen' => $email->IsRead,
+ 'draft' => '',
+ 'mdnsent' => '',
+ 'mdnnotsent' => '',
+ 'label1' => '',
+ 'label2' => '',
+ 'label3' => '',
+ 'label4' => '',
+ 'label5' => '',
+ 'sender_address' => $from,
+ 'to_address' => $to,
+ 'cc_addresses' => explode(',', $cc ),
+ );
+ // We add '@', since without it the Name is not displayed
+ // We don't have the actual email address because EWS fetches that only when it fetches the actual email
+ // EGW fetches the email headers altogether in get_rows, even though in preview you just see the current mail
+ }
+
+ return array( 'header' => $emails, 'info' => array( 'total' => $array['count'] ) );
+ }
+ function getMessageAttachments($_uid, $_partID=null, Horde_Mime_Part $_structure=null, $fetchEmbeddedImages=true, $fetchTextCalendar=false, $resolveTNEF=true, $_folder='')
+ {
+ // Get all attachments for message
+
+ $_uid = str_replace(' ','+', $_uid );
+ list($mailID,) = explode('||', $_uid);
+ $attachments = $this->getAllAttachments( $mailID );
+ $only = array();
+ foreach ( $attachments as $attachment )
+ if ( !$attachment['cid'] ) $only[] = $attachment;
+
+ return $only;
+ }
+ function getMessageBody($_uid, $_htmlOptions='', $_partID=null, Horde_Mime_Part $_structure=null, $_preserveSeen = false, $_folder = '', &$calendar_part=null)
+ {
+ // Get Message Body
+
+ $_uid = str_replace(' ','+', $_uid );
+ list($mailID,) = explode('||', $_uid);
+
+ $email = Lib::getMail( $this->profileID, $mailID );
+ return array( array(
+ 'body' => $email->Body->_,
+ // 'mimeType' => 'text/html',
+ 'mimeType' => ($email->Body->BodyType == 'HTML' ? 'text/html' : 'text/plain'),
+ 'charSet' => 'utf-8',
+ ));
+ }
+ function getMessageRawBody($_uid, $_partID = '', $_folder='', $_stream=false)
+ {
+ // Get Raw message (eml)
+ $_uid = str_replace(' ','+', $_uid );
+ list($mailID,) = explode('||', $_uid);
+
+ $raw = Lib::getMailRaw( $this->profileID, $mailID );
+
+ if ( $_stream ) {
+ $tmp = fopen('php://temp', 'w+');
+
+ if (!is_null($raw)) {
+ fwrite($tmp, $raw);
+ rewind($tmp);
+ }
+ $message = $tmp;
+ }
+ else
+ $message = $raw;
+
+ return $message;
+ }
+ function getMessageRawHeader($_uid, $_partID = '', $_folder = '')
+ {
+ // Show only First part of raw message, before line break
+ $body = $this->getMessageRawBody( $_uid, $_partID, $_folder );
+
+ $br = strpos( $body, "\r\n\r\n" );
+ if ( $br )
+ $body = substr( $body, 0, $br );
+
+ return $body;
+ }
+ function getMessageEnvelope($_uid, $_partID = '',$decode=false, $_folder='', $_useHeaderInsteadOfEnvelope=false)
+ {
+ // Get Mail and Headers
+
+ $_uid = str_replace(' ','+', $_uid );
+ list($mailID,) = explode('||', $_uid);
+
+ $email = Lib::getMail( $this->profileID, $mailID );
+ $arrays = array( 'From', 'ToRecipients', 'CcRecipients', 'Sender' );
+ $addresses = array();
+ foreach ( $arrays as $property ) {
+ $mailboxes = ( is_array( $email->$property->Mailbox ) ? $email->$property->Mailbox : array( $email->$property->Mailbox ) );
+
+ $tmp = array();
+ foreach ( $mailboxes as $mailbox ) {
+ $tmp[] = $mailbox->Name .' <'. $mailbox->EmailAddress .'>';
+ }
+ $addresses[ $property ] = $tmp;
+ }
+
+ // Convert datetimes
+ $properties = array( 'DateTimeReceived' );
+ $zone = new \DateTimeZone( $GLOBALS['egw_info']['server']['server_timezone'] );
+ foreach ( $properties as $property ) {
+ $date = new \DateTime($email->$property);
+ $date->setTimezone($zone);
+ $email->$property = $date->format('c');
+ }
+
+ return array(
+ 'SENDER' => $addresses['Sender'],
+ 'TO' => $addresses['ToRecipients'],
+ 'FROM' => $addresses['From'],
+ 'CC' => $addresses['CcRecipients'],
+ 'BCC' => Array(),
+ 'REPLY-TO' => Array(),
+ 'DATE' => $email->DateTimeReceived,
+ 'MESSAGE-ID' => $email->InternetMessageId,
+ 'IN-REPLY-TO' => '',
+ 'REFERENCES' => '',
+ 'SUBJECT' => $email->Subject,
+ 'CONTENT-MD5' => '',
+ 'MIME-VERSION' => '',
+ 'CONTENT-TYPE' => '',
+ 'CONTENT-TRANSFER-ENCODING' => '',
+ 'CONTENT-ID' => '',
+ 'CONTENT-DESCRIPTION' => '',
+ 'CONTENT-BASE' => '',
+ 'CONTENT-DISPOSITION' => '',
+ 'CONTENT-DURATION' => '',
+ 'CONTENT-LOCATION' => '',
+ 'CONTENT-FEATURES' => '',
+ 'CONTENT-LANGUAGE' => '',
+ 'CONTENT-ALTERNATIVE' => '',
+ 'IMPORTANCE' => $email->Importance,
+ 'X-PRIORITY' => '',
+ 'LIST-HELP' => '',
+ 'LIST-UNSUBSCRIBE' => '',
+ 'LIST-SUBSCRIBE' => '',
+ 'LIST-OWNER' => '',
+ 'LIST-POST' => '',
+ 'LIST-ARCHIVE' => '',
+ 'LIST-ID' => '',
+ 'BODY' => $email->Body->_,
+ );
+ }
+ function getSortedList($_folderName, $_sort, &$_reverse, $_filter, &$resultByUid=true, $setSession=true)
+ {
+ // Get All mails in specific folder
+ $folderID = $this->getFolderId( $_folderName );
+
+ $sort_map = array(
+ 'subject' => 'item:Subject',
+ 'size' => 'item:Size',
+ 'date' => 'item:DateTimeSent',
+ 'arrival' => 'item:DateTimeCreated',
+ 'uid' => 'item:ItemId',
+ 'attachments' => 'item:HasAttachments',
+ 'seen' => 'item:IsRead',
+ 'toaddress' => 'item:DisplayTo',
+ 'fromaddress' => 'message:From',
+ 'address' => 'message:From',
+ );
+ $sort = array(
+ 'order' => $sort_map[ $_sort ],
+ 'sort' => 'Descending',
+ );
+ if ( !$sort['order'] )
+ $sort['order'] = 'item:DateTimeCreated';
+
+ $_startMessage--;
+
+ $array = Lib::getMails( $this->profileID, $folderID, 0, 999999, $sort, $_filter, true );
+
+ if ( empty( $array['rows'] ) )
+ return array();
+
+ $emails = array();
+ foreach ( $array['rows'] as $email )
+ $emails[] = $email->ItemId->Id;
+
+ return array( 'match' => $emails, 'header' => $emails, 'info' => array( 'total' => $array['count'] ) );
+ }
+ function getDefaultFolder() {
+ return $this->profileID.self::DELIMITER.Lib::getDefaultFolder( $this->profileID );
+ }
+ /**
+ * formatAttachments: Normalize attachments array
+ *
+ * @param object $email Must have Attachments and Body property (to check for embedded)
+ * @param boolean $skip_embedded
+ * @param boolean $details
+ * @return array
+ */
+ function formatAttachments( $email, $skip_embedded = false ) {
+ // Format data (one or many)
+ $files = array();
+ $items = array();
+ if ( $email->Attachments->FileAttachment )
+ $files = ( is_array( $email->Attachments->FileAttachment ) ? $email->Attachments->FileAttachment : array( $email->Attachments->FileAttachment ) );
+ if ( $email->Attachments->ItemAttachment )
+ $items = ( is_array( $email->Attachments->ItemAttachment ) ? $email->Attachments->ItemAttachment : array( $email->Attachments->ItemAttachment ) );
+ $data = array_merge( $files, $items );
+
+ // Format for use in header
+ $attachments = array();
+ foreach ( $data as $attachment ) {
+ // Double check: search email for CID in case ContentId is set, but doesn't exist in body
+ $cid = '';
+ if ( $attachment->ContentId && strpos($email->Body->_, $attachment->ContentId) !== FALSE )
+ $cid = $attachment->ContentId;
+
+ if ( $cid && $skip_embedded ) continue;
+
+ $type = $attachment->ContentType;
+ if ( !$type )
+ $type = Api\MimeMagic::ext2mime( pathinfo( $attachment->Name, PATHINFO_EXTENSION ) );
+
+ $attachments[] = array(
+ 'size' => ( $attachment->Size ? $attachment->Size : ''),
+ 'filename' => $attachment->Name,
+ 'type' => $type,
+ 'mimeType' => $type,
+ 'uid' => $attachment->AttachmentId->Id,
+ 'cid' => $cid,
+ 'partID' => $attachment->AttachmentId->Id,
+ 'name' => $attachment->Name,
+ 'folder' => 'foo',
+ );
+ }
+
+ return $attachments;
+ }
+ function moveMessages($_foldername, $_messageUID, $deleteAfterMove=true, $currentFolder = Null, $returnUIDs = false, $_sourceProfileID = Null, $_targetProfileID = Null)
+ {
+ $folderID = $this->getFolderId( $_foldername );
+ $curfolderID = $this->getFolderId( $currentFolder );
+
+ // Check permissions
+ if ( !Lib::can_move( $this->profileID, $curfolderID, $folderID ) )
+ throw new Exception("Operation not allowed, not enough permissions.");
+
+ foreach( $_messageUID as $message ) {
+ list($mailID, $changeKey) = explode( '||', $message );
+ try {
+ Lib::moveMail( $this->profileID, $mailID, $changeKey, $folderID, $deleteAfterMove );
+ }
+ catch (\Exception $e ) {
+ $operation = ( $deleteAfterMove ? 'Moving' : 'Copying' );
+ throw new Exception("$operation to Folder $_foldername failed! Error:".$e->getMessage());
+ }
+ }
+
+ return true;
+ }
+ function createFolder($_parent, $_folderName, &$_error)
+ {
+ $parentID = $this->getFolderId( $_parent );
+
+ if ( $parentID && !Lib::can_manage_folder( $this->profileID, $parentID ) )
+ throw new Exception("Operation not allowed, not enough permissions.");
+
+ try{
+ Lib::createFolder( $this->profileID, $parentID, $_folderName );
+ }
+ catch (\Exception $e ) {
+ throw new Exception( $e->getMessage() );
+ }
+
+ return $_folderName;
+ }
+ function deleteFolder($_folderName)
+ {
+ $folderID = $this->getFolderId( $_folderName );
+ if ( $folderID && !Lib::can_manage_folder( $this->profileID, $folderID ) )
+ throw new Exception("Operation not allowed, not enough permissions.");
+
+ if ( $folderID ) {
+ try{
+ Lib::deleteFolder( $this->profileID, $folderID );
+ }
+ catch (\Exception $e ) {
+ throw new Exception( $e->getMessage() );
+ }
+ }
+
+ return true;
+ }
+ function renameFolder($_oldFolderName, $_parent, $_folderName)
+ {
+ // renameFolder gets called both for moving and renaming
+
+ // Check which operation we need to perform
+ $oldParent = null;
+ $oldName = $_oldFolderName;
+ if ( strpos( $_oldFolderName, '/' ) !== FALSE ) {
+ $path = explode('/', $_oldFolderName);
+ $oldName = array_pop( $path );
+ $oldParent = implode('/', $path );
+ }
+
+ if ( $oldParent != $_parent ) {
+ // Move
+ try {
+ $folderID = $this->getFolderId( $_oldFolderName );
+ $parentID = $this->getFolderId( $_parent );
+
+ if ( !Lib::can_manage_folder( $this->profileID, $folderID ) || !Lib::can_manage_folder( $this->profileID, $parentID ) )
+ throw new Exception("Operation not allowed, not enough permissions.");
+
+ Lib::moveFolder( $this->profileID, $folderID, $parentID );
+ }
+ catch (\Exception $e ) {
+ throw new Exception( $e->getMessage() );
+ }
+ }
+ else if ( $oldName != $_folderName ) {
+ // Rename
+
+ // Get Folder to find its changeKey
+ $folderID = $this->getFolderId( $_oldFolderName );
+ $folder = Lib::getFolder( $this->profileID, $folderID, true );
+ $changeKey = $folder->FolderId->ChangeKey;
+
+ if ( !Lib::can_manage_folder( $this->profileID, $folderID ) )
+ throw new Exception("Operation not allowed, not enough permissions.");
+
+ // Rename Folder
+ try {
+ Lib::renameFolder( $this->profileID, $folderID, $changeKey, $_folderName );
+ }
+ catch (\Exception $e ) {
+ throw new Exception( $e->getMessage() );
+ }
+ }
+
+ $newFolderName = ( $_parent ? "$_parent/$_folderName" : $_folderName );
+
+ // Update Session
+ $ids = Api\Cache::getSession('mail', $this->profileID.'::ews_folder_ids' );
+ unset( $ids[ $_oldFolderName ] );
+ $ids[ $newFolderName ] = $folderID;
+ Api\Cache::setSession('mail', $this->profileID.'::ews_folder_ids' , $ids );
+
+ // Update DB
+ Lib::renameFolderDB( $this->profileID, $folderID, $newFolderName );
+
+ // Hook for other actions
+ Api\Hooks::process(array(
+ 'location' => 'mail_after_folder_rename',
+ 'profile' => $this->profileID,
+ 'folder' => $_oldFolderName,
+ 'new_folder' => $newFolderName,
+ ));
+
+ return $newFolderName;
+ }
+ static function getFolderPermissions( $profile_id ) {
+ // From Lib
+ if ( !$profile_id ) return array();
+ $folders = Lib::getSettingsFolders( $profile_id );
+
+ // From Db
+ $rows = Lib::getDBFolders( $profile_id );
+ $final = array('');
+ $used = array();
+ foreach( $rows as $row) {
+ if ( $row['ews_permissions'] ) {
+ $permissions = unserialize( $row['ews_permissions'] );
+ foreach( $permissions as $permission => $value )
+ $row[ $permission ] = $value;
+ }
+ $row['ews_is_default'] = (int) $row['ews_is_default'];
+ $row['ews_apply_permissions'] = (int) $row['ews_apply_permissions'];
+ $row['ews_move_anywhere'] = (int) $row['ews_move_anywhere'];
+ $row['ews_move_to'] = explode(',', $row['ews_move_to'] );
+ $final[] = $row;
+ $used[] = $row['ews_folder'];
+ }
+
+ // Consolidate
+ foreach( $folders as $folder ) {
+ if ( !in_array( $folder['id'], $used ) )
+ $final[] = array(
+ 'ews_folder' => $folder['id'],
+ 'ews_name' => $folder['name'],
+ );
+ }
+
+ return $final;
+ }
+ static function getFolderPermissionsSelOptions( $profile_id ) {
+ // From Lib
+ if ( !$profile_id ) return array();
+ $folders = Lib::getSettingsFolders( $profile_id );
+
+ $normalize = array();
+ foreach( $folders as $folder )
+ $normalize[ $folder['id'] ] = $folder['name'];
+
+ $sel_options = array('');
+ foreach( $folders as $folder )
+ $sel_options[]['ews_move_to'] = $normalize;
+
+ return $sel_options;
+ }
+ static function storeFolderPermissions( $content, $profile_id ) {
+ if ( !$profile_id ) return ;
+ $db = clone( $GLOBALS['egw']->db );
+ $sql = "DELETE FROM egw_ea_ews WHERE ews_profile=$profile_id";
+ $db->query($sql);
+
+ $fields = array( 'read', 'write', 'delete', 'manage_folder' );
+ foreach ( $content as $folder ) {
+ if (!$folder || !$folder['ews_folder']) continue;
+
+ $permissions = array();
+ foreach( $fields as $field )
+ $permissions[ $field ] = $folder[ $field ];
+
+
+ $obj = new Api\Storage\Base('api','egw_ea_ews',null);
+ $folder['ews_profile'] = $profile_id;
+ $folder['ews_permissions'] = serialize( $permissions );
+ if ( $folder['ews_move_to'] )
+ $folder['ews_move_to'] = implode(',', $folder['ews_move_to'] );
+ $db->insert( 'egw_ea_ews', $folder, false, __LINE__, __FILE__ );
+ }
+ return true;
+ }
+
+ function getFolderId( $_folderName ) {
+ $_folderName = str_replace('+',' ', $_folderName );
+ $ids = Api\Cache::getSession('mail', $this->profileID.'::ews_folder_ids' );
+ if ( $ids[ $_folderName ] ) {
+ $folderID = $ids[ $_folderName ];
+ $folderID = str_replace(' ','+', $folderID );
+ }
+ else
+ $folderID = $_folderName;
+
+ return $folderID;
+ }
+ function getJunkFolder($_checkexistance=TRUE)
+ {
+ return $this->getSpecialFolder('acc_folder_junk');
+ }
+ function getDraftFolder($_checkexistance=TRUE)
+ {
+ return $this->getSpecialFolder('acc_folder_draft');
+ }
+ function getTemplateFolder($_checkexistance=TRUE)
+ {
+ return $this->getSpecialFolder('acc_folder_template');
+ }
+ function getTrashFolder($_checkexistance=TRUE)
+ {
+ return $this->getSpecialFolder('acc_folder_trash');
+ }
+ function getSentFolder($_checkexistance=TRUE)
+ {
+ return $this->getSpecialFolder('acc_folder_sent');
+ }
+ function getOutboxFolder($_checkexistance=TRUE)
+ {
+ return $this->getSpecialFolder('acc_folder_sent');
+ }
+ function getArchiveFolder($_checkexistance=TRUE)
+ {
+ return $this->getSpecialFolder('acc_folder_archive');
+ }
+ function getSpecialFolder( $folder ) {
+ $acc = Mail\Account::read($this->profileID);
+ return $acc->params[ $folder ];
+ }
+
+ // Empty functions to comply with Api\Mail
+ function reopen($_foldername)
+ {
+ return true;
+ }
+ function closeConnection()
+ {
+ return true;
+ }
+ function getStructure($_uid, $_partID=null, $_folder=null, $_preserveSeen=false)
+ {
+ return new Horde_Mime_Part();
+ }
+ function getMessageHeader($_uid, $_partID = '',$decode=false, $preserveUnSeen=false, $_folder='')
+ {
+ return array();
+ }
+ function getFlags ($_messageUID)
+ {
+ return null;
+ }
+}
+
diff --git a/composer.json b/composer.json
index 470a0fe931b..4daccf78b2f 100644
--- a/composer.json
+++ b/composer.json
@@ -36,7 +36,8 @@
"npm-asset/as-jqplot" : "1.0.*",
"npm-asset/gridster":"0.5.*",
"adldap2/adldap2": "=4.0.4",
- "egroupware/ckeditor": "^4"
+ "egroupware/ckeditor": "^4",
+ "php-ews/php-ews": "dev-composer"
},
"require-dev": {
}
diff --git a/composer.lock b/composer.lock
index e0f40329763..d6615e840e4 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1564,13 +1564,55 @@
"BSD License"
],
"description": "Selection of methods that are often needed when working with XML documents. Functionality includes creating of attribute lists from arrays, creation of tags, validation of XML names and more."
+ },
+ {
+ "name": "php-ews/php-ews",
+ "version": "dev-composer",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/jamesiarmes/php-ews.git",
+ "reference": "b373c3804364aa6f31ef77d1561cc4edddc870c8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/jamesiarmes/php-ews/zipball/b373c3804364aa6f31ef77d1561cc4edddc870c8",
+ "reference": "b373c3804364aa6f31ef77d1561cc4edddc870c8",
+ "shasum": ""
+ },
+ "require": {
+ "ext-soap": "*",
+ "php": ">=5.3.9"
+ },
+ "require-dev": {
+ "phpspec/phpspec": "~2",
+ "symfony/console": "~2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Adam Elsodaney",
+ "email": "adam.elso@gmail.com"
+ }
+ ],
+ "description": "The PHP Exchange Web Services library (php-ews) is intended to make communication with Microsoft Exchange servers using Exchange Web Services easier. It handles the NTLM authentication required to use the SOAP services and provides an object-oriented interface to the complex types required to form a request.",
+ "time": "2016-04-03 15:23:19"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
- "php": 15
+ "php": 15,
+ "php-ews/php-ews": 20
},
"prefer-stable": false,
"prefer-lowest": false,
diff --git a/mail/inc/class.mail_compose.inc.php b/mail/inc/class.mail_compose.inc.php
index 158b2fe77a3..993a3055e75 100644
--- a/mail/inc/class.mail_compose.inc.php
+++ b/mail/inc/class.mail_compose.inc.php
@@ -481,6 +481,12 @@ function compose(array $_content=null,$msg=null, $_focusElement='to',$suppressSi
try
{
$success = $this->send($_content);
+ if ( Api\Hooks::count( 'mail_compose_after_save' ) ) {
+ Api\Hooks::process( array(
+ 'location' => 'mail_compose_after_save',
+ 'content' => $_content,
+ ));
+ }
if ($success==false)
{
$sendOK=false;
@@ -589,6 +595,21 @@ function compose(array $_content=null,$msg=null, $_focusElement='to',$suppressSi
$_content['mimeType'] = $content['mimeType']='plain';
}
+ if ( Api\Hooks::count( 'mail_compose_index' ) ) {
+ $hooks= Api\Hooks::process( array(
+ 'location' => 'mail_compose_index',
+ 'get' => $_GET,
+ 'replyID' => $replyID,
+ 'content' => $content,
+ 'sel_options' => $sel_options,
+ 'preserv' => $preserv,
+ 'mail_bo' => $this->mail_bo,
+ ));
+ foreach( $hooks as $app => $hook ) {
+ list( $hook_content, $hook_preserve ) = $hook;
+ $content = array_merge( $content, $hook_content );
+ $preserv = array_merge( $preserv, $hook_preserve );
+ }
}
// user might have switched desired mimetype, so we should convert
if ($content['is_html'] && $content['mimeType']=='plain')
diff --git a/mail/inc/class.mail_tree.inc.php b/mail/inc/class.mail_tree.inc.php
index c5319737467..99dd6abf439 100644
--- a/mail/inc/class.mail_tree.inc.php
+++ b/mail/inc/class.mail_tree.inc.php
@@ -246,8 +246,10 @@ function getTree ($_parent = null, $_profileID = '', $_openTopLevel = 1, $_noChe
array_unshift($path, $_profileID);
+ $nodeId = $_profileID.self::DELIMITER.$folder['MAILBOX'];
+
$data = array(
- Tree::ID=>$_profileID.self::DELIMITER.$folder['MAILBOX'],
+ Tree::ID=>$nodeId,
Tree::AUTOLOAD_CHILDREN => $_allInOneGo?false:self::nodeHasChildren($folder),
Tree::CHILDREN =>array(),
Tree::LABEL =>lang($folder['MAILBOX']),
diff --git a/mail/inc/class.mail_ui.inc.php b/mail/inc/class.mail_ui.inc.php
index 0482d360a03..ee84975b601 100644
--- a/mail/inc/class.mail_ui.inc.php
+++ b/mail/inc/class.mail_ui.inc.php
@@ -55,7 +55,8 @@ class mail_ui
'importMessageFromVFS2DraftAndDisplay'=>True,
'subscription' => True,
'folderManagement' => true,
- 'smimeExportCert' => true
+ 'smimeExportCert' => true,
+ 'moveEWS' => true,
);
/**
@@ -1184,6 +1185,18 @@ private function get_actions()
} else {
$group++;
}
+ // Move in Exchange
+ $actions['exchange_move'] = array(
+ 'caption' => 'Move to EWS folder',
+ 'icon' => 'move',
+ 'group' => 2,
+ 'allowOnMultiple' => true,
+ 'nm_action' => 'popup',
+ 'popup' => '500x600',
+ 'url' => 'menuaction=mail.mail_ui.moveEWS&id=$row_id',
+ 'enableClass' => 'is_ews',
+ 'hideOnDisabled' => true,
+ );
$spam_actions = $this->getSpamActions();
$group++;
foreach ($spam_actions as &$action)
@@ -1321,7 +1334,7 @@ private function get_actions()
'icon' => 'tag_message',
'group' => ++$group,
// note this one is NOT a real CAPABILITY reported by the server, but added by selectMailbox
- 'enabled' => $this->mail_bo->icServer->hasCapability('SUPPORTS_KEYWORDS'),
+ 'enabled' => 'javaScript:app.mail.capability_enabled',
'hideOnDisabled' => true,
'children' => array(
'unlabel' => array(
@@ -1376,6 +1389,7 @@ private function get_actions()
'onExecute' => 'javaScript:app.mail.mail_flag',
'hint' => 'Flag or Unflag a mail',
'shortcut' => KeyManager::shortcut(KeyManager::F, true, true),
+ 'enabled' => 'javaScript:app.mail.capability_enabled',
'toolbarDefault' => true
),
'read' => array(
@@ -1415,6 +1429,16 @@ private function get_actions()
//'onExecute' => 'javaScript:app.mail.mail_dragStart',
)
);
+ $extra_actions = Api\Hooks::process(array(
+ 'location' => 'mail_extra_actions',
+ 'group' => $group,
+ ));
+ if ( is_array( $extra_actions ) ) {
+ foreach ( $extra_actions as $app => $extra)
+ if ( is_array( $extra ) )
+ $actions += $extra;
+ }
+
//error_log(__METHOD__.__LINE__.array2string(array_keys($actions)));
// 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
if (!isset($GLOBALS['egw_info']['user']['apps']['infolog']))
@@ -1467,7 +1491,7 @@ public static function get_rows(&$query,&$rows,&$readonlys)
$rows=array();
return 0;
}
- if (empty($folderName)) $query['selectedFolder'] = $_profileID.self::$delimiter.'INBOX';
+ if (empty($folderName)) $query['selectedFolder'] = $mail_ui->mail_bo->getDefaultFolder();
}
}
if (!isset($mail_ui))
@@ -1482,7 +1506,7 @@ public static function get_rows(&$query,&$rows,&$readonlys)
$rows=array();
return 0;
}
- if (empty($query['selectedFolder'])) $query['selectedFolder'] = $mail_ui->mail_bo->profileID.self::$delimiter.'INBOX';
+ if (empty($query['selectedFolder'])) $query['selectedFolder'] = $mail_ui->mail_bo->getDefaultFolder();
}
//error_log(__METHOD__.__LINE__.' SelectedFolder:'.$query['selectedFolder'].' Start:'.$query['start'].' NumRows:'.$query['num_rows'].array2string($query['order']).'->'.array2string($query['sort']));
//Mail::$debugTimes=true;
@@ -1601,7 +1625,7 @@ public static function get_rows(&$query,&$rows,&$readonlys)
$rByUid
);
$rowsFetched['messages'] = $_sR['count'];
- $ids = $_sR['match']->ids;
+ $ids = ( is_array( $_sR['match'] ) ? $_sR['match'] : $_sR['match']->ids );
// if $sR is false, something failed fundamentally
if($reverse === true) $ids = ($ids===false?array():array_reverse((array)$ids));
$sR = array_slice((array)$ids,($offset==0?0:$offset-1),$maxMessages); // we need only $maxMessages of uids
@@ -1821,6 +1845,7 @@ public function header2gridelements($_headers, $cols, $_folderName, $_folderType
if (Mail::$debugTimes) $starttime = microtime(true);
$rv = array();
$i=0;
+ $account = Mail\Account::read($this->mail_bo->profileID);
foreach((array)$_headers as $header)
{
$i++;
@@ -1898,6 +1923,9 @@ public function header2gridelements($_headers, $cols, $_folderName, $_folderType
if ($header['label5']) {
$css_styles[] = 'labelfive';
}
+ if ( $account->is_ews() ){
+ $css_styles[] = 'is_ews';
+ }
//error_log(__METHOD__.array2string($css_styles));
@@ -2315,6 +2343,46 @@ function getDisplayToolbarActions ()
return array_reverse($actions2,true);
}
+ /**
+ * Move mails between EWS folders
+ */
+ function moveEWS (array $content = null)
+ {
+ $dtmpl = new Etemplate('mail.move_ews');
+ $ids = $_GET['id']? $_GET['id']: $content['id'];
+ $content['id'] = $ids;
+ $ids = explode(',', $ids );
+ list($app, $user, $profile, $folder64, $message_uid ) = explode('::', $ids[0]);
+ $folderName = base64_decode( $folder64 );
+ $folderID = $this->mail_bo->getFolderId( $folderName );
+ $sel_options['folder'] = Mail\EWS\Lib::getWriteFolders( $profile, $folderID );
+
+ if ( !$content['folder'] )
+ $content['msg'] = lang('No Folder Selected');
+
+ if ( $content['folder'] && ($content['move'] || $content['copy'] ) ) {
+ $move = ( $content['move'] ? true : false );
+ $messageUIDs = array();
+ foreach( $ids as $uid ) {
+ list($app, $user, $profile, $folder64, $message_uid ) = explode('::', $uid);
+ $messageUIDs[] = $message_uid;
+ }
+ $res = $this->mail_bo->moveMessages( $content['folder'], $messageUIDs, $move, $folderName );
+ if ( $res ) {
+ $msg = 'Operation Successful';
+ if ( $move )
+ Framework::refresh_opener( $msg, 'mail', $ids, 'delete' );
+ Framework::window_close();
+ }
+ }
+
+ $readonlys = array();
+ // Preserv
+ $preserv = array(
+ 'id' => $content['id']
+ );
+ $dtmpl->exec('mail.mail_ui.moveEWS', $content,$sel_options,$readonlys,$preserv);
+ }
/**
* helper function to create the attachment block/table
*
@@ -4520,6 +4588,9 @@ public static function ajax_changeProfile($icServerID, $getFolders = true, $exec
{
$mail_ui = new mail_ui(true); // run constructor
}
+ // Return Default Folder to select
+ $defaultFolder = $mail_ui->mail_bo->getDefaultFolder();
+ Api\Json\Response::get()->data( $defaultFolder );
}
/**
@@ -4914,7 +4985,7 @@ function ajax_flagMessages($_flag, $_messageList, $_sendJsonResponse=true)
$rByUid = true,
false
);
- $messageListForToggle = $_sRt['match']->ids;
+ $messageListForToggle = ( !is_array( $_sRt['match'] ) ? $_sRt['match']->ids : $_sRt['match'] );
$filter['status'] = array($_flag);
if ($query['filter'] && $query['filter'] !='any')
{
@@ -4928,7 +4999,7 @@ function ajax_flagMessages($_flag, $_messageList, $_sendJsonResponse=true)
$rByUid = true,
false
);
- $messageList = $_sR['match']->ids;
+ $messageList = ( !is_array( $_sR['match'] ) ? $_sR['match']->ids : $_sR['match'] );
if (count($messageListForToggle)>0)
{
$flag2set = (strtolower($_flag));
@@ -4965,7 +5036,7 @@ function ajax_flagMessages($_flag, $_messageList, $_sendJsonResponse=true)
$rByUid,
false
);
- $messageList = $_sR['match']->ids;
+ $messageList = ( !is_array( $_sR['match'] ) ? $_sR['match']->ids : $_sR['match'] );
unset($_messageList['all']);
$_messageList['msg'] = array();
}
@@ -5094,7 +5165,7 @@ function ajax_deleteMessages($_messageList,$_forceDeleteMethod=null)
$rByUid,
false
);
- $messageList = $_sR['match']->ids;
+ $messageList = ( !is_array( $_sR['match'] ) ? $_sR['match']->ids : $_sR['match'] );
}
else
{
@@ -5255,7 +5326,7 @@ function ajax_copyMessages($_folderName, $_messageList, $_copyOrMove='copy', $_m
$rByUid=true,
false
);
- $messageList = $_sR['match']->ids;
+ $messageList = ( !is_array( $_sR['match'] ) ? $_sR['match']->ids : $_sR['match'] );
foreach($messageList as $uID)
{
//error_log(__METHOD__.__LINE__.$uID);
@@ -5419,4 +5490,27 @@ function ajax_folderMgmt_delete ($_folderName)
$response->data($res);
}
}
-}
\ No newline at end of file
+
+ /**
+ * Check whether capability is enabled
+ *
+ * @param string $profile
+ * @return boolean
+ */
+ function ajax_checkCapability( $capability )
+ {
+ $res = $this->mail_bo->icServer->hasCapability( $capability );
+ Api\Json\Response::get()->data( $res );
+ }
+ /**
+ * Function check whether profile is ews
+ *
+ * @param string $profile
+ * @return boolean
+ */
+ function ajax_isEws( $profile )
+ {
+ $account = Mail\Account::read( $profile );
+ Api\Json\Response::get()->data( $account->is_ews() );
+ }
+}
diff --git a/mail/js/app.js b/mail/js/app.js
index a6b11142fa6..2be2babbe69 100644
--- a/mail/js/app.js
+++ b/mail/js/app.js
@@ -372,6 +372,11 @@ app.classes.mail = AppJS.extend(
{
switch(_app)
{
+ //fkara
+ case 'infolog':
+ // If new infolog is created, store its id. It will be fetched by popup windows
+ this.latest_infolog = _id;
+ break;
case 'mail':
if (_id === 'sieve')
{
@@ -1560,6 +1565,27 @@ app.classes.mail = AppJS.extend(
return false;
}
+ var actions = ['add','edit','delete','subscribe','unsubscribe'];
+ if ( actions.indexOf( action.id ) !== -1 ) {
+ var row_id = node.id.split('::');
+ var id = row_id[0];
+ var is_root = ( row_id.length == 1 );
+ var req = egw.json('mail.mail_ui.ajax_isEws',[ id ],null,this,false).sendRequest();
+ var is_ews = req.responseJSON.response[0].data;
+
+ switch( action.id ) {
+ case 'subscribe':
+ case 'unsubscribe':
+ if ( is_ews ) return false;
+ break;
+ case 'add':
+ case 'edit':
+ case 'delete':
+ if ( is_ews && is_root ) return false;
+ break;
+ }
+ }
+
return true;
},
@@ -1618,6 +1644,28 @@ app.classes.mail = AppJS.extend(
return node && node.data && node.data.sieve;
},
+ /**
+ * Check if capability is enabled on that account
+ *
+ * @param {object} _action
+ * @param {object} _senders the representation of the tree leaf to be manipulated
+ * @param {object} _currentNode
+ */
+ capability_enabled: function( _action, _senders, _currentNode )
+ {
+ var capability;
+ switch ( _action.id ) {
+ case 'setLabel':
+ capability = 'SUPPORTS_KEYWORDS';
+ break;
+ case 'flagged':
+ capability = 'SUPPORTS_FLAGS';
+ break;
+ }
+ var req = egw.json('mail.mail_ui.ajax_checkCapability',[ capability ],null,this,false).sendRequest();
+ return req.responseJSON.response[0].data;
+ },
+
/**
* Check if ACL is enabled on that account
*
@@ -2094,11 +2142,10 @@ app.classes.mail = AppJS.extend(
_widget.openItem(folder, true);
this.lock_tree();
- egw.json('mail_ui::ajax_changeProfile',[folder, getFolders, this.et2._inst.etemplate_exec_id], jQuery.proxy(function() {
+ egw.json('mail_ui::ajax_changeProfile',[folder, getFolders, this.et2._inst.etemplate_exec_id], jQuery.proxy(function( defaultFolder ) {
// Profile changed, select inbox
- var inbox = folder + '::INBOX';
- _widget.reSelectItem(inbox);
- this.mail_changeFolder(inbox,_widget,'');
+ _widget.reSelectItem( defaultFolder );
+ this.mail_changeFolder( defaultFolder,_widget,'');
this.unlock_tree();
},this))
.sendRequest(true);
diff --git a/mail/templates/default/compose.xet b/mail/templates/default/compose.xet
index eb461ce62d1..27a516ed64e 100644
--- a/mail/templates/default/compose.xet
+++ b/mail/templates/default/compose.xet
@@ -6,7 +6,7 @@