diff --git a/plugins/kolab_activesync/config.inc.php.dist b/plugins/kolab_activesync/config.inc.php.dist index 7cab16ca..5d6aade2 100644 --- a/plugins/kolab_activesync/config.inc.php.dist +++ b/plugins/kolab_activesync/config.inc.php.dist @@ -1,4 +1,7 @@ not subscribed, 1 => subscribed, 2 => subscribed with alarm +$config['activesync_force_subscriptions'] = array('windowsoutlook15' => array('INBOX' => 1, 'Sent' => 1, 'Trash' => 1)); diff --git a/plugins/kolab_activesync/kolab_activesync.js b/plugins/kolab_activesync/kolab_activesync.js index 26a56ac9..aac57f38 100644 --- a/plugins/kolab_activesync/kolab_activesync.js +++ b/plugins/kolab_activesync/kolab_activesync.js @@ -1,210 +1,210 @@ /** * Client scripts for the Kolab ActiveSync configuration utitlity * * @version @package_version@ * @author Aleksander Machniak * @author Thomas Bruederli * * Copyright (C) 2011-2012, Kolab Systems AG * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ function kolab_activesync_config() { /* private members */ var me = this, http_lock = null, active_device = null; rcmail.register_command('plugin.save-config', save_config); rcmail.register_command('plugin.delete-device', delete_device_config); rcmail.addEventListener('plugin.activesync_save_complete', save_complete); if (rcmail.gui_objects.devicelist) { var devicelist = new rcube_list_widget(rcmail.gui_objects.devicelist, { multiselect:true, draggable:false, keyboard:true }); devicelist.addEventListener('select', select_device) .init() .focus(); rcmail.devicelist = devicelist; setTimeout(function() { rcmail.triggerEvent('listupdate', {list: devicelist, rowcount: devicelist.rowcount}); }, 100); // load frame if there are no devices if (!rcmail.env.devicecount) device_select(); } else if (rcmail.env.action != 'edit-folder') { if (rcmail.env.active_device) rcmail.enable_command('plugin.save-config', true); $('input.alarm').change(function(e) { if (this.checked) $('#'+this.id.replace(/_alarm/, '')).prop('checked', this.checked); }); $('input.subscription').change(function(e) { if (!this.checked) $('#'+this.id+'_alarm').prop('checked', false); }); var fn = function(elem) { var classname = elem.className.split(' ')[0], - list = $(elem).closest('table').find('input.' + classname), + list = $(elem).closest('table').find('input.' + classname).not('[disabled]'), check = list.not(':checked').length > 0; list.prop('checked', check).change(); }; $('th.subscription,th.alarm').click(function() { fn(this); }) .keydown(function(e) { if (e.which == 13 || e.which == 32) fn(this); }); } /* private methods */ function select_device(list) { active_device = list.get_single_selection(); rcmail.enable_command('plugin.delete-device', list.get_selection().length > 0); if (active_device && !list.multi_selecting) device_select(active_device); else if (rcmail.env.contentframe) rcmail.show_contentframe(false); }; function device_select(id) { var win, target = window, url = '&_action=plugin.activesync-config'; if (id) url += '&_id='+urlencode(id); else if (!rcmail.env.devicecount) url += '&_init=1'; else { rcmail.show_contentframe(false); return; } if (win = rcmail.get_frame_window(rcmail.env.contentframe)) { target = win; url += '&_framed=1'; } if (String(target.location.href).indexOf(url) >= 0) rcmail.show_contentframe(true); else rcmail.location_href(rcmail.env.comm_path+url, target, true); }; // submit current configuration form to server function save_config() { // TODO: validate device info var data = { cmd: 'save', id: rcmail.env.active_device, devicealias: $('#config-device-alias').val() }; if (data.devicealias == data.id) data.devicealias = ''; data.subscribed = {}; $('input.subscription:checked').each(function(i, elem) { data.subscribed[elem.value] = 1; }); $('input.alarm:checked').each(function(i, elem) { if (data.subscribed[elem.value]) data.subscribed[elem.value] = 2; }); http_lock = rcmail.set_busy(true, 'kolab_activesync.savingdata'); rcmail.http_post('plugin.activesync-json', data, http_lock); }; // callback function when saving has completed function save_complete(p) { // device updated if (p.success && p.alias) parent.window.activesync_object.update_list(p.id, p.alias); // device deleted if (p.success && p.id && p['delete']) { active_device = null; device_select(); if (p.id == 'ALL') devicelist.clear(); else devicelist.remove_row(p.id); rcmail.triggerEvent('listupdate', {list: devicelist, rowcount: devicelist.rowcount}); rcmail.enable_command('plugin.delete-device', false); } }; // handler for delete commands function delete_device_config() { var selection = devicelist.get_selection(); if (selection.length && confirm(rcmail.gettext('devicedeleteconfirm', 'kolab_activesync'))) { http_lock = rcmail.set_busy(true, 'kolab_activesync.savingdata'); rcmail.http_post('plugin.activesync-json', { cmd:'delete', id:selection }, http_lock); } }; this.update_list = function(id, name) { $('#devices-table tr.selected span.devicealias').html(name); }; this.update_sync_data = function(elem) { elem.name.match(/^_(subscriptions|alarms)\[(.+)\]$/); var flag, type = RegExp.$1, device = RegExp.$2, http_lock = rcmail.set_busy(true, 'kolab_activesync.savingdata'); // set subscription flag if (elem.checked) { flag = type == 'alarms' ? 2 : 1; } else { flag = type == 'alarms' ? 1 : 0; } // make sure subscription checkbox is checked if alarms is checked if (flag == 2) { $('input[name="_subscriptions[' + device + ']"]').prop('checked', true); } // make sure alarms checkbox is unchecked if subscription is unchecked else if (flag == 0) { $('input[name="_alarms[' + device + ']"]').prop('checked', false); } // send the request rcmail.http_post('plugin.activesync-json', {cmd: 'update', id: device, flag: flag, folder: rcmail.env.folder}, http_lock); }; }; window.rcmail && rcmail.addEventListener('init', function(evt) { activesync_object = new kolab_activesync_config(); }); diff --git a/plugins/kolab_activesync/kolab_activesync_ui.php b/plugins/kolab_activesync/kolab_activesync_ui.php index 1b2c1c17..f39f39d9 100644 --- a/plugins/kolab_activesync/kolab_activesync_ui.php +++ b/plugins/kolab_activesync/kolab_activesync_ui.php @@ -1,301 +1,324 @@ * @author Aleksander Machniak * * Copyright (C) 2011-2013, Kolab Systems AG * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ class kolab_activesync_ui { private $rc; private $plugin; + private $force_subscriptions = array(); public $device = array(); const SETUP_URL = 'https://kb.kolabenterprise.com/documentation/setting-up-an-activesync-client'; public function __construct($plugin) { $this->plugin = $plugin; $this->rc = rcube::get_instance(); $skin_path = $this->plugin->local_skin_path() . '/'; $this->skin_path = 'plugins/kolab_activesync/' . $skin_path; + $this->plugin->load_config(); + $this->force_subscriptions = $this->rc->config->get('activesync_force_subscriptions', array()); + $this->plugin->include_stylesheet($skin_path . 'config.css'); } public function device_list($attrib = array()) { $attrib += array('id' => 'devices-list'); $devices = $this->plugin->list_devices(); $table = new html_table(); foreach ($devices as $id => $device) { $name = $device['ALIAS'] ? $device['ALIAS'] : $id; $table->add_row(array('id' => 'rcmrow' . $id)); $table->add(null, html::span('devicealias', rcube::Q($name)) . ' ' . html::span('devicetype secondary', rcube::Q($device['TYPE']))); } $this->rc->output->add_gui_object('devicelist', $attrib['id']); $this->rc->output->set_env('devicecount', count($devices)); $this->rc->output->include_script('list.js'); return $table->show($attrib); } public function device_config_form($attrib = array()) { $table = new html_table(array('cols' => 2)); $field_id = 'config-device-alias'; $input = new html_inputfield(array('name' => 'devicealias', 'id' => $field_id, 'size' => 40)); $table->add('title', html::label($field_id, $this->plugin->gettext('devicealias'))); $table->add(null, $input->show($this->device['ALIAS'] ? $this->device['ALIAS'] : $this->device['_id'])); // read-only device information $info = $this->plugin->device_info($this->device['ID']); if (!empty($info)) { foreach ($info as $key => $value) { if ($value) { $table->add('title', html::label(null, rcube::Q($this->plugin->gettext($key)))); $table->add(null, rcube::Q($value)); } } } if ($attrib['form']) { $this->rc->output->add_gui_object('editform', $attrib['form']); } return $table->show($attrib); } + private function is_protected($folder, $devicetype) + { + $devicetype = strtolower($devicetype); + if (array_key_exists($devicetype, $this->force_subscriptions)) { + return array_key_exists($folder, $this->force_subscriptions[$devicetype]); + } + return false; + } + public function folder_subscriptions($attrib = array()) { if (!$attrib['id']) { $attrib['id'] = 'foldersubscriptions'; } // group folders by type (show only known types) $folder_groups = array('mail' => array(), 'contact' => array(), 'event' => array(), 'task' => array(), 'note' => array()); $folder_types = kolab_storage::folders_typedata(); $use_fieldsets = rcube_utils::get_boolean($attrib['use-fieldsets']); $imei = $this->device['_id']; $subscribed = array(); if ($imei) { $folder_meta = $this->plugin->folder_meta(); } + $devicetype = strtolower($this->device['TYPE']); + $device_force_subscriptions = $this->force_subscriptions[$devicetype]; + foreach ($this->plugin->list_folders() as $folder) { if ($folder_types[$folder]) { list($type, ) = explode('.', $folder_types[$folder]); } else { $type = 'mail'; } if (is_array($folder_groups[$type])) { $folder_groups[$type][] = $folder; - if (!empty($folder_meta) && ($meta = $folder_meta[$folder]) + if ($device_force_subscriptions && array_key_exists($folder, $device_force_subscriptions)) { + $subscribed[$folder] = intval($device_force_subscriptions[$folder]); + } else if (!empty($folder_meta) && ($meta = $folder_meta[$folder]) && $meta['FOLDER'] && $meta['FOLDER'][$imei]['S'] ) { $subscribed[$folder] = intval($meta['FOLDER'][$imei]['S']); } } } // build block for every folder type foreach ($folder_groups as $type => $group) { if (empty($group)) { continue; } $attrib['type'] = $type; $table = $this->folder_subscriptions_block($group, $attrib, $subscribed); $label = $this->plugin->gettext($type); if ($use_fieldsets) { $html .= html::tag('fieldset', 'subscriptionblock', html::tag('legend', $type, $label) . $table); } else { $html .= html::div('subscriptionblock', html::tag('h3', $type, $label) . $table); } } $this->rc->output->add_gui_object('subscriptionslist', $attrib['id']); return html::div($attrib, $html); } public function folder_subscriptions_block($a_folders, $attrib, $subscribed) { $alarms = ($attrib['type'] == 'event' || $attrib['type'] == 'task'); $table = new html_table(array('cellspacing' => 0, 'class' => 'table-striped')); $table->add_header(array( 'class' => 'subscription checkbox-cell', 'title' => $this->plugin->gettext('synchronize'), 'tabindex' => 0 ), $attrib['syncicon'] ? html::img(array('src' => $this->skin_path . $attrib['syncicon'])) : $this->plugin->gettext('synchronize') ); if ($alarms) { $table->add_header(array( 'class' => 'alarm checkbox-cell', 'title' => $this->plugin->gettext('withalarms'), 'tabindex' => 0 ), $attrib['alarmicon'] ? html::img(array('src' => $this->skin_path . $attrib['alarmicon'])) : $this->plugin->gettext('withalarms') ); } $table->add_header('foldername', $this->plugin->gettext('folder')); $checkbox_sync = new html_checkbox(array('name' => 'subscribed[]', 'class' => 'subscription')); $checkbox_alarm = new html_checkbox(array('name' => 'alarm[]', 'class' => 'alarm')); $names = array(); foreach ($a_folders as $folder) { $foldername = $origname = kolab_storage::object_prettyname($folder); // find folder prefix to truncate (the same code as in kolab_addressbook plugin) for ($i = count($names)-1; $i >= 0; $i--) { if (strpos($foldername, $names[$i].' » ') === 0) { $length = strlen($names[$i].' » '); $prefix = substr($foldername, 0, $length); $count = count(explode(' » ', $prefix)); $foldername = str_repeat('  ', $count-1) . '» ' . substr($foldername, $length); break; } } $folder_id = 'rcmf' . rcube_utils::html_identifier($folder); $names[] = $origname; $classes = array('mailbox'); if ($folder_class = $this->rc->folder_classname($folder)) { if ($this->rc->text_exists($folder_class)) { $foldername = html::quote($this->rc->gettext($folder_class)); } $classes[] = $folder_class; } $table->add_row(); + + $disabled = $this->is_protected($folder, $this->device['TYPE']); + $table->add('subscription checkbox-cell', $checkbox_sync->show( !empty($subscribed[$folder]) ? $folder : null, - array('value' => $folder, 'id' => $folder_id))); + array('value' => $folder, 'id' => $folder_id, 'disabled' => $disabled))); if ($alarms) { $table->add('alarm checkbox-cell', $checkbox_alarm->show( intval($subscribed[$folder]) > 1 ? $folder : null, - array('value' => $folder, 'id' => $folder_id.'_alarm'))); + array('value' => $folder, 'id' => $folder_id.'_alarm', 'disabled' => $disabled))); } $table->add(join(' ', $classes), html::label($folder_id, $foldername)); } return $table->show(); } public function folder_options_table($folder_name, $devices, $type) { $alarms = $type == 'event' || $type == 'task'; $meta = $this->plugin->folder_meta(); $folder_data = (array) ($meta[$folder_name] ? $meta[$folder_name]['FOLDER'] : null); $table = new html_table(array('cellspacing' => 0, 'id' => 'folder-sync-options', 'class' => 'records-table')); // table header $table->add_header(array('class' => 'device'), $this->plugin->gettext('devicealias')); $table->add_header(array('class' => 'subscription'), $this->plugin->gettext('synchronize')); if ($alarms) { $table->add_header(array('class' => 'alarm'), $this->plugin->gettext('withalarms')); } // table records foreach ($devices as $id => $device) { $info = $this->plugin->device_info($device['ID']); $name = $id; $title = ''; $checkbox = new html_checkbox(array('name' => "_subscriptions[$id]", 'value' => 1, 'onchange' => 'return activesync_object.update_sync_data(this)')); if (!empty($info)) { $_name = trim($info['friendlyname'] . ' ' . $info['os']); $title = $info['useragent']; if ($_name) { $name .= " ($_name)"; } } + $disabled = $this->is_protected($folder_name, $device['TYPE']); + $table->add_row(); $table->add(array('class' => 'device', 'title' => $title), $name); - $table->add('subscription checkbox-cell', $checkbox->show(!empty($folder_data[$id]['S']) ? 1 : 0)); + $table->add('subscription checkbox-cell', $checkbox->show(!empty($folder_data[$id]['S']) ? 1 : 0, array('disabled' => $disabled))); if ($alarms) { $checkbox_alarm = new html_checkbox(array('name' => "_alarms[$id]", 'value' => 1, 'onchange' => 'return activesync_object.update_sync_data(this)')); - $table->add('alarm checkbox-cell', $checkbox_alarm->show($folder_data[$id]['S'] > 1 ? 1 : 0)); + $table->add('alarm checkbox-cell', $checkbox_alarm->show($folder_data[$id]['S'] > 1 ? 1 : 0, array('disabled' => $disabled))); } } return $table->show(); } /** * Displays initial page (when no devices are registered) */ function init_message() { $this->plugin->load_config(); $this->rc->output->add_handlers(array( 'initmessage' => array($this, 'init_message_content') )); $this->rc->output->send('kolab_activesync.configempty'); } /** * Handler for initmessage template object */ function init_message_content() { $url = $this->rc->config->get('activesync_setup_url', self::SETUP_URL); $vars = array('url' => $url); $msg = $this->plugin->gettext(array('name' => 'nodevices', 'vars' => $vars)); return $msg; } }