summaryrefslogtreecommitdiff
path: root/plugins/acl
diff options
context:
space:
mode:
authoralec <alec@208e9e7b-5314-0410-a742-e7e81cd9613c>2011-05-13 10:17:44 +0000
committeralec <alec@208e9e7b-5314-0410-a742-e7e81cd9613c>2011-05-13 10:17:44 +0000
commit32ebf533a2f82571c029a908c5fee27f4febf21d (patch)
tree5d3ef7e6b969736bc9209ea800aaa2586153a5b9 /plugins/acl
parent72d2cc157fc40665ec12e31714b6ef419f9b4cd0 (diff)
- ACL plugin (initial commit)
git-svn-id: https://svn.roundcube.net/trunk@4757 208e9e7b-5314-0410-a742-e7e81cd9613c
Diffstat (limited to 'plugins/acl')
-rw-r--r--plugins/acl/acl.js268
-rw-r--r--plugins/acl/acl.php458
-rw-r--r--plugins/acl/config.inc.php10
-rw-r--r--plugins/acl/config.inc.php.dist13
-rw-r--r--plugins/acl/localization/en_US.inc79
-rw-r--r--plugins/acl/localization/pl_PL.inc79
-rw-r--r--plugins/acl/skins/default/acl.css89
-rw-r--r--plugins/acl/skins/default/images/flagged.pngbin0 -> 674 bytes
-rw-r--r--plugins/acl/skins/default/templates/table.html54
9 files changed, 1050 insertions, 0 deletions
diff --git a/plugins/acl/acl.js b/plugins/acl/acl.js
new file mode 100644
index 000000000..0bf194804
--- /dev/null
+++ b/plugins/acl/acl.js
@@ -0,0 +1,268 @@
+/**
+ * ACL plugin script
+ *
+ * @version 0.1
+ * @author Aleksander Machniak <alec@alec.pl>
+ */
+
+if (window.rcmail) {
+ rcmail.addEventListener('init', function() {
+ if (rcmail.gui_objects.acltable)
+ rcmail.acl_list_init();
+
+ 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('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 = '';
+
+ $(':checkbox', this.env.acl_advanced ? $('#advancedrights') : sim_ul = $('#simplerights')).map(function() {
+ if (this.checked)
+ rights += this.value;
+ });
+
+ if (!user) {
+ alert(this.get_label('acl.nouser'));
+ return;
+ }
+ if (!rights) {
+ alert(this.get_label('acl.norights'));
+ return;
+ }
+
+ this.http_request('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.acl_form.hide();
+}
+
+// Update data after save (and hide form)
+rcube_webmail.prototype.acl_update = function(o)
+{
+ // delete row
+ if (o.old)
+ this.acl_remove_row(o.old);
+
+ // add new row
+ this.acl_add_row(o, true);
+ // 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('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)
+ 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<len; n++) {
+ row = list.rows[selection[n]].obj;
+ cell = $('td.user', row);
+ if (cell.length == 1)
+ users.push(cell.text());
+ }
+
+ return users;
+}
+
+// Removes ACL table row
+rcube_webmail.prototype.acl_remove_row = function(id)
+{
+ this.acl_list.remove_row(id);
+ // we don't need it anymore (remove id conflict)
+ $('#rcmrow'+id).remove();
+ this.env.acl[id] = null;
+}
+
+// Adds ACL table row
+rcube_webmail.prototype.acl_add_row = function(o, sel)
+{
+ var n, len, ids = [], 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 cl = this.className.replace(/^acl/, '');
+
+ if (items && items[cl])
+ cl = items[cl];
+
+ if (cl == 'user')
+ $(this).text(o.username);
+ else
+ $(this).addClass(String(o.acl).match(RegExp(cl)) ? 'enabled' : 'disabled').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])
+ ids.push(n);
+ ids.sort();
+
+ // find current id
+ for (n=0, len=ids.length; n<len; n++)
+ if (ids[n] == id)
+ break;
+
+ // add row
+ if (n && n < len) {
+ $('#rcmrow'+ids[n-1]).after(row);
+ list.init_row(row);
+ list.rowcount++;
+ }
+ else
+ list.insert_row(row);
+
+ if (sel)
+ list.select_row(o.id);
+}
+
+// Initializes and shows ACL create/edit form
+rcube_webmail.prototype.acl_init_form = function(id)
+{
+ var ul, row, li_elements, body = $('body'),
+ adv_ul = $('#advancedrights'), sim_ul = $('#simplerights'),
+ name_input = $('#acluser');
+
+ this.acl_form = $('#aclform');
+
+ // Hide unused items
+ if (this.env.acl_advanced) {
+ adv_ul.show();
+ sim_ul.hide();
+ ul = adv_ul;
+ }
+ else {
+ sim_ul.show();
+ adv_ul.hide();
+ ul = sim_ul;
+ }
+
+ // initialize form fields
+ li_elements = $(':checkbox', ul);
+ li_elements.attr('checked', false);
+
+ if (id) {
+ row = this.acl_list.rows[id].obj;
+ li_elements.map(function() {
+ var val = this.value, td = $('td.'+this.id, row);
+ if (td && td.hasClass('enabled'))
+ this.checked = true;
+ });
+ name_input.val($('td.user', row).text());
+ }
+ else {
+ name_input.val('');
+ }
+
+ this.acl_id = id;
+
+ // position the form horizontally
+ var bw = body.width(), mw = this.acl_form.width();
+
+ if (bw >= mw)
+ this.acl_form.css({left: parseInt((bw - mw)/2)+'px'});
+
+ // display it
+ this.acl_form.show();
+ name_input.focus();
+}
diff --git a/plugins/acl/acl.php b/plugins/acl/acl.php
new file mode 100644
index 000000000..8222d2449
--- /dev/null
+++ b/plugins/acl/acl.php
@@ -0,0 +1,458 @@
+<?php
+
+/**
+ * Folders Access Control Lists Management
+ *
+ * @version 0.1
+ * @author Aleksander Machniak <alec@alec.pl>
+ *
+ *
+ * Copyright (C) 2011, 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';
+
+ private $rc;
+ private $supported = null;
+ private $mbox;
+
+ /**
+ * Plugin initialization
+ */
+ function init()
+ {
+ $this->rc = rcmail::get_instance();
+
+ $this->require_plugin('jqueryui');
+
+ // Register hooks
+ $this->add_hook('folder_form', array($this, 'folder_form'));
+ // Plugin actions
+ $this->register_action('plugin.acl', array($this, 'acl_actions'));
+ }
+
+ /**
+ * Handler for plugin actions (AJAX)
+ */
+ function acl_actions()
+ {
+ $action = trim(get_input_value('_act', RCUBE_INPUT_GPC));
+
+ // Connect to IMAP
+ $this->rc->imap_init();
+ $this->rc->imap_connect();
+
+ // 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 '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;
+ }
+
+ // Do nothing if no ACL support
+ if (!$this->rc->imap->get_capability('ACL')) {
+ return $args;
+ }
+
+ // Get MYRIGHTS
+ if (!($myrights = $args['options']['rights'])) {
+ 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');
+
+ // 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('mailbox', $mbox_imap);
+ $this->rc->output->add_handlers(array(
+ 'acltable' => array($this, 'templ_table'),
+ 'aclrights' => array($this, 'templ_rights'),
+ ));
+
+ $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
+ *
+ * @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,
+ 'full' => 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 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->imap->get_acl($this->mbox);
+
+ if (!is_array($acl)) {
+ $acl = array();
+ }
+
+ // Sort the list by username
+ uksort($acl, 'strnatcasecmp');
+
+ // 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,
+ 'full' => implode($supported),
+ );
+ }
+
+ // Create the table
+ $attrib['noheader'] = true;
+ $table = new html_table($attrib);
+
+ // Create table header
+ $table->add_header('user', $this->gettext('username'));
+ foreach (array_keys($items) as $key) {
+ $table->add_header('acl'.$key, $this->gettext('shortacl'.$key));
+ }
+
+ $i = 1;
+ $js_table = array();
+ foreach ($acl as $user => $rights) {
+ if ($this->rc->imap->conn->user == $user) {
+ continue;
+ }
+
+ // filter out virtual rights (c or d) the server may return
+ $userrights = array_intersect($rights, $supported);
+ $userid = html_identifier($user);
+
+ $table->add_row(array('id' => 'rcmrow'.$userid));
+ $table->add('user', Q($user));
+
+ foreach ($items as $key => $right) {
+ $in = $this->acl_compare($userrights, $right);
+ $table->add('acl'.$key . ' ' . ($in ? 'enabled' : 'disabled'), '');
+ }
+
+ $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());
+
+ if (!strpos($user, '@') && ($realm = $this->rc->config->get('acl_username_realm'))) {
+ $user .= '@' . rcube_idn_to_ascii(preg_replace('/^@/', '', $realm));
+ }
+
+ if ($acl && $user && strlen($mbox)
+ && $this->rc->imap->set_acl($mbox, $user, $acl)
+ ) {
+ $ret = array('id' => html_identifier($user),
+ 'username' => $user, 'acl' => implode($acl), 'old' => $oldid);
+ $this->rc->output->command('acl_update', $ret);
+ $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) {
+ if ($this->rc->imap->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[^>]+>/', '/<\/table>$/'), '', $out);
+
+ $this->rc->output->command('acl_list_update', $out);
+ }
+
+ /**
+ * Creates <UL> 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' => '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 boolean Comparision result
+ */
+ 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);
+
+ return count($res) == count($acl2);
+ }
+
+ /**
+ * 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->imap->get_capability('RIGHTS');
+
+ if (is_array($capa)) {
+ $rights = strtolower($capa[0]);
+ }
+ else {
+ $rights = 'cd';
+ }
+
+ return $this->supported = str_split('lrswi' . $rights . 'pa');
+ }
+}
diff --git a/plugins/acl/config.inc.php b/plugins/acl/config.inc.php
new file mode 100644
index 000000000..c634c1af1
--- /dev/null
+++ b/plugins/acl/config.inc.php
@@ -0,0 +1,10 @@
+<?php
+
+// Sets default look of access rights table
+// In advanced mode all access rights are displayed separately
+// In simple mode access rights are grouped into four groups: read, write, delete, full
+$rcmail_config['acl_advanced_mode'] = true;
+
+$rcmail_config['acl_username_realm'] = 'test90.kolabsys.com';
+
+?>
diff --git a/plugins/acl/config.inc.php.dist b/plugins/acl/config.inc.php.dist
new file mode 100644
index 000000000..6cb3093f3
--- /dev/null
+++ b/plugins/acl/config.inc.php.dist
@@ -0,0 +1,13 @@
+<?php
+
+// Default look of access rights table
+// In advanced mode all access rights are displayed separately
+// In simple mode access rights are grouped into four groups: read, write, delete, full
+$rcmail_config['acl_advanced_mode'] = false;
+
+// Username realm
+// When user enters a username without domain part, realm
+// alows to add it to the username (and display correct username in the table)
+$rcmail_config['acl_username_realm'] = '';
+
+?>
diff --git a/plugins/acl/localization/en_US.inc b/plugins/acl/localization/en_US.inc
new file mode 100644
index 000000000..7f1655f40
--- /dev/null
+++ b/plugins/acl/localization/en_US.inc
@@ -0,0 +1,79 @@
+<?php
+
+$labels['sharing'] = 'Sharing';
+$labels['myrights'] = 'Access Rights';
+$labels['username'] = 'Username';
+$labels['advanced'] = 'advanced mode';
+$labels['newuser'] = 'Add entry';
+$labels['actions'] = 'Access right actions...';
+
+$labels['acll'] = 'Lookup';
+$labels['aclr'] = 'Read messages';
+$labels['acls'] = 'Keep Seen state';
+$labels['aclw'] = 'Write flags';
+$labels['acli'] = 'Insert (Copy into)';
+$labels['aclp'] = 'Post';
+$labels['aclc'] = 'Create subfolders';
+$labels['aclk'] = 'Create subfolders';
+$labels['acld'] = 'Delete messages';
+$labels['aclt'] = 'Delete messages';
+$labels['acle'] = 'Expunge';
+$labels['aclx'] = 'Delete folder';
+$labels['acla'] = 'Administer';
+
+$labels['aclfull'] = 'Full control';
+$labels['aclread'] = 'Read';
+$labels['aclwrite'] = 'Write';
+$labels['acldelete'] = 'Delete';
+
+$labels['shortacll'] = 'Lookup';
+$labels['shortaclr'] = 'Read';
+$labels['shortacls'] = 'Keep';
+$labels['shortaclw'] = 'Write';
+$labels['shortacli'] = 'Insert';
+$labels['shortaclp'] = 'Post';
+$labels['shortaclc'] = 'Create';
+$labels['shortaclk'] = 'Create';
+$labels['shortacld'] = 'Delete';
+$labels['shortaclt'] = 'Delete';
+$labels['shortacle'] = 'Expunge';
+$labels['shortaclx'] = 'Folder delete';
+$labels['shortacla'] = 'Administer';
+
+$labels['shortaclfull'] = 'Full';
+$labels['shortaclread'] = 'Read';
+$labels['shortaclwrite'] = 'Write';
+$labels['shortacldelete'] = 'Delete';
+
+$labels['longacll'] = 'The folder is visible on lists and can be subscribed to';
+$labels['longaclr'] = 'The folder can be opened for reading';
+$labels['longacls'] = 'Messages Seen flag can be changed';
+$labels['longaclw'] = 'Messages flags and keywords can be changed, except Seen and Deleted';
+$labels['longacli'] = 'Messages can be written or copied to the folder';
+$labels['longaclp'] = 'Messages can be posted to this folder';
+$labels['longaclc'] = 'Folders can be created (or renamed) directly under this folder';
+$labels['longaclk'] = 'Folders can be created (or renamed) directly under this folder';
+$labels['longacld'] = 'Messages Delete flag can be changed';
+$labels['longaclt'] = 'Messages Delete flag can be changed';
+$labels['longacle'] = 'Messages can be expunged';
+$labels['longaclx'] = 'The folder can be deleted or renamed';
+$labels['longacla'] = 'The folder access rights can be changed';
+
+$labels['longaclfull'] = 'Full control including folder administration';
+$labels['longaclread'] = 'The folder can be opened for reading';
+$labels['longaclwrite'] = 'Messages can be marked, written or copied to the folder';
+$labels['longacldelete'] = 'Messages can be deleted';
+
+$messages['deleting'] = 'Deleting access rights...';
+$messages['saving'] = 'Saving access rights...';
+$messages['updatesuccess'] = 'Successfully changed access rights';
+$messages['deletesuccess'] = 'Successfully deleted access rights';
+$messages['createsuccess'] = 'Successfully added access rights';
+$messages['updateerror'] = 'Ubable to update access rights';
+$messages['deleteerror'] = 'Unable to delete access rights';
+$messages['createerror'] = 'Unable to add access rights';
+$messages['deleteconfirm'] = 'Are you sure, you want to remove access rights of selected user(s)?';
+$messages['norights'] = 'No rights has been specified!';
+$messages['nouser'] = 'No username has been specified!';
+
+?>
diff --git a/plugins/acl/localization/pl_PL.inc b/plugins/acl/localization/pl_PL.inc
new file mode 100644
index 000000000..cba12d927
--- /dev/null
+++ b/plugins/acl/localization/pl_PL.inc
@@ -0,0 +1,79 @@
+<?php
+
+$labels['sharing'] = 'Udostępnianie';
+$labels['myrights'] = 'Prawa dostępu';
+$labels['username'] = 'Nazwa użytkownika';
+$labels['advanced'] = 'tryb zaawansowany';
+$labels['newuser'] = 'Dodaj rekord';
+$labels['actions'] = 'Akcje na prawach...';
+
+$labels['acll'] = 'Podgląd (Lookup)';
+$labels['aclr'] = 'Odczyt (Read)';
+$labels['acls'] = 'Zmiana stanu wiadomości (Keep)';
+$labels['aclw'] = 'Zmiana flag wiadomości (Write)';
+$labels['acli'] = 'Dodawanie/Kopiowanie do (Insert)';
+$labels['aclp'] = 'Wysyłanie (Post)';
+$labels['aclc'] = 'Tworzenie podfolderów (Create)';
+$labels['aclk'] = 'Tworzenie podfolderów (Create)';
+$labels['acld'] = 'Usuwanie wiadomości (Delete)';
+$labels['aclt'] = 'Usuwanie wiadomości (Delete)';
+$labels['acle'] = 'Porządkowanie folderu (Expunge)';
+$labels['aclx'] = 'Usuwanie folder (Delete)';
+$labels['acla'] = 'Administracja (Administer)';
+
+$labels['aclfull'] = 'Wszystkie';
+$labels['aclread'] = 'Odczyt';
+$labels['aclwrite'] = 'Zapis';
+$labels['acldelete'] = 'Usuwanie';
+
+$labels['shortacll'] = 'Podgląd';
+$labels['shortaclr'] = 'Odczyt';
+$labels['shortacls'] = 'Zmiana';
+$labels['shortaclw'] = 'Zmiana flag';
+$labels['shortacli'] = 'Dodawanie';
+$labels['shortaclp'] = 'Wysyłanie';
+$labels['shortaclc'] = 'Tworzenie';
+$labels['shortaclk'] = 'Tworzenie';
+$labels['shortacld'] = 'Usuwanie';
+$labels['shortaclt'] = 'Usuwanie';
+$labels['shortacle'] = 'Porządkowanie';
+$labels['shortaclx'] = 'Usuwanie folderu';
+$labels['shortacla'] = 'Administracja';
+
+$labels['shortaclfull'] = 'Wszystkie';
+$labels['shortaclread'] = 'Odczyt';
+$labels['shortaclwrite'] = 'Zapis';
+$labels['shortacldelete'] = 'Usuwanie';
+
+$labels['longacll'] = 'Pozwala na subskrybowanie folderu i powoduje, że jest on widoczny na liście';
+$labels['longaclr'] = 'Pozwala na otwarcie folderu w trybie do odczytu';
+$labels['longacls'] = 'Pozwala na zmienę stanu wiadomości';
+$labels['longaclw'] = 'Pozwala zmieniać wszystkie flagi wiadomości, oprócz "Przeczytano" i "Usunięto"';
+$labels['longacli'] = 'Pozwala zapisywać wiadomości i kopiować do folderu';
+$labels['longaclp'] = 'Pozwala wysyłać wiadomości do folderu';
+$labels['longaclc'] = 'Pozwala tworzyć (lub zmieniać nazwę) podfoldery';
+$labels['longaclk'] = 'Pozwala tworzyć (lub zmieniać nazwę) podfoldery';
+$labels['longacld'] = 'Pozwala zmianiać flagę "Usunięto" wiadomości';
+$labels['longaclt'] = 'Pozwala zmianiać flagę "Usunięto" wiadomości';
+$labels['longacle'] = 'Pozwala na usuwanie wiadomości oznaczonych do usunięcia';
+$labels['longaclx'] = 'Pozwala na zmianę nazwy lub usunięcie folderu';
+$labels['longacla'] = 'Pozwala na zmiane praw dostępu do folderu';
+
+$labels['longaclfull'] = 'Pełna kontrola włącznie z administrowaniem folderem';
+$labels['longaclread'] = 'Folder może być otwarty w trybie do odczytu';
+$labels['longaclwrite'] = 'Wiadomości mogą być oznaczane, zapisywane i kopiowane do folderu';
+$labels['longacldelete'] = 'Wiadomości mogą być usuwane';
+
+$messages['deleting'] = 'Usuwanie praw dostępu...';
+$messages['saving'] = 'Zapisywanie praw dostępu...';
+$messages['updatesuccess'] = 'Pomyślnie zmieniono prawa dostępu';
+$messages['deletesuccess'] = 'Pomyślnie usunięto prawa dostępu';
+$messages['createsuccess'] = 'Pomyślnie dodano prawa dostępu';
+$messages['updateerror'] = 'Nie udało się zmienić praw dostępu';
+$messages['deleteerror'] = 'Nie udało się usunąć praw dostępu';
+$messages['createerror'] = 'Nie udało się dodać praw dostępu';
+$messages['deleteconfirm'] = 'Czy na pewno chcesz usunąć prawa wybranym użytkownikom?';
+$messages['norights'] = 'Nie wybrano praw dostępu!';
+$messages['nouser'] = 'Nie podano nazwy użytkownika!';
+
+?>
diff --git a/plugins/acl/skins/default/acl.css b/plugins/acl/skins/default/acl.css
new file mode 100644
index 000000000..8731a6503
--- /dev/null
+++ b/plugins/acl/skins/default/acl.css
@@ -0,0 +1,89 @@
+#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.enabled
+{
+ background: url(images/flagged.png) center no-repeat;
+}
+
+#acltable tr.selected td
+{
+ color: #FFFFFF;
+ background-color: #CC3333;
+}
+
+#acladvswitch
+{
+ position: absolute;
+ right: 4px;
+ text-align: right;
+ line-height: 22px;
+}
+
+#acladvswitch input
+{
+ vertical-align: middle;
+}
+
+#acladvswitch span
+{
+ display: block;
+}
+
+#aclform
+{
+ top: 100px;
+ width: 400px;
+ padding: 10px;
+}
+
+#aclform div
+{
+ padding: 0;
+ text-align: center;
+ clear: both;
+}
diff --git a/plugins/acl/skins/default/images/flagged.png b/plugins/acl/skins/default/images/flagged.png
new file mode 100644
index 000000000..98215f68c
--- /dev/null
+++ b/plugins/acl/skins/default/images/flagged.png
Binary files 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..8caafe7dc
--- /dev/null
+++ b/plugins/acl/skins/default/templates/table.html
@@ -0,0 +1,54 @@
+<!--[if lte IE 6]>
+ <style type="text/css">
+ #aclmanager { height: expression(Math.min(302, parseInt(document.documentElement.clientHeight))+'px'); }
+ </style>
+<![endif]-->
+
+<div id="aclmanager">
+<div id="aclcontainer" class="boxlistcontent" style="top:0">
+ <roundcube:object name="acltable" id="acltable" class="records-table" />
+</div>
+<div class="boxfooter">
+ <roundcube:button command="acl-create" id="aclcreatelink" type="link" title="acl.newuser" class="buttonPas addgroup" classAct="button addgroup" content=" " />
+ <roundcube:button name="aclmenulink" id="aclmenulink" type="link" title="acl.actions" class="button groupactions" onclick="show_aclmenu(); return false" content=" " />
+ <roundcube:if condition="!in_array('acl_advanced_mode', (array)config:dont_override)" />
+ <div id="acladvswitch" class="pagenav">
+ <span><label for="acl-switch"><roundcube:label name="acl.advanced" /></label>
+ <input type="checkbox" id="acl-switch" onclick="rcmail.command('acl-mode-switch')"<roundcube:exp expression="config:acl_advanced_mode == true ? ' checked=checked' : ''" /> />
+ </span>
+ </div>
+ <roundcube:endif />
+</div>
+</div>
+
+<div id="aclmenu" class="popupmenu">
+ <ul>
+ <li><roundcube:button command="acl-edit" label="edit" classAct="active" /></li>
+ <li><roundcube:button command="acl-delete" label="delete" classAct="active" /></li>
+ </ul>
+</div>
+
+<div id="aclform" class="popupmenu">
+ <fieldset class="thinbordered"><legend><roundcube:label name="acl.username" /></legend>
+ <input type="text" name="acluser" value="" id="acluser" size="45" style="width: 95%" />
+ </fieldset>
+ <fieldset class="thinbordered"><legend><roundcube:label name="acl.myrights" /></legend>
+ <roundcube:object name="aclrights" class="toolbarmenu" />
+ </fieldset>
+ <div>
+ <roundcube:button command="acl-cancel" type="input" class="button" label="cancel" />
+ <roundcube:button command="acl-save" type="input" class="button mainaction" label="save" />
+ </div>
+</div>
+
+<script type="text/javascript">
+function show_aclmenu()
+{
+ if (!rcmail_ui) {
+ rcube_init_mail_ui();
+ rcmail_ui.popups.aclmenu = {id:'aclmenu', above:1, obj: $('#aclmenu')};
+ }
+
+ rcmail_ui.show_popup('aclmenu');
+}
+</script>