From 48e9c14ebded89d858c8be0333f77f77a81b0877 Mon Sep 17 00:00:00 2001 From: thomascube Date: Sat, 31 Mar 2012 12:25:48 +0000 Subject: Move plugins repository into roundcubemail root folder; svn:externals are not defined anymore --- plugins/acl/acl.js | 351 ++++++++++++ plugins/acl/acl.php | 715 +++++++++++++++++++++++++ plugins/acl/config.inc.php.dist | 19 + plugins/acl/localization/de_DE.inc | 83 +++ plugins/acl/localization/en_US.inc | 83 +++ plugins/acl/localization/pl_PL.inc | 83 +++ plugins/acl/package.xml | 63 +++ plugins/acl/skins/default/acl.css | 100 ++++ plugins/acl/skins/default/images/enabled.png | Bin 0 -> 674 bytes plugins/acl/skins/default/images/partial.png | Bin 0 -> 389 bytes plugins/acl/skins/default/templates/table.html | 54 ++ plugins/acl/skins/larry/acl.css | 113 ++++ plugins/acl/skins/larry/images/enabled.png | Bin 0 -> 674 bytes plugins/acl/skins/larry/images/partial.png | Bin 0 -> 389 bytes plugins/acl/skins/larry/templates/table.html | 34 ++ 15 files changed, 1698 insertions(+) create mode 100644 plugins/acl/acl.js create mode 100644 plugins/acl/acl.php create mode 100644 plugins/acl/config.inc.php.dist create mode 100644 plugins/acl/localization/de_DE.inc create mode 100644 plugins/acl/localization/en_US.inc create mode 100644 plugins/acl/localization/pl_PL.inc create mode 100644 plugins/acl/package.xml create mode 100644 plugins/acl/skins/default/acl.css create mode 100644 plugins/acl/skins/default/images/enabled.png create mode 100644 plugins/acl/skins/default/images/partial.png create mode 100644 plugins/acl/skins/default/templates/table.html create mode 100644 plugins/acl/skins/larry/acl.css create mode 100644 plugins/acl/skins/larry/images/enabled.png create mode 100644 plugins/acl/skins/larry/images/partial.png create mode 100644 plugins/acl/skins/larry/templates/table.html (limited to 'plugins/acl') diff --git a/plugins/acl/acl.js b/plugins/acl/acl.js new file mode 100644 index 000000000..aa9e06d3d --- /dev/null +++ b/plugins/acl/acl.js @@ -0,0 +1,351 @@ +/** + * ACL plugin script + * + * @version @package_version@ + * @author Aleksander Machniak + */ + +if (window.rcmail) { + rcmail.addEventListener('init', function() { + if (rcmail.gui_objects.acltable) { + rcmail.acl_list_init(); + // enable autocomplete on user input + if (rcmail.env.acl_users_source) { + rcmail.init_address_input_events($('#acluser'), {action:'settings/plugin.acl-autocomplete'}); + // fix inserted value + rcmail.addEventListener('autocomplete_insert', function(e) { + if (e.field.id != 'acluser') + return; + + var value = e.insert; + // get UID from the entry value + if (value.match(/\s*\(([^)]+)\)[, ]*$/)) + value = RegExp.$1; + e.field.value = value; + }); + } + } + + rcmail.enable_command('acl-create', 'acl-save', 'acl-cancel', 'acl-mode-switch', true); + rcmail.enable_command('acl-delete', 'acl-edit', false); + }); +} + +// Display new-entry form +rcube_webmail.prototype.acl_create = function() +{ + this.acl_init_form(); +} + +// Display ACL edit form +rcube_webmail.prototype.acl_edit = function() +{ + // @TODO: multi-row edition + var id = this.acl_list.get_single_selection(); + if (id) + this.acl_init_form(id); +} + +// ACL entry delete +rcube_webmail.prototype.acl_delete = function() +{ + var users = this.acl_get_usernames(); + + if (users && users.length && confirm(this.get_label('acl.deleteconfirm'))) { + this.http_request('settings/plugin.acl', '_act=delete&_user='+urlencode(users.join(',')) + + '&_mbox='+urlencode(this.env.mailbox), + this.set_busy(true, 'acl.deleting')); + } +} + +// Save ACL data +rcube_webmail.prototype.acl_save = function() +{ + var user = $('#acluser').val(), rights = '', type; + + $(':checkbox', this.env.acl_advanced ? $('#advancedrights') : sim_ul = $('#simplerights')).map(function() { + if (this.checked) + rights += this.value; + }); + + if (type = $('input:checked[name=usertype]').val()) { + if (type != 'user') + user = type; + } + + if (!user) { + alert(this.get_label('acl.nouser')); + return; + } + if (!rights) { + alert(this.get_label('acl.norights')); + return; + } + + this.http_request('settings/plugin.acl', '_act=save' + + '&_user='+urlencode(user) + + '&_acl=' +rights + + '&_mbox='+urlencode(this.env.mailbox) + + (this.acl_id ? '&_old='+this.acl_id : ''), + this.set_busy(true, 'acl.saving')); +} + +// Cancel/Hide form +rcube_webmail.prototype.acl_cancel = function() +{ + this.ksearch_blur(); + this.acl_form.hide(); +} + +// Update data after save (and hide form) +rcube_webmail.prototype.acl_update = function(o) +{ + // delete old row + if (o.old) + this.acl_remove_row(o.old); + // make sure the same ID doesn't exist + else if (this.env.acl[o.id]) + this.acl_remove_row(o.id); + + // add new row + this.acl_add_row(o, true); + // hide autocomplete popup + this.ksearch_blur(); + // hide form + this.acl_form.hide(); +} + +// Switch table display mode +rcube_webmail.prototype.acl_mode_switch = function(elem) +{ + this.env.acl_advanced = !this.env.acl_advanced; + this.enable_command('acl-delete', 'acl-edit', false); + this.http_request('settings/plugin.acl', '_act=list' + + '&_mode='+(this.env.acl_advanced ? 'advanced' : 'simple') + + '&_mbox='+urlencode(this.env.mailbox), + this.set_busy(true, 'loading')); +} + +// ACL table initialization +rcube_webmail.prototype.acl_list_init = function() +{ + this.acl_list = new rcube_list_widget(this.gui_objects.acltable, + {multiselect:true, draggable:false, keyboard:true, toggleselect:true}); + this.acl_list.addEventListener('select', function(o) { rcmail.acl_list_select(o); }); + this.acl_list.addEventListener('dblclick', function(o) { rcmail.acl_list_dblclick(o); }); + this.acl_list.addEventListener('keypress', function(o) { rcmail.acl_list_keypress(o); }); + this.acl_list.init(); +} + +// ACL table row selection handler +rcube_webmail.prototype.acl_list_select = function(list) +{ + rcmail.enable_command('acl-delete', list.selection.length > 0); + rcmail.enable_command('acl-edit', list.selection.length == 1); + list.focus(); +} + +// ACL table double-click handler +rcube_webmail.prototype.acl_list_dblclick = function(list) +{ + this.acl_edit(); +} + +// ACL table keypress handler +rcube_webmail.prototype.acl_list_keypress = function(list) +{ + if (list.key_pressed == list.ENTER_KEY) + this.command('acl-edit'); + else if (list.key_pressed == list.DELETE_KEY || list.key_pressed == list.BACKSPACE_KEY) + if (!this.acl_form || !this.acl_form.is(':visible')) + this.command('acl-delete'); +} + +// Reloads ACL table +rcube_webmail.prototype.acl_list_update = function(html) +{ + $(this.gui_objects.acltable).html(html); + this.acl_list_init(); +} + +// Returns names of users in selected rows +rcube_webmail.prototype.acl_get_usernames = function() +{ + var users = [], n, len, cell, row, + list = this.acl_list, + selection = list.get_selection(); + + for (n=0, len=selection.length; n= 0) { + users.push(selection[n]); + } + else if (row = list.rows[selection[n]]) { + cell = $('td.user', row.obj); + if (cell.length == 1) + users.push(cell.text()); + } + } + + return users; +} + +// Removes ACL table row +rcube_webmail.prototype.acl_remove_row = function(id) +{ + var list = this.acl_list; + + list.remove_row(id); + list.clear_selection(); + + // we don't need it anymore (remove id conflict) + $('#rcmrow'+id).remove(); + this.env.acl[id] = null; + + this.enable_command('acl-delete', list.selection.length > 0); + this.enable_command('acl-edit', list.selection.length == 1); +} + +// Adds ACL table row +rcube_webmail.prototype.acl_add_row = function(o, sel) +{ + var n, len, ids = [], spec = [], id = o.id, list = this.acl_list, + items = this.env.acl_advanced ? [] : this.env.acl_items, + table = this.gui_objects.acltable, + row = $('thead > tr', table).clone(); + + // Update new row + $('td', row).map(function() { + var r, cl = this.className.replace(/^acl/, ''); + + if (items && items[cl]) + cl = items[cl]; + + if (cl == 'user') + $(this).text(o.username); + else + $(this).addClass(rcmail.acl_class(o.acl, cl)).text(''); + }); + + row.attr('id', 'rcmrow'+id); + row = row.get(0); + + this.env.acl[id] = o.acl; + + // sorting... (create an array of user identifiers, then sort it) + for (n in this.env.acl) { + if (this.env.acl[n]) { + if (this.env.acl_specials.length && $.inArray(n, this.env.acl_specials) >= 0) + spec.push(n); + else + ids.push(n); + } + } + ids.sort(); + // specials on the top + ids = spec.concat(ids); + + // find current id + for (n=0, len=ids.length; n= mw) + this.acl_form.css({left: parseInt((bw - mw)/2)+'px'}); + + // display it + this.acl_form.show(); + if (type == 'user') + name_input.focus(); + + // unfocus the list, make backspace key in name input field working + this.acl_list.blur(); +} + +// Returns class name according to ACL comparision result +rcube_webmail.prototype.acl_class = function(acl1, acl2) +{ + var i, len, found = 0; + + acl1 = String(acl1); + acl2 = String(acl2); + + for (i=0, len=acl2.length; i -1) + found++; + + if (found == len) + return 'enabled'; + else if (found) + return 'partial'; + + return 'disabled'; +} diff --git a/plugins/acl/acl.php b/plugins/acl/acl.php new file mode 100644 index 000000000..ab981ab89 --- /dev/null +++ b/plugins/acl/acl.php @@ -0,0 +1,715 @@ + + * + * + * 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 General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +class acl extends rcube_plugin +{ + public $task = 'settings|addressbook|calendar'; + + private $rc; + private $supported = null; + private $mbox; + private $ldap; + private $specials = array('anyone', 'anonymous'); + + /** + * Plugin initialization + */ + function init() + { + $this->rc = rcmail::get_instance(); + + // Register hooks + $this->add_hook('folder_form', array($this, 'folder_form')); + // kolab_addressbook plugin + $this->add_hook('addressbook_form', array($this, 'folder_form')); + $this->add_hook('calendar_form_kolab', array($this, 'folder_form')); + // Plugin actions + $this->register_action('plugin.acl', array($this, 'acl_actions')); + $this->register_action('plugin.acl-autocomplete', array($this, 'acl_autocomplete')); + } + + /** + * Handler for plugin actions (AJAX) + */ + function acl_actions() + { + $action = trim(get_input_value('_act', RCUBE_INPUT_GPC)); + + // Connect to IMAP + $this->rc->storage_init(); + + // Load localization and configuration + $this->add_texts('localization/'); + $this->load_config(); + + if ($action == 'save') { + $this->action_save(); + } + else if ($action == 'delete') { + $this->action_delete(); + } + else if ($action == 'list') { + $this->action_list(); + } + + // Only AJAX actions + $this->rc->output->send(); + } + + /** + * Handler for user login autocomplete request + */ + function acl_autocomplete() + { + $this->load_config(); + + $search = get_input_value('_search', RCUBE_INPUT_GPC, true); + $sid = get_input_value('_id', RCUBE_INPUT_GPC); + $users = array(); + + if ($this->init_ldap()) { + $max = (int) $this->rc->config->get('autocomplete_max', 15); + $mode = (int) $this->rc->config->get('addressbook_search_mode'); + + $this->ldap->set_pagesize($max); + $result = $this->ldap->search('*', $search, $mode); + + foreach ($result->records as $record) { + $user = $record['uid']; + + if (is_array($user)) { + $user = array_filter($user); + $user = $user[0]; + } + + if ($user) { + if ($record['name']) + $user = $record['name'] . ' (' . $user . ')'; + + $users[] = $user; + } + } + } + + sort($users, SORT_LOCALE_STRING); + + $this->rc->output->command('ksearch_query_results', $users, $search, $sid); + $this->rc->output->send(); + } + + /** + * Handler for 'folder_form' hook + * + * @param array $args Hook arguments array (form data) + * + * @return array Hook arguments array + */ + function folder_form($args) + { + // Edited folder name (empty in create-folder mode) + $mbox_imap = $args['options']['name']; + if (!strlen($mbox_imap)) { + return $args; + } +/* + // Do nothing on protected folders (?) + if ($args['options']['protected']) { + return $args; + } +*/ + // Namespace root + if ($args['options']['is_root']) { + return $args; + } + + // Get MYRIGHTS + if (!($myrights = $args['options']['rights'])) { + return $args; + } + + // Do nothing if no ACL support + if (!$this->rc->storage->get_capability('ACL')) { + return $args; + } + + // Load localization and include scripts + $this->load_config(); + $this->add_texts('localization/', array('deleteconfirm', 'norights', + 'nouser', 'deleting', 'saving')); + $this->include_script('acl.js'); + $this->rc->output->include_script('list.js'); + $this->include_stylesheet($this->local_skin_path().'/acl.css'); + + // add Info fieldset if it doesn't exist + if (!isset($args['form']['props']['fieldsets']['info'])) + $args['form']['props']['fieldsets']['info'] = array( + 'name' => rcube_label('info'), + 'content' => array()); + + // Display folder rights to 'Info' fieldset + $args['form']['props']['fieldsets']['info']['content']['myrights'] = array( + 'label' => Q($this->gettext('myrights')), + 'value' => $this->acl2text($myrights) + ); + + // Return if not folder admin + if (!in_array('a', $myrights)) { + return $args; + } + + // The 'Sharing' tab + $this->mbox = $mbox_imap; + $this->rc->output->set_env('acl_users_source', (bool) $this->rc->config->get('acl_users_source')); + $this->rc->output->set_env('mailbox', $mbox_imap); + $this->rc->output->add_handlers(array( + 'acltable' => array($this, 'templ_table'), + 'acluser' => array($this, 'templ_user'), + 'aclrights' => array($this, 'templ_rights'), + )); + + $this->rc->output->set_env('autocomplete_max', (int)$this->rc->config->get('autocomplete_max', 15)); + $this->rc->output->set_env('autocomplete_min_length', $this->rc->config->get('autocomplete_min_length')); + $this->rc->output->add_label('autocompletechars', 'autocompletemore'); + + $args['form']['sharing'] = array( + 'name' => Q($this->gettext('sharing')), + 'content' => $this->rc->output->parse('acl.table', false, false), + ); + + return $args; + } + + /** + * Creates ACL rights table + * + * @param array $attrib Template object attributes + * + * @return string HTML Content + */ + function templ_table($attrib) + { + if (empty($attrib['id'])) + $attrib['id'] = 'acl-table'; + + $out = $this->list_rights($attrib); + + $this->rc->output->add_gui_object('acltable', $attrib['id']); + + return $out; + } + + /** + * Creates ACL rights form (rights list part) + * + * @param array $attrib Template object attributes + * + * @return string HTML Content + */ + function templ_rights($attrib) + { + // Get supported rights + $supported = $this->rights_supported(); + + // depending on server capability either use 'te' or 'd' for deleting msgs + $deleteright = implode(array_intersect(str_split('ted'), $supported)); + + $out = ''; + $ul = ''; + $input = new html_checkbox(); + + // Advanced rights + $attrib['id'] = 'advancedrights'; + foreach ($supported as $val) { + $id = "acl$val"; + $ul .= html::tag('li', null, + $input->show('', array( + 'name' => "acl[$val]", 'value' => $val, 'id' => $id)) + . html::label(array('for' => $id, 'title' => $this->gettext('longacl'.$val)), + $this->gettext('acl'.$val))); + } + + $out = html::tag('ul', $attrib, $ul, html::$common_attrib); + + // Simple rights + $ul = ''; + $attrib['id'] = 'simplerights'; + $items = array( + 'read' => 'lrs', + 'write' => 'wi', + 'delete' => $deleteright, + 'other' => preg_replace('/[lrswi'.$deleteright.']/', '', implode($supported)), + ); + + foreach ($items as $key => $val) { + $id = "acl$key"; + $ul .= html::tag('li', null, + $input->show('', array( + 'name' => "acl[$val]", 'value' => $val, 'id' => $id)) + . html::label(array('for' => $id, 'title' => $this->gettext('longacl'.$key)), + $this->gettext('acl'.$key))); + } + + $out .= "\n" . html::tag('ul', $attrib, $ul, html::$common_attrib); + + $this->rc->output->set_env('acl_items', $items); + + return $out; + } + + /** + * Creates ACL rights form (user part) + * + * @param array $attrib Template object attributes + * + * @return string HTML Content + */ + function templ_user($attrib) + { + // Create username input + $attrib['name'] = 'acluser'; + + $textfield = new html_inputfield($attrib); + + $fields['user'] = html::label(array('for' => 'iduser'), $this->gettext('username')) + . ' ' . $textfield->show(); + + // Add special entries + if (!empty($this->specials)) { + foreach ($this->specials as $key) { + $fields[$key] = html::label(array('for' => 'id'.$key), $this->gettext($key)); + } + } + + $this->rc->output->set_env('acl_specials', $this->specials); + + // Create list with radio buttons + if (count($fields) > 1) { + $ul = ''; + $radio = new html_radiobutton(array('name' => 'usertype')); + foreach ($fields as $key => $val) { + $ul .= html::tag('li', null, $radio->show($key == 'user' ? 'user' : '', + array('value' => $key, 'id' => 'id'.$key)) + . $val); + } + + $out = html::tag('ul', array('id' => 'usertype'), $ul, html::$common_attrib); + } + // Display text input alone + else { + $out = $fields['user']; + } + + return $out; + } + + /** + * Creates ACL rights table + * + * @param array $attrib Template object attributes + * + * @return string HTML Content + */ + private function list_rights($attrib=array()) + { + // Get ACL for the folder + $acl = $this->rc->storage->get_acl($this->mbox); + + if (!is_array($acl)) { + $acl = array(); + } + + // Keep special entries (anyone/anonymous) on top of the list + if (!empty($this->specials) && !empty($acl)) { + foreach ($this->specials as $key) { + if (isset($acl[$key])) { + $acl_special[$key] = $acl[$key]; + unset($acl[$key]); + } + } + } + + // Sort the list by username + uksort($acl, 'strnatcasecmp'); + + if (!empty($acl_special)) { + $acl = array_merge($acl_special, $acl); + } + + // Get supported rights and build column names + $supported = $this->rights_supported(); + + // depending on server capability either use 'te' or 'd' for deleting msgs + $deleteright = implode(array_intersect(str_split('ted'), $supported)); + + // Use advanced or simple (grouped) rights + $advanced = $this->rc->config->get('acl_advanced_mode'); + + if ($advanced) { + $items = array(); + foreach ($supported as $sup) { + $items[$sup] = $sup; + } + } + else { + $items = array( + 'read' => 'lrs', + 'write' => 'wi', + 'delete' => $deleteright, + 'other' => preg_replace('/[lrswi'.$deleteright.']/', '', implode($supported)), + ); + } + + // Create the table + $attrib['noheader'] = true; + $table = new html_table($attrib); + + // Create table header + $table->add_header('user', $this->gettext('identifier')); + foreach (array_keys($items) as $key) { + $label = $this->gettext('shortacl'.$key); + $table->add_header(array('class' => 'acl'.$key, 'title' => $label), $label); + } + + $i = 1; + $js_table = array(); + foreach ($acl as $user => $rights) { + if ($this->rc->storage->conn->user == $user) { + continue; + } + + // filter out virtual rights (c or d) the server may return + $userrights = array_intersect($rights, $supported); + $userid = html_identifier($user); + + if (!empty($this->specials) && in_array($user, $this->specials)) { + $user = $this->gettext($user); + } + + $table->add_row(array('id' => 'rcmrow'.$userid)); + $table->add('user', Q($user)); + + foreach ($items as $key => $right) { + $in = $this->acl_compare($userrights, $right); + switch ($in) { + case 2: $class = 'enabled'; break; + case 1: $class = 'partial'; break; + default: $class = 'disabled'; break; + } + $table->add('acl' . $key . ' ' . $class, ''); + } + + $js_table[$userid] = implode($userrights); + } + + $this->rc->output->set_env('acl', $js_table); + $this->rc->output->set_env('acl_advanced', $advanced); + + $out = $table->show(); + + return $out; + } + + /** + * Handler for ACL update/create action + */ + private function action_save() + { + $mbox = trim(get_input_value('_mbox', RCUBE_INPUT_GPC, true)); // UTF7-IMAP + $user = trim(get_input_value('_user', RCUBE_INPUT_GPC)); + $acl = trim(get_input_value('_acl', RCUBE_INPUT_GPC)); + $oldid = trim(get_input_value('_old', RCUBE_INPUT_GPC)); + + $acl = array_intersect(str_split($acl), $this->rights_supported()); + $users = $oldid ? array($user) : explode(',', $user); + + foreach ($users as $user) { + $user = trim($user); + + if (!empty($this->specials) && in_array($user, $this->specials)) { + $username = $this->gettext($user); + } + else { + if (!strpos($user, '@') && ($realm = $this->get_realm())) { + $user .= '@' . rcube_idn_to_ascii(preg_replace('/^@/', '', $realm)); + } + $username = $user; + } + + if (!$acl || !$user || !strlen($mbox)) { + continue; + } + + if ($user != $_SESSION['username'] && $username != $_SESSION['username']) { + if ($this->rc->storage->set_acl($mbox, $user, $acl)) { + $ret = array('id' => html_identifier($user), + 'username' => $username, 'acl' => implode($acl), 'old' => $oldid); + $this->rc->output->command('acl_update', $ret); + $result++; + } + } + } + + if ($result) { + $this->rc->output->show_message($oldid ? 'acl.updatesuccess' : 'acl.createsuccess', 'confirmation'); + } + else { + $this->rc->output->show_message($oldid ? 'acl.updateerror' : 'acl.createerror', 'error'); + } + } + + /** + * Handler for ACL delete action + */ + private function action_delete() + { + $mbox = trim(get_input_value('_mbox', RCUBE_INPUT_GPC, true)); //UTF7-IMAP + $user = trim(get_input_value('_user', RCUBE_INPUT_GPC)); + + $user = explode(',', $user); + + foreach ($user as $u) { + $u = trim($u); + if ($this->rc->storage->delete_acl($mbox, $u)) { + $this->rc->output->command('acl_remove_row', html_identifier($u)); + } + else { + $error = true; + } + } + + if (!$error) { + $this->rc->output->show_message('acl.deletesuccess', 'confirmation'); + } + else { + $this->rc->output->show_message('acl.deleteerror', 'error'); + } + } + + /** + * Handler for ACL list update action (with display mode change) + */ + private function action_list() + { + if (in_array('acl_advanced_mode', (array)$this->rc->config->get('dont_override'))) { + return; + } + + $this->mbox = trim(get_input_value('_mbox', RCUBE_INPUT_GPC, true)); // UTF7-IMAP + $advanced = trim(get_input_value('_mode', RCUBE_INPUT_GPC)); + $advanced = $advanced == 'advanced' ? true : false; + + // Save state in user preferences + $this->rc->user->save_prefs(array('acl_advanced_mode' => $advanced)); + + $out = $this->list_rights(); + + $out = preg_replace(array('/^]+>/', '/<\/table>$/'), '', $out); + + $this->rc->output->command('acl_list_update', $out); + } + + /** + * Creates
    list with descriptive access rights + * + * @param array $rights MYRIGHTS result + * + * @return string HTML content + */ + function acl2text($rights) + { + if (empty($rights)) { + return ''; + } + + $supported = $this->rights_supported(); + $list = array(); + $attrib = array( + 'name' => 'rcmyrights', + 'style' => 'margin:0; padding:0 15px;', + ); + + foreach ($supported as $right) { + if (in_array($right, $rights)) { + $list[] = html::tag('li', null, Q($this->gettext('acl' . $right))); + } + } + + if (count($list) == count($supported)) + return Q($this->gettext('aclfull')); + + return html::tag('ul', $attrib, implode("\n", $list)); + } + + /** + * Compares two ACLs (according to supported rights) + * + * @param array $acl1 ACL rights array (or string) + * @param array $acl2 ACL rights array (or string) + * + * @param int Comparision result, 2 - full match, 1 - partial match, 0 - no match + */ + function acl_compare($acl1, $acl2) + { + if (!is_array($acl1)) $acl1 = str_split($acl1); + if (!is_array($acl2)) $acl2 = str_split($acl2); + + $rights = $this->rights_supported(); + + $acl1 = array_intersect($acl1, $rights); + $acl2 = array_intersect($acl2, $rights); + $res = array_intersect($acl1, $acl2); + + $cnt1 = count($res); + $cnt2 = count($acl2); + + if ($cnt1 == $cnt2) + return 2; + else if ($cnt1) + return 1; + else + return 0; + } + + /** + * Get list of supported access rights (according to RIGHTS capability) + * + * @return array List of supported access rights abbreviations + */ + function rights_supported() + { + if ($this->supported !== null) { + return $this->supported; + } + + $capa = $this->rc->storage->get_capability('RIGHTS'); + + if (is_array($capa)) { + $rights = strtolower($capa[0]); + } + else { + $rights = 'cd'; + } + + return $this->supported = str_split('lrswi' . $rights . 'pa'); + } + + /** + * Username realm detection. + * + * @return string Username realm (domain) + */ + private function get_realm() + { + // When user enters a username without domain part, realm + // alows to add it to the username (and display correct username in the table) + + if (isset($_SESSION['acl_username_realm'])) { + return $_SESSION['acl_username_realm']; + } + + // find realm in username of logged user (?) + list($name, $domain) = explode('@', $_SESSION['username']); + + // Use (always existent) ACL entry on the INBOX for the user to determine + // whether or not the user ID in ACL entries need to be qualified and how + // they would need to be qualified. + if (empty($domain)) { + $acl = $this->rc->storage->get_acl('INBOX'); + if (is_array($acl)) { + $regexp = '/^' . preg_quote($_SESSION['username'], '/') . '@(.*)$/'; + foreach (array_keys($acl) as $name) { + if (preg_match($regexp, $name, $matches)) { + $domain = $matches[1]; + break; + } + } + } + } + + return $_SESSION['acl_username_realm'] = $domain; + } + + /** + * Initializes autocomplete LDAP backend + */ + private function init_ldap() + { + if ($this->ldap) + return $this->ldap->ready; + + // get LDAP config + $config = $this->rc->config->get('acl_users_source'); + + if (empty($config)) { + return false; + } + + // not an array, use configured ldap_public source + if (!is_array($config)) { + $ldap_config = (array) $this->rc->config->get('ldap_public'); + $config = $ldap_config[$config]; + } + + $uid_field = $this->rc->config->get('acl_users_field', 'mail'); + $filter = $this->rc->config->get('acl_users_filter'); + + if (empty($uid_field) || empty($config)) { + return false; + } + + // get name attribute + if (!empty($config['fieldmap'])) { + $name_field = $config['fieldmap']['name']; + } + // ... no fieldmap, use the old method + if (empty($name_field)) { + $name_field = $config['name_field']; + } + + // add UID field to fieldmap, so it will be returned in a record with name + $config['fieldmap'] = array( + 'name' => $name_field, + 'uid' => $uid_field, + ); + + // search in UID and name fields + $config['search_fields'] = array_values($config['fieldmap']); + $config['required_fields'] = array($uid_field); + + // set search filter + if ($filter) + $config['filter'] = $filter; + + // disable vlv + $config['vlv'] = false; + + // Initialize LDAP connection + $this->ldap = new rcube_ldap($config, + $this->rc->config->get('ldap_debug'), + $this->rc->config->mail_domain($_SESSION['imap_host'])); + + return $this->ldap->ready; + } +} diff --git a/plugins/acl/config.inc.php.dist b/plugins/acl/config.inc.php.dist new file mode 100644 index 000000000..f957a233a --- /dev/null +++ b/plugins/acl/config.inc.php.dist @@ -0,0 +1,19 @@ + diff --git a/plugins/acl/localization/de_DE.inc b/plugins/acl/localization/de_DE.inc new file mode 100644 index 000000000..92c7e4290 --- /dev/null +++ b/plugins/acl/localization/de_DE.inc @@ -0,0 +1,83 @@ + diff --git a/plugins/acl/localization/en_US.inc b/plugins/acl/localization/en_US.inc new file mode 100644 index 000000000..f5b1ae64d --- /dev/null +++ b/plugins/acl/localization/en_US.inc @@ -0,0 +1,83 @@ + diff --git a/plugins/acl/localization/pl_PL.inc b/plugins/acl/localization/pl_PL.inc new file mode 100644 index 000000000..0b3689927 --- /dev/null +++ b/plugins/acl/localization/pl_PL.inc @@ -0,0 +1,83 @@ + diff --git a/plugins/acl/package.xml b/plugins/acl/package.xml new file mode 100644 index 000000000..7ea0f3297 --- /dev/null +++ b/plugins/acl/package.xml @@ -0,0 +1,63 @@ + + + acl + pear.roundcube.net + Folders Access Control Lists + IMAP Folders Access Control Lists Management (RFC4314, RFC2086). + + Aleksander Machniak + alec + alec@alec.pl + yes + + 2012-03-13 + + 0.8 + 0.7 + + + stable + stable + + GNU GPLv2 + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5.2.1 + + + 1.7.0 + + + + + diff --git a/plugins/acl/skins/default/acl.css b/plugins/acl/skins/default/acl.css new file mode 100644 index 000000000..cf3391f49 --- /dev/null +++ b/plugins/acl/skins/default/acl.css @@ -0,0 +1,100 @@ +#aclmanager +{ + position: relative; + border: 1px solid #999; + min-height: 302px; +} + +#aclcontainer +{ + overflow-x: auto; +} + +#acltable +{ + width: 100%; + border-collapse: collapse; + background-color: #F9F9F9; +} + +#acltable td +{ + width: 1%; + white-space: nowrap; +} + +#acltable thead td +{ + padding: 0 4px 0 2px; +} + +#acltable tbody td +{ + text-align: center; + padding: 2px; + border-bottom: 1px solid #999999; + cursor: default; +} + +#acltable tbody td.user +{ + width: 96%; + text-align: left; + overflow: hidden; + text-overflow: ellipsis; + -o-text-overflow: ellipsis; +} + +#acltable tbody td.partial +{ + background: url(images/partial.png) center no-repeat; +} + +#acltable tbody td.enabled +{ + background: url(images/enabled.png) center no-repeat; +} + +#acltable tr.selected td +{ + color: #FFFFFF; + background-color: #CC3333; +} + +#acltable tr.unfocused td +{ + color: #FFFFFF; + background-color: #929292; +} + +#acladvswitch +{ + position: absolute; + right: 4px; + text-align: right; + line-height: 22px; +} + +#acladvswitch input +{ + vertical-align: middle; +} + +#acladvswitch span +{ + display: block; +} + +#aclform +{ + top: 80px; + width: 480px; + padding: 10px; +} + +#aclform div +{ + padding: 0; + text-align: center; + clear: both; +} diff --git a/plugins/acl/skins/default/images/enabled.png b/plugins/acl/skins/default/images/enabled.png new file mode 100644 index 000000000..98215f68c Binary files /dev/null and b/plugins/acl/skins/default/images/enabled.png differ diff --git a/plugins/acl/skins/default/images/partial.png b/plugins/acl/skins/default/images/partial.png new file mode 100644 index 000000000..12023f057 Binary files /dev/null and b/plugins/acl/skins/default/images/partial.png differ diff --git a/plugins/acl/skins/default/templates/table.html b/plugins/acl/skins/default/templates/table.html new file mode 100644 index 000000000..2365ef757 --- /dev/null +++ b/plugins/acl/skins/default/templates/table.html @@ -0,0 +1,54 @@ + + +
    +
    + +
    +
    + + + + + +
    +
    + +
    +
      +
    • +
    • +
    +
    + +
    +
    + +
    +
    + +
    +
    + + +
    +
    + + diff --git a/plugins/acl/skins/larry/acl.css b/plugins/acl/skins/larry/acl.css new file mode 100644 index 000000000..589d688f6 --- /dev/null +++ b/plugins/acl/skins/larry/acl.css @@ -0,0 +1,113 @@ +#aclcontainer +{ + overflow-x: auto; + border: 1px solid #CCDDE4; + background-color: #D9ECF4; + height: 272px; + box-shadow: none; +} + +#acllist-content +{ + position: relative; + height: 230px; + background-color: white; +} + +#acllist-footer +{ + position: relative; +} + +#acltable +{ + width: 100%; + border-collapse: collapse; + border: none; +} + +#acltable td +{ + white-space: nowrap; + border: none; + text-align: center; +} + +#acltable thead tr td +{ + border-left: #BBD3DA dotted 1px; + font-size: 11px; + font-weight: bold; +} + +#acltable tbody td +{ + border-bottom: #DDDDDD 1px solid; + text-align: center; + padding: 2px; + cursor: default; +} + +#acltable thead td.user +{ + border-left: none; +} + +#acltable tbody td.user +{ + text-align: left; + overflow: hidden; + text-overflow: ellipsis; + -o-text-overflow: ellipsis; + border-left: none; + width: 50px; +} + +#acltable tbody td.partial +{ + background-image: url(images/partial.png); + background-position: center; + background-repeat: no-repeat; +} + +#acltable tbody td.enabled +{ + background-image: url(images/enabled.png); + background-position: center; + background-repeat: no-repeat; +} + +#acltable tbody tr.selected td.partial +{ + background-color: #019bc6; + background-image: url(images/partial.png), -moz-linear-gradient(top, #019bc6 0%, #017cb4 100%); + background-image: url(images/partial.png), -webkit-gradient(linear, left top, left bottom, color-stop(0%,#019bc6), color-stop(100%,#017cb4)); + background-image: url(images/partial.png), -o-linear-gradient(top, #019bc6 0%, #017cb4 100%); + background-image: url(images/partial.png), -ms-linear-gradient(top, #019bc6 0%, #017cb4 100%); + background-image: url(images/partial.png), linear-gradient(top, #019bc6 0%, #017cb4 100%); +} + +#acltable tbody tr.selected td.enabled +{ + background-color: #019bc6; + background-image: url(images/enabled.png), -moz-linear-gradient(top, #019bc6 0%, #017cb4 100%); + background-image: url(images/enabled.png), -webkit-gradient(linear, left top, left bottom, color-stop(0%,#019bc6), color-stop(100%,#017cb4)); + background-image: url(images/enabled.png), -o-linear-gradient(top, #019bc6 0%, #017cb4 100%); + background-image: url(images/enabled.png), -ms-linear-gradient(top, #019bc6 0%, #017cb4 100%); + background-image: url(images/enabled.png), linear-gradient(top, #019bc6 0%, #017cb4 100%); +} + +#aclform +{ + top: 80px; + width: 480px; + padding: 10px; + background-color: white; +} + +#aclform div +{ + padding: 0; + text-align: center; + clear: both; +} diff --git a/plugins/acl/skins/larry/images/enabled.png b/plugins/acl/skins/larry/images/enabled.png new file mode 100644 index 000000000..98215f68c Binary files /dev/null and b/plugins/acl/skins/larry/images/enabled.png differ diff --git a/plugins/acl/skins/larry/images/partial.png b/plugins/acl/skins/larry/images/partial.png new file mode 100644 index 000000000..12023f057 Binary files /dev/null and b/plugins/acl/skins/larry/images/partial.png differ diff --git a/plugins/acl/skins/larry/templates/table.html b/plugins/acl/skins/larry/templates/table.html new file mode 100644 index 000000000..c8dff81b1 --- /dev/null +++ b/plugins/acl/skins/larry/templates/table.html @@ -0,0 +1,34 @@ +
    +
    + +
    + +
    + +
    +
      +
    • +
    • +
    +
    + +
    +
    + +
    +
    + +
    +
    + + +
    +
    -- cgit v1.2.3