diff options
-rw-r--r-- | config/main.inc.php.dist | 14 | ||||
-rw-r--r-- | index.php | 6 | ||||
-rw-r--r-- | program/include/main.inc | 51 | ||||
-rw-r--r-- | program/include/rcube_ldap.inc | 259 | ||||
-rw-r--r-- | program/js/app.js | 163 | ||||
-rw-r--r-- | program/localization/en/labels.inc | 9 | ||||
-rw-r--r-- | program/localization/en/messages.inc | 9 | ||||
-rw-r--r-- | program/steps/addressbook/func.inc | 3 | ||||
-rw-r--r-- | program/steps/addressbook/ldapsearchform.inc | 264 | ||||
-rw-r--r-- | program/steps/addressbook/save.inc | 162 | ||||
-rw-r--r-- | skins/default/images/buttons/ldap_act.png | bin | 0 -> 335 bytes | |||
-rw-r--r-- | skins/default/images/buttons/ldap_pas.png | bin | 0 -> 335 bytes | |||
-rw-r--r-- | skins/default/includes/ldapscripts.html | 73 | ||||
-rw-r--r-- | skins/default/ldapsearchform.css | 54 | ||||
-rw-r--r-- | skins/default/templates/ldappublicsearch.html | 31 |
15 files changed, 1019 insertions, 79 deletions
diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index 17beaf188..e7ccc913d 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -131,6 +131,20 @@ $rcmail_config['generic_message_footer'] = ''; // this string is used as a delimiter for message headers when sending $rcmail_config['mail_header_delimiter'] = "\r\n"; +// public ldap servers to search for contacts +$rcmail_config['ldap_public'] = false; + +/** example config for Verisign directory +$rcmail_config['ldap_public']['Verisign'] = array('hosts' => array('directory.verisign.com'), + 'port' => 389, + 'base_dn' => '', + 'search_fields' => array('mail', 'cn'), + 'name_field' => 'cn', + 'mail_field' => 'mail', + 'scope' => 'sub', + 'fuzzy_search' => 0); +**/ + /***** these settings can be overwritten by user's preferences *****/ @@ -1,5 +1,4 @@ <?php - /* +-----------------------------------------------------------------------+ | RoundCube Webmail IMAP Client | @@ -281,6 +280,9 @@ if ($_task=='addressbook') if ($_action=='list' && $_GET['_remote']) include('program/steps/addressbook/list.inc'); + + if ($_action=='ldappublicsearch') + include('program/steps/addressbook/ldapsearchform.inc'); } @@ -325,4 +327,4 @@ raise_error(array('code' => 404, 'file' => __FILE__, 'message' => "Invalid request"), TRUE, TRUE); -?>
\ No newline at end of file +?> diff --git a/program/include/main.inc b/program/include/main.inc index 24110d343..c64ac7d38 100644 --- a/program/include/main.inc +++ b/program/include/main.inc @@ -924,6 +924,8 @@ function rcube_xml_command($command, $str_attrib, $a_attrib=NULL) 'recordscountdisplay' => 'rcmail_rowcount_display', 'contactdetails' => 'rcmail_contact_details', 'contacteditform' => 'rcmail_contact_editform', + 'ldappublicsearch' => 'rcmail_ldap_public_search_form', + 'ldappublicaddresslist' => 'rcmail_ldap_public_list', // USER SETTINGS 'userprefs' => 'rcmail_user_prefs_form', @@ -1110,7 +1112,7 @@ function rcube_menu($attrib) -function rcube_table_output($attrib, $sql_result, $a_show_cols, $id_col) +function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col) { global $DB; @@ -1128,21 +1130,44 @@ function rcube_table_output($attrib, $sql_result, $a_show_cols, $id_col) $table .= "</tr></thead>\n<tbody>\n"; $c = 0; - while ($sql_result && ($sql_arr = $DB->fetch_assoc($sql_result))) + + if (!is_array($table_data)) { - $zebra_class = $c%2 ? 'even' : 'odd'; + while ($table_data && ($sql_arr = $DB->fetch_assoc($table_data))) + { + $zebra_class = $c%2 ? 'even' : 'odd'; - $table .= sprintf('<tr id="rcmrow%d" class="contact '.$zebra_class.'">'."\n", $sql_arr[$id_col]); + $table .= sprintf('<tr id="rcmrow%d" class="contact '.$zebra_class.'">'."\n", $sql_arr[$id_col]); - // format each col - foreach ($a_show_cols as $col) - { - $cont = rep_specialchars_output($sql_arr[$col]); - $table .= '<td class="'.$col.'">' . $cont . "</td>\n"; + // format each col + foreach ($a_show_cols as $col) + { + $cont = rep_specialchars_output($sql_arr[$col]); + $table .= '<td class="'.$col.'">' . $cont . "</td>\n"; + } + + $table .= "</tr>\n"; + $c++; } + } + else + { + foreach ($table_data as $row_data) + { + $zebra_class = $c%2 ? 'even' : 'odd'; + + $table .= sprintf('<tr id="rcmrow%d" class="contact '.$zebra_class.'">'."\n", $row_data[$id_col]); + + // format each col + foreach ($a_show_cols as $col) + { + $cont = rep_specialchars_output($row_data[$col]); + $table .= '<td class="'.$col.'">' . $cont . "</td>\n"; + } - $table .= "</tr>\n"; - $c++; + $table .= "</tr>\n"; + $c++; + } } // complete message table @@ -1420,7 +1445,6 @@ function rcmail_charset_selector($attrib) } - function rcube_timer() { list($usec, $sec) = explode(" ", microtime()); @@ -1442,5 +1466,4 @@ function rcube_print_time($timer, $label='Timer') console(sprintf("%s: %0.4f sec", $label, $diff)); } - -?>
\ No newline at end of file +?> diff --git a/program/include/rcube_ldap.inc b/program/include/rcube_ldap.inc new file mode 100644 index 000000000..7cb9dee53 --- /dev/null +++ b/program/include/rcube_ldap.inc @@ -0,0 +1,259 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/include/rcube_ldap.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | Licensed under the GNU GPL | + | | + | PURPOSE: | + | Manage an LDAP connection | + | | + +-----------------------------------------------------------------------+ + | Author: Jeremy Jongsma <jeremy@jongsma.org> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +require_once("bugs.inc"); + +class rcube_ldap + { + var $conn; + var $host; + var $port; + var $protocol; + var $base_dn; + var $bind_dn; + var $bind_pass; + + // PHP 5 constructor + function __construct() + { + } + + // PHP 4 constructor + function rcube_ldap() + { + $this->__construct(); + } + + function connect($hosts, $port=389, $protocol=3) + { + if (!function_exists('ldap_connect')) + raise_error(array("type" => "ldap", + "message" => "No ldap support in this installation of php."), + TRUE); + + if (is_resource($this->conn)) + return TRUE; + + if (!is_array($hosts)) + $hosts = array($hosts); + + foreach ($hosts as $host) + { + if ($lc = @ldap_connect($host, $port)) + { + @ldap_set_option($lc, LDAP_OPT_PROTOCOL_VERSION, $protocol); + $this->host = $host; + $this->port = $port; + $this->protocol = $protocol; + $this->conn = $lc; + return TRUE; + } + } + + if (!is_resource($this->conn)) + raise_error(array("type" => "ldap", + "message" => "Could not connect to any LDAP server, tried $host:$port last"), + TRUE); + } + + function close() + { + if ($this->conn) + { + if (@ldap_unbind($this->conn)) + return TRUE; + else + raise_error(array("code" => ldap_errno($this->conn), + "type" => "ldap", + "message" => "Could not close connection to LDAP server: ".ldap_error($this->conn)), + TRUE); + } + return FALSE; + } + + // Merge with connect()? + function bind($dn=null, $pass=null) + { + if ($this->conn) + { + if ($dn) + if (@ldap_bind($this->conn, $dn, $pass)) + return TRUE; + else + raise_error(array("code" => ldap_errno($this->conn), + "type" => "ldap", + "message" => "Bind failed for dn=$dn: ".ldap_error($this->conn)), + TRUE); + else + if (@ldap_bind($this->conn)) + return TRUE; + else + raise_error(array("code" => ldap_errno($this->conn), + "type" => "ldap", + "message" => "Anonymous bind failed: ".ldap_error($this->conn)), + TRUE); + } + else + raise_error(array("type" => "ldap", + "message" => "Attempted bind on nonexistent connection"), TRUE); + return FALSE; + } + + function count($base, $filter=null, $attributes=null, $scope="sub") + { + if ($this->conn) + { + if ($scope === 'sub') + $sr = @ldap_search($this->conn, $base, $filter, $attributes, 0, $limit); + else if ($scope === 'one') + $sr = @ldap_list($this->conn, $base, $filter, $attributes, 0, $limit); + else if ($scope === 'base') + $sr = @ldap_read($this->conn, $base, $filter, $attributes, 0, $limit); + if ($sr) + return @ldap_count_entries($this->conn, $sr); + } + else + raise_error(array("type" => "ldap", + "message" => "Attempted count search on nonexistent connection"), TRUE); + return FALSE; + } + + function search($base, $filter=null, $attributes=null, $scope='sub', $sort=null, $limit=0) + { + if ($this->conn) + { + if ($scope === 'sub') + $sr = @ldap_search($this->conn, $base, $filter, $attributes, 0, $limit); + else if ($scope === 'one') + $sr = @ldap_list($this->conn, $base, $filter, $attributes, 0, $limit); + else if ($scope === 'base') + $sr = @ldap_read($this->conn, $base, $filter, $attributes, 0, $limit); + if ($sr) + { + if ($sort && $scope !== "base") + { + if (is_array($sort)) + { + // Start from the end so first sort field has highest priority + $sortfields = array_reverse($sort); + foreach ($sortfields as $sortfield) + @ldap_sort($this->conn, $sr, $sortfield); + } + else + @ldap_sort($this->conn, $sr, $sort); + } + return @ldap_get_entries($this->conn, $sr); + } + } + else + raise_error(array("type" => "ldap", + "message" => "Attempted search on nonexistent connection"), TRUE); + return FALSE; + } + + function add($dn, $object) + { + if ($this->conn) + { + if (@ldap_add($this->conn, $dn, $object)) + return TRUE; + else + raise_error(array("code" => ldap_errno($this->conn), + "type" => "ldap", + "message" => "Add object failed: ".ldap_error($this->conn)), + TRUE); + } + else + raise_error(array("type" => "ldap", + "message" => "Add object faile: no connection"), + TRUE); + return FALSE; + } + + function modify($dn, $object) + { + if ($this->conn) + { + if (@ldap_modify($this->conn, $dn, $object)) + return TRUE; + else + raise_error(array("code" => ldap_errno($this->conn), + "type" => "ldap", + "message" => "Modify object failed: ".ldap_error($this->conn)), + TRUE); + } + else + raise_error(array("type" => "ldap", + "message" => "Modify object failed: no connection"), + TRUE); + return FALSE; + } + + function rename($dn, $newrdn, $parentdn) + { + if ($this->protocol < 3) + { + raise_error(array("type" => "ldap", + "message" => "rename() support requires LDAPv3 or above "), + TRUE); + return FALSE; + } + + if ($this->conn) + { + if (@ldap_rename($this->conn, $dn, $newrdn, $parentdn, TRUE)) + return TRUE; + else + raise_error(array("code" => ldap_errno($this->conn), + "type" => "ldap", + "message" => "Rename object failed: ".ldap_error($this->conn)), + TRUE); + } + else + raise_error(array("type" => "ldap", + "message" => "Rename object failed: no connection"), + TRUE); + return FALSE; + } + + function delete($dn) + { + if ($this->conn) + { + if (@ldap_delete($this->conn, $dn)) + return TRUE; + else + raise_error(array("code" => ldap_errno($this->conn), + "type" => "ldap", + "message" => "Delete object failed: ".ldap_error($this->conn)), + TRUE); + } + else + raise_error(array("type" => "ldap", + "message" => "Delete object failed: no connection"), + TRUE); + return FALSE; + } + + } + +// vi: et ts=2 sw=2 +?> diff --git a/program/js/app.js b/program/js/app.js index dc0275c4b..6d76d04e2 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -160,10 +160,15 @@ function rcube_webmail() case 'addressbook': - var contacts_list = this.gui_objects.contactslist; + var contacts_list = this.gui_objects.contactslist; + var ldap_contacts_list = this.gui_objects.ldapcontactslist; + if (contacts_list) this.init_contactslist(contacts_list); + if (ldap_contacts_list) + this.init_ldapsearchlist(ldap_contacts_list); + this.set_page_buttons(); if (this.env.cid) @@ -172,7 +177,7 @@ function rcube_webmail() if ((this.env.action=='add' || this.env.action=='edit') && this.gui_objects.editform) this.enable_command('save', true); - this.enable_command('list', 'add', true); + this.enable_command('list', 'add', 'ldappublicsearch', true); break; @@ -372,6 +377,26 @@ function rcube_webmail() }; + // get all contact rows from HTML table and init each row + this.init_ldapsearchlist = function(ldap_contacts_list) + { + if (ldap_contacts_list && ldap_contacts_list.tBodies[0]) + { + this.ldap_contact_rows = new Array(); + + var row; + for(var r=0; r<ldap_contacts_list.tBodies[0].childNodes.length; r++) + { + row = ldap_contacts_list.tBodies[0].childNodes[r]; + this.init_table_row(row, 'ldap_contact_rows'); + } + } + + // alias to common rows array + this.list_rows = this.ldap_contact_rows; + }; + + // make references in internal array and set event handlers this.init_table_row = function(row, array_name) { @@ -548,7 +573,15 @@ function rcube_webmail() case 'add': if (this.task=='addressbook') - this.load_contact(0, 'add'); + if (!window.frames[this.env.contentframe].rcmail) + this.load_contact(0, 'add'); + else + { + if (window.frames[this.env.contentframe].rcmail.selection.length) + this.add_ldap_contacts(); + else + this.load_contact(0, 'add'); + } else if (this.task=='settings') { this.clear_selection(); @@ -682,7 +715,8 @@ function rcube_webmail() this.show_message(this.env.prev_uid); //location.href = this.env.comm_path+'&_action=show&_uid='+this.env.prev_uid+'&_mbox='+this.env.mailbox; break; - + + case 'compose': var url = this.env.comm_path+'&_action=compose'; @@ -699,21 +733,36 @@ function rcube_webmail() // get selected contacts else { - for (var n=0; n<this.selection.length; n++) - a_cids[a_cids.length] = this.selection[n]; + if (!window.frames[this.env.contentframe].rcmail.selection.length) + { + for (var n=0; n<this.selection.length; n++) + a_cids[a_cids.length] = this.selection[n]; + } + else + { + var frameRcmail = window.frames[this.env.contentframe].rcmail; + // get the email address(es) + for (var n=0; n<frameRcmail.selection.length; n++) + a_cids[a_cids.length] = frameRcmail.ldap_contact_rows[frameRcmail.selection[n]].obj.cells[1].innerHTML; + } } - if (a_cids.length) url += '&_to='+a_cids.join(','); else break; + } else if (props) url += '&_to='+props; + + // don't know if this is necessary... + url = url.replace(/&_framed=1/, ""); this.set_busy(true); - location.href = url; - break; + + // need parent in case we are coming from the contact frame + parent.window.location.href = url; + break; case 'send': if (!this.gui_objects.messageform) @@ -777,6 +826,15 @@ function rcube_webmail() case 'add-contact': this.add_contact(props); break; + + + // ldap search + case 'ldappublicsearch': + if (this.gui_objects.ldappublicsearchform) + this.gui_objects.ldappublicsearchform.submit(); + else + this.ldappublicsearch(command); + break; // user settings commands @@ -954,7 +1012,7 @@ function rcube_webmail() // onmouseup-handler of message list row this.click_row = function(e, id) { - var ctrl = this.check_ctrlkey(e); + var shift = this.check_shiftkey(e); // don't do anything (another action processed before) if (this.dont_select) @@ -964,26 +1022,58 @@ function rcube_webmail() } if (!this.drag_active && this.in_selection_before==id) - this.select(id, (ctrl && this.task!='settings')); + { + this.select(id, (shift && this.task!='settings')); + } this.drag_start = false; this.in_selection_before = false; // row was double clicked - if (this.task=='mail' && this.list_rows && this.list_rows[id].clicked && !ctrl) + if (this.task=='mail' && this.list_rows && this.list_rows[id].clicked && !shift) { this.show_message(id); return false; } else if (this.task=='addressbook') { - if (this.selection.length==1 && this.env.contentframe) + if (this.contact_rows && this.selection.length==1) + { this.load_contact(this.selection[0], 'show', true); - else if (this.task=='addressbook' && this.list_rows && this.list_rows[id].clicked) + // change the text for the add contact button + var links = parent.document.getElementById('abooktoolbar').getElementsByTagName('A'); + for (i = 0; i < links.length; i++) + { + var onclickstring = new String(links[i].onclick); + if (onclickstring.search('\"add\"') != -1) + links[i].title = this.env.newcontact; + } + } + else if (this.contact_rows && this.contact_rows[id].clicked) { this.load_contact(id, 'show'); return false; } + else if (this.ldap_contact_rows && !this.ldap_contact_rows[id].clicked) + { + // clear selection + parent.rcmail.clear_selection(); + + // disable delete + parent.rcmail.set_button('delete', 'pas'); + + // change the text for the add contact button + var links = parent.document.getElementById('abooktoolbar').getElementsByTagName('A'); + for (i = 0; i < links.length; i++) + { + var onclickstring = new String(links[i].onclick); + if (onclickstring.search('\"add\"') != -1) + links[i].title = this.env.addcontact; + } + } + // handle double click event + else if (this.ldap_contact_rows && this.selection.length==1 && this.ldap_contact_rows[id].clicked) + this.command('compose', this.ldap_contact_rows[id].obj.cells[1].innerHTML); else if (this.env.contentframe) { var elm = document.getElementById(this.env.contentframe); @@ -1004,7 +1094,6 @@ function rcube_webmail() - /*********************************************************/ /********* (message) list functionality *********/ /*********************************************************/ @@ -1919,6 +2008,50 @@ function rcube_webmail() }; + + // load ldap search form + this.ldappublicsearch = function(action) + { + var add_url = ''; + var target = window; + if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) + { + add_url = '&_framed=1'; + target = window.frames[this.env.contentframe]; + document.getElementById(this.env.contentframe).style.visibility = 'inherit'; + } + else + return false; + + + if (action == 'ldappublicsearch') + target.location.href = this.env.comm_path+'&_action='+action+add_url; + }; + + // add ldap contacts to address book + this.add_ldap_contacts = function() + { + if (window.frames[this.env.contentframe].rcmail) + { + var frame = window.frames[this.env.contentframe]; + + // build the url + var url = '&_framed=1'; + var emails = '&_emails='; + var names = '&_names='; + var end = ''; + for (var n=0; n<frame.rcmail.selection.length; n++) + { + end = n < frame.rcmail.selection.length - 1 ? ',' : ''; + emails += frame.rcmail.ldap_contact_rows[frame.rcmail.selection[n]].obj.cells[1].innerHTML + end; + names += frame.rcmail.ldap_contact_rows[frame.rcmail.selection[n]].obj.cells[0].innerHTML + end; + } + + frame.location.href = this.env.comm_path + '&_action=save&_framed=1' + emails + names; + } + return false; + } + /*********************************************************/ diff --git a/program/localization/en/labels.inc b/program/localization/en/labels.inc index 086c3080f..1355c071d 100644 --- a/program/localization/en/labels.inc +++ b/program/localization/en/labels.inc @@ -143,12 +143,19 @@ $labels['save'] = 'Save'; $labels['delete'] = 'Delete'; $labels['newcontact'] = 'Create new contact card'; +$labels['addcontact'] = 'Add selected contact to your addressbook'; $labels['deletecontact'] = 'Delete selected contacts'; $labels['composeto'] = 'Compose mail to'; $labels['contactsfromto'] = 'Contacts $from to $to of $count'; $labels['print'] = 'Print'; $labels['export'] = 'Export'; +$labels['ldappublicsearchname'] = 'Contact name'; +$labels['ldappublicsearchtype'] = 'Exact match?'; +$labels['ldappublicserverselect'] = 'Select servers'; +$labels['ldappublicsearchfield'] = 'Search on'; +$labels['ldappublicsearchform'] = 'Look for a contact'; +$labels['ldappublicsearch'] = 'Search'; // settings $labels['settingsfor'] = 'Settings for'; @@ -183,4 +190,4 @@ $labels['sortby'] = 'Sort by'; $labels['sortasc'] = 'Sort ascending'; $labels['sortdesc'] = 'Sort descending'; -?>
\ No newline at end of file +?> diff --git a/program/localization/en/messages.inc b/program/localization/en/messages.inc index c1520ecce..5a662d279 100644 --- a/program/localization/en/messages.inc +++ b/program/localization/en/messages.inc @@ -78,5 +78,12 @@ $messages['nobodywarning'] = 'Send this message without text?'; $messages['notsentwarning'] = 'Message has not been sent. Do you want to discard your message?'; +$messages['notsentwarning'] = 'Message has not been sent. Do you want to discard your message?'; + +$messages['noldapserver'] = 'Please select an ldap server to search'; + +$messages['nocontactsreturned'] = 'No contacts were found'; + +$messages['nosearchname'] = 'Please enter a contact name or email address'; -?>
\ No newline at end of file +?> diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc index 8065219b0..ee3b8804a 100644 --- a/program/steps/addressbook/func.inc +++ b/program/steps/addressbook/func.inc @@ -81,6 +81,7 @@ function rcmail_contacts_list($attrib) $javascript = sprintf("%s.gui_object('contactslist', '%s');\n", $JS_OBJECT_NAME, $attrib['id']); $javascript .= sprintf("%s.set_env('current_page', %d);\n", $JS_OBJECT_NAME, $CONTACTS_LIST['page']); $javascript .= sprintf("%s.set_env('pagecount', %d);\n", $JS_OBJECT_NAME, ceil($rowcount/$CONFIG['pagesize'])); + $javascript .= "rcmail.set_env('newcontact', '" . rcube_label('newcontact') . "');"; //$javascript .= sprintf("%s.set_env('contacts', %s);", $JS_OBJECT_NAME, array2js($a_js_message_arr)); $OUTPUT->add_script($javascript); @@ -191,4 +192,4 @@ function rcmail_get_rowcount_text($max=NULL) return $out; } -?>
\ No newline at end of file +?> diff --git a/program/steps/addressbook/ldapsearchform.inc b/program/steps/addressbook/ldapsearchform.inc new file mode 100644 index 000000000..f7d7cc9f5 --- /dev/null +++ b/program/steps/addressbook/ldapsearchform.inc @@ -0,0 +1,264 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/addressbook/ldapsearch.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | Licensed under the GNU GPL | + | | + | PURPOSE: | + | Show an ldap search form in the addressbook | + | | + +-----------------------------------------------------------------------+ + | Author: Justin Randell <justin.randell@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ +require_once 'include/rcube_ldap.inc'; + +/** + * draw the ldap public search form + */ +function rcmail_ldap_public_search_form($attrib) + { + global $CONFIG, $JS_OBJECT_NAME, $OUTPUT; + if (!$CONFIG['ldap_public']) + { + // no ldap servers to search + show_message('noldapserver', 'warning'); + rcmail_overwrite_action('add'); + return false; + } + else + { + // store some information in the session + $_SESSION['ldap_public']['server_count'] = $server_count = count($CONFIG['ldap_public']); + $_SESSION['ldap_public']['server_names'] = $server_names = array_keys($CONFIG['ldap_public']); + } + + list($form_start, $form_end) = get_form_tags($attrib); + $out = "$form_start<table id=\"ldap_public_search_table\">\n\n"; + + // search name field + $search_name = new textfield(array('name' => '_ldap_public_search_name', + 'id' => 'rcmfd_ldap_public_search_name')); + $out .= "<tr><td class=\"title\"><label for=\"rcmfd_ldap_public_search_name\">" . + rep_specialchars_output(rcube_label('ldappublicsearchname')) . + "</label></td><td>" . $search_name->show() . "</td></tr>\n"; + + + // there's more than one server to search for, show a dropdown menu + if ($server_count > 1) + { + $select_server = new select(array('name' => '_ldap_public_servers', + 'id' => 'rcfmd_ldap_public_servers')); + + $select_server->add($server_names, $server_names); + + $out .= '<tr><td class="title"><label for="rcfmd_ldap_public_servers">' . + rep_specialchars_output(rcube_label('ldappublicserverselect')) . + "</label></td><td>" . $select_server->show() . "</td></tr>\n"; + } + + // foreach configured ldap server, set up the search fields + for ($i = 0; $i < $server_count; $i++) + { + $server = $CONFIG['ldap_public'][$server_names[$i]]; + + // only display one search fields select - js takes care of the rest + if (!$i) + { + $field_name = '_ldap_public_search_field'; + $field_id = 'rcfmd_ldap_public_search_field'; + + $search_fields = new select(array('name' => $field_name, + 'id' => $field_id)); + + $search_fields->add($server['search_fields'], $server['search_fields']); + $out .= '<tr><td class="title"><label for="' . $field_id . '">' . + rep_specialchars_output(rcube_label('ldappublicsearchfield')) . + "</label></td><td>" . $search_fields->show() . "</td></tr>\n"; + + $search_type = new checkbox(array('name' => '_ldap_public_search_type', + 'id' => 'rcmfd_ldap_public_search_type', 'value' => 0)); + + $out .= '<tr id="ldap_fuzzy_search"><td class="title"><label for="rcmfd_ldap_public_search_type">' . + rep_specialchars_output(rcube_label('ldappublicsearchtype')) . + "</label></td><td>" . $search_type->show() . "</td></tr>\n"; + } + + // store the search fields in a js array for each server + $js = ''; + foreach ($server['search_fields'] as $k => $search_field) + $js .= "'$search_field', "; + + // store whether this server accepts fuzzy search as last item in array + $js .= $server['fuzzy_search'] ? "'fuzzy'" : "'exact'"; + $OUTPUT->add_script("rcmail.set_env('{$server_names[$i]}_search_fields', new Array($js));"); + } + + // add contact button label text + $OUTPUT->add_script("rcmail.set_env('addcontact', '" . rcube_label('addcontact') . "');"); + + $out .= "\n</table>$form_end"; + return $out; + } + +/** + * get search values and return ldap contacts + */ +function rcmail_ldap_public_list() + { + // just return if we are not being called from a search form + if (!isset($_POST['_action'])) + return null; + + global $CONFIG, $OUTPUT, $JS_OBJECT_NAME; + + // show no search name warning and exit + if (empty($_POST['_ldap_public_search_name']) || trim($_POST['_ldap_public_search_name']) == '') + { + show_message('nosearchname', 'warning'); + return false; + } + + // set up ldap server(s) array or bail + if ($_SESSION['ldap_public']['server_count'] > 1) + // show no ldap server warning and exit + if (empty($_POST['_ldap_public_servers'])) + { + show_message('noldappublicserver', 'warning'); + return false; + } + else + $server_name = $_POST['_ldap_public_servers']; + else if ($_SESSION['ldap_public']['server_count'] == 1) + $server_name = $_SESSION['ldap_public']['server_names'][0]; + else + return false; + + // get search parameters + $search_value = $_POST['_ldap_public_search_name']; + $search_field = $_POST['_ldap_public_search_field']; + + // only use the post var for search type if the ldap server allows 'like' + $exact = true; + if ($CONFIG['ldap_public'][$server_name]['fuzzy_search']) + $exact = isset($_POST['_ldap_public_search_type']) ? true : false; + + // perform an ldap search + $contacts = rcmail_ldap_contact_search($search_value, + $search_field, + $CONFIG['ldap_public'][$server_name], + $exact); + + // if no results, show a warning and return + if (!$contacts) + { + show_message('nocontactsreturned', 'warning'); + return false; + } + + // add id to message list table if not specified + if (!strlen($attrib['id'])) + $attrib['id'] = 'ldapAddressList'; + + // define table class + $attrib['class'] = 'records-table'; + $attrib['cellspacing'] = 0; + + // define list of cols to be displayed + $a_show_cols = array('name', 'email'); + + // create XHTML table + $out = rcube_table_output($attrib, $contacts, $a_show_cols, 'row_id'); + + // set client env + $javascript = "$JS_OBJECT_NAME.gui_object('ldapcontactslist', '{$attrib['id']}');\n"; + $OUTPUT->add_script($javascript); + + return $out; + } + +/** + * perform search for contacts from given public ldap server + */ +function rcmail_ldap_contact_search($search_value, $search_field, $server, $exact=true) + { + global $CONFIG; + + $attributes = array($server['name_field'], $server['mail_field']); + + $LDAP = new rcube_ldap(); + if ($LDAP->connect($server['hosts'], $server['port'], $server['protocol'])) + { + $filter = "$search_field=" . ($exact ? $search_value : "*$search_value*"); + $result = $LDAP->search($server['base_dn'], + $filter, + $attributes, + $server['scope'], + $sort=null); + + // add any results to contact array + if ($result['count']) + { + for ($n = 0; $n < $result['count']; $n++) + { + $contacts[$n]['name'] = $result[$n][$server['name_field']][0]; + $contacts[$n]['email'] = $result[$n][$server['mail_field']][0]; + $contacts[$n]['row_id'] = $n + 1; + } + } + } + else + return false; + + // cleanup + $LDAP->close(); + + if (!$result['count']) + return false; + + // weed out duplicate emails + for ($n = 0; $n < $result['count']; $n++) + for ($i = 0; $i < $result['count']; $i++) + if ($contacts[$i]['email'] == $contacts[$n]['email'] && $i != $n) + unset($contacts[$n]); + + return $contacts; + } + +function get_form_tags($attrib) + { + global $OUTPUT, $JS_OBJECT_NAME, $EDIT_FORM, $SESS_HIDDEN_FIELD; + + $form_start = ''; + if (!strlen($EDIT_FORM)) + { + $hiddenfields = new hiddenfield(array('name' => '_task', 'value' => $GLOBALS['_task'])); + $hiddenfields->add(array('name' => '_action', 'value' => 'ldappublicsearch')); + + if ($_GET['_framed'] || $_POST['_framed']) + $hiddenfields->add(array('name' => '_framed', 'value' => 1)); + + $form_start .= !strlen($attrib['form']) ? '<form name="form" action="./" method="post">' : ''; + $form_start .= "\n$SESS_HIDDEN_FIELD\n"; + $form_start .= $hiddenfields->show(); + } + + $form_end = (strlen($EDIT_FORM) && !strlen($attrib['form'])) ? '</form>' : ''; + $form_name = strlen($attrib['form']) ? $attrib['form'] : 'form'; + + $OUTPUT->add_script("$JS_OBJECT_NAME.gui_object('ldappublicsearchform', '$form_name');"); + + $EDIT_FORM = $form_name; + + return array($form_start, $form_end); + } + +parse_template('ldappublicsearch'); +?> diff --git a/program/steps/addressbook/save.inc b/program/steps/addressbook/save.inc index f5ba139b9..24e375ef7 100644 --- a/program/steps/addressbook/save.inc +++ b/program/steps/addressbook/save.inc @@ -19,18 +19,17 @@ */ - -$a_save_cols = array('name', 'firstname', 'surname', 'email'); - - // check input -if (empty($_POST['_name']) || empty($_POST['_email'])) +if ((empty($_POST['_name']) || empty($_POST['_email'])) && empty($_GET['_framed'])) { show_message('formincomplete', 'warning'); rcmail_overwrite_action($_POST['_cid'] ? 'show' : 'add'); return; } +// setup some vars we need +$a_save_cols = array('name', 'firstname', 'surname', 'email'); +$contacts_table = get_table_name('contacts'); // update an existing contact if ($_POST['_cid']) @@ -48,7 +47,7 @@ if ($_POST['_cid']) if (sizeof($a_write_sql)) { - $DB->query("UPDATE ".get_table_name('contacts')." + $DB->query("UPDATE $contacts_table SET changed=now(), ".join(', ', $a_write_sql)." WHERE contact_id=? AND user_id=? @@ -70,7 +69,7 @@ if ($_POST['_cid']) $a_show_cols = array('name', 'email'); $a_js_cols = array(); - $sql_result = $DB->query("SELECT * FROM ".get_table_name('contacts')." + $sql_result = $DB->query("SELECT * FROM $contacts_table WHERE contact_id=? AND user_id=? AND del<>1", @@ -104,64 +103,137 @@ if ($_POST['_cid']) else { $a_insert_cols = $a_insert_values = array(); - + // check for existing contacts - $sql_result = $DB->query("SELECT 1 FROM ".get_table_name('contacts')." - WHERE user_id=? - AND email=? - AND del<>1", - $_SESSION['user_id'], - $_POST['_email']); + $sql = "SELECT 1 FROM $contacts_table + WHERE user_id = {$_SESSION['user_id']} + AND del <> '1' "; + + // get email and name, build sql for existing user check + if (isset($_GET['_emails']) && isset($_GET['_names'])) + { + $sql .= "AND email IN ("; + $emails = explode(',', $_GET['_emails']); + $names = explode(',', $_GET['_names']); + $count = count($emails); + $n = 0; + foreach ($emails as $email) + { + $end = (++$n == $count) ? '' : ','; + $sql .= $DB->quote(strip_tags($email)) . $end; + } + $sql .= ")"; + $ldap_form = true; + } + else if (isset($_POST['_email'])) + $sql .= "AND email = " . $DB->quote(strip_tags($_POST['_email'])); + + $sql_result = $DB->query($sql); // show warning message if ($DB->num_rows($sql_result)) { show_message('contactexists', 'warning'); - $_action = 'add'; + + if ($ldap_form) + rcmail_overwrite_action('ldappublicsearch'); + else + rcmail_overwrite_action('add'); + return; } - foreach ($a_save_cols as $col) + if ($ldap_form) { - $fname = '_'.$col; - if (!isset($_POST[$fname])) - continue; - - $a_insert_cols[] = $col; - $a_insert_values[] = $DB->quote(strip_tags($_POST[$fname])); + $n = 0; + foreach ($emails as $email) + { + $DB->query("INSERT INTO $contacts_table + (user_id, name, email) + VALUES ({$_SESSION['user_id']}," . $DB->quote(strip_tags($names[$n++])) . "," . + $DB->quote(strip_tags($email)) . ")"); + $insert_id[] = $DB->insert_id(); + } } - - if (sizeof($a_insert_cols)) + else { - $DB->query("INSERT INTO ".get_table_name('contacts')." + foreach ($a_save_cols as $col) + { + $fname = '_'.$col; + if (!isset($_POST[$fname])) + continue; + + $a_insert_cols[] = $col; + $a_insert_values[] = $DB->quote(strip_tags($_POST[$fname])); + } + + if (sizeof($a_insert_cols)) + { + $DB->query("INSERT INTO $contacts_table (user_id, changed, del, ".join(', ', $a_insert_cols).") VALUES (?, now(), 0, ".join(', ', $a_insert_values).")", $_SESSION['user_id']); - $insert_id = $DB->insert_id(get_sequence_name('contacts')); + $insert_id = $DB->insert_id(get_sequence_name('contacts')); + } } if ($insert_id) { - $_action = 'show'; - $_GET['_cid'] = $insert_id; + if (!$ldap_form) + { + $_action = 'show'; + $_GET['_cid'] = $insert_id; - if ($_POST['_framed']) + if ($_POST['_framed']) + { + // add contact row or jump to the page where it should appear + $commands = sprintf("if(parent.%s)parent.", $JS_OBJECT_NAME); + $sql_result = $DB->query("SELECT * FROM $contacts_table + WHERE contact_id=? + AND user_id=?", + $insert_id, + $_SESSION['user_id']); + $commands .= rcmail_js_contacts_list($sql_result, $JS_OBJECT_NAME); + + $commands .= sprintf("if(parent.%s)parent.%s.select('%d');\n", + $JS_OBJECT_NAME, + $JS_OBJECT_NAME, + $insert_id); + + // update record count display + $commands .= sprintf("if(parent.%s)parent.%s.set_rowcount('%s');\n", + $JS_OBJECT_NAME, + $JS_OBJECT_NAME, + rcmail_get_rowcount_text()); + + $OUTPUT->add_script($commands); + } + + // show confirmation + show_message('successfullysaved', 'confirmation'); + } + else { // add contact row or jump to the page where it should appear - $commands = sprintf("if(parent.%s)parent.", $JS_OBJECT_NAME); - $sql_result = $DB->query("SELECT * FROM ".get_table_name('contacts')." - WHERE contact_id=? - AND user_id=?", - $insert_id, - $_SESSION['user_id']); - $commands .= rcmail_js_contacts_list($sql_result, $JS_OBJECT_NAME); + $commands = ''; + foreach ($insert_id as $id) + { + $sql_result = $DB->query("SELECT * FROM $contacts_table + WHERE contact_id = $id + AND user_id = {$_SESSION['user_id']}"); + + $commands .= sprintf("if(parent.%s)parent.", $JS_OBJECT_NAME); + $commands .= rcmail_js_contacts_list($sql_result, $JS_OBJECT_NAME); + $last_id = $id; + } + // display the last insert id $commands .= sprintf("if(parent.%s)parent.%s.select('%d');\n", - $JS_OBJECT_NAME, - $JS_OBJECT_NAME, - $insert_id); - + $JS_OBJECT_NAME, + $JS_OBJECT_NAME, + $last_id); + // update record count display $commands .= sprintf("if(parent.%s)parent.%s.set_rowcount('%s');\n", $JS_OBJECT_NAME, @@ -169,10 +241,11 @@ else rcmail_get_rowcount_text()); $OUTPUT->add_script($commands); - - // show confirmation - show_message('successfullysaved', 'confirmation'); + rcmail_overwrite_action('ldappublicsearch'); } + + // show confirmation + show_message('successfullysaved', 'confirmation'); } else { @@ -182,5 +255,4 @@ else } } - -?>
\ No newline at end of file +?> diff --git a/skins/default/images/buttons/ldap_act.png b/skins/default/images/buttons/ldap_act.png Binary files differnew file mode 100644 index 000000000..b09f26707 --- /dev/null +++ b/skins/default/images/buttons/ldap_act.png diff --git a/skins/default/images/buttons/ldap_pas.png b/skins/default/images/buttons/ldap_pas.png Binary files differnew file mode 100644 index 000000000..b09f26707 --- /dev/null +++ b/skins/default/images/buttons/ldap_pas.png diff --git a/skins/default/includes/ldapscripts.html b/skins/default/includes/ldapscripts.html new file mode 100644 index 000000000..0dfda6cb7 --- /dev/null +++ b/skins/default/includes/ldapscripts.html @@ -0,0 +1,73 @@ +<script type="text/javascript"> +var ldap_server_select = document.getElementById('rcfmd_ldap_public_servers'); +if (ldap_server_select) { + // attach event to ldap server drop down + ldap_server_select.onchange = function() { + updateLdapSearchFields(this); + return false; + } +} + +// update the fields on page load +updateLdapSearchFields(ldap_server_select); + +/** + * function to change the attributes of the ldap server search fields select box + * this function is triggered by an onchange event in the server select box + */ +function updateLdapSearchFields(element) { + + // get the search fields select element + var search_fields = document.getElementById('rcfmd_ldap_public_search_field'); + + // get rid of the current options for the select + for (i = search_fields.length - 1; i>=0; i--) + search_fields.remove(i); + + // get the array containing this servers search fields + var server_fields = rcmail.env[element.value + '_search_fields']; + + // add a new option for each of the possible search fields for the selected server + for (i = 0; i < server_fields.length; i++) { + + // the last array value is for fuzzy search, so skip that one + if (i < (server_fields.length - 1)) { + var new_option = document.createElement('option'); + new_option.text = server_fields[i]; + new_option.value = server_fields[i]; + + // standards compliant browsers + try { + search_fields.add(new_option, null); + } + // for the standards challenged one... + catch(e) { + search_fields.add(new_option); + } + } else { + // ok, last member of array, so check the value of fuzzy_search + var fuzzy_search = server_fields[i]; + var search_check_box = document.getElementById('rcmfd_ldap_public_search_type'); + + if (fuzzy_search == 'fuzzy') { + // we should enable the check box + if (search_check_box.disabled) + search_check_box.disabled = false; + + // make sure the checkbox is unchecked + if (search_check_box.checked) + search_check_box.checked = false; + + } else { + // we should disable the check box + if (!search_check_box.disabled) + search_check_box.disabled = true; + + // check the checkbox (just a visual clue for the user) + if (!search_check_box.checked) + search_check_box.checked = true; + } + } + } +} +</script> diff --git a/skins/default/ldapsearchform.css b/skins/default/ldapsearchform.css new file mode 100644 index 000000000..9661442eb --- /dev/null +++ b/skins/default/ldapsearchform.css @@ -0,0 +1,54 @@ +/***** RoundCube|Mail address book task styles *****/ + + +body.iframe, +{ + background-color: #F9F9F9; +} + +#ldapsearch-title +{ + height: 12px !important; +/* height: 20px; */ + padding: 4px 20px 3px 20px; + border-bottom: 1px solid #999999; + color: #333333; + font-size: 11px; + font-weight: bold; + background-color: #EBEBEB; + background-image: url(images/listheader_aqua.gif); +} + +#ldapsearch-details +{ + padding: 15px 20px 10px 20px; +} + +#ldapsearch-details table td.title +{ + color: #666666; + font-weight: bold; + text-align: right; + padding-right: 10px; +} + +#ldapAddressList +{ + width: 100%; + table-layout: fixed; + /* css hack for IE */ + width: expression(document.getElementById('addresslist').clientWidth); +} + +#ldapAddressList table +{ + border-top: 1px solid #999999; +} + +#ldap-search-results div +{ + width: 100%; + color: red; + background-color: green; +} + diff --git a/skins/default/templates/ldappublicsearch.html b/skins/default/templates/ldappublicsearch.html new file mode 100644 index 000000000..70570c06f --- /dev/null +++ b/skins/default/templates/ldappublicsearch.html @@ -0,0 +1,31 @@ +<!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> +<link rel="stylesheet" type="text/css" href="/common.css" /> +<link rel="stylesheet" type="text/css" href="/ldapsearchform.css" /> +</head> +<body class="iframe"> + +<div id="ldapsearch-title"><roundcube:label name="ldappublicsearchform" /></div> + +<div id="ldapsearch-details"> +<roundcube:object name="ldappublicsearch" size="40" /> +<p> +<roundcube:button command="ldappublicsearch" type="input" class="button" label="ldappublicsearch" /> +<input type="button" value="<roundcube:label name="cancel" />" class="button" onclick="history.back()" /> +<br /></p> +</div> + + +<div id="ldapsearch-results"> +<roundcube:object name="ldappublicaddresslist" + id="ldappublicaddresslist" + cellspacing="0" + summary="Ldap email address list" /> +</div> + +<roundcube:include file="/includes/ldapscripts.html" /> + +</body> +</html> |