mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-26 16:48:49 +01:00
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:
parent
65ed3e090c
commit
e28329b1d7
383
api/src/Accounts/Import.php
Normal file
383
api/src/Accounts/Import.php
Normal 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();
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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!
|
||||
|
@ -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!
|
||||
|
@ -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">
|
||||
|
Loading…
Reference in New Issue
Block a user