diff --git a/addressbook/inc/class.so_ldap.inc.php b/addressbook/inc/class.so_ldap.inc.php index 491e368b59..75d776dfc5 100644 --- a/addressbook/inc/class.so_ldap.inc.php +++ b/addressbook/inc/class.so_ldap.inc.php @@ -31,10 +31,14 @@ define('ADDRESSBOOK_GROUP',3); class so_ldap { var $data; - //var $db_data_cols; - //var $db_key_cols; - var $groupName = 'Default'; + /** + * internal name of the id, gets mapped to uid + * + * @var string + */ + var $contacts_id='id'; + /** * @var string $accountName holds the accountname of the current user */ @@ -216,7 +220,7 @@ class so_ldap $GLOBALS['egw']->ldap =& CreateObject('phpgwapi.ldap'); } // if ldap is NOT the contact repository, we only do accounts and need to use the account-data - if ($GLOBALS['egw_info']['server']['contact_repository'] != 'ldap') + if (substr($GLOBALS['egw_info']['server']['contact_repository'],-4) != 'ldap') // not (ldap or sql-ldap) { $GLOBALS['egw_info']['server']['ldap_contact_host'] = $GLOBALS['egw_info']['server']['ldap_host']; $GLOBALS['egw_info']['server']['ldap_contact_context'] = $GLOBALS['egw_info']['server']['ldap_context']; @@ -264,14 +268,22 @@ class so_ldap /** * reads contact data * - * @param string $contact_id contact_id or 'account:'.account_id + * @param string/array $contact_id contact_id or array with values for id or account_id * @return array/boolean data if row could be retrived else False */ function read($contact_id) { - $contact_id = ldap::quote($contact_id); + if (is_array($contact_id) && isset($contact_id['account_id']) || substr($contact_id,0,8) == 'account:') + { + $filter = 'uidNumber='.(int)(is_array($contact_id) ? $contact_id['account_id'] : substr($contact_id,8)); + } + else + { + $contact_id = ldap::quote(is_array($contact_id) ? $contact_id['id'] : $contact_id); + $filter = "(|(entryUUID=$contact_id)(uid=$contact_id))"; + } $rows = $this->_searchLDAP($GLOBALS['egw_info']['server']['ldap_contact_context'], - "(|(entryUUID=$contact_id)(uid=$contact_id))", $this->all_attributes, ADDRESSBOOK_ALL); + $filter, $this->all_attributes, ADDRESSBOOK_ALL); return $rows ? $rows[0] : false; } @@ -305,17 +317,25 @@ class so_ldap } $baseDN = 'cn='. ldap::quote($cn) .','.($data['owner'] < 0 ? $this->sharedContactsDN : $this->personalContactsDN); } - elseif ($GLOBALS['egw_info']['user']['apps']['admin']) + // only an admin or the user itself is allowed to change the data of an account + elseif ($data['account_id'] && ($GLOBALS['egw_info']['user']['apps']['admin'] || + $data['account_id'] == $GLOBALS['egw_info']['user']['account_id'])) { // account $baseDN = $GLOBALS['egw_info']['server']['ldap_context']; $cn = false; // we need an admin connection $this->ds = $GLOBALS['egw']->ldap->ldapConnect(); + + // for sql-ldap we need to account_lid/uid as id, NOT the contact_id in id! + if ($GLOBALS['egw_info']['server']['contact_repository'] == 'sql-ldap') + { + $data['id'] = $GLOBALS['egw']->accounts->id2name($data['account_id']); + } } else { - return true; // only admin is allowd to write accounts! + return true; // only admin or the user itself is allowd to write accounts! } // check if $baseDN exists. If not create it @@ -795,19 +815,19 @@ class so_ldap { // personal addressbook $contact['owner'] = $GLOBALS['egw']->accounts->name2id($matches[1],'account_lid','u'); - $contact['private'] = true; + $contact['private'] = 1; } elseif(preg_match('/cn=([^,]+),'.preg_quote($this->sharedContactsDN).'$/i',$entry['dn'],$matches)) { // group addressbook $contact['owner'] = $GLOBALS['egw']->accounts->name2id($matches[1],'account_lid','g'); - $contact['private'] = false; + $contact['private'] = 0; } else { // accounts $contact['owner'] = 0; - $contact['private'] = false; + $contact['private'] = 0; } foreach(array( 'creatorsname' => 'creator', diff --git a/addressbook/inc/class.socontacts.inc.php b/addressbook/inc/class.socontacts.inc.php index d81c6366d9..9d9c3835a3 100755 --- a/addressbook/inc/class.socontacts.inc.php +++ b/addressbook/inc/class.socontacts.inc.php @@ -219,7 +219,6 @@ class socontacts if ($this->account_repository != $this->contact_repository) { $this->so_accounts =& CreateObject('addressbook.so_ldap'); - $this->so_accounts->contacts_id = 'id'; $this->account_cols_to_search = $this->ldap_search_attributes; } else @@ -238,7 +237,6 @@ class socontacts // ToDo: it should be the other way arround, the backend should set the grants it uses $this->somain->grants =& $this->grants; - $this->somain->contacts_id = 'id'; $this->soextra =& CreateObject('etemplate.so_sql'); $this->soextra->so_sql('phpgwapi',$this->extra_table); @@ -265,6 +263,10 @@ class socontacts */ function read_customfields($ids) { + if ($this->contact_repository == 'ldap') + { + return array(); // ldap does not support custom-fields (non-nummeric uid) + } foreach($ids as $key => $id) { if (!(int)$id) unset($ids[$key]); @@ -327,7 +329,7 @@ class socontacts { // LDAP uses the uid attributes for the contact-id (dn), // which need to be the account_lid for accounts! - $contact['id'] = $GLOBALS['egw']->account->id2name($contact['account_id']); + $contact['id'] = $GLOBALS['egw']->accounts->id2name($contact['account_id']); } ExecMethod('addressbook.so_ldap.delete',$contact); } @@ -355,7 +357,15 @@ class socontacts } else { + // contact_repository sql-ldap (accounts in ldap) the person_id is the uid (account_lid) + // for the sql write here we need to find out the existing contact_id + if ($this->contact_repository == 'sql-ldap' && $contact['id'] && !is_numeric($contact['id']) && + $contact['account_id'] && ($old = $this->somain->read(array('account_id' => $contact['account_id'])))) + { + $contact['id'] = $old['id']; + } $this->somain->data = $this->data2db($contact); + if (!($error_nr = $this->somain->save())) { $contact['id'] = $this->somain->data['id']; @@ -367,7 +377,7 @@ class socontacts { // LDAP uses the uid attributes for the contact-id (dn), // which need to be the account_lid for accounts! - $data['id'] = $GLOBALS['egw']->account->id2name($contact['account_id']); + $data['id'] = $GLOBALS['egw']->accounts->id2name($contact['account_id']); } ExecMethod('addressbook.so_ldap.save',$data); } @@ -407,10 +417,9 @@ class socontacts */ function read($contact_id) { - if (substr($contact_id,0,8) == 'account:' && - (!($contact_id = $GLOBALS['egw']->accounts->id2name((int) substr($contact_id,8),'person_id')))) + if (!is_array($contact_id) && substr($contact_id,0,8) == 'account:') { - return false; + $contact_id = array('account_id' => (int) substr($contact_id,8)); } // read main data $backend =& $this->get_backend($contact_id); @@ -418,14 +427,13 @@ class socontacts { return $contact; } - // read customfields - $keys = array( - $this->extra_id => $contact['id'], - $this->extra_owner => $contact['owner'], - ); - if ($this->customfields) // try reading customfields only if we have some + // try reading customfields only if we have some (none for LDAP!) + if ($this->customfields && $this->contact_repository != 'ldap') { - $customfields = $this->soextra->search($keys,false); + $customfields = $this->soextra->search(array( + $this->extra_id => $contact['id'], + $this->extra_owner => $contact['owner'], + ),false); foreach ((array)$customfields as $field) { $contact['#'.$field[$this->extra_key]] = $field[$this->extra_value]; @@ -862,4 +870,69 @@ error_log("socontacts::search(".print_r($criteria,true).",'$only_keys','$order_b //echo "unsupported fields=";_debug_array(array_diff($all_fields,$supported_fields)); return array_diff($all_fields,$supported_fields); } + + /** + * Migrates an SQL contact storage to LDAP or SQL-LDAP + * + * @param string $type "contacts" (default), "contacts+accounts" or "contacts+accounts-back" (sql-ldap!) + */ + function migrate2ldap($type) + { + $sql_contacts =& CreateObject('addressbook.socontacts_sql'); + $ldap_contacts =& CreateObject('addressbook.so_ldap'); + + $start = $n = 0; + $num = 100; + while (($contacts = $sql_contacts->search(false,false,'n_family,n_given','','',false,'AND', + array($start,$num),$type != 'contacts,accounts' ? array('contact_owner != 0') : false))) + { + foreach($contacts as $contact) + { + if ($contact['account_id']) $contact['id'] = $GLOBALS['egw']->accounts->id2name($contact['account_id']); + + $ldap_contacts->data = $contact; + $n++; + if (!($err = $ldap_contacts->save())) + { + echo '

'.$n.': '.$contact['n_fn']. + ($contact['org_name'] ? ' ('.$contact['org_name'].')' : '')." --> LDAP

\n"; + } + else + { + echo '

'.$n.': '.$contact['n_fn']. + ($contact['org_name'] ? ' ('.$contact['org_name'].')' : '').': '.$err."

\n"; + } + } + $start += $num; + } + if ($type == 'contacts,accounts-back') // migrate the accounts to sql + { + foreach($ldap_contacts->search(false,false,'n_family,n_given','','',false,'AND', + false,array('owner' => 0)) as $contact) + { + if ($contact['jpegphoto']) // photo is NOT read by LDAP backend on search, need to do an extra read + { + $contact = $ldap_contacts->read($contact['id']); + } + unset($contact['id']); // ldap uid/account_lid + if ($contact['account_id'] && ($old = $sql_contacts->read(array('account_id' => $contact['account_id'])))) + { + $contact['id'] = $old['id']; + } + $sql_contacts->data = $contact; + + $n++; + if (!($err = $sql_contacts->save())) + { + echo '

'.$n.': '.$contact['n_fn']. + ($contact['org_name'] ? ' ('.$contact['org_name'].')' : '')." --> SQL (".lang('User').")

\n"; + } + else + { + echo '

'.$n.': '.$contact['n_fn']. + ($contact['org_name'] ? ' ('.$contact['org_name'].')' : '').': '.$err."

\n"; + } + } + } + } } diff --git a/addressbook/inc/class.socontacts_sql.inc.php b/addressbook/inc/class.socontacts_sql.inc.php index 79fe1c2584..764f1ef655 100644 --- a/addressbook/inc/class.socontacts_sql.inc.php +++ b/addressbook/inc/class.socontacts_sql.inc.php @@ -27,6 +27,13 @@ class socontacts_sql extends so_sql var $account_repository = 'sql'; var $contact_repository = 'sql'; + /** + * internal name of the id, gets mapped to uid + * + * @var string + */ + var $contacts_id='id'; + function socontacts_sql() { $this->so_sql('phpgwapi','egw_addressbook',null,'contact_'); // calling the constructor of the extended class @@ -219,7 +226,7 @@ class socontacts_sql extends so_sql unset($filter['cat_id']); } // add filter for read ACL in sql, if user is NOT the owner of the addressbook - if (!(isset($filter['owner']) && $filter['owner'] == $GLOBALS['egw_info']['user']['account_id'])) + if (isset($this->grants) && !(isset($filter['owner']) && $filter['owner'] == $GLOBALS['egw_info']['user']['account_id'])) { // we have no private grants in addressbook at the moment, they have then to be added here too if (isset($filter['owner'])) diff --git a/addressbook/inc/class.uicontacts.inc.php b/addressbook/inc/class.uicontacts.inc.php index cd3cb191a0..fad8460b5d 100644 --- a/addressbook/inc/class.uicontacts.inc.php +++ b/addressbook/inc/class.uicontacts.inc.php @@ -31,6 +31,7 @@ class uicontacts extends bocontacts 'index' => True, 'photo' => True, 'emailpopup'=> True, + 'migrate2ldap' => True, ); var $prefs; /** @@ -1350,4 +1351,22 @@ $readonlys['button[vcard]'] = true; } '; } + + function migrate2ldap() + { + $GLOBALS['egw_info']['flags']['app_header'] = lang('Addressbook').' - '.lang('Migration to LDAP'); + $GLOBALS['egw']->common->egw_header(); + parse_navbar(); + + if (!$GLOBALS['egw_info']['user']['apps']['admin']) + { + echo '

'.lang('Permission denied !!!')."

\n"; + } + else + { + parent::migrate2ldap($_GET['type']); + echo '

'.lang('Migration finished')."

\n"; + } + $GLOBALS['egw']->common->egw_footer(); + } } diff --git a/addressbook/setup/phpgw_de.lang b/addressbook/setup/phpgw_de.lang index a07667ab68..64ff2e8ba8 100644 --- a/addressbook/setup/phpgw_de.lang +++ b/addressbook/setup/phpgw_de.lang @@ -70,6 +70,9 @@ contact id addressbook de Kontakt ID contact repository admin de Speicherort Kontakte contact saved addressbook de Kontakt gespeichert contact settings admin de Kontakt Einstellungen +contacts and account contact-data to ldap admin de Kontakte und Kontaktdaten der Benutzer nach LDAP +contacts to ldap admin de Kontakte nach LDAP +contacts to ldap, account contact-data to sql admin de Kontakte nach LDAP, Kontaktdaten der Benutzer nach SQL copied by %1, from record #%2. addressbook de Kopiert von %1, vom Datensatz Nr. %2. copy a contact and edit the copy addressbook de Kopiert einen Kontakt und bearbeitet dann die Kopie country common de Land @@ -125,6 +128,7 @@ field name addressbook de Feldname fields for the csv export addressbook de Felder für den CSV Export fields to show in address list addressbook de Felder, die in der Adressliste angezeigt werden sollen fieldseparator addressbook de Feldtrenner +for read only ldap admin de für nur lesendes LDAP freebusy uri addressbook de Freebusy URI full name addressbook de vollständiger Name geo addressbook de GEO @@ -140,6 +144,7 @@ home state addressbook de Bundesland privat home street addressbook de Straße privat home zip code addressbook de PLZ privat icon addressbook de Icon +if accounts are already in ldap admin de wenn die Benutzer bereits im LDAP sind import addressbook de Import import contacts addressbook de Kontakte importieren import csv-file into addressbook addressbook de Import CSV-Datei ins Adressbuch @@ -169,6 +174,8 @@ locations addressbook de Standorte mark records as private addressbook de Eintrag als Privat kennzeichnen message phone addressbook de Anrufbeantworter middle name addressbook de Zweiter Vorname +migration finished addressbook de Migration beendet +migration to ldap admin de Migration nach LDAP mobile addressbook de Mobil mobile phone addressbook de Mobiltelefon modem phone addressbook de Modem @@ -214,6 +221,7 @@ select a view addressbook de Eine Ansicht ausw select addressbook type addressbook de Typ des Adressbuchs auswählen select all addressbook de Alles auswählen select an action or addressbook to move to addressbook de Befehl oder Adressbuch zum Verschieben auswählen +select migration type admin de Migrationstyp auswählen select multiple contacts for a further action addressbook de Mehrere Adressen für weiteren Befehl auswählen select phone number as prefered way of contact addressbook de Telefonnummer als präferierten Kontaktweg auswählen select the type of conversion addressbook de Typ der Umwandlung auswählen @@ -224,6 +232,7 @@ show a column for %1 addressbook de Zeige eine %1 Spalte show birthday reminders on main screen addressbook de Geburtstagserinnerungen auf der Startseite anzeigen show the contacts of this organisation addressbook de Kontakte dieser Organisation anzeigen size of popup (wxh, eg.400x300, if a popup should be used) admin de Größe des Popup (WxH, zB. 400x300, falls ein Popup verwendet werden soll) +start admin de Starten startrecord addressbook de Startdatensatz state common de Bundesland street common de Straße @@ -244,6 +253,7 @@ update a single entry by passing the fields. addressbook de Aktualisiert einen e upload or delete the photo addressbook de Foto hochladen oder löschen url to link telephone numbers to (use %1 for the number) admin de URL mit denen Telefonnummern verlinkt werden sollen (%1 für die Nummber verwenden) use country list addressbook de Länderliste benutzen +use setup for a full account-migration admin de für eine komplette Benutzer Migration setup verwenden used for links and for the own sorting of the list addressbook de wird für Verküpfungen und die eigene Sortierung der Liste benützt vcard common de VCard vcards require a first name entry. addressbook de VCards benötigen einen Vornamen. diff --git a/addressbook/setup/phpgw_en.lang b/addressbook/setup/phpgw_en.lang index dabe7f264d..516c00998c 100644 --- a/addressbook/setup/phpgw_en.lang +++ b/addressbook/setup/phpgw_en.lang @@ -70,6 +70,9 @@ contact id addressbook en Contact ID contact repository admin en Contact repository contact saved addressbook en Contact saved contact settings admin en Contact Settings +contacts and account contact-data to ldap admin en contacts and account contact-data to LDAP +contacts to ldap admin en contacts to LDAP +contacts to ldap, account contact-data to sql admin en contacts to LDAP, account contact-data to SQL copied by %1, from record #%2. addressbook en Copied by %1, from record #%2. copy a contact and edit the copy addressbook en Copy a contact and edit the copy country common en Country @@ -125,6 +128,7 @@ field name addressbook en Field Name fields for the csv export addressbook en Fields for the CSV export fields to show in address list addressbook en Fields to show in address list fieldseparator addressbook en Fieldseparator +for read only ldap admin en for read only LDAP freebusy uri addressbook en Freebusy URI full name addressbook en Full Name geo addressbook en GEO @@ -140,6 +144,7 @@ home state addressbook en Home State home street addressbook en Home Street home zip code addressbook en Home ZIP Code icon addressbook en Icon +if accounts are already in ldap admin en if accounts are already in LDAP import addressbook en Import import contacts addressbook en Import Contacts import csv-file into addressbook addressbook en Import CSV-File into Addressbook @@ -169,6 +174,8 @@ locations addressbook en locations mark records as private addressbook en Mark records as private message phone addressbook en Message Phone middle name addressbook en Middle Name +migration finished addressbook en Migration finished +migration to ldap admin en Migration to LDAP mobile addressbook en Mobile mobile phone addressbook en Mobile Phone modem phone addressbook en Modem Phone @@ -214,6 +221,7 @@ select a view addressbook en Select a view select addressbook type addressbook en Select addressbook type select all addressbook en Select all select an action or addressbook to move to addressbook en Select an action or addressbook to move to +select migration type admin en Select migration type select multiple contacts for a further action addressbook en Select multiple contacts for a further action select phone number as prefered way of contact addressbook en select phone number as prefered way of contact select the type of conversion addressbook en Select the type of conversion @@ -224,6 +232,7 @@ show a column for %1 addressbook en Show a column for %1 show birthday reminders on main screen addressbook en Show birthday reminders on main screen show the contacts of this organisation addressbook en Show the contacts of this organisation size of popup (wxh, eg.400x300, if a popup should be used) admin en Size of popup (WxH, eg.400x300, if a popup should be used) +start admin en Start startrecord addressbook en Startrecord state common en State street common en Street @@ -244,6 +253,7 @@ update a single entry by passing the fields. addressbook en Update a single entr upload or delete the photo addressbook en Upload or delete the photo url to link telephone numbers to (use %1 for the number) admin en URL to link telephone numbers to (use %1 for the number) use country list addressbook en Use Country List +use setup for a full account-migration admin en use setup for a full account-migration used for links and for the own sorting of the list addressbook en used for links and for the own sorting of the list vcard common en VCard vcards require a first name entry. addressbook en VCards require a first name entry. diff --git a/addressbook/templates/default/config.tpl b/addressbook/templates/default/config.tpl index 2b926e876a..7a8f9b8183 100644 --- a/addressbook/templates/default/config.tpl +++ b/addressbook/templates/default/config.tpl @@ -59,6 +59,18 @@ README + +  {lang_Migration_to_LDAP} + + + + +