Changeset View
Changeset View
Standalone View
Standalone View
plugins/kolab_addressbook/kolab_addressbook.php
Show All 25 Lines | |||||
* You should have received a copy of the GNU Affero General Public License | * 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/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
class kolab_addressbook extends rcube_plugin | class kolab_addressbook extends rcube_plugin | ||||
{ | { | ||||
public $task = '?(?!logout).*'; | public $task = '?(?!logout).*'; | ||||
public $driver; | |||||
public $bonnie_api = false; | |||||
private $sources; | private $sources; | ||||
private $folders; | |||||
private $rc; | private $rc; | ||||
private $ui; | private $ui; | ||||
private $driver_class; | |||||
public $bonnie_api = false; | |||||
const GLOBAL_FIRST = 0; | const GLOBAL_FIRST = 0; | ||||
const PERSONAL_FIRST = 1; | const PERSONAL_FIRST = 1; | ||||
const GLOBAL_ONLY = 2; | const GLOBAL_ONLY = 2; | ||||
const PERSONAL_ONLY = 3; | const PERSONAL_ONLY = 3; | ||||
/** | /** | ||||
* Startup method of a Roundcube plugin | * Startup method of a Roundcube plugin | ||||
*/ | */ | ||||
public function init() | public function init() | ||||
{ | { | ||||
require_once(dirname(__FILE__) . '/lib/rcube_kolab_contacts.php'); | |||||
$this->rc = rcube::get_instance(); | $this->rc = rcube::get_instance(); | ||||
// load required plugin | // load required plugin | ||||
$this->require_plugin('libkolab'); | $this->require_plugin('libkolab'); | ||||
$this->load_config(); | |||||
$this->driver = $this->rc->config->get('kolab_addressbook_driver') ?: 'kolab'; | |||||
$this->driver_class = 'rcube_' . $this->driver . '_contacts'; | |||||
require_once(dirname(__FILE__) . '/lib/' . $this->driver_class . '.php'); | |||||
// register hooks | // register hooks | ||||
$this->add_hook('addressbooks_list', array($this, 'address_sources')); | $this->add_hook('addressbooks_list', array($this, 'address_sources')); | ||||
$this->add_hook('addressbook_get', array($this, 'get_address_book')); | $this->add_hook('addressbook_get', array($this, 'get_address_book')); | ||||
$this->add_hook('config_get', array($this, 'config_get')); | $this->add_hook('config_get', array($this, 'config_get')); | ||||
if ($this->rc->task == 'addressbook') { | if ($this->rc->task == 'addressbook') { | ||||
$this->add_texts('localization'); | $this->add_texts('localization'); | ||||
$this->add_hook('contact_form', array($this, 'contact_form')); | $this->add_hook('contact_form', array($this, 'contact_form')); | ||||
Show All 10 Lines | public function init() | ||||
$this->register_action('plugin.contact-diff', array($this, 'contact_diff')); | $this->register_action('plugin.contact-diff', array($this, 'contact_diff')); | ||||
$this->register_action('plugin.contact-restore', array($this, 'contact_restore')); | $this->register_action('plugin.contact-restore', array($this, 'contact_restore')); | ||||
// get configuration for the Bonnie API | // get configuration for the Bonnie API | ||||
$this->bonnie_api = libkolab::get_bonnie_api(); | $this->bonnie_api = libkolab::get_bonnie_api(); | ||||
// Load UI elements | // Load UI elements | ||||
if ($this->api->output->type == 'html') { | if ($this->api->output->type == 'html') { | ||||
$this->load_config(); | |||||
require_once($this->home . '/lib/kolab_addressbook_ui.php'); | require_once($this->home . '/lib/kolab_addressbook_ui.php'); | ||||
$this->ui = new kolab_addressbook_ui($this); | $this->ui = new kolab_addressbook_ui($this); | ||||
if ($this->bonnie_api) { | if ($this->bonnie_api) { | ||||
$this->add_button(array( | $this->add_button(array( | ||||
'command' => 'contact-history-dialog', | 'command' => 'contact-history-dialog', | ||||
'class' => 'history contact-history disabled', | 'class' => 'history contact-history disabled', | ||||
'classact' => 'history contact-history active', | 'classact' => 'history contact-history active', | ||||
'innerclass' => 'icon inner', | 'innerclass' => 'icon inner', | ||||
'label' => 'kolab_addressbook.showhistory', | 'label' => 'kolab_addressbook.showhistory', | ||||
'type' => 'link-menuitem' | 'type' => 'link-menuitem' | ||||
), 'contactmenu'); | ), 'contactmenu'); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
else if ($this->rc->task == 'settings') { | else if ($this->rc->task == 'settings') { | ||||
$this->add_texts('localization'); | $this->add_texts('localization'); | ||||
$this->add_hook('preferences_list', array($this, 'prefs_list')); | $this->add_hook('preferences_list', array($this, 'prefs_list')); | ||||
$this->add_hook('preferences_save', array($this, 'prefs_save')); | $this->add_hook('preferences_save', array($this, 'prefs_save')); | ||||
} | } | ||||
if ($this->driver == 'kolab') { | |||||
$this->add_hook('folder_delete', array($this, 'prefs_folder_delete')); | $this->add_hook('folder_delete', array($this, 'prefs_folder_delete')); | ||||
$this->add_hook('folder_rename', array($this, 'prefs_folder_rename')); | $this->add_hook('folder_rename', array($this, 'prefs_folder_rename')); | ||||
$this->add_hook('folder_update', array($this, 'prefs_folder_update')); | $this->add_hook('folder_update', array($this, 'prefs_folder_update')); | ||||
} | } | ||||
} | |||||
/** | /** | ||||
* Handler for the addressbooks_list hook. | * Handler for the addressbooks_list hook. | ||||
* | * | ||||
* This will add all instances of available Kolab-based address books | * This will add all instances of available Kolab-based address books | ||||
* to the list of address sources of Roundcube. | * to the list of address sources of Roundcube. | ||||
* This will also hide some addressbooks according to kolab_addressbook_prio setting. | * This will also hide some addressbooks according to kolab_addressbook_prio setting. | ||||
* | * | ||||
▲ Show 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | public function directorylist_html($args) | ||||
// list all non-kolab sources first (also exclude hidden sources) | // list all non-kolab sources first (also exclude hidden sources) | ||||
$filter = function($source){ return empty($source['kolab']) && empty($source['hidden']); }; | $filter = function($source){ return empty($source['kolab']) && empty($source['hidden']); }; | ||||
foreach (array_filter($sources, $filter) as $j => $source) { | foreach (array_filter($sources, $filter) as $j => $source) { | ||||
$id = strval(strlen($source['id']) ? $source['id'] : $j); | $id = strval(strlen($source['id']) ? $source['id'] : $j); | ||||
$out .= $this->addressbook_list_item($id, $source, $jsdata) . '</li>'; | $out .= $this->addressbook_list_item($id, $source, $jsdata) . '</li>'; | ||||
} | } | ||||
$filter = function($source) { return !empty($source['kolab']) && empty($source['hidden']); }; | |||||
$folders = array_filter($sources, $filter); | |||||
// render a hierarchical list of kolab contact folders | // render a hierarchical list of kolab contact folders | ||||
kolab_storage::folder_hierarchy($this->folders, $tree); | // TODO: Move this to the drivers | ||||
if ($this->driver == 'kolab') { | |||||
kolab_storage::folder_hierarchy($folders, $tree); | |||||
if ($tree && !empty($tree->children)) { | if ($tree && !empty($tree->children)) { | ||||
$out .= $this->folder_tree_html($tree, $sources, $jsdata); | $out .= $this->folder_tree_html($tree, $sources, $jsdata); | ||||
} | } | ||||
} | |||||
else { | |||||
foreach ($folders as $j => $source) { | |||||
$id = strval(strlen($source['id']) ? $source['id'] : $j); | |||||
$out .= $this->addressbook_list_item($id, $source, $jsdata) . '</li>'; | |||||
} | |||||
} | |||||
$this->rc->output->set_env('contactgroups', array_filter($jsdata, function($src){ return $src['type'] == 'group'; })); | $this->rc->output->set_env('contactgroups', array_filter($jsdata, function($src){ return $src['type'] == 'group'; })); | ||||
$this->rc->output->set_env('address_sources', array_filter($jsdata, function($src){ return $src['type'] != 'group'; })); | $this->rc->output->set_env('address_sources', array_filter($jsdata, function($src){ return $src['type'] != 'group'; })); | ||||
$args['content'] = html::tag('ul', $args, $out, html::$common_attrib); | $args['content'] = html::tag('ul', $args, $out, html::$common_attrib); | ||||
return $args; | return $args; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | protected function addressbook_list_item($id, $source, &$jsdata, $search_mode = false) | ||||
html::a(array( | html::a(array( | ||||
'href' => $this->rc->url(array('_source' => $id)), | 'href' => $this->rc->url(array('_source' => $id)), | ||||
'rel' => $source['id'], | 'rel' => $source['id'], | ||||
'id' => $label_id, | 'id' => $label_id, | ||||
'onclick' => "return " . rcmail_output::JS_OBJECT_NAME.".command('list','" . rcube::JQ($id) . "',this)", | 'onclick' => "return " . rcmail_output::JS_OBJECT_NAME.".command('list','" . rcube::JQ($id) . "',this)", | ||||
), $name) | ), $name) | ||||
); | ); | ||||
if (isset($source['subscribed'])) { | if ($this->driver == 'kolab' && isset($source['subscribed'])) { | ||||
$inner .= html::span(array( | $inner .= html::span(array( | ||||
'class' => 'subscribed', | 'class' => 'subscribed', | ||||
'title' => $this->gettext('foldersubscribe'), | 'title' => $this->gettext('foldersubscribe'), | ||||
'role' => 'checkbox', | 'role' => 'checkbox', | ||||
'aria-checked' => $source['subscribed'] ? 'true' : 'false', | 'aria-checked' => $source['subscribed'] ? 'true' : 'false', | ||||
), ''); | ), ''); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | public function config_get($args) | ||||
} | } | ||||
} | } | ||||
$args['result'] = $sources; | $args['result'] = $sources; | ||||
return $args; | return $args; | ||||
} | } | ||||
/** | /** | ||||
* Getter for the rcube_addressbook instance | * Getter for the rcube_addressbook instance | ||||
* | * | ||||
* @param array $p Hash array with hook parameters | * @param array $p Hash array with hook parameters | ||||
* | * | ||||
* @return array Hash array with modified hook parameters | * @return array Hash array with modified hook parameters | ||||
*/ | */ | ||||
public function get_address_book($p) | public function get_address_book($p) | ||||
{ | { | ||||
if ($p['id']) { | if ($p['id']) { | ||||
$id = kolab_storage::id_decode($p['id']); | if ($source = $this->driver_class::get_address_book($p['id'])) { | ||||
$folder = kolab_storage::get_folder($id); | $p['instance'] = $source; | ||||
// try with unencoded (old-style) identifier | |||||
if ((!$folder || $folder->type != 'contact') && $id != $p['id']) { | |||||
$folder = kolab_storage::get_folder($p['id']); | |||||
} | |||||
if ($folder && $folder->type == 'contact') { | |||||
$p['instance'] = new rcube_kolab_contacts($folder->name); | |||||
// flag source as writeable if 'i' right is given | // flag source as writeable if 'i' right is given | ||||
if ($p['writeable'] && $this->rc->action == 'save' && strpos($p['instance']->rights, 'i') !== false) { | if ($p['writeable'] && $this->rc->action == 'save' && strpos($p['instance']->rights, 'i') !== false) { | ||||
$p['instance']->readonly = false; | $p['instance']->readonly = false; | ||||
} | } | ||||
else if ($this->rc->action == 'delete' && strpos($p['instance']->rights, 't') !== false) { | else if ($this->rc->action == 'delete' && strpos($p['instance']->rights, 't') !== false) { | ||||
$p['instance']->readonly = false; | $p['instance']->readonly = false; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return $p; | return $p; | ||||
} | } | ||||
/** | |||||
* List addressbook sources list | |||||
*/ | |||||
private function _list_sources() | private function _list_sources() | ||||
{ | { | ||||
// already read sources | // already read sources | ||||
if (isset($this->sources)) | if (isset($this->sources)) { | ||||
return $this->sources; | return $this->sources; | ||||
} | |||||
kolab_storage::$encode_ids = true; | $this->sources = []; | ||||
$this->sources = array(); | |||||
$this->folders = array(); | |||||
$abook_prio = $this->addressbook_prio(); | $abook_prio = $this->addressbook_prio(); | ||||
// Personal address source(s) disabled? | // Personal address source(s) disabled? | ||||
if ($abook_prio == self::GLOBAL_ONLY) { | if ($abook_prio == kolab_addressbook::GLOBAL_ONLY) { | ||||
return $this->sources; | return $this->sources; | ||||
} | } | ||||
// get all folders that have "contact" type | // get all folders that have "contact" type | ||||
$folders = kolab_storage::sort_folders(kolab_storage::get_folders('contact')); | foreach ($this->driver_class::list_folders() as $id => $source) { | ||||
$this->sources[$id] = $source; | |||||
if (PEAR::isError($folders)) { | |||||
rcube::raise_error(array( | |||||
'code' => 600, 'type' => 'php', | |||||
'file' => __FILE__, 'line' => __LINE__, | |||||
'message' => "Failed to list contact folders from Kolab server:" . $folders->getMessage()), | |||||
true, false); | |||||
} | |||||
else { | |||||
// we need at least one folder to prevent from errors in Roundcube core | |||||
// when there's also no sql nor ldap addressbook (Bug #2086) | |||||
if (empty($folders)) { | |||||
if ($folder = kolab_storage::create_default_folder('contact')) { | |||||
$folders = array(new kolab_storage_folder($folder, 'contact')); | |||||
} | |||||
} | |||||
// convert to UTF8 and sort | |||||
foreach ($folders as $folder) { | |||||
// create instance of rcube_contacts | |||||
$abook_id = $folder->id; | |||||
$abook = new rcube_kolab_contacts($folder->name); | |||||
$this->sources[$abook_id] = $abook; | |||||
$this->folders[$abook_id] = $folder; | |||||
} | |||||
} | } | ||||
return $this->sources; | return $this->sources; | ||||
} | } | ||||
/** | /** | ||||
* Plugin hook called before rendering the contact form or detail view | * Plugin hook called before rendering the contact form or detail view | ||||
* | * | ||||
* @param array $p Hash array with hook parameters | * @param array $p Hash array with hook parameters | ||||
* | * | ||||
* @return array Hash array with modified hook parameters | * @return array Hash array with modified hook parameters | ||||
*/ | */ | ||||
public function contact_form($p) | public function contact_form($p) | ||||
▲ Show 20 Lines • Show All 303 Lines • ▼ Show 20 Lines | private function _resolve_contact_identity($id, $abook, &$folder = null) | ||||
return array($uid, $mailbox, $msguid); | return array($uid, $mailbox, $msguid); | ||||
} | } | ||||
/** | /** | ||||
* | * | ||||
*/ | */ | ||||
private function _sort_form_fields($contents, $source) | private function _sort_form_fields($contents, $source) | ||||
{ | { | ||||
$block = array(); | $block = []; | ||||
foreach (array_keys($source->coltypes) as $col) { | foreach (array_keys($source->coltypes) as $col) { | ||||
if (isset($contents[$col])) | if (isset($contents[$col])) { | ||||
$block[$col] = $contents[$col]; | $block[$col] = $contents[$col]; | ||||
} | } | ||||
} | |||||
return $block; | return $block; | ||||
} | } | ||||
/** | /** | ||||
* Handler for user preferences form (preferences_list hook) | * Handler for user preferences form (preferences_list hook) | ||||
* | * | ||||
* @param array $args Hash array with hook parameters | * @param array $args Hash array with hook parameters | ||||
* | * | ||||
* @return array Hash array with modified hook parameters | * @return array Hash array with modified hook parameters | ||||
*/ | */ | ||||
public function prefs_list($args) | public function prefs_list($args) | ||||
▲ Show 20 Lines • Show All 131 Lines • ▼ Show 20 Lines | public function book_save() | ||||
$this->rc->output->send('iframe'); | $this->rc->output->send('iframe'); | ||||
} | } | ||||
/** | /** | ||||
* | * | ||||
*/ | */ | ||||
public function book_search() | public function book_search() | ||||
{ | { | ||||
$results = array(); | $results = []; | ||||
$query = rcube_utils::get_input_value('q', rcube_utils::INPUT_GPC); | $query = rcube_utils::get_input_value('q', rcube_utils::INPUT_GPC); | ||||
$source = rcube_utils::get_input_value('source', rcube_utils::INPUT_GPC); | $source = rcube_utils::get_input_value('source', rcube_utils::INPUT_GPC); | ||||
kolab_storage::$encode_ids = true; | kolab_storage::$encode_ids = true; | ||||
$search_more_results = false; | $search_more_results = false; | ||||
$this->sources = array(); | $this->sources = array(); | ||||
$this->folders = array(); | $this->folders = array(); | ||||
// find unsubscribed IMAP folders that have "event" type | // find unsubscribed IMAP folders that have "event" type | ||||
if ($source == 'folders') { | if ($source == 'folders') { | ||||
▲ Show 20 Lines • Show All 131 Lines • ▼ Show 20 Lines | private function book_delete() | ||||
$this->rc->output->send(); | $this->rc->output->send(); | ||||
} | } | ||||
/** | /** | ||||
* Returns value of kolab_addressbook_prio setting | * Returns value of kolab_addressbook_prio setting | ||||
*/ | */ | ||||
private function addressbook_prio() | private function addressbook_prio() | ||||
{ | { | ||||
// Load configuration | |||||
if (!$this->config_loaded) { | |||||
$this->load_config(); | |||||
$this->config_loaded = true; | |||||
} | |||||
$abook_prio = (int) $this->rc->config->get('kolab_addressbook_prio'); | $abook_prio = (int) $this->rc->config->get('kolab_addressbook_prio'); | ||||
// Make sure any global addressbooks are defined | // Make sure any global addressbooks are defined | ||||
if ($abook_prio == 0 || $abook_prio == 2) { | if ($abook_prio == 0 || $abook_prio == 2) { | ||||
$ldap_public = $this->rc->config->get('ldap_public'); | $ldap_public = $this->rc->config->get('ldap_public'); | ||||
if (empty($ldap_public)) { | if (empty($ldap_public)) { | ||||
$abook_prio = 1; | $abook_prio = 1; | ||||
▲ Show 20 Lines • Show All 75 Lines • Show Last 20 Lines |