Merge branch 'master' of ssh://git.kolabsys.com/git/roundcube

This commit is contained in:
Thomas Bruederli 2011-06-27 12:04:45 -04:00
commit 8c5d9db9cc
14 changed files with 1087 additions and 42 deletions

View file

@ -95,6 +95,8 @@ abstract class calendar_driver
/**
* Delete the given calendar with all its contents
*
* @param array Hash array with calendar properties
* id: Calendar Identifier
* @return boolean True on success, Fales on failure
*/
abstract function remove_calendar($prop);

View file

@ -12,6 +12,7 @@
| |
+-------------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+-------------------------------------------------------------------------+
*/
@ -20,7 +21,7 @@ class kolab_calendar
public $id;
public $ready = false;
public $readonly = true;
private $cal;
private $storage;
private $events;
@ -29,7 +30,7 @@ class kolab_calendar
private $sensitivity_map = array('public', 'private', 'confidential');
private $priority_map = array('low', 'normal', 'high');
private $fieldmap = array(
// kolab => roundcube
'summary' => 'title',
@ -42,14 +43,14 @@ class kolab_calendar
'show-time-as' => 'free_busy',
'alarm','alarms'
);
/**
* Default constructor
*/
public function __construct($imap_folder, $calendar)
{
$this->cal = $calendar;
if (strlen($imap_folder))
$this->imap_folder = $imap_folder;
@ -75,7 +76,19 @@ class kolab_calendar
$dispname = preg_replace(array('!INBOX/Calendar/!', '!^INBOX/!', '!^shared/!', '!^user/([^/]+)/!'), array('','','','(\\1) '), $this->imap_folder);
return rcube_charset_convert(strlen($dispname) ? $dispname : $this->imap_folder, "UTF7-IMAP");
}
/**
* Getter for the IMAP folder name
*
* @return string Name of the IMAP folder
*/
public function get_realname()
{
return $this->imap_folder;
}
/**
* Getter for the top-end calendar folder name (not the entire path)
*
@ -84,22 +97,24 @@ class kolab_calendar
public function get_foldername()
{
$parts = explode('/', $this->imap_folder);
return end($parts);
return rcube_charset_convert(end($parts), 'UTF7-IMAP');
}
/**
* Return color to display this calendar
*/
public function get_color($owner)
public function get_color()
{
// TODO: read color from backend (not yet supported)
//temporary color deffirence between own calendars and the rest
if ($owner == $_SESSION['username'])
return 'd63355';
return '1f9ebe';
// Store temporarily calendar color in user prefs (will be changed)
$prefs = $this->cal->rc->config->get('kolab_calendars', array());
if (!empty($prefs[$this->id]) && !empty($prefs[$this->id]['color']))
return $prefs[$this->id]['color'];
return 'cc0000';
}
/**
* Getter for a single event object
*/

View file

@ -71,7 +71,7 @@ class kolab_driver extends calendar_driver
'id' => $calendar->id,
'name' => $calendar->get_name(),
'editname' => $calendar->get_foldername(),
'color' => $calendar->get_color($c_folder->_owner),
'color' => $calendar->get_color(),
'readonly' => $c_folder->_owner != $_SESSION['username'],
);
}
@ -102,7 +102,7 @@ class kolab_driver extends calendar_driver
if ($this->create_calendar(array('name' => 'Default', 'color' => 'cc0000')))
$this->_read_calendars();
}
return $this->calendars;
}
@ -117,10 +117,30 @@ class kolab_driver extends calendar_driver
*/
public function create_calendar($prop)
{
$folder = rcube_charset_convert($prop['name'], RCMAIL_CHARSET, 'UTF7-IMAP');
// add namespace prefix (when needed)
$this->rc->imap_init();
$folder = $this->rc->imap->mod_mailbox($folder, 'in');
// create ID
$id = rcube_kolab::folder_id($folder);
// create IMAP folder
if (rcube_kolab::folder_create($folder, 'event')) {
// save color in user prefs (temp. solution)
$prefs['kolab_calendars'] = $this->rc->config->get('kolab_calendars', array());
$prefs['kolab_calendars'][$id]['color'] = $prop['color'];
$this->rc->user->save_prefs($prefs);
return $id;
}
return false;
}
/**
* Update properties of an existing calendar
*
@ -128,9 +148,36 @@ class kolab_driver extends calendar_driver
*/
public function edit_calendar($prop)
{
if ($prop['id'] && ($cal = $this->folders[$prop['id']])) {
$newfolder = rcube_charset_convert($prop['name'], RCMAIL_CHARSET, 'UTF7-IMAP');
$oldfolder = $cal->get_realname();
// add namespace prefix (when needed)
$this->rc->imap_init();
$newfolder = $this->rc->imap->mod_mailbox($newfolder, 'in');
if ($newfolder != $oldfolder)
$result = rcube_kolab::folder_rename($oldfolder, $newfolder);
else
$result = true;
if ($result) {
// create ID
$id = rcube_kolab::folder_id($newfolder);
// save color in user prefs (temp. solution)
$prefs['kolab_calendars'] = $this->rc->config->get('kolab_calendars', array());
$prefs['kolab_calendars'][$id]['color'] = $prop['color'];
unset($prefs['kolab_calendars'][$prop['id']]);
$this->rc->user->save_prefs($prefs);
return true;
}
}
return false;
}
/**
* Delete the given calendar with all its contents
*
@ -138,7 +185,18 @@ class kolab_driver extends calendar_driver
*/
public function remove_calendar($prop)
{
if ($prop['id'] && ($cal = $this->folders[$prop['id']])) {
$folder = $cal->get_realname();
if (rcube_kolab::folder_delete($folder)) {
// remove color in user prefs (temp. solution)
$prefs['kolab_calendars'] = $this->rc->config->get('kolab_calendars', array());
unset($prefs['kolab_calendars'][$prop['id']]);
$this->rc->user->save_prefs($prefs);
return true;
}
}
return false;
}
@ -153,7 +211,7 @@ class kolab_driver extends calendar_driver
$cid = $event['calendar'] ? $event['calendar'] : reset(array_keys($this->calendars));
if ($storage = $this->_get_storage($cid))
return $storage->insert_event($event);
return false;
}
@ -167,7 +225,7 @@ class kolab_driver extends calendar_driver
{
if ($storage = $this->_get_storage($event['calendar']))
return $storage->update_event($event);
return false;
}
@ -181,7 +239,7 @@ class kolab_driver extends calendar_driver
{
if (($storage = $this->_get_storage($event['calendar'])) && ($ev = $storage->get_event($event['id'])))
return $storage->update_event($event + $ev);
return false;
}

View file

@ -0,0 +1,256 @@
if (window.rcmail) {
rcmail.addEventListener('init', function() {
rcmail.set_book_actions();
if (rcmail.gui_objects.editform && rcmail.env.action.match(/^plugin\.book/)) {
rcmail.enable_command('book-save', true);
}
});
rcmail.addEventListener('listupdate', function() {
rcmail.set_book_actions();
});
}
// (De-)activates address book management commands
rcube_webmail.prototype.set_book_actions = function()
{
var source = this.env.source,
sources = this.env.address_sources;
this.enable_command('book-create', true);
this.enable_command('book-edit', 'book-delete', source && sources[source] && sources[source].kolab && sources[source].editable);
};
rcube_webmail.prototype.book_create = function()
{
this.book_show_contentframe('create');
};
rcube_webmail.prototype.book_edit = function()
{
this.book_show_contentframe('edit');
};
rcube_webmail.prototype.book_delete = function()
{
if (this.env.source != '' && confirm(this.get_label('kolab_addressbook.bookdeleteconfirm'))) {
var lock = this.set_busy(true, 'kolab_addressbook.bookdeleting');
this.http_request('plugin.book', '_act=delete&_source='+urlencode(this.book_realname()), lock);
}
};
// displays page with book edit/create form
rcube_webmail.prototype.book_show_contentframe = function(action, framed)
{
var add_url = '', target = window;
// unselect contact
this.contact_list.clear_selection();
this.enable_command('edit', 'delete', 'compose', false);
if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
add_url = '&_framed=1';
target = window.frames[this.env.contentframe];
this.show_contentframe(true);
}
else if (framed)
return false;
if (action) {
this.set_busy(true);
this.location_href(this.env.comm_path+'&_action=plugin.book&_act='+action
+'&_source='+urlencode(this.book_realname())
+add_url, target);
}
return true;
};
// submits book create/update form
rcube_webmail.prototype.book_save = function()
{
var form = this.gui_objects.editform,
input = $("input[name='_name']", form)
if (input.length && input.val() == '') {
alert(this.get_label('kolab_addressbook.nobooknamewarning'));
input.focus();
return;
}
input = this.display_message(this.get_label('kolab_addressbook.booksaving'), 'loading');
$('<input type="hidden" name="_unlock" />').val(input).appendTo(form);
form.submit();
};
// action executed after book delete
rcube_webmail.prototype.book_delete_done = function(id)
{
var n, g, li = this.get_folder_li(id), groups = this.env.contactgroups;
// remove folder and its groups rows
for (n in groups)
if (groups[n].source == id && (g = this.get_folder_li(n))) {
$(g).remove();
delete this.env.contactgroups[n];
}
$(li).remove();
delete this.env.address_sources[id];
delete this.env.contactfolders[id];
};
// action executed after book create/update
rcube_webmail.prototype.book_update = function(data, old)
{
var n, i, id, len, row, refrow, olddata, name = '', realname = '', sources, level,
folders = [], class_name = 'addressbook',
list = this.gui_objects.folderlist,
groups = this.env.contactgroups;
this.env.contactfolders[data.id] = this.env.address_sources[data.id] = data;
this.show_contentframe(false);
// update
if (old && old != data.id) {
olddata = this.env.address_sources[old];
delete this.env.address_sources[old];
delete this.env.contactfolders[old];
// update source ID in groups
for (n in groups)
if (groups[n].source == old)
this.env.contactgroups[n].source = data.id;
refrow = $('#rcmli'+old);
}
// create
else if (!old) {
refrow = $('li', list).get(0);
// this shouldn't happen
if (!refrow)
this.redirect(this.get_task_url('addressbook'));
}
if (!refrow)
return;
sources = this.env.address_sources;
// clone a table row if there are existing rows
row = $(refrow).clone();
// set row attributes
if (data.readonly)
class_name += ' readonly';
if (data.class_name)
class_name += ' '+data.class_name;
// updated currently selected book
if (this.env.source != '' && this.env.source == old) {
class_name += ' selected';
this.env.source = data.id;
}
row.attr({id: 'rcmli'+data.id, 'class': class_name});
$('a', row).html(data.name).attr({onclick: '', rel: data.id, href: '#'})
.click({id: data.id}, function(e) { return rcmail.command('list', e.data.id, this); });
// sort kolab folders, to put the new one in order
for (n in sources)
if (sources[n].kolab && (name = sources[n].realname))
folders.push(name);
folders.sort();
// find current id
for (n=0, len=folders.length; n<len; n++)
if (folders[n] == data.realname)
break;
// add row
if (n && n < len) {
// find the row before
name = folders[n-1];
for (n in sources)
if (sources[n].realname && sources[n].realname == name) {
row.insertAfter('#rcmli'+n);
break;
}
}
else if (olddata) {
row.insertBefore(refrow);
}
else {
row.appendTo(list);
}
if (olddata) {
// remove old row (just after the new row has been inserted)
refrow.remove();
old += '-';
level = olddata.realname.split(this.env.delimiter).length - data.realname.split(this.env.delimiter).length;
// update (realname and ID of) subfolders
for (n in sources) {
if (n.indexOf(old) == 0) {
// new ID
id = data.id + '-' + n.substr(old.length);
name = sources[n].name;
realname = data.realname + sources[n].realname.substr(olddata.realname.length);
// update display name
if (level > 0) {
for (i=level; i>0; i--)
name = name.replace(/^&nbsp;&nbsp;/, '');
}
else if (level < 0) {
for (i=level; i<0; i++)
name = '&nbsp;&nbsp;' + name;
}
// update existing row
refrow = $('#rcmli'+n);
refrow.remove().attr({id: 'rcmli'+id});
$('a', refrow).html(name).attr({onclick: '', rel: id, href: '#'})
.click({id: id}, function(e) { return rcmail.command('list', e.data.id, this); });
// move the row to the new place
refrow.insertAfter(row);
row = refrow;
// update list data
sources[n].id = id;
sources[n].name = name;
sources[n].realname = realname;
this.env.address_sources[id] = this.env.contactfolders[id] = sources[n];
delete this.env.address_sources[n];
delete this.env.contactfolders[n];
// update groups
for (i in groups) {
if (groups[i].source == n) {
// update existing row
refrow = $('#rcmli'+i);
refrow.remove().attr({id: 'rcmliG'+id+groups[i].id});
$('a', refrow).attr('onclick', '')
.click({source: id, id: groups[i].id}, function(e) {
return rcmail.command('listgroup', {'source': e.data.source, 'id': e.data.id}, this); });
refrow.insertAfter(row);
row = refrow;
// update group data
groups[i].source = id;
this.env.contactgroups['G'+id+groups[i].id] = groups[i];
delete this.env.contactgroups[i];
}
}
}
}
}
};
// returns real IMAP folder name
rcube_webmail.prototype.book_realname = function()
{
var source = this.env.source, sources = this.env.address_sources;
return source != '' && sources[source] && sources[source].realname ? sources[source].realname : '';
};

View file

@ -1,12 +1,11 @@
<?php
require_once(dirname(__FILE__) . '/rcube_kolab_contacts.php');
/**
* Kolab address book
* Kolab address book 0.5
*
* Sample plugin to add a new address book source with data from Kolab storage
* This is work-in-progress for the Roundcube+Kolab integration.
* It provides also a possibilities to manage contact folders
* (create/rename/delete/acl) directly in Addressbook UI.
*
* @author Thomas Bruederli <roundcube@gmail.com>
* @author Aleksander Machniak <machniak@kolabsys.com>
@ -28,9 +27,12 @@ require_once(dirname(__FILE__) . '/rcube_kolab_contacts.php');
*/
class kolab_addressbook extends rcube_plugin
{
public $task = 'mail|settings|addressbook';
private $folders;
private $sources;
private $rc;
private $ui;
const GLOBAL_FIRST = 0;
const PERSONAL_FIRST = 1;
@ -42,6 +44,8 @@ class kolab_addressbook extends rcube_plugin
*/
public function init()
{
require_once(dirname(__FILE__) . '/lib/rcube_kolab_contacts.php');
$this->rc = rcmail::get_instance();
// load required plugin
@ -54,6 +58,16 @@ class kolab_addressbook extends rcube_plugin
if ($this->rc->task == 'addressbook') {
$this->add_texts('localization');
$this->add_hook('contact_form', array($this, 'contact_form'));
// Plugin actions
$this->register_action('plugin.book', array($this, 'book_actions'));
$this->register_action('plugin.book-save', array($this, 'book_save'));
// Load UI elements
if ($this->api->output->type == 'html') {
require_once($this->home . '/lib/kolab_addressbook_ui.php');
$this->ui = new kolab_addressbook_ui($this);
}
}
else if ($this->rc->task == 'settings') {
$this->add_texts('localization');
@ -92,13 +106,33 @@ class kolab_addressbook extends rcube_plugin
}
$sources = array();
$names = array();
foreach ($this->_list_sources() as $abook_id => $abook) {
$name = $origname = $abook->get_name();
// find folder prefix to truncate
for ($i = count($names)-1; $i >= 0; $i--) {
if (strpos($name, $names[$i].' &raquo; ') === 0) {
$length = strlen($names[$i].' &raquo; ');
$prefix = substr($name, 0, $length);
$count = count(explode(' &raquo; ', $prefix));
$name = str_repeat('&nbsp;&nbsp;', $count-1) . '&raquo; ' . substr($name, $length);
break;
}
}
$names[] = $origname;
// register this address source
$sources[$abook_id] = array(
'id' => $abook_id,
'name' => $abook->get_name(),
'id' => $abook_id,
'name' => $name,
'readonly' => $abook->readonly,
'groups' => $abook->groups,
'editable' => $abook->editable,
'groups' => $abook->groups,
'realname' => rcube_charset_convert($abook->get_realname(), 'UTF7-IMAP'), // IMAP folder name
'class_name' => $abook->get_namespace(),
'kolab' => true,
);
}
@ -200,10 +234,17 @@ class kolab_addressbook extends rcube_plugin
true, false);
}
else {
foreach ($this->folders as $c_folder) {
// convert to UTF8 and sort
$names = array();
foreach ($this->folders as $c_folder)
$names[$c_folder->name] = rcube_charset_convert($c_folder->name, 'UTF7-IMAP');
asort($names, SORT_LOCALE_STRING);
foreach ($names as $utf7name => $name) {
// create instance of rcube_contacts
$abook_id = rcube_kolab::folder_id($c_folder->name);
$abook = new rcube_kolab_contacts($c_folder->name);
$abook_id = rcube_kolab::folder_id($utf7name);
$abook = new rcube_kolab_contacts($utf7name);
$this->sources[$abook_id] = $abook;
}
}
@ -282,7 +323,7 @@ class kolab_addressbook extends rcube_plugin
// Load configuration
$this->load_config();
// Load localization and configuration
// Load localization
$this->add_texts('localization');
// Check that configuration is not disabled
@ -333,4 +374,180 @@ class kolab_addressbook extends rcube_plugin
return $args;
}
/**
* Handler for plugin actions
*/
public function book_actions()
{
$action = trim(get_input_value('_act', RCUBE_INPUT_GPC));
if ($action == 'create') {
$this->ui->book_edit();
}
else if ($action == 'edit') {
$this->ui->book_edit();
}
else if ($action == 'delete') {
$this->book_delete();
}
}
/**
* Handler for address book create/edit form submit
*/
public function book_save()
{
$folder = trim(get_input_value('_name', RCUBE_INPUT_POST, true, 'UTF7-IMAP'));
$oldfolder = trim(get_input_value('_oldname', RCUBE_INPUT_POST, true)); // UTF7-IMAP
$path = trim(get_input_value('_parent', RCUBE_INPUT_POST, true)); // UTF7-IMAP
$delimiter = $_SESSION['imap_delimiter'];
// sanity checks (from steps/settings/save_folder.inc)
if (!strlen($folder)) {
$error = rcube_label('cannotbeempty');
}
else if (strlen($name) > 128) {
$error = rcube_label('nametoolong');
}
else {
// these characters are problematic e.g. when used in LIST/LSUB
foreach (array($delimiter, '%', '*') as $char) {
if (strpos($name, $delimiter) !== false) {
$error = rcube_label('forbiddencharacter') . " ($char)";
break;
}
}
}
if (!$error) {
// @TODO: $options
$options = array();
if ($options['protected'] || $options['norename']) {
$folder = $oldfolder;
}
else if (strlen($path)) {
$folder = $path . $delimiter . $folder;
}
else {
// add namespace prefix (when needed)
$this->rc->imap_init();
$folder = $this->rc->imap->mod_mailbox($folder, 'in');
}
// update the folder name
if (strlen($oldfolder)) {
$type = 'update';
$plugin = $this->rc->plugins->exec_hook('addressbook_update', array(
'name' => $folder, 'oldname' => $oldfolder));
if (!$plugin['abort']) {
if ($oldfolder != $folder)
$result = rcube_kolab::folder_rename($oldfolder, $folder);
else
$result = true;
}
else {
$result = $plugin['result'];
}
}
// create new folder
else {
$type = 'create';
$plugin = $this->rc->plugins->exec_hook('addressbook_create', array('name' => $folder));
$folder = $plugin['name'];
if (!$plugin['abort']) {
$result = rcube_kolab::folder_create($folder, 'contact', false);
}
else {
$result = $plugin['result'];
}
}
}
if ($result) {
$kolab_folder = new rcube_kolab_contacts($folder);
// create display name for the folder (see self::address_sources())
if (strpos($folder, $delimiter)) {
$names = array();
foreach ($this->_list_sources() as $abook_id => $abook) {
$realname = $abook->get_realname();
// The list can be not updated yet, handle old folder name
if ($type == 'update' && $realname == $oldfolder) {
$abook = $kolab_folder;
$realname = $folder;
}
$name = $origname = $abook->get_name();
// find folder prefix to truncate
for ($i = count($names)-1; $i >= 0; $i--) {
if (strpos($name, $names[$i].' &raquo; ') === 0) {
$length = strlen($names[$i].' &raquo; ');
$prefix = substr($name, 0, $length);
$count = count(explode(' &raquo; ', $prefix));
$name = str_repeat('&nbsp;&nbsp;', $count-1) . '&raquo; ' . substr($name, $length);
break;
}
}
$names[] = $origname;
if ($realname == $folder) {
break;
}
}
}
else {
$name = $kolab_folder->get_name();
}
$this->rc->output->show_message('kolab_addressbook.book'.$type.'d', 'confirmation');
$this->rc->output->command('set_env', 'delimiter', $delimiter);
$this->rc->output->command('book_update', array(
'id' => rcube_kolab::folder_id($folder),
'name' => $name,
'readonly' => false,
'editable' => true,
'groups' => true,
'realname' => rcube_charset_convert($folder, 'UTF7-IMAP'), // IMAP folder name
'class_name' => $kolab_folder->get_namespace(),
'kolab' => true,
), rcube_kolab::folder_id($oldfolder));
$this->rc->output->send('iframe');
}
if (!$error)
$error = $plugin['message'] ? $plugin['message'] : 'kolab_addressbook.book'.$type.'error';
$this->rc->output->show_message($error, 'error');
// display the form again
$this->ui->book_edit();
}
/**
* Handler for address book delete action (AJAX)
*/
private function book_delete()
{
$folder = trim(get_input_value('_source', RCUBE_INPUT_GPC, true, 'UTF7-IMAP'));
if (rcube_kolab::folder_delete($folder)) {
$this->rc->output->show_message('kolab_addressbook.bookdeleted', 'confirmation');
$this->rc->output->set_env('pagecount', 0);
$this->rc->output->command('set_rowcount', rcmail_get_rowcount_text(new rcube_result_set()));
$this->rc->output->command('list_contacts_clear');
$this->rc->output->command('book_delete_done', rcube_kolab::folder_id($folder));
}
else {
$this->rc->output->show_message('kolab_addressbook.bookdeleteerror', 'error');
}
$this->rc->output->send();
}
}

View file

@ -0,0 +1,273 @@
<?php
/**
* Kolab address book UI
*
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* 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 kolab_addressbook_ui
{
private $plugin;
private $rc;
/**
* Class constructor
*
* @param kolab_addressbook $plugin Plugin object
*/
public function __construct($plugin)
{
$this->rc = rcmail::get_instance();
$this->plugin = $plugin;
$this->init_ui();
}
/**
* Adds folders management functionality to Addressbook UI
*/
private function init_ui()
{
if (!empty($this->rc->action) && !preg_match('/^plugin\.book/', $this->rc->action)) {
return;
}
// Include script
$this->plugin->include_script('kolab_addressbook.js');
if (empty($this->rc->action)) {
// Include stylesheet (for directorylist)
$this->plugin->include_stylesheet($this->plugin->local_skin_path().'/kolab_addressbook.css');
// Add actions on address books
$options = array('book-create', 'book-edit', 'book-delete');
$idx = 0;
foreach ($options as $command) {
$content = html::tag('li', $idx ? null : array('class' => 'separator_above'),
$this->plugin->api->output->button(array(
'label' => 'kolab_addressbook.'.str_replace('-', '', $command),
'domain' => $this->ID,
'classact' => 'active',
'command' => $command
)));
$this->plugin->api->add_content($content, 'groupoptions');
$idx++;
}
$this->rc->output->add_label('kolab_addressbook.bookdeleteconfirm',
'kolab_addressbook.bookdeleting');
}
// book create/edit form
else {
$this->rc->output->add_label('kolab_addressbook.nobooknamewarning',
'kolab_addressbook.booksaving');
}
}
/**
* Handler for address book create/edit action
*/
public function book_edit()
{
$this->rc->output->add_handler('bookdetails', array($this, 'book_form'));
$this->rc->output->send('kolab_addressbook.bookedit');
}
/**
* Handler for 'bookdetails' object returning form content for book create/edit
*
* @param array $attr Object attributes
*
* @return string HTML output
*/
public function book_form($attrib)
{
$action = trim(get_input_value('_act', RCUBE_INPUT_GPC));
$folder = trim(get_input_value('_source', RCUBE_INPUT_GPC, true)); // UTF8
$name = trim(get_input_value('_name', RCUBE_INPUT_GPC, true)); // UTF8
$old = trim(get_input_value('_oldname', RCUBE_INPUT_GPC, true)); // UTF7-IMAP
$path_imap = trim(get_input_value('_parent', RCUBE_INPUT_GPC, true)); // UTF7-IMAP
$hidden_fields[] = array('name' => '_source', 'value' => $folder);
$folder = rcube_charset_convert($folder, RCMAIL_CHARSET, 'UTF7-IMAP');
$delim = $_SESSION['imap_delimiter'];
$form = array();
if ($this->rc->action == 'plugin.book-save') {
// save error
$path_imap = $folder;
$hidden_fields[] = array('name' => '_oldname', 'value' => $old);
if (strlen($old)) {
$this->rc->imap_connect();
$options = $this->rc->imap->mailbox_info($old);
}
}
else if ($action == 'edit') {
$path_imap = explode($delim, $folder);
$name = rcube_charset_convert(array_pop($path_imap), 'UTF7-IMAP');
$path_imap = implode($path_imap, $delim);
$this->rc->imap_connect();
$options = $this->rc->imap->mailbox_info($folder);
$hidden_fields[] = array('name' => '_oldname', 'value' => $folder);
}
else {
$path_imap = $folder;
$name = '';
}
// General tab
$form['props'] = array(
'name' => $this->rc->gettext('properties'),
);
$foldername = new html_inputfield(array('name' => '_name', 'id' => '_name', 'size' => 30));
$foldername = $foldername->show($name);
$form['props']['fieldsets']['location'] = array(
'name' => $this->rc->gettext('location'),
'content' => array(
'name' => array(
'label' => $this->plugin->gettext('bookname'),
'value' => $foldername,
),
),
);
if (strlen($path_imap)) {
$path = rcube_charset_convert($path_imap, 'UTF7-IMAP');
// @TODO: $options
if (!empty($options) && ($options['norename'] || $options['namespace'] != 'personal')) {
// prevent user from moving folder
$hidden_fields[] = array('name' => '_parent', 'value' => $path_imap);
}
else {
$radio1 = new html_radiobutton(array('name' => '_parent', 'value' => ''));
$radio2 = new html_radiobutton(array('name' => '_parent', 'value' => $path_imap));
$html_path = str_replace($delim, ' &raquo; ', $path);
$folderpath = $radio1->show($path_imap) . Q(rcube_label('none')) . '&nbsp;'
.$radio2->show($path_imap) . Q($html_path);
$form['props']['fieldsets']['location']['content']['path'] = array(
'label' => $this->plugin->gettext('parentbook'),
'value' => $folderpath,
);
}
}
// Allow plugins to modify address book form content (e.g. with ACL form)
$plugin = $this->rc->plugins->exec_hook('addressbook_form',
array('form' => $form, 'options' => $options, 'name' => $folder));
$form = $plugin['form'];
// Set form tags and hidden fields
list($form_start, $form_end) = $this->get_form_tags($attrib, 'plugin.book-save', null, $hidden_fields);
unset($attrib['form']);
// return the complete edit form as table
$out = "$form_start\n";
// Create form output
foreach ($form as $tab) {
if (!empty($tab['fieldsets']) && is_array($tab['fieldsets'])) {
$content = '';
foreach ($tab['fieldsets'] as $fieldset) {
$subcontent = $this->get_form_part($fieldset);
if ($subcontent) {
$content .= html::tag('fieldset', null, html::tag('legend', null, Q($fieldset['name'])) . $subcontent) ."\n";
}
}
}
else {
$content = $this->get_form_part($tab);
}
if ($content) {
$out .= html::tag('fieldset', null, html::tag('legend', null, Q($tab['name'])) . $content) ."\n";
}
}
$out .= "\n$form_end";
return $out;
}
private function get_form_part($form)
{
$content = '';
if (is_array($form['content']) && !empty($form['content'])) {
$table = new html_table(array('cols' => 2));
foreach ($form['content'] as $col => $colprop) {
$colprop['id'] = '_'.$col;
$label = !empty($colprop['label']) ? $colprop['label'] : rcube_label($col);
$table->add('title', sprintf('<label for="%s">%s</label>', $colprop['id'], Q($label)));
$table->add(null, $colprop['value']);
}
$content = $table->show();
}
else {
$content = $form['content'];
}
return $content;
}
private function get_form_tags($attrib, $action, $id = null, $hidden = null)
{
$form_start = $form_end = '';
$request_key = $action . (isset($id) ? '.'.$id : '');
$form_start = $this->rc->output->request_form(array(
'name' => 'form',
'method' => 'post',
'task' => $this->rc->task,
'action' => $action,
'request' => $request_key,
'noclose' => true,
) + $attrib);
if (is_array($hidden)) {
foreach ($hidden as $field) {
$hiddenfield = new html_hiddenfield($field);
$form_start .= $hiddenfield->show();
}
}
$form_end = !strlen($attrib['form']) ? '</form>' : '';
$EDIT_FORM = !empty($attrib['form']) ? $attrib['form'] : 'form';
$this->rc->output->add_gui_object('editform', $EDIT_FORM);
return array($form_start, $form_end);
}
}

View file

@ -13,8 +13,9 @@
class rcube_kolab_contacts extends rcube_addressbook
{
public $primary_key = 'ID';
public $readonly = false;
public $groups = true;
public $readonly = true;
public $editable = false;
public $groups = false;
public $coltypes = array(
'name' => array('limit' => 1),
'firstname' => array('limit' => 1),
@ -55,9 +56,6 @@ class rcube_kolab_contacts extends rcube_addressbook
);
private $gid;
private $imap;
private $kolab;
private $folder;
private $contactstorage;
private $liststorage;
private $contacts;
@ -66,6 +64,7 @@ class rcube_kolab_contacts extends rcube_addressbook
private $id2uid;
private $filter;
private $result;
private $namespace;
private $imap_folder = 'INBOX/Contacts';
private $gender_map = array(0 => 'male', 1 => 'female');
private $phonetypemap = array('home' => 'home1', 'work' => 'business1', 'work2' => 'business2', 'workfax' => 'businessfax');
@ -120,6 +119,25 @@ class rcube_kolab_contacts extends rcube_addressbook
$this->liststorage = rcube_kolab::get_storage($this->imap_folder, 'distributionlist');
$this->ready = !PEAR::isError($this->contactstorage) && !PEAR::isError($this->liststorage);
// Set readonly and editable flags according to folder permissions
if ($this->ready) {
if ($this->get_owner() == $_SESSION['username']) {
$this->editable = true;
$this->readonly = false;
}
else {
$acl = $this->contactstorage->_folder->getACL();
$acl = $acl[$_SESSION['username']];
if (strpos($acl, 'i') !== false)
$this->readonly = false;
if (strpos($acl, 'a') !== false || strpos($acl, 'x') !== false)
$this->editable = true;
}
if (!$this->readonly)
$this->groups = true;
}
}
@ -130,9 +148,113 @@ class rcube_kolab_contacts extends rcube_addressbook
*/
public function get_name()
{
$folder = rcube_charset_convert($this->imap_folder, 'UTF7-IMAP');
// @TODO: use namespace prefixes
return strtr(preg_replace('!^(INBOX|user)/!i', '', $folder), '/', ':');
$folder = $this->imap_folder;
$namespace = $_SESSION['imap_namespace']; // from rcube_imap class
$found = false;
if (!empty($namespace['shared'])) {
foreach ($namespace['shared'] as $ns) {
if (strlen($ns[0]) && strpos($folder, $ns[0]) === 0) {
$prefix = '';
$folder = substr($folder, strlen($ns[0]));
$delim = $ns[1];
$found = true;
$this->namespace = 'shared';
break;
}
}
}
if (!$found && !empty($namespace['other'])) {
foreach ($namespace['other'] as $ns) {
if (strlen($ns[0]) && strpos($folder, $ns[0]) === 0) {
// remove namespace prefix
$folder = substr($folder, strlen($ns[0]));
$delim = $ns[1];
// get username
$pos = strpos($folder, $delim);
$prefix = '('.substr($folder, 0, $pos).') ';
$found = true;
$this->namespace = 'other';
break;
}
}
}
if (!$found && !empty($namespace['personal'])) {
foreach ($namespace['personal'] as $ns) {
if (strlen($ns[0]) && strpos($folder, $ns[0]) === 0) {
// remove namespace prefix
$folder = substr($folder, strlen($ns[0]));
$prefix = '';
$delim = $ns[1];
$found = true;
$this->namespace = 'personal';
break;
}
}
}
if (empty($delim))
$delim = $_SESSION['imap_delimiter']; // from rcube_imap class
$folder = rcube_charset_convert($folder, 'UTF7-IMAP');
$folder = str_replace($delim, ' &raquo; ', $folder);
if ($prefix)
$folder = $prefix . ' ' . $folder;
return $folder;
}
/**
* Getter for the IMAP folder name
*
* @return string Name of the IMAP folder
*/
public function get_realname()
{
return $this->imap_folder;
}
/**
* Getter for the IMAP folder owner
*
* @return string Name of the folder owner
*/
public function get_owner()
{
return $this->contactstorage->_folder->getOwner();
}
/**
* Getter for the name of the namespace to which the IMAP folder belongs
*
* @return string Name of the namespace (personal, other, shared)
*/
public function get_namespace()
{
if ($this->namespace) {
return $this->namespace;
}
$folder = $this->imap_folder;
$namespace = $_SESSION['imap_namespace']; // from rcube_imap class
if (!empty($namespace)) {
foreach ($namespace as $nsname => $nsvalue) {
if (in_array($nsname, array('personal', 'other', 'shared')) && !empty($nsvalue)) {
foreach ($nsvalue as $ns) {
if (strlen($ns[0]) && strpos($folder, $ns[0]) === 0) {
return $this->namespace = $nsname;
}
}
}
}
}
return $this->namespace = 'personal';
}
@ -770,6 +892,10 @@ class rcube_kolab_contacts extends rcube_addressbook
// read contacts
$this->contacts = $this->id2uid = array();
foreach ((array)$this->contactstorage->getObjects() as $record) {
// Because of a bug, sometimes group records are returned
if ($record['__type'] == 'Group')
continue;
$contact = $this->_to_rcube_contact($record);
$id = $contact['ID'];
$this->contacts[$id] = $contact;

View file

@ -9,10 +9,28 @@ $labels['pgppublickey'] = 'PGP publickey';
$labels['freebusyurl'] = 'Free-busy URL';
$labels['settings'] = 'Settings';
$labels['bookcreate'] = 'Create address book';
$labels['bookedit'] = 'Edit address book';
$labels['bookdelete'] = 'Delete address book';
$labels['bookproperties'] = 'Address book properties';
$labels['bookname'] = 'Book name';
$labels['parentbook'] = 'Superior book';
$labels['addressbookprio'] = 'Address book(s) selection/behaviour';
$labels['personalfirst'] = 'Personal address book(s) first';
$labels['globalfirst'] = 'Global address book(s) first';
$labels['personalonly'] = 'Personal address book(s) only';
$labels['globalonly'] = 'Global address book(s) only';
$messages['bookdeleteconfirm'] = 'Do you really want to delete the selected address book and all contacts in it?';
$messages['bookdeleting'] = 'Deleting address book...';
$messages['booksaving'] = 'Saving address book...';
$messages['bookdeleted'] = 'Address book deleted successfully';
$messages['bookupdated'] = 'Address book updated successfully';
$messages['bookcreated'] = 'Address book created successfully';
$messages['bookdeleteerror'] = 'An error occured while deleting address book';
$messages['bookupdateerror'] = 'An error occured while updating address book';
$messages['bookcreateerror'] = 'An error occured while creating address book';
$messages['nobooknamewarning'] = 'Please, enter address book name';
?>

View file

@ -15,4 +15,22 @@ $labels['globalfirst'] = 'Najpierw globalne książki adresowe';
$labels['personalonly'] = 'Tylko osobiste książki adresowe';
$labels['globalonly'] = 'Tylko globalne książki adresowe';
$labels['bookcreate'] = 'Utwórz książkę';
$labels['bookedit'] = 'Edytuj książkę';
$labels['bookdelete'] = 'Usuń książkę';
$labels['bookproperties'] = 'Właściwości książki adresowej';
$labels['bookname'] = 'Nazwa książki';
$labels['parentbook'] = 'Książka nadrzędna';
$messages['bookdeleteconfirm'] = 'Czy na pewno chcesz usunąć wybraną książkę i wszystkie kontakty w niej zapisane?';
$messages['bookdeleting'] = 'Usuwanie książki adresowej...';
$messages['booksaving'] = 'Zapisywanie książki adresowej...';
$messages['bookdeleted'] = 'Książka adresowa została usunięta';
$messages['bookupdated'] = 'Książka adresowa została zaktualizowana';
$messages['bookcreated'] = 'Książka adresowa została utworzona';
$messages['bookdeleteerror'] = 'Wystąpił błąd podczas usuwania książki adresowej';
$messages['bookupdateerror'] = 'Wystąpił błąd podczas zmiany książki adresowej';
$messages['bookcreateerror'] = 'Wystąpił błąd podczas tworzenia książki adresowej';
$messages['nobooknamewarning'] = 'Proszę podać nazwę książki adresowej';
?>

View file

@ -0,0 +1,35 @@
#directorylist li.addressbook.readonly
{
/* don't use 'background' to not reset background color */
background-image: url(kolab_folders.png);
background-position: 5px 0px;
background-repeat: no-repeat;
}
#directorylist li.addressbook.shared
{
background-image: url(kolab_folders.png);
background-position: 5px -54px;
background-repeat: no-repeat;
}
#directorylist li.addressbook.shared.readonly
{
background-image: url(kolab_folders.png);
background-position: 5px -72px;
background-repeat: no-repeat;
}
#directorylist li.addressbook.other
{
background-image: url(kolab_folders.png);
background-position: 5px -18px;
background-repeat: no-repeat;
}
#directorylist li.addressbook.other.readonly
{
background-image: url(kolab_folders.png);
background-position: 5px -36px;
background-repeat: no-repeat;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -0,0 +1,27 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title><roundcube:object name="pagetitle" /></title>
<roundcube:include file="/includes/links.html" />
<link rel="stylesheet" type="text/css" href="/settings.css" />
<script type="text/javascript" src="/functions.js"></script>
</head>
<body class="iframe">
<div id="folder-title" class="boxtitle"><roundcube:label name="kolab_addressbook.bookproperties" /></div>
<div id="folder-details" class="boxcontent">
<roundcube:object name="bookdetails" />
<p>
<!--
<roundcube:if condition="!strlen(request:_source)" />
<input type="button" value="<roundcube:label name="cancel" />" class="button" onclick="history.back()" />&nbsp;
<roundcube:endif />
-->
<roundcube:button command="book-save" type="input" class="button mainaction" label="save" />
</p>
</div>
<script type="text/javascript">rcube_init_tabs('folder-details > form')</script>
</body>
</html>

View file

@ -21,7 +21,7 @@ pear install Net_SMTP
pear install Mail_mimeDecode
pear install Auth_SASL
pear install Date
pear install HTTP_Request
Configuration
-------------

View file

@ -137,7 +137,7 @@ class kolab_folders extends rcube_plugin
// Add type-based style for table rows
// See kolab_folders::folder_class_name()
for ($i=0, $cnt=$table->size(); $i<$cnt; $i++) {
for ($i=1, $cnt=$table->size(); $i<=$cnt; $i++) {
$attrib = $table->get_row_attribs($i);
$folder = $attrib['foldername']; // UTF7-IMAP
$data = $folderdata[$folder];