- readonly LDAP sync (acount-repository SQL --> LDAP) is fully working now

- migration for contacts to LDAP
- some general addressbook fixes
This commit is contained in:
Ralf Becker 2006-07-08 00:36:23 +00:00
parent ad7972870c
commit bd7f7f417d
7 changed files with 178 additions and 27 deletions

View File

@ -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',

View File

@ -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 '<p style="margin: 0px;">'.$n.': '.$contact['n_fn'].
($contact['org_name'] ? ' ('.$contact['org_name'].')' : '')." --> LDAP</p>\n";
}
else
{
echo '<p style="margin: 0px; color: red;">'.$n.': '.$contact['n_fn'].
($contact['org_name'] ? ' ('.$contact['org_name'].')' : '').': '.$err."</p>\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 '<p style="margin: 0px;">'.$n.': '.$contact['n_fn'].
($contact['org_name'] ? ' ('.$contact['org_name'].')' : '')." --> SQL (".lang('User').")</p>\n";
}
else
{
echo '<p style="margin: 0px; color: red;">'.$n.': '.$contact['n_fn'].
($contact['org_name'] ? ' ('.$contact['org_name'].')' : '').': '.$err."</p>\n";
}
}
}
}
}

View File

@ -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']))

View File

@ -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;
}
</script>';
}
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 '<h1>'.lang('Permission denied !!!')."</h1>\n";
}
else
{
parent::migrate2ldap($_GET['type']);
echo '<p style="margin-top: 20px;"><b>'.lang('Migration finished')."</b></p>\n";
}
$GLOBALS['egw']->common->egw_footer();
}
}

View File

@ -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.

View File

@ -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.

View File

@ -59,6 +59,18 @@
<a href="addressbook/doc/README" target="_blank">README</a>
</td>
</tr>
<tr class="row_on">
<td>&nbsp;<b>{lang_Migration_to_LDAP}</b></td>
<td>
<select name="migrate">
<option value="">{lang_Select_migration_type}</option>
<option value="contacts" title="{lang_if_accounts_are_already_in_LDAP}">{lang_contacts_to_LDAP}</option>
<option value="contacts,accounts" title="{lang_use_setup_for_a_full_account-migration}">{lang_contacts_and_account_contact-data_to_LDAP}</option>
<option value="contacts,accounts-back" title="{lang_for_read_only_LDAP}">{lang_contacts_to_LDAP,_account_contact-data_to_SQL}</option>
</select>
<input type="button" onclick="if (this.form.migrate.value) document.location.href='index.php?menuaction=addressbook.uicontacts.migrate2ldap&type='+this.form.migrate.value;" value="{lang_Start}" />
</td>
</tr>
<!-- END body -->
<!-- BEGIN footer -->
<tr valign="bottom" style="height: 30px;">