WIP ADS/LDAP account-import:

- refactored to a class
- added async import and logging
--> ToDo: groups, deleting of users and testing with LDAP
This commit is contained in:
ralf 2022-06-28 16:58:38 +02:00
parent 65ed3e090c
commit e28329b1d7
5 changed files with 444 additions and 199 deletions

383
api/src/Accounts/Import.php Normal file
View File

@ -0,0 +1,383 @@
<?php
/**
* EGroupware Setup - Account import from LDAP (incl. ADS) to SQL
*
* @link https://www.egroupware.org
* @package setup
* @author Ralf Becker <rb@egroupware.org>
* @license https://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
namespace EGroupware\Api\Accounts;
use EGroupware\Api;
class Import
{
public function __construct()
{
// if we run from setup, we need to take care of loading db and egw_info/server
if (isset($GLOBALS['egw_setup']))
{
if (!is_object($GLOBALS['egw_setup']->db))
{
$GLOBALS['egw_setup']->loaddb();
}
$GLOBALS['egw_info']['server'] += Api\Config::read('phpgwapi');
}
}
/**
* @param bool $initial_import true: initial sync, false: incremental sync
* @param callable|null $logger function($str, $level) level: "debug", "detail", "info", "error" or "fatal"
* @return array with int values for keys 'created', 'updated', 'uptodate', 'errors' and string 'result'
* @throws \Exception also gets logged as level "fatal"
* @throws \InvalidArgumentException if not correctly configured
*/
public function run(bool $initial_import=true, callable $logger=null)
{
try {
if (!isset($logger))
{
$logger = static function($str, $level){};
}
// determine from where we migrate to what
if (!in_array($source = $GLOBALS['egw_info']['server']['account_import_source'], ['ldap', 'ads']))
{
throw new \InvalidArgumentException("Invalid account_import_source='{$GLOBALS['egw_info']['server']['account_import_source']}'!");
}
if (!in_array($type = $GLOBALS['egw_info']['server']['account_import_type'], ['users', 'users_groups']))
{
throw new \InvalidArgumentException("Invalid account_import_type='{$GLOBALS['egw_info']['server']['account_import_type']}'!");
}
if (!in_array($delete = $GLOBALS['egw_info']['server']['account_import_delete'], ['yes', 'deactivate', 'no']))
{
throw new \InvalidArgumentException("Invalid account_import_delete='{$GLOBALS['egw_info']['server']['account_import_delete']}'!");
}
if (!$initial_import && empty($GLOBALS['egw_info']['server']['account_import_lastrun']))
{
throw new \InvalidArgumentException(lang("You need to run the inital import first!"));
}
$class = 'EGroupware\\Api\\Contacts\\'.ucfirst($source);
/** @var Api\Contacts\Ldap $contacts */
$contacts = new $class($GLOBALS['egw_info']['server']);
$contacts_sql = new Api\Contacts\Sql();
$class = 'EGroupware\\Api\\Accounts\\'.ucfirst($source);
/** @var Api\Accounts\Ldap $accounts */
$accounts = new $class(new Api\Accounts(['account_repository' => $source]+$GLOBALS['egw_info']['server']));
$accounts_sql = new Api\Accounts\Sql(new Api\Accounts(['account_repository' => 'sql']+$GLOBALS['egw_info']['server']));
Api\Accounts::cache_invalidate(); // to not get any cached data eg. from the wrong backend
$filter = [
'owner' => '0',
];
if (!$initial_import)
{
$filter[] = 'modified>='.$GLOBALS['egw_info']['server']['account_import_lastrun'];
}
$last_modified = null;
$start_import = time();
$created = $updated = $uptodate = $errors = 0;
$cookie = '';
$start = ['', 5, &$cookie]; // cookie must be a reference!
do
{
foreach ($contacts->search('', false, '', 'account_lid', '', '', 'AND', $start, $filter) as $contact)
{
$new = null;
if (!isset($last_modified) || (int)$last_modified < (int)$contact['modified'])
{
$last_modified = $contact['modified'];
}
$account = $accounts->read($contact['account_id']);
$logger(json_encode($contact + $account), 'debug');
// check if account exists in sql
if (!($account_id = $accounts_sql->name2id($account['account_lid'])))
{
$sql_account = $account;
if ($accounts_sql->save($sql_account, true) > 0)
{
$logger("Successful created user '$account[account_lid]' (#$account_id)", 'detail');
}
else
{
$logger("Error creaing user '$account[account_lid]' (#$account_id)", 'error');
$errors++;
continue;
}
}
elseif ($account_id < 0)
{
throw new \Exception("User '$account[account_lid]' already exists as group!");
}
elseif (!($sql_account = $accounts_sql->read($account_id)))
{
throw new \Exception("User '$account[account_lid]' (#$account_id) should exist, but not found!");
}
else
{
// ignore LDAP specific fields, and empty fields
$relevant = array_filter(array_intersect_key($account, $sql_account), static function ($attr) {
return $attr !== null && $attr !== '';
});
unset($relevant['person_id']); // is always different as it's the UID, no need to consider
$to_update = $relevant + $sql_account;
// fix accounts without firstname
if (!isset($to_update['account_firstname']) && $to_update['account_lastname'] === $to_update['account_fullname'])
{
$to_update['account_firstname'] = null;
}
if (($diff = array_diff_assoc($to_update, $sql_account)))
{
if ($accounts_sql->save($to_update) > 0)
{
$logger("Successful updated user '$account[account_lid]' (#$account_id): " .
json_encode($diff, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), 'detail');
if (!$new) $new = false;
}
else
{
$logger("Error updating user '$account[account_lid]' (#$account_id)", 'error');
$errors++;
continue;
}
}
else
{
$logger("User '$account[account_lid]' (#$account_id) already up to date", 'debug');
}
}
if (!($sql_contact = $contacts_sql->read(['account_id' => $account_id])))
{
$sql_contact = $contact;
unset($sql_contact['id']); // LDAP contact-id is the UID!
if (!$contacts_sql->save($sql_contact))
{
$sql_contact['id'] = $contacts_sql->data['id'];
$logger("Successful created contact for user '$account[account_lid]' (#$account_id)", 'detail');
$new = true;
}
else
{
$logger("Error creating contact for user '$account[account_lid]' (#$account_id)", 'error');
$errors++;
continue;
}
}
else
{
$to_update = array_merge($sql_contact, array_filter($contact, static function ($attr) {
return $attr !== null && $attr !== '';
}));
$to_update['id'] = $sql_contact['id'];
if (($diff = array_diff_assoc($to_update, $sql_contact)))
{
if ($contacts_sql->save($to_update) === 0)
{
$logger("Successful updated contact data of '$account[account_lid]' (#$account_id): ".
json_encode($diff, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE), 'detail');
if (!$new) $new = false;
}
else
{
$logger("Error updating contact data of '$account[account_lid]' (#$account_id)", 'error');
++$errors;
continue;
}
}
else
{
$logger("Contact data of '$account[account_lid]' (#$account_id) already up to date", 'debug');
}
}
if ($new)
{
++$created;
}
elseif ($new === false)
{
++$updated;
}
else
{
++$uptodate;
}
}
}
while ($start[2] !== '');
$last_run = max($start_import-1, $last_modified);
Api\Config::save_value('account_import_lastrun', $last_run, 'phpgwapi');
$str = gmdate('Y-m-d H:i:s', $last_run). ' UTC';
if (!$errors)
{
$logger("Setting new incremental import time to: $str ($last_run)", 'detail');
}
if ($created || $updated || $errors)
{
$result = "Created $created and updated $updated users, with $errors errors.";
}
else
{
$result = "All users are up-to-date.";
}
$logger($result, 'info');
if ($initial_import && self::installAsyncJob())
{
$logger('Aync job for periodic import installed', 'info');
}
}
catch(\Exception $e) {
$logger($e->getMessage(), 'fatal');
throw $e;
}
return [
'created' => $created,
'updated' => $updated,
'uptodate' => $uptodate,
'errors' => $errors,
'result' => $result,
];
}
/**
* Hook called when setup configuration is being stored:
* - install/removing cron job to periodic import accounts from LDAP/ADS
*
* @param array $location key "newsettings" with reference to changed settings from setup > configuration
* @throws \Exception for errors
*/
public static function setupConfig(array $location)
{
$config =& $location['newsettings'];
// check if periodic import is configured AND initial sync already done
foreach(['account_import_type', 'account_import_source', 'account_import_frequency'] as $name)
{
if (empty($config[$name]))
{
self::installAsyncJob();
return;
}
}
if (empty(Api\Config::read('phpgwapi')['account_import_lastrun']))
{
self::installAsyncJob();
}
self::installAsyncJob((float)$config['account_import_frequency'], $config['account_import_time']);
}
const ASYNC_JOB_ID = 'AccountsImport';
/**
* Install async job for periodic import, if configured
*
* @param float $frequency
* @param string|null $time
* @return bool true: job installed, false: job canceled, if it was already installed
*/
protected static function installAsyncJob(float $frequency=0.0, string $time=null)
{
$async = new Api\Asyncservice();
$async->cancel_timer(self::ASYNC_JOB_ID);
if (empty($frequency) && !empty($time) && preg_match('/^\d{2}:\d{2}$/', $time))
{
$frequency = 24;
}
if ($frequency > 0.0)
{
[$hour, $min] = explode(':', $time ?: '00:00');
$times = ['hour' => (int)$hour, 'min' => (int)$min];
if ($frequency >= 36)
{
$times['day'] = '*/'.round($frequency/24.0); // 48h => day: */2
}
elseif ($frequency >= 24)
{
$times['day'] = '*';
}
elseif ($frequency >= 1)
{
$times['hour'] = round($frequency) == 1 ? '*' : '*/'.round($frequency);
}
elseif ($frequency >= .1)
{
$times = ['min' => '*/'.(5*round(12*$frequency))]; // .1 => */5, .5 => */30
}
$async->set_timer($times, self::ASYNC_JOB_ID, self::class.'::async');
return true;
}
return false;
}
const LOG_FILE = 'setup/account-import.log';
/**
* Run incremental import via async job
*
* @return void
*/
public static function async()
{
try {
$import = new self();
$log = $GLOBALS['egw_info']['server']['files_dir'].'/'.self::LOG_FILE;
if (!file_exists($dir=dirname($log)) && !mkdir($dir) || !is_dir($dir) ||
!($fp = fopen($log, 'a+')))
{
$logger = static function($str, $level)
{
if (!in_array($level, ['debug', 'detail']))
{
error_log(__METHOD__.' '.strtoupper($level).' '.$str);
}
};
}
else
{
$logger = static function($str, $level) use ($fp)
{
if (!in_array($level, ['debug', 'detail']))
{
fwrite($fp, date('Y-m-d H:i:s O').' '.strtoupper($level).' '.$str."\n");
}
};
}
$logger(date('Y-m-d H:i:s O').' LDAP account import started', 'info');
$import->run(false, $logger);
$logger(date('Y-m-d H:i:s O').' LDAP account import finished'.(!empty($fp)?"\n":''), 'info');
}
catch (\InvalidArgumentException $e) {
_egw_log_exception($e);
// disable async job, something is not configured correct
self::installAsyncJob();
$logger('Async job for periodic import canceled', 'fatal');
}
catch (\Exception $e) {
_egw_log_exception($e);
}
if (!empty($fp)) fclose($fp);
}
/**
* Tail the async import log
*
* @return void
* @throws Api\Exception\WrongParameter
* @todo get this working in setup
*/
public function showLog()
{
echo (new Api\Framework\Minimal())->header(['pngfix' => '']);
$tailer = new Api\Json\Tail(self::LOG_FILE);
echo $tailer->show();
}
}

View File

@ -2,12 +2,10 @@
/**
* EGroupware Setup - Account import from LDAP (incl. ADS) to SQL
*
* The migration is done from the account-repository configured for EGroupware!
*
* @link http://www.egroupware.org
* @link https://www.egroupware.org
* @package setup
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @author Ralf Becker <rb@egroupware.org>
* @license https://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
use EGroupware\Api;
@ -28,204 +26,33 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && $_POST)
Api\Csrf::validate($_POST['csrf_token'], __FILE__);
}
// determine from where we migrate to what
if (!is_object($GLOBALS['egw_setup']->db))
{
$GLOBALS['egw_setup']->loaddb();
}
// Load configuration values account_repository and auth_type, as setup has not yet done so
foreach($GLOBALS['egw_setup']->db->select($GLOBALS['egw_setup']->config_table,'config_name,config_value',
"config_name LIKE 'ldap%' OR config_name LIKE 'account_%' OR config_name LIKE '%encryption%' OR ".
"config_name IN ('auth_type','install_id','mail_suffix') OR config_name LIKE 'ads_%' OR config_name LIKE 'account_import_%'",
__LINE__,__FILE__) as $row)
{
$GLOBALS['egw_info']['server'][$row['config_name']] = $row['config_value'];
}
try {
if (!in_array($source = $GLOBALS['egw_info']['server']['account_import_source'], ['ldap', 'ads']))
$import = new Api\Accounts\Import();
if (!empty($_GET['log']))
{
throw new \InvalidArgumentException("Invalid account_import_source='{$GLOBALS['egw_info']['server']['account_import_source']}'!");
$import->showLog();
return;
}
if (!in_array($type = $GLOBALS['egw_info']['server']['account_import_type'], ['users', 'users_groups']))
$import->run(!empty($_GET['initial']) && $_GET['initial'] !== 'false', static function($str, $level)
{
throw new \InvalidArgumentException("Invalid account_import_type='{$GLOBALS['egw_info']['server']['account_import_type']}'!");
}
if (!in_array($delete = $GLOBALS['egw_info']['server']['account_import_delete'], ['yes', 'deactivate', 'no']))
{
throw new \InvalidArgumentException("Invalid account_import_delete='{$GLOBALS['egw_info']['server']['account_import_delete']}'!");
}
if (!($initial_import=!empty($_REQUEST['initial'])) && empty($GLOBALS['egw_info']['server']['account_import_lastrun']))
{
throw new \InvalidArgumentException(lang("You need to run the inital import first!"));
}
$class = 'EGroupware\\Api\\Contacts\\'.ucfirst($source);
/** @var Api\Contacts\Ldap $contacts */
$contacts = new $class($GLOBALS['egw_info']['server']);
$contacts_sql = new Api\Contacts\Sql();
$class = 'EGroupware\\Api\\Accounts\\'.ucfirst($source);
/** @var Api\Accounts\Ldap $accounts */
$accounts = new $class(new Api\Accounts(['account_repository' => $source]+$GLOBALS['egw_info']['server']));
$accounts_sql = new Api\Accounts\Sql(new Api\Accounts(['account_repository' => 'sql']+$GLOBALS['egw_info']['server']));
Api\Accounts::cache_invalidate(); // to not get any cached data eg. from the wrong backend
$filter = [
'owner' => '0',
];
if (!$initial_import)
{
$filter[] = 'modified>='.$GLOBALS['egw_info']['server']['account_import_lastrun'];
}
$last_modified = null;
$start_import = time();
$created = $updated = $uptodate = $errors = 0;
$cookie = '';
$start = ['', 5, &$cookie];
do
{
foreach ($contacts->search('', false, '', 'account_lid', '', '', 'AND', $start, $filter) as $contact)
switch($level)
{
$new = null;
if (!isset($last_modified) || (int)$last_modified < (int)$contact['modified'])
{
$last_modified = $contact['modified'];
}
$account = $accounts->read($contact['account_id']);
echo "<p>" . json_encode($contact + $account) . "</p>\n";
// check if account exists in sql
if (!($account_id = $accounts_sql->name2id($account['account_lid'])))
{
$sql_account = $account;
if ($accounts_sql->save($account, true) > 0)
{
echo "<p>Successful created user '$account[account_lid]' (#$account_id)<br/>\n";
$new;
}
else
{
echo "<p><b>Error creaing user '$account[account_lid]' (#$account_id)<br/>\n";
$errors++;
continue;
}
}
elseif ($account_id < 0)
{
throw new \Exception("User '$account[account_lid]' already exists as group!");
}
elseif (!($sql_account = $accounts_sql->read($account_id)))
{
throw new \Exception("User '$account[account_lid]' (#$account_id) should exist, but not found!");
}
else
{
// ignore LDAP specific fields, and empty fields
$relevant = array_filter(array_intersect_key($account, $sql_account), static function ($attr) {
return $attr !== null && $attr !== '';
});
unset($relevant['person_id']); // is always different as it's the UID, no need to consider
$to_update = $relevant + $sql_account;
// fix accounts without firstname
if (!isset($to_update['account_firstname']) && $to_update['account_lastname'] === $to_update['account_fullname'])
{
$to_update['account_firstname'] = null;
}
if (($diff = array_diff_assoc($to_update, $sql_account)))
{
if ($accounts_sql->save($to_update) > 0)
{
echo "<p>Successful updated user '$account[account_lid]' (#$account_id): " . json_encode($diff, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "<br/>\n";
if (!$new) $new = false;
}
else
{
echo "<p><b>Error updating user '$account[account_lid]' (#$account_id)</b><br/>\n";
$errors++;
continue;
}
}
else
{
echo "<p>User '$account[account_lid]' (#$account_id) already up to date<br/>\n";
}
}
if (!($sql_contact = $contacts_sql->read(['account_id' => $account_id])))
{
$sql_contact = $contact;
unset($sql_contact['id']); // LDAP contact-id is the UID!
if (!$contacts_sql->save($sql_contact))
{
$sql_contact['id'] = $contacts_sql->data['id'];
echo "Successful created contact for user '$account[account_lid]' (#$account_id)</p>\n";
$new = true;
}
else
{
echo "<b>Error creating contact for user '$account[account_lid]' (#$account_id)</b></p>\n";
$errors++;
continue;
}
}
else
{
$to_update = array_merge($sql_contact, array_filter($contact, static function ($attr) {
return $attr !== null && $attr !== '';
}));
$to_update['id'] = $sql_contact['id'];
if (($diff = array_diff_assoc($to_update, $sql_contact)))
{
if ($contacts_sql->save($to_update) === 0)
{
echo "Successful updated contact data of '$account[account_lid]' (#$account_id): ".json_encode($diff, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE)."</p>\n";
if (!$new) $new = false;
}
else
{
echo "<b>Error updating contact data of '$account[account_lid]' (#$account_id)</b></p>\n";
++$errors;
continue;
}
}
else
{
echo "Contact data of '$account[account_lid]' (#$account_id) already up to date</p>\n";
}
}
if ($new)
{
++$created;
}
elseif ($new === false)
{
++$updated;
}
else
{
++$uptodate;
}
case 'fatal':
echo "<p style='color: red'><b>$str</b></p>\n";
break;
case 'error':
case 'info':
echo "<p><b>$str</b></p>\n";
break;
default:
echo "<p>$str</p>\n";
break;
}
}
while ($start[2] !== '');
$last_run = max($start_import-1, $last_modified);
Api\Config::save_value('account_import_lastrun', $last_run, 'phpgwapi');
$str = gmdate('Y-m-d H:i:s', $last_run). ' UTC';
if ($created || $updated || $errors)
{
echo "<p><b>Created $created and updated $updated users, with $errors errors.</b></p>";
}
else
{
echo "<p><b>All users are up-to-date.</b></p>\n";
}
if (!$errors)
{
echo "<p><b>Setting new incremental import time to: $str ($last_run)</b></p>\n";
}
});
}
catch (\Exception $e) {
echo "<p style'color: red'>".$e->getMessage()."</p>\n";
http_response_code(500);
exit;
// message already output through logger above
}

View File

@ -236,6 +236,7 @@ db root password setup de Datenbank Root-Passwort
db root username setup de Datenbank Root-Benutzername
db type setup de Datenbank-Typ
db user setup de Datenbank-Benutzer
deactivate user setup de Benutzer deaktivieren
default setup de empfohlene Vorgabe
default file system space per user/group ? setup de Vorgabewert Festplattenplatz pro Benutzer / Gruppe ?
delete setup de Löschen
@ -243,6 +244,7 @@ delete all existing accounts from sql database setup de Lösche alle bestehenden
delete all existing sql accounts, groups, acls and preferences (normally not necessary)? setup de Alle existierende SQL-Benutzer, Gruppen, ACLs und Einstellungen löschen (normalerweise nicht nötig)?
delete all my tables and data setup de Alle meine Tabellen und Daten löschen
delete all old languages and install new ones setup de Alle installierten Sprachen löschen und neu installieren
delete user and his data setup de Benutzer UND seine Daten löschen
deleting tables setup de Lösche Tabellen
demo server setup setup de Demo Server Setup
deny access setup de Zugriff verweigern
@ -252,6 +254,7 @@ deregistered setup de nicht registriert
details for admin account setup de Details des Admin-Kontos
developers' table schema toy setup de Entwickler Tabellen Schema "Spielzeug"
did not find any valid db support! setup de Konnte keine gültige Datenbankunterstützung finden!
do not check for deleted user setup de Nicht auf gelöschte Benutzer prüfen
do you want persistent connections (higher performance, but consumes more resources) setup de Wollen Sie eine permanente Datenbankverbindung (höhere Performance, braucht aber mehr Ressourcen)
do you want to manage homedirectory and loginshell attributes? setup de Wollen Sie Benutzerverzeichnisse und Login-Shell Attribute verwalten?
documentation setup de Dokumentation
@ -313,6 +316,7 @@ error in group-creation !!! setup de Fehler beim Anlegen der Gruppen !!!
error listing "dn=%1"! setup de Fehler beim Auflisten von "dn=%1"!
error modifying dn=%1: %2='%3'! setup de Fehler beim Ändern von dn=%1: %2='%3'!
error searching "dn=%1" for "%2"! setup de Fehler beim Suchen von "dn=%1" nach "%2"!
every setup de Alle
export has been completed! setup de Export ist abgeschlossen!
failed to mount backup directory! setup de Konnte Datensicherungsverzeichnis nicht mounten!
failed updating user "%1" dn="%2"! setup de Konnte Benutzer "%1" dn="%2" nicht aktualisieren!
@ -353,6 +357,8 @@ host,{imap | pop3 | imaps | pop3s},[domain],[{standard(default)|vmailmgr = add d
host/ip domain controler setup de Hostname/IP des Domänencontrollers
hostname/ip of database server setup de Hostname/IP des Datenbank-Servers
hour (0-24) setup de Stunde (0-24)
hours at setup de Stunden um
how frequent should the import run? setup de Wie häufig soll der Import ausgeführt werden?
however the tables are still in the database setup de Wie auch immer, die Tabellen sind noch immer in der Datenbank
however, the application is otherwise installed setup de Wie auch immer, die Anwendung ist ansonsten installiert
however, the application may still work setup de Wie auch immer, die Anwendung mag dennoch funktionieren
@ -376,6 +382,8 @@ if you use only languages of the same charset (eg. western european ones) you do
image type selection order setup de Auswahlreihenfolge der Bilddateitypen
import has been completed! setup de Import ist beendet!
include_path need to contain "." - the current directory setup de include_path muss "." - das aktuelle Verzeichnis - enthalten
incremental import setup de Inkrementeller Import
initial import setup de Initialer Import
install setup de Installieren
install all setup de Alle Installieren
install applications setup de Anwendungen installieren
@ -397,6 +405,7 @@ is in the webservers docroot setup de ist im Dokumentenverzeichnis (Documentroot
is not writeable by the webserver setup de ist nicht vom Webserver schreibbar
it needs upgrading to version %1! use --update-header <password>[,<user>] to do so (--usage gives more options). setup de Benötigt eine Aktualisierung auf Version %1! Benutzen Sie --update-header Passwort[,Benutzer] dafür (--usage gibt weitere Optionen)
just now setup de nur jetzt
just users setup de nur Benutzer
label to display as option on login page setup de Beschriftung zur Anzeige als Option auf der Login Seite
languages updated. setup de Sprachen aktualisiert.
ldap accounts configuration setup de LDAP-Benutzerkonten Konfiguration
@ -422,6 +431,7 @@ login as user postgres, eg. by using su as root setup de Als Benutzer postgres e
login to mysql - setup de mysql aufrufen -
loginname needed for domain configuration setup de Benutzername für die Konfiguration der Domain
logout setup de Abmelden
logs to setup de loggt nach
mail account of %1 migraged setup de Mail Konto von %1 Migriert
mail account of %1 migrated setup de Mail Konto von %1 migriert
mail domain (for virtual mail manager) setup de Mail Domain (für Virtual Mail Manager)
@ -510,6 +520,7 @@ path of egroupware install directory (default auto-detected) setup de Pfad des E
path to user and group files has to be outside of the webservers document-root!!! setup de Pfad zu Benutzer und Gruppen Dateien MUSS AUSSERHALB des Wurzelverzeichnisses (document root) des Webservers sein!!!
path to various directories: have to exist and be writeable by the webserver setup de Pfade zu verschiedenen Verzeichnissen: Diese müssen vorhanden sein und vom Webserver beschreibbar
pem certificate setup de PEM Zertifikat
periodic import from ads or ldap into egroupware database setup de Periodischer Import von ADS oder LDAP in die EGroupware Datenbank
persistent connections setup de Permanente Verbindungen
php client setup de PHP Klient
php proxy setup de PHP Proxy
@ -616,6 +627,7 @@ smtp server port setup de SMTP Server Port
smtp-authentication required setup de SMTP Authentifizierung benötigt
some information for the own service provider metadata setup de Einige Information für die Metadaten des eigenen Service Provider / Dienst
some or all of its tables are missing setup de Einige oder alle Tabellen fehlen
source (must be configured above) setup de Quelle (muss oberhalb konfiguriert werden)
sources deleted/missing setup de Quellen gelöscht/fehlen
sql encryption type setup de SQL-Verschlüsselungstyp für das Passwort
ssl validation: setup de SSL Validierung:
@ -728,6 +740,7 @@ user for smtp-authentication (leave it empty if no auth required) setup de Benut
usernames (comma-separated) which can get vfs root access (beside setup user) setup de Benutzernamen (mit Komma getrennt) die VFS root Zugriff bekommen können (neben dem Setup Benutzer)
usernames are casesensitive setup de Benutzername mit Unterscheidung zwischen Groß- und Kleinschreibung
users choice setup de Benutzerauswahl
users, groups and memberships setup de Benutzer, Gruppen und Mitgliedschaften
usually more annoying.<br />admins can use admin >> manage accounts or groups to give access to further apps. setup de Normalerweise mehr ärgerlich als nützlich<br />
utf-8 (unicode) setup de utf-8 (Unicode)
validation errors setup de Fehler bei der Prüfung der Eingaben
@ -739,10 +752,13 @@ virtual mail manager (login-name includes domain) setup de Virtual Mail Manager
warning! setup de Warnung!
we can proceed setup de Wir können fortfahren
we could not determine the version of %1, please make sure it is at least %2 setup de Wir konnten die Version von %1 nicht ermitteln, bitte stellen Sie sicher das sie mindestens %2 ist.
we strongly recomment to run a db backup before running the import! setup de Wir empfehlen dringend eine Datensicherung zu machen BEVOR der Import ausgeführt wird!
we will automatically update your tables/records to %1 setup de Wir werden Ihre Tabellen/Einträge automatisch zu %1 aktualisieren
we will now run a series of tests, which may take a few minutes. click the link below to proceed. setup de Wir werden jetzt eine Serie von Tests durchführen. Das kann ein paar Minuten dauern. Klicken sie auf den folgenden Link um Fortzufahren.
weekly setup de wöchentlich
welcome to the egroupware installation setup de Herzlich willkommen zur EGroupware Installation
what to do in egroupware if an user get deleted? setup de Was soll gemacht werden in EGroupware, wenn ein Benutzer gelöscht wurde?
what to import? setup de Was soll importiert werden?
where should egroupware store file content setup de Wo soll EGroupware den Inhalt von Dateien speichern
which database type do you want to use with egroupware? setup de Welchen Datenbanktyp wollen Sie mit EGroupware verwenden?
will be downloaded once, unless changed. setup de Wird einmalig heruntergeladen, außer bei Änderung.
@ -778,6 +794,7 @@ you dont have tnef or ytnef installed! it is needed to decode winmail.dat attach
you have not created your header.inc.php yet!<br /> you can create it now. setup de Sie haben bisher noch keine header.inc.php angelegt!<br />Sie können sie jetzt anlegen.
you have successfully logged out setup de Sie haben sich erfolgreich abgemeldet.
you must enter a username for the admin setup de Sie müssen einen Benutzernamen für den Administrator eingeben!
you must save and run an inital import, before the periodic import will start setup de Sie müssen erst speichern UND den Initialen Import ausführen, bevor der periodische Import startet
you need to add at least one egroupware domain / database instance. setup de Sie müssen mindestens eine EGroupware Domain / Datenbank Instanz hinzufügen.
you need to configure egroupware: setup de Sie müssen EGroupware konfigurieren:
you need to fix the above errors, before the configuration file header.inc.php can be written! setup de Sie müssen die obigen Fehler beheben, bevor die Konfigurationsdatei header.inc.php gespeichert werden kann!

View File

@ -237,6 +237,7 @@ db root password setup en DB root password
db root username setup en DB root username
db type setup en DB type
db user setup en DB user
deactivate user setup en Deactivate user
default setup en Recommended default
default file system space per user/group ? setup en Default file system space per user/group ?
delete setup en Delete
@ -244,6 +245,7 @@ delete all existing accounts from sql database setup en Delete all existing acco
delete all existing sql accounts, groups, acls and preferences (normally not necessary)? setup en Delete all existing SQL accounts, groups, ACLs and preferences. Normally not necessary.
delete all my tables and data setup en Delete all my tables and data
delete all old languages and install new ones setup en Delete all old languages and install new ones
delete user and his data setup en Delete user AND his data
deleting tables setup en Deleting tables.
demo server setup setup en Demo server setup
deny access setup en Deny access
@ -253,6 +255,7 @@ deregistered setup en De-registered
details for admin account setup en Details for Admin account
developers' table schema toy setup en Developers' Table Schema Toy
did not find any valid db support! setup en Did not find any valid DB support!
do not check for deleted user setup en Do NOT check for deleted user
do you want persistent connections (higher performance, but consumes more resources) setup en Do you want persistent connections (higher performance, but consumes more resources)
do you want to manage homedirectory and loginshell attributes? setup en Do you want to manage home directory and login shell attributes?
documentation setup en Documentation
@ -315,6 +318,7 @@ error in group-creation !!! setup en Error in group creation!
error listing "dn=%1"! setup en Error listing "dn=%1"!
error modifying dn=%1: %2='%3'! setup en Error modifying dn=%1: %2='%3'!
error searching "dn=%1" for "%2"! setup en Error searching "dn=%1" for "%2"!
every setup en Every
export has been completed! setup en Export has been completed!
failed to mount backup directory! setup en Failed to mount Backup directory!
failed updating user "%1" dn="%2"! setup en Failed updating user "%1" dn="%2"!
@ -356,6 +360,8 @@ host,{imap | pop3 | imaps | pop3s},[domain],[{standard(default)|vmailmgr = add d
host/ip domain controler setup en Host/IP domain controller
hostname/ip of database server setup en Hostname/IP of database server
hour (0-24) setup en Hour (0-24)
hours at setup en hours at
how frequent should the import run? setup en How frequent should the import run?
however the tables are still in the database setup en The tables are still in the database.
however, the application is otherwise installed setup en The application is otherwise installed.
however, the application may still work setup en The application may still work
@ -380,6 +386,8 @@ image type selection order setup en Image type selection order
imap: admin user,password,emailadmin_imap(|_cyrus|_dovecot) setup en IMAP: Admin user,Password,emailadmin_imap(|_cyrus|_dovecot)
import has been completed! setup en Import has been completed!
include_path need to contain "." - the current directory setup en include_path need to contain "." - the current directory
incremental import setup en Incremental import
initial import setup en Initial import
install setup en Install
install all setup en Install all
install applications setup en Install applications
@ -401,6 +409,7 @@ is in the webservers docroot setup en is in the web servers docroot
is not writeable by the webserver setup en is not writeable by the web server
it needs upgrading to version %1! use --update-header <password>[,<user>] to do so (--usage gives more options). setup en It needs upgrading to version %1! Use --update-header <password>[,<user>] to do so (--usage gives more options).
just now setup en just now
just users setup en just users
label to display as option on login page setup en Label to display as option on login page
languages updated. setup en Languages updated.
ldap accounts configuration setup en LDAP accounts configuration
@ -426,6 +435,7 @@ login as user postgres, eg. by using su as root setup en Login as user postgres,
login to mysql - setup en Login to mysql -
loginname needed for domain configuration setup en Login name needed for domain configuration
logout setup en Logout
logs to setup en logs to
mail account of %1 migraged setup en Mail account of %1 migraged
mail account of %1 migrated setup en Mail account of %1 migrated
mail domain (for virtual mail manager) setup en Mail domain (for Virtual mail manager)
@ -514,6 +524,7 @@ path of egroupware install directory (default auto-detected) setup en Path of EG
path to user and group files has to be outside of the webservers document-root!!! setup en Path to user and group files HAS TO BE OUTSIDE of the web servers document-root!
path to various directories: have to exist and be writeable by the webserver setup en Path to various directories: have to exist and be writable by the web server
pem certificate setup en PEM certificate
periodic import from ads or ldap into egroupware database setup en Periodic import from ADS or LDAP into EGroupware database
persistent connections setup en Persistent connections
php client setup en PHP client
php proxy setup en PHP proxy
@ -620,6 +631,7 @@ smtp server port setup en SMTP server port
smtp-authentication required setup en SMTP-authentication required
some information for the own service provider metadata setup en Some information for the own Service Provider metadata
some or all of its tables are missing setup en Some or all of its tables are missing
source (must be configured above) setup en Source (must be configured above)
sources deleted/missing setup en Sources deleted/missing
sql encryption type setup en SQL encryption type for passwords
ssl validation: setup en SSL validation:
@ -734,6 +746,7 @@ user for smtp-authentication (leave it empty if no auth required) setup en User
usernames (comma-separated) which can get vfs root access (beside setup user) setup en User names, comma-separated, which can get VFS root access (beside setup user)
usernames are casesensitive setup en User names are case sensitive
users choice setup en Users choice
users, groups and memberships setup en users, groups and memberships
usually more annoying.<br />admins can use admin >> manage accounts or groups to give access to further apps. setup en Usually more annoying.<br />Admins can use Admin >> Manage accounts or groups to give access to further apps.
utf-8 (unicode) setup en utf-8 (Unicode)
validation errors setup en Validation errors
@ -745,10 +758,13 @@ virtual mail manager (login-name includes domain) setup en Virtual mail manager
warning! setup en Warning!
we can proceed setup en We can proceed
we could not determine the version of %1, please make sure it is at least %2 setup en We could not determine the version of %1, please make sure it is at least %2
we strongly recomment to run a db backup before running the import! setup en We strongly recomment to run a DB backup BEFORE running the import!
we will automatically update your tables/records to %1 setup en We will automatically update your tables/records to %1
we will now run a series of tests, which may take a few minutes. click the link below to proceed. setup en We will now run a series of tests, which may take a few minutes. Click the link below to proceed.
weekly setup en weekly
welcome to the egroupware installation setup en Welcome to the EGroupware installation
what to do in egroupware if an user get deleted? setup en What to do in EGroupware if an user get deleted?
what to import? setup en What to import?
where should egroupware store file content setup en Where should EGroupware store file content
which database type do you want to use with egroupware? setup en Which database type do you want to use with EGroupware?
will be downloaded once, unless changed. setup en Will be downloaded once, unless changed.
@ -784,6 +800,7 @@ you dont have tnef or ytnef installed! it is needed to decode winmail.dat attach
you have not created your header.inc.php yet!<br /> you can create it now. setup en You have not created your header.inc.php yet!<br /> You can create it now.
you have successfully logged out setup en You have successfully logged out.
you must enter a username for the admin setup en You must enter a username for the admin.
you must save and run an inital import, before the periodic import will start setup en You must save AND run an inital import, before the periodic import will start
you need to add at least one egroupware domain / database instance. setup en You need to add at least one EGroupware domain / database instance.
you need to configure egroupware: setup en You need to configure EGroupware:
you need to fix the above errors, before the configuration file header.inc.php can be written! setup en You need to fix the above errors, before the configuration file header.inc.php can be written!

View File

@ -467,7 +467,7 @@
<td>
<select name="newsettings[account_import_type]">
<option value="users" {selected_account_import_source_user}>{lang_just_users}</option>
<option value="users_groups" {selected_account_import_source_users_groups}>{lang_users,_groups_and_memberships}</option>
<option value="users_groups" disabled {selected_account_import_source_users_groups}>{lang_users,_groups_and_memberships}</option>
</select>
</td>
</tr>
@ -475,8 +475,8 @@
<td>{lang_What_to_do_in_EGroupware_if_an_user_get_deleted?}:</td>
<td>
<select name="newsettings[account_import_delete]">
<option value="yes" {selected_account_import_delete_yes}>{lang_Delete_user_AND_his_data}</option>
<option value="deactivate" {selected_account_import_delete_deactivate}>{lang_Deactivate_user}</option>
<option value="yes" disabled {selected_account_import_delete_yes}>{lang_Delete_user_AND_his_data}</option>
<option value="deactivate" disabled {selected_account_import_delete_deactivate}>{lang_Deactivate_user}</option>
<option value="no" {selected_account_import_delete_no}>{lang_Do_NOT_check_for_deleted_user}</option>
</select>
</td>
@ -488,6 +488,7 @@
<input type="number" name="newsettings[account_import_frequency]" style="width: 3em" value="{value_account_import_frequency}"/>
{lang_hours_at}
<input type="time" name="newsettings[account_import_time]" value="{value_account_import_time}"/>
{lang_logs_to}: {value_files_dir}/setup/account-import.log
</td>
</tr>
<tr class="row_on">