Search in LDAP and collect accessible folders (#3041)
- Add LDAP user search capabilities to kolab_storage class (using kolab_auth plugin classes) - Introduce virtual 'user' folder objects and add methods to list them - New 'user calendar' class in calendar (kolab driver) - Render folder search results as hierarchical list
This commit is contained in:
parent
8a47c676d5
commit
701c3391fe
11 changed files with 726 additions and 109 deletions
|
@ -2739,7 +2739,9 @@ function rcube_calendar_ui(settings)
|
|||
selectable: true,
|
||||
save_state: true,
|
||||
searchbox: '#calendarlistsearch',
|
||||
search_action: 'calendar/calendar'
|
||||
search_action: 'calendar/calendar',
|
||||
search_sources: [ 'folders', 'users' ],
|
||||
search_title: rcmail.gettext('calsearchresults','calendar')
|
||||
});
|
||||
calendars_list.addEventListener('select', function(node) {
|
||||
me.select_calendar(node.id);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
* @author Aleksander Machniak <machniak@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com>
|
||||
* Copyright (C) 2012-2014, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
|
@ -35,11 +35,30 @@ class kolab_calendar
|
|||
public $storage;
|
||||
public $name;
|
||||
|
||||
private $cal;
|
||||
private $events = array();
|
||||
private $imap_folder = 'INBOX/Calendar';
|
||||
private $search_fields = array('title', 'description', 'location', 'attendees');
|
||||
protected $cal;
|
||||
protected $events = array();
|
||||
protected $imap_folder = 'INBOX/Calendar';
|
||||
protected $search_fields = array('title', 'description', 'location', 'attendees');
|
||||
|
||||
/**
|
||||
* Factory method to instantiate a kolab_calendar object
|
||||
*
|
||||
* @param string Calendar ID (encoded IMAP folder name)
|
||||
* @param object calendar plugin object
|
||||
* @return object kolab_calendar instance
|
||||
*/
|
||||
public static function factory($id, $calendar)
|
||||
{
|
||||
$imap = $calendar->rc->get_storage();
|
||||
$imap_folder = kolab_storage::id_decode($id);
|
||||
$info = $imap->folder_info($imap_folder, true);
|
||||
if (empty($info) || $info['noselect'] || kolab_storage::folder_type($imap_folder) != 'event') {
|
||||
return new kolab_user_calendar($imap_folder, $calendar);
|
||||
}
|
||||
else {
|
||||
return new kolab_calendar($imap_folder, $calendar);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
|
@ -177,14 +196,6 @@ class kolab_calendar
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the corresponding kolab_storage_folder instance
|
||||
*/
|
||||
public function get_folder()
|
||||
{
|
||||
return $this->storage;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for a single event object
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
*/
|
||||
|
||||
require_once(dirname(__FILE__) . '/kolab_calendar.php');
|
||||
require_once(dirname(__FILE__) . '/kolab_user_calendar.php');
|
||||
|
||||
class kolab_driver extends calendar_driver
|
||||
{
|
||||
|
@ -78,14 +79,20 @@ class kolab_driver extends calendar_driver
|
|||
return $this->calendars;
|
||||
|
||||
// get all folders that have "event" type, sorted by namespace/name
|
||||
$folders = kolab_storage::sort_folders(kolab_storage::get_folders('event'));
|
||||
$folders = kolab_storage::sort_folders(kolab_storage::get_folders('event') + kolab_storage::get_user_folders(true));
|
||||
$this->calendars = array();
|
||||
|
||||
foreach ($folders as $folder) {
|
||||
$calendar = new kolab_calendar($folder->name, $this->cal);
|
||||
$this->calendars[$calendar->id] = $calendar;
|
||||
if (!$calendar->readonly)
|
||||
$this->has_writeable = true;
|
||||
if ($folder instanceof kolab_storage_user_folder)
|
||||
$calendar = new kolab_user_calendar($folder->name, $this->cal);
|
||||
else
|
||||
$calendar = new kolab_calendar($folder->name, $this->cal);
|
||||
|
||||
if ($calendar->ready) {
|
||||
$this->calendars[$calendar->id] = $calendar;
|
||||
if (!$calendar->readonly)
|
||||
$this->has_writeable = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->calendars;
|
||||
|
@ -114,18 +121,32 @@ class kolab_driver extends calendar_driver
|
|||
$calendars = $names = array();
|
||||
|
||||
// include virtual folders for a full folder tree
|
||||
if (!$active && !$personal && !$this->rc->output->ajax_call && in_array($this->rc->action, array('index','')))
|
||||
if (!is_null($tree))
|
||||
$folders = kolab_storage::folder_hierarchy($folders, $tree);
|
||||
|
||||
foreach ($folders as $id => $cal) {
|
||||
$fullname = $cal->get_name();
|
||||
$listname = kolab_storage::folder_displayname($fullname, $names);
|
||||
$listname = $cal->get_foldername();
|
||||
$imap_path = explode('/', $cal->name);
|
||||
$topname = array_pop($imap_path);
|
||||
$parent_id = kolab_storage::folder_id(join('/', $imap_path), true);
|
||||
|
||||
// special handling for virtual folders
|
||||
if ($cal->virtual) {
|
||||
// special handling for user or virtual folders
|
||||
if ($cal instanceof kolab_storage_user_folder) {
|
||||
$calendars[$cal->id] = array(
|
||||
'id' => $cal->id,
|
||||
'name' => kolab_storage::object_name($fullname),
|
||||
'listname' => $listname,
|
||||
'editname' => $cal->get_foldername(),
|
||||
'color' => $cal->get_color(),
|
||||
'active' => $cal->is_active(),
|
||||
'owner' => $cal->get_owner(),
|
||||
'virtual' => false,
|
||||
'readonly' => true,
|
||||
'class_name' => 'user',
|
||||
);
|
||||
}
|
||||
else if ($cal->virtual) {
|
||||
$calendars[$cal->id] = array(
|
||||
'id' => $cal->id,
|
||||
'name' => $fullname,
|
||||
|
@ -230,8 +251,8 @@ class kolab_driver extends calendar_driver
|
|||
{
|
||||
// create calendar object if necesary
|
||||
if (!$this->calendars[$id] && $id !== self::BIRTHDAY_CALENDAR_ID) {
|
||||
$foldername = kolab_storage::id_decode($id);
|
||||
$calendar = new kolab_calendar($foldername, $this->cal);
|
||||
$calendar = kolab_calendar::factory($id, $this->cal);
|
||||
console($id, $calendar->id, $calendar->ready);
|
||||
if ($calendar->ready)
|
||||
$this->calendars[$calendar->id] = $calendar;
|
||||
}
|
||||
|
@ -389,8 +410,20 @@ class kolab_driver extends calendar_driver
|
|||
$this->calendars[$calendar->id] = $calendar;
|
||||
}
|
||||
}
|
||||
// find other user's virtual calendars
|
||||
else if ($source == 'users') {
|
||||
// TODO: implement this
|
||||
foreach (kolab_storage::search_users($query, 0) as $user) {
|
||||
$calendar = new kolab_user_calendar($user, $this->cal);
|
||||
$this->calendars[$calendar->id] = $calendar;
|
||||
|
||||
// search for calendar folders shared by this user
|
||||
foreach (kolab_storage::list_user_folders($user, 'event', false) as $foldername) {
|
||||
if (1 || !kolab_storage::folder_is_subscribed($foldername, true)) {
|
||||
$cal = new kolab_calendar($foldername, $this->cal);
|
||||
$this->calendars[$cal->id] = $cal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// don't list the birthday calendar
|
||||
|
|
219
plugins/calendar/drivers/kolab/kolab_user_calendar.php
Normal file
219
plugins/calendar/drivers/kolab/kolab_user_calendar.php
Normal file
|
@ -0,0 +1,219 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Kolab calendar storage class simulating a virtual user calendar
|
||||
*
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2014, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
class kolab_user_calendar extends kolab_calendar
|
||||
{
|
||||
public $id = 'unknown';
|
||||
public $ready = false;
|
||||
public $readonly = true;
|
||||
public $attachments = false;
|
||||
public $name;
|
||||
|
||||
protected $userdata = array();
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public function __construct($user_or_folder, $calendar)
|
||||
{
|
||||
$this->cal = $calendar;
|
||||
|
||||
// full user record is provided
|
||||
if (is_array($user_or_folder)) {
|
||||
$this->userdata = $user_or_folder;
|
||||
$this->storage = new kolab_storage_user_folder($this->userdata['kolabtargetfolder'], '', $this->userdata);
|
||||
}
|
||||
else { // get user record from LDAP
|
||||
$this->storage = new kolab_storage_user_folder($user_or_folder);
|
||||
$this->userdata = $this->storage->ldaprec;
|
||||
}
|
||||
|
||||
$this->ready = !empty($this->userdata['kolabtargetfolder']);
|
||||
|
||||
if ($this->ready) {
|
||||
// ID is derrived from the user's kolabtargetfolder attribute
|
||||
$this->id = kolab_storage::folder_id($this->userdata['kolabtargetfolder'], true);
|
||||
$this->imap_folder = $this->userdata['kolabtargetfolder'];
|
||||
$this->name = $this->storage->get_name();
|
||||
|
||||
// user-specific alarms settings win
|
||||
$prefs = $this->cal->rc->config->get('kolab_calendars', array());
|
||||
if (isset($prefs[$this->id]['showalarms']))
|
||||
$this->alarms = $prefs[$this->id]['showalarms'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for a nice and human readable name for this calendar
|
||||
*
|
||||
* @return string Name of this calendar
|
||||
*/
|
||||
public function get_name()
|
||||
{
|
||||
return $this->userdata['name'] ?: $this->userdata['mail'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for the IMAP folder owner
|
||||
*
|
||||
* @return string Name of the folder owner
|
||||
*/
|
||||
public function get_owner()
|
||||
{
|
||||
return $this->userdata['mail'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
return 'user';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for the top-end calendar folder name (not the entire path)
|
||||
*
|
||||
* @return string Name of this calendar
|
||||
*/
|
||||
public function get_foldername()
|
||||
{
|
||||
return $this->get_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return color to display this calendar
|
||||
*/
|
||||
public function get_color()
|
||||
{
|
||||
// calendar color is stored in local user prefs
|
||||
$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';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose an URL for CalDAV access to this calendar (if configured)
|
||||
*/
|
||||
public function get_caldav_url()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for a single event object
|
||||
*/
|
||||
public function get_event($id)
|
||||
{
|
||||
// TODO: implement this
|
||||
return $this->events[$id];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param integer Event's new start (unix timestamp)
|
||||
* @param integer Event's new end (unix timestamp)
|
||||
* @param string Search query (optional)
|
||||
* @param boolean Include virtual events (optional)
|
||||
* @param array Additional parameters to query storage
|
||||
* @return array A list of event records
|
||||
*/
|
||||
public function list_events($start, $end, $search = null, $virtual = 1, $query = array())
|
||||
{
|
||||
// TODO: implement this
|
||||
console('kolab_user_calendar::list_events()');
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new event record
|
||||
*
|
||||
* @see calendar_driver::new_event()
|
||||
*
|
||||
* @return mixed The created record ID on success, False on error
|
||||
*/
|
||||
public function insert_event($event)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a specific event record
|
||||
*
|
||||
* @see calendar_driver::new_event()
|
||||
* @return boolean True on success, False on error
|
||||
*/
|
||||
|
||||
public function update_event($event, $exception_id = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an event record
|
||||
*
|
||||
* @see calendar_driver::remove_event()
|
||||
* @return boolean True on success, False on error
|
||||
*/
|
||||
public function delete_event($event, $force = true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore deleted event record
|
||||
*
|
||||
* @see calendar_driver::undelete_event()
|
||||
* @return boolean True on success, False on error
|
||||
*/
|
||||
public function restore_event($event)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert from Kolab_Format to internal representation
|
||||
*/
|
||||
private function _to_rcube_event($record)
|
||||
{
|
||||
$record['id'] = $record['uid'];
|
||||
$record['calendar'] = $this->id;
|
||||
|
||||
// TODO: implement this
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
}
|
|
@ -205,6 +205,7 @@ class calendar_ui
|
|||
{
|
||||
$html = '';
|
||||
$jsenv = array();
|
||||
$tree = true;
|
||||
$calendars = $this->cal->driver->list_calendars(false, false, $tree);
|
||||
|
||||
// walk folder tree
|
||||
|
@ -273,19 +274,22 @@ class calendar_ui
|
|||
*/
|
||||
public function calendar_list_item($id, $prop, &$jsenv)
|
||||
{
|
||||
unset($prop['user_id']);
|
||||
$prop['alarms'] = $this->cal->driver->alarms;
|
||||
$prop['attendees'] = $this->cal->driver->attendees;
|
||||
$prop['freebusy'] = $this->cal->driver->freebusy;
|
||||
$prop['attachments'] = $this->cal->driver->attachments;
|
||||
$prop['undelete'] = $this->cal->driver->undelete;
|
||||
$prop['feedurl'] = $this->cal->get_url(array('_cal' => $this->cal->ical_feed_hash($id) . '.ics', 'action' => 'feed'));
|
||||
// enrich calendar properties with settings from the driver
|
||||
if (!$prop['virtual']) {
|
||||
unset($prop['user_id']);
|
||||
$prop['alarms'] = $this->cal->driver->alarms;
|
||||
$prop['attendees'] = $this->cal->driver->attendees;
|
||||
$prop['freebusy'] = $this->cal->driver->freebusy;
|
||||
$prop['attachments'] = $this->cal->driver->attachments;
|
||||
$prop['undelete'] = $this->cal->driver->undelete;
|
||||
$prop['feedurl'] = $this->cal->get_url(array('_cal' => $this->cal->ical_feed_hash($id) . '.ics', 'action' => 'feed'));
|
||||
|
||||
if (!$prop['virtual'])
|
||||
$jsenv[$id] = $prop;
|
||||
}
|
||||
|
||||
$class = 'calendar cal-' . asciiwords($id, true);
|
||||
$title = $prop['name'] != $prop['listname'] ? html_entity_decode($prop['name'], ENT_COMPAT, RCMAIL_CHARSET) : '';
|
||||
$title = $prop['name'] != $prop['listname'] || strlen($prop['name']) > 25 ?
|
||||
html_entity_decode($prop['name'], ENT_COMPAT, RCMAIL_CHARSET) : '';
|
||||
$is_collapsed = false; // TODO: determine this somehow?
|
||||
|
||||
if ($prop['virtual'])
|
||||
|
|
|
@ -269,12 +269,12 @@ pre {
|
|||
#calendars .treelist div.readonly span.calname {
|
||||
background-position: right -20px;
|
||||
}
|
||||
/*
|
||||
#calendars .treelist div.other span.calname {
|
||||
|
||||
#calendars .treelist div.user span.calname {
|
||||
background-position: right -38px;
|
||||
}
|
||||
|
||||
#calendars .treelist div.other.readonly span.calname {
|
||||
/*
|
||||
#calendars .treelist div.user.readonly span.calname {
|
||||
background-position: right -56px;
|
||||
}
|
||||
|
||||
|
|
|
@ -213,7 +213,6 @@ class kolab_auth_ldap extends rcube_ldap_generic
|
|||
* 0 - partial (*abc*),
|
||||
* 1 - strict (=),
|
||||
* 2 - prefix (abc*)
|
||||
* @param boolean $select True if results are requested, False if count only
|
||||
* @param array $required List of fields that cannot be empty
|
||||
* @param int $limit Number of records
|
||||
*
|
||||
|
|
|
@ -46,7 +46,7 @@ function kolab_folderlist(node, p)
|
|||
// create treelist widget to present the search results
|
||||
if (!search_results_widget) {
|
||||
search_results_container = $('<div class="searchresults"></div>')
|
||||
.html('<h2 class="boxtitle">' + rcmail.gettext('calsearchresults','calendar') + '</h2>')
|
||||
.html(p.search_title ? '<h2 class="boxtitle">' + p.search_title + '</h2>' : '')
|
||||
.insertAfter(me.container);
|
||||
|
||||
search_results_widget = new rcube_treelist_widget('<ul class="treelist listing"></ul>', {
|
||||
|
@ -63,36 +63,64 @@ function kolab_folderlist(node, p)
|
|||
|
||||
var li = $(this).closest('li'),
|
||||
id = li.attr('id').replace(new RegExp('^'+p.id_prefix), ''),
|
||||
node = search_results_widget.get_node(id),
|
||||
prop = search_results[id],
|
||||
parent_id = prop.parent || null;
|
||||
parent_id = prop.parent || null,
|
||||
has_children = node.children && node.children.length,
|
||||
dom_node = has_children ? li.children().first().clone(true, true) : li.children().first();
|
||||
|
||||
// find parent node and insert at the right place
|
||||
if (parent_id && $('#' + p.id_prefix + parent_id, me.container).length) {
|
||||
prop.listname = prop.editname;
|
||||
li.children().first().children('span,a').first().html(Q(prop.listname));
|
||||
dom_node.children('span,a').first().html(Q(prop.listname));
|
||||
}
|
||||
|
||||
// move this result item to the main list widget
|
||||
me.insert({
|
||||
id: id,
|
||||
classes: [],
|
||||
html: li.children().first()
|
||||
}, parent_id, parent_id ? true : false);
|
||||
// TODO: copy parent tree too
|
||||
|
||||
// replace virtual node with a real one
|
||||
if (me.get_node(id)) {
|
||||
$(me.get_item(id, true)).children().first()
|
||||
.replaceWith(dom_node)
|
||||
.removeClass('virtual');
|
||||
}
|
||||
else {
|
||||
// move this result item to the main list widget
|
||||
me.insert({
|
||||
id: id,
|
||||
classes: [],
|
||||
virtual: prop.virtual,
|
||||
html: dom_node,
|
||||
}, parent_id, parent_id ? true : false);
|
||||
}
|
||||
|
||||
delete prop.html;
|
||||
me.triggerEvent('insert-item', { id: id, data: prop, item: li });
|
||||
li.remove();
|
||||
|
||||
if (has_children) {
|
||||
li.find('input[type=checkbox]').first().prop('disabled', true).get(0).checked = true;
|
||||
}
|
||||
else {
|
||||
li.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// add results to list
|
||||
for (var prop, i=0; i < results.length; i++) {
|
||||
for (var prop, item, i=0; i < results.length; i++) {
|
||||
prop = results[i];
|
||||
item = $(prop.html);
|
||||
search_results[prop.id] = prop;
|
||||
$('<li>')
|
||||
.attr('id', p.id_prefix + prop.id)
|
||||
.html(prop.html)
|
||||
.appendTo(search_results_widget.container);
|
||||
search_results_widget.insert({
|
||||
id: prop.id,
|
||||
classes: prop.class_name ? String(prop.class_name).split(' ') : [],
|
||||
html: item,
|
||||
collapsed: true
|
||||
}, prop.parent);
|
||||
|
||||
// disable checkbox if item already exists in main list
|
||||
if (me.get_node(prop.id) && !me.get_node(prop.id).virtual) {
|
||||
item.find('input[type=checkbox]').first().prop('disabled', true).get(0).checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
search_results_container.show();
|
||||
|
@ -110,7 +138,7 @@ function kolab_folderlist(node, p)
|
|||
|
||||
// send search request(s) to server
|
||||
if (search.query && search.execute) {
|
||||
var sources = [ 'folders' /*, 'users'*/ ];
|
||||
var sources = p.search_sources || [ 'folders' ];
|
||||
var reqid = rcmail.multi_thread_http_request({
|
||||
items: sources,
|
||||
threads: rcmail.env.autocomplete_threads || 1,
|
||||
|
|
|
@ -44,6 +44,7 @@ class kolab_storage
|
|||
private static $states;
|
||||
private static $config;
|
||||
private static $imap;
|
||||
private static $ldap;
|
||||
|
||||
// Default folder names
|
||||
private static $default_folders = array(
|
||||
|
@ -101,6 +102,40 @@ class kolab_storage
|
|||
return self::$ready;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes LDAP object to resolve Kolab users
|
||||
*/
|
||||
public static function ldap()
|
||||
{
|
||||
if (self::$ldap) {
|
||||
return self::$ldap;
|
||||
}
|
||||
|
||||
$rcmail = rcube::get_instance();
|
||||
$config = $rcmail->config->get('kolab_users_directory', $rcmail->config->get('kolab_auth_addressbook'));
|
||||
|
||||
if (!is_array($config)) {
|
||||
$ldap_config = (array)$rcmail->config->get('ldap_public');
|
||||
$config = $ldap_config[$config];
|
||||
}
|
||||
|
||||
if (empty($config)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// overwrite filter option
|
||||
if ($filter = $rcmail->config->get('kolab_users_filter')) {
|
||||
$rcmail->config->set('kolab_auth_filter', $filter);
|
||||
}
|
||||
|
||||
// re-use the LDAP wrapper class from kolab_auth plugin
|
||||
require_once rtrim(RCUBE_PLUGINS_DIR, '/') . '/kolab_auth/kolab_auth_ldap.php';
|
||||
|
||||
self::$ldap = new kolab_auth_ldap($config);
|
||||
|
||||
return self::$ldap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a list of storage folders for the given data type
|
||||
|
@ -490,7 +525,7 @@ class kolab_storage
|
|||
$folder = substr($folder, $pos+1);
|
||||
}
|
||||
else {
|
||||
$prefix = $folder;
|
||||
$prefix = '('.$folder.')';
|
||||
$folder = '';
|
||||
}
|
||||
|
||||
|
@ -811,7 +846,7 @@ class kolab_storage
|
|||
|
||||
// $folders is a result of get_folders() we can assume folders were already sorted
|
||||
foreach (array_keys($nsnames) as $ns) {
|
||||
// asort($nsnames[$ns], SORT_LOCALE_STRING);
|
||||
asort($nsnames[$ns], SORT_LOCALE_STRING);
|
||||
foreach (array_keys($nsnames[$ns]) as $utf7name) {
|
||||
$out[] = $folders[$utf7name];
|
||||
}
|
||||
|
@ -829,13 +864,18 @@ class kolab_storage
|
|||
*
|
||||
* @return array Flat folders list
|
||||
*/
|
||||
public static function folder_hierarchy($folders, &$tree)
|
||||
public static function folder_hierarchy($folders, &$tree = null)
|
||||
{
|
||||
$_folders = array();
|
||||
$delim = rcube::get_instance()->get_storage()->get_hierarchy_delimiter();
|
||||
$tree = new virtual_kolab_storage_folder('', '<root>', ''); // create tree root
|
||||
$delim = self::$imap->get_hierarchy_delimiter();
|
||||
$other_ns = self::$imap->get_namespace('other');
|
||||
$tree = new kolab_storage_virtual_folder('', '<root>', ''); // create tree root
|
||||
$refs = array('' => $tree);
|
||||
|
||||
if (is_array($other_ns)) {
|
||||
$other_ns = rtrim($other_ns[0][0], '/');
|
||||
}
|
||||
|
||||
foreach ($folders as $idx => $folder) {
|
||||
$path = explode($delim, $folder->name);
|
||||
array_pop($path);
|
||||
|
@ -852,9 +892,15 @@ class kolab_storage
|
|||
|
||||
while (count($path) >= $depth && ($parent = join($delim, $path))) {
|
||||
array_pop($path);
|
||||
$name = kolab_storage::object_name($parent, $folder->get_namespace());
|
||||
$parent_parent = join($delim, $path);
|
||||
if (!$refs[$parent]) {
|
||||
$refs[$parent] = new virtual_kolab_storage_folder($parent, $name, $folder->get_namespace(), join($delim, $path));
|
||||
if ($parent_parent == $other_ns) {
|
||||
$refs[$parent] = new kolab_storage_user_folder($parent, $parent_parent);
|
||||
}
|
||||
else {
|
||||
$name = kolab_storage::object_name($parent, $folder->get_namespace());
|
||||
$refs[$parent] = new kolab_storage_virtual_folder($parent, $name, $folder->get_namespace(), $parent_parent);
|
||||
}
|
||||
$parents[] = $refs[$parent];
|
||||
}
|
||||
}
|
||||
|
@ -1023,14 +1069,22 @@ class kolab_storage
|
|||
* Change subscription status of this folder
|
||||
*
|
||||
* @param string $folder Folder name
|
||||
* @param boolean $temp Only remove temporary subscription
|
||||
*
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
public static function folder_unsubscribe($folder)
|
||||
public static function folder_unsubscribe($folder, $temp = false)
|
||||
{
|
||||
self::setup();
|
||||
|
||||
if (self::$imap->unsubscribe($folder)) {
|
||||
// temporary/session subscription
|
||||
if ($temp) {
|
||||
if (is_array($_SESSION['kolab_subscribed_folders']) && ($i = array_search($folder, $_SESSION['kolab_subscribed_folders'])) !== false) {
|
||||
unset($_SESSION['kolab_subscribed_folders'][$i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (self::$imap->unsubscribe($folder)) {
|
||||
self::$subscriptions === null;
|
||||
return true;
|
||||
}
|
||||
|
@ -1078,10 +1132,8 @@ class kolab_storage
|
|||
*/
|
||||
public static function folder_deactivate($folder)
|
||||
{
|
||||
// remove from temp subscriptions
|
||||
if (is_array($_SESSION['kolab_subscribed_folders']) && ($i = array_search($folder, $_SESSION['kolab_subscribed_folders'])) !== false) {
|
||||
unset($_SESSION['kolab_subscribed_folders'][$i]);
|
||||
}
|
||||
// remove from temp subscriptions, really?
|
||||
self::folder_unsubscribe($folder, true);
|
||||
|
||||
return self::set_state($folder, false);
|
||||
}
|
||||
|
@ -1241,6 +1293,106 @@ class kolab_storage
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mixed $query Search value (or array of field => value pairs)
|
||||
* @param int $mode Matching mode: 0 - partial (*abc*), 1 - strict (=), 2 - prefix (abc*)
|
||||
* @param array $required List of fields that shall ot be empty
|
||||
* @param int $limit Number of records
|
||||
*
|
||||
* @return array List or false on error
|
||||
*/
|
||||
public static function search_users($query, $mode = 1, $required = array(), $limit = 0)
|
||||
{
|
||||
// requires a working LDAP setup
|
||||
if (!self::ldap()) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// FIXME: make search attributes configurable
|
||||
$results = self::$ldap->search(array('cn','mail','alias'), $query, $mode, $required, $limit);
|
||||
|
||||
// resolve to IMAP folder name
|
||||
$other_ns = self::$imap->get_namespace('other');
|
||||
$user_attrib = rcube::get_instance()->config->get('kolab_auth_login', 'mail');
|
||||
|
||||
array_walk($results, function(&$user, $dn) use ($other_ns, $user_attrib) {
|
||||
list($localpart, $domain) = explode('@', $user[$user_attrib]);
|
||||
$root = $other_ns[0][0];
|
||||
$user['kolabtargetfolder'] = $root . $localpart;
|
||||
});
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of IMAP folders shared by the given user
|
||||
*
|
||||
* @param array User entry from LDAP
|
||||
* @param string Data type to list folders for (contact,event,task,journal,file,note,mail,configuration)
|
||||
* @param boolean Return subscribed folders only (null to use configured subscription mode)
|
||||
* @param array Will be filled with folder-types data
|
||||
*
|
||||
* @return array List of folders
|
||||
*/
|
||||
public static function list_user_folders($user, $type, $subscribed = null, &$folderdata = array())
|
||||
{
|
||||
$folders = array();
|
||||
|
||||
// use localpart of user attribute as root for folder listing
|
||||
$user_attrib = rcube::get_instance()->config->get('kolab_auth_login', 'mail');
|
||||
if (!empty($user[$user_attrib])) {
|
||||
list($mbox) = explode('@', $user[$user_attrib]);
|
||||
|
||||
$other_ns = self::$imap->get_namespace('other');
|
||||
if (is_array($other_ns)) {
|
||||
$other_ns = $other_ns[0][0];
|
||||
}
|
||||
|
||||
$folders = self::list_folders($other_ns . $mbox, '*', $type, $subscribed, $folderdata);
|
||||
}
|
||||
|
||||
return $folders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a list of (virtual) top-level folders from the other users namespace
|
||||
*
|
||||
* @param boolean Enable to return subscribed folders only (null to use configured subscription mode)
|
||||
*
|
||||
* @return array List of Kolab_Folder objects (folder names in UTF7-IMAP)
|
||||
*/
|
||||
public static function get_user_folders($subscribed)
|
||||
{
|
||||
$folders = $folderdata = array();
|
||||
|
||||
if (self::setup()) {
|
||||
$delimiter = self::$imap->get_hierarchy_delimiter();
|
||||
$other_ns = self::$imap->get_namespace('other');
|
||||
if (is_array($other_ns)) {
|
||||
$other_ns = rtrim($other_ns[0][0], $delimiter);
|
||||
$other_depth = count(explode($delimiter, $other_ns));
|
||||
}
|
||||
|
||||
foreach ((array)self::list_folders($other_ns, '*', '', $subscribed) as $foldername) {
|
||||
$path = explode($delimiter, $foldername);
|
||||
$depth = count($path) - $other_depth;
|
||||
array_pop($path);
|
||||
|
||||
// only list top-level folders of the 'other' namespace
|
||||
if ($depth == 1) {
|
||||
$folders[$foldername] = new kolab_storage_user_folder($foldername, $other_ns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $folders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handler for user_delete plugin hooks
|
||||
*
|
||||
|
@ -1255,43 +1407,3 @@ class kolab_storage
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class that represents a virtual IMAP folder
|
||||
* with a subset of the kolab_storage_folder API.
|
||||
*/
|
||||
class virtual_kolab_storage_folder
|
||||
{
|
||||
public $id;
|
||||
public $name;
|
||||
public $namespace;
|
||||
public $parent = '';
|
||||
public $children = array();
|
||||
public $virtual = true;
|
||||
protected $displayname;
|
||||
|
||||
public function __construct($name, $dispname, $ns, $parent = '')
|
||||
{
|
||||
$this->id = kolab_storage::folder_id($name);
|
||||
$this->name = $name;
|
||||
$this->namespace = $ns;
|
||||
$this->parent = $parent;
|
||||
$this->displayname = $dispname;
|
||||
}
|
||||
|
||||
public function get_namespace()
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
public function get_name()
|
||||
{
|
||||
// this is already kolab_storage::object_name() result
|
||||
return $this->displayname;
|
||||
}
|
||||
|
||||
public function get_foldername()
|
||||
{
|
||||
$parts = explode('/', $this->name);
|
||||
return rcube_charset::convert(end($parts), 'UTF7-IMAP');
|
||||
}
|
||||
}
|
||||
|
|
123
plugins/libkolab/lib/kolab_storage_user_folder.php
Normal file
123
plugins/libkolab/lib/kolab_storage_user_folder.php
Normal file
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Class that represents a (virtual) folder in the 'other' namespace
|
||||
* implementing a subset of the kolab_storage_folder API.
|
||||
*
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2014, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
class kolab_storage_user_folder extends kolab_storage_virtual_folder
|
||||
{
|
||||
protected static $ldapcache = array();
|
||||
|
||||
public $ldaprec;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public function __construct($name, $parent = '', $ldaprec = null)
|
||||
{
|
||||
parent::__construct($name, $name, 'other', $parent);
|
||||
|
||||
if (!empty($ldaprec)) {
|
||||
self::$ldapcache[$name] = $this->ldaprec = $ldaprec;
|
||||
}
|
||||
// use value cached in memory for repeated lookups
|
||||
else if (array_key_exists($name, self::$ldapcache)) {
|
||||
$this->ldaprec = self::$ldapcache[$name];
|
||||
}
|
||||
// lookup user in LDAP and set $this->ldaprec
|
||||
else if ($ldap = kolab_storage::ldap()) {
|
||||
// get domain from current user
|
||||
list(,$domain) = explode('@', rcube::get_instance()->get_user_name());
|
||||
$this->ldaprec = $ldap->get_user_record(parent::get_foldername($this->name) . '@' . $domain, $_SESSION['imap_host']);
|
||||
if (!empty($this->ldaprec)) {
|
||||
$this->ldaprec['kolabtargetfolder'] = $name;
|
||||
}
|
||||
self::$ldapcache[$name] = $this->ldaprec;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the top-end folder name to be displayed
|
||||
*
|
||||
* @return string Name of this folder
|
||||
*/
|
||||
public function get_foldername()
|
||||
{
|
||||
return $this->ldaprec ? ($this->ldaprec['displayname'] ?: $this->ldaprec['name']) :
|
||||
parent::get_foldername();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owner of the folder.
|
||||
*
|
||||
* @return string The owner of this folder.
|
||||
*/
|
||||
public function get_owner()
|
||||
{
|
||||
return $this->ldaprec['mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check activation status of this folder
|
||||
*
|
||||
* @return boolean True if enabled, false if not
|
||||
*/
|
||||
public function is_active()
|
||||
{
|
||||
return kolab_storage::folder_is_active($this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change activation status of this folder
|
||||
*
|
||||
* @param boolean The desired subscription status: true = active, false = not active
|
||||
*
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
public function activate($active)
|
||||
{
|
||||
return $active ? kolab_storage::folder_activate($this->name) : kolab_storage::folder_deactivate($this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check subscription status of this folder
|
||||
*
|
||||
* @return boolean True if subscribed, false if not
|
||||
*/
|
||||
public function is_subscribed()
|
||||
{
|
||||
return kolab_storage::folder_is_subscribed($this->name, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change subscription status of this folder
|
||||
*
|
||||
* @param boolean The desired subscription status: true = subscribed, false = not subscribed
|
||||
*
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
public function subscribe($subscribed)
|
||||
{
|
||||
return $subscribed ?
|
||||
kolab_storage::folder_subscribe($this->name, true) :
|
||||
kolab_storage::folder_unsubscribe($this->name, true);
|
||||
}
|
||||
|
||||
}
|
86
plugins/libkolab/lib/kolab_storage_virtual_folder.php
Normal file
86
plugins/libkolab/lib/kolab_storage_virtual_folder.php
Normal file
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Helper class that represents a virtual IMAP folder
|
||||
* with a subset of the kolab_storage_folder API.
|
||||
*
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2014, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
class kolab_storage_virtual_folder
|
||||
{
|
||||
public $id;
|
||||
public $name;
|
||||
public $namespace;
|
||||
public $parent = '';
|
||||
public $children = array();
|
||||
public $virtual = true;
|
||||
|
||||
protected $displayname;
|
||||
|
||||
public function __construct($name, $dispname, $ns, $parent = '')
|
||||
{
|
||||
$this->id = kolab_storage::folder_id($name);
|
||||
$this->name = $name;
|
||||
$this->namespace = $ns;
|
||||
$this->parent = $parent;
|
||||
$this->displayname = $dispname;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the display name value of this folder
|
||||
*
|
||||
* @return string Folder name
|
||||
*/
|
||||
public function get_name()
|
||||
{
|
||||
// this is already kolab_storage::object_name() result
|
||||
return $this->displayname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the top-end folder name (not the entire path)
|
||||
*
|
||||
* @return string Name of this folder
|
||||
*/
|
||||
public function get_foldername()
|
||||
{
|
||||
$parts = explode('/', $this->name);
|
||||
return rcube_charset::convert(end($parts), 'UTF7-IMAP');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the color value stored in metadata
|
||||
*
|
||||
* @param string Default color value to return if not set
|
||||
* @return mixed Color value from IMAP metadata or $default is not set
|
||||
*/
|
||||
public function get_color($default = null)
|
||||
{
|
||||
return $default;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue