Compare commits

..

10 commits

14 changed files with 1486 additions and 424 deletions

View file

@ -23,3 +23,14 @@
// Accesslog base DN // Accesslog base DN
define('LS_ACCESSLOG_BASEDN', 'cn=ldapsaisie-accesslog'); define('LS_ACCESSLOG_BASEDN', 'cn=ldapsaisie-accesslog');
/*
* Enable logging write events on LDAP entries managed by LdapSaisie in the accesslog base
*
* The feature permit to LdapSaisie to write it self log entries in accesslog base about
* modifications made with its system account with the right author DN.
*
* Note: If your LDAP directory ACL permit to your users to do them self modifications on LDAP
* directory, the recommanded way is to use authz proxy authentication (see useAuthzProxyControl
* configuration parameter).
*/
define('LS_ACCESSLOG_LOG_WRITE_EVENTS', false);

View file

@ -0,0 +1,36 @@
table.objectAccessLogs tbody tr {
border-bottom: 1px dotted;
}
table.objectAccessLogs table.mods {
width: 100%;
table-layout: fixed;
border-collapse: collapse;
}
table.objectAccessLogs table.mods td, table.objectAccessLogs table.mods th {
border-left: 1px dotted;
}
table.objectAccessLogs table.mods tr {
border: none!important;
}
table.objectAccessLogs .col-attr {
width: 15em;
}
table.objectAccessLogs .col-op {
width: 6em;
}
table.objectAccessLogs .col-value {
width: 25em;
}
table.objectAccessLogs .col-value div {
overflow: scroll;
width: 100%;
margin: 0;
padding: 0;
}

View file

@ -25,30 +25,37 @@ LSerror :: defineError('ACCESSLOG_SUPPORT_01',
); );
$GLOBALS['accesslog_reqTypes'] = array( $GLOBALS['accesslog_reqTypes'] = array(
'add' => _('Add'), 'add' => ___('Add'),
'bind' => _('Log in'), 'bind' => ___('Log in'),
'compare' => _('Compare'), 'compare' => ___('Compare'),
'delete' => _('Delete'), 'delete' => ___('Delete'),
'extended' => _('Extended'), 'extended' => ___('Extended'),
'modify' => _('Modify'), 'modify' => ___('Modify'),
'modrdn' => _('Modify RDN'), 'modrdn' => ___('Modify RDN'),
'search' => _('Search'), 'search' => ___('Search'),
'unbind' => _('Log out'), 'unbind' => ___('Log out'),
); );
$GLOBALS['accesslog_modOps'] = array( $GLOBALS['accesslog_modOps'] = array(
'+' => _('Add'), '+' => ___('Add'),
'-' => _('Delete'), '-' => ___('Delete'),
'=' => _('Replace'), '=' => ___('Replace'),
'' => _('Replace'), '' => ___('Replace'),
'#' => _('Increment'), '#' => ___('Increment'),
); );
function LSaddon_accesslog_support() { function LSaddon_accesslog_support() {
if (!defined('LS_ACCESSLOG_BASEDN')) { $MUST_DEFINE_CONST= array(
LSerror :: addErrorCode('ACCESSLOG_SUPPORT_01', 'LS_ACCESSLOG_BASEDN'); 'LS_ACCESSLOG_BASEDN',
'LS_ACCESSLOG_LOG_WRITE_EVENTS',
);
foreach($MUST_DEFINE_CONST as $const) {
if (!defined($const) || is_empty(constant($const))) {
LSerror :: addErrorCode('ACCESSLOG_SUPPORT_01', $const);
return false; return false;
} }
}
if (php_sapi_name() === 'cli') { if (php_sapi_name() === 'cli') {
LScli::add_command( LScli::add_command(
'getEntryAccessLog', 'getEntryAccessLog',
@ -57,10 +64,18 @@ function LSaddon_accesslog_support() {
'[entry DN] [page]' '[entry DN] [page]'
); );
} }
elseif (LS_ACCESSLOG_LOG_WRITE_EVENTS && LSsession :: loadLSclass('LSldap')) {
LSldap :: addEvent('updated', 'onEntryUpdated');
LSldap :: addEvent('moved', 'onEntryMoved');
LSldap :: addEvent('user_password_updated', 'onEntryUserPasswordUpdated');
LSldap :: addEvent('deleted', 'onEntryDeleted');
}
return true; return true;
} }
function mapAccessLogEntry(&$entry) { function mapAccessLogEntry(&$entry) {
foreach($entry['attrs'] as $attr => $values)
$entry['attrs'][$attr] = ensureIsArray($values);
$attrs = $entry['attrs']; $attrs = $entry['attrs'];
$entry['start'] = LSldap::parseDate(LSldap::getAttr($attrs, 'reqStart')); $entry['start'] = LSldap::parseDate(LSldap::getAttr($attrs, 'reqStart'));
$entry['end'] = LSldap::parseDate(LSldap::getAttr($attrs, 'reqEnd')); $entry['end'] = LSldap::parseDate(LSldap::getAttr($attrs, 'reqEnd'));
@ -73,21 +88,23 @@ function mapAccessLogEntry(&$entry) {
$entry['type'] = LSldap::getAttr($attrs, 'reqType'); $entry['type'] = LSldap::getAttr($attrs, 'reqType');
$entry['result'] = ldap_err2str(LSldap::getAttr($attrs, 'reqResult')); $entry['result'] = ldap_err2str(LSldap::getAttr($attrs, 'reqResult'));
$entry['message'] = LSldap::getAttr($attrs, 'reqMessage'); $entry['message'] = LSldap::getAttr($attrs, 'reqMessage');
if ($entry['type'] === 'modify' && LSldap::getAttr($attrs, 'reqMod', true)) {
$mods = array(); $mods = array();
foreach(LSldap::getAttr($attrs, 'reqMod', true) as $mod) { foreach(LSldap::getAttr($attrs, 'reqMod', true) as $mod) {
if (preg_match('/^([^\:]+)\:([^ ]?) (.*)$/', $mod, $m)) { if (preg_match('/^(?P<attr>[^\:]+)\:(?P<op>[^ ]?)( (?P<value>.*))?$/', $mod, $m)) {
$attr = $m[1]; $attr = $m['attr'];
$op = $m[2]; $op = $m['op'];
$value = $m[3]; $value = isset($m['value'])?$m['value']:null;
if (!array_key_exists($attr, $mods)) { if (!array_key_exists($attr, $mods)) {
$mods[$attr] = array( $mods[$attr] = array(
'mods' => array(), 'changes' => array(),
'old_values' => array(), 'old_values' => array(),
); );
} }
$mods[$attr]['changes'][] = array( $mods[$attr]['changes'][] = array(
'op' => array_key_exists($op, $GLOBALS['accesslog_modOps']) ? $GLOBALS['accesslog_modOps'][$op] : $op, 'op' => (
array_key_exists($op, $GLOBALS['accesslog_modOps'])?
_($GLOBALS['accesslog_modOps'][$op]): $op
),
'value' => $value, 'value' => $value,
); );
} }
@ -99,23 +116,39 @@ function mapAccessLogEntry(&$entry) {
} }
} }
} }
if ($mods)
$entry['mods'] = $mods; $entry['mods'] = $mods;
if ($entry['type'] === 'modrdn') {
$new_rdn = LSldap::getAttr($attrs, 'reqNewRDN', false);
$superior_dn = LSldap::getAttr($attrs, 'reqNewSuperior', false);
if (!$superior_dn) {
$superior_dn = parentDn(LSldap::getAttr($attrs, 'reqDN', false));
}
$entry['new_dn'] = "$new_rdn,$superior_dn";
} }
if (array_key_exists($entry['type'], $GLOBALS['accesslog_reqTypes'])) { if (array_key_exists($entry['type'], $GLOBALS['accesslog_reqTypes'])) {
$entry['type'] = $GLOBALS['accesslog_reqTypes'][$entry['type']]; $entry['type'] = _($GLOBALS['accesslog_reqTypes'][$entry['type']]);
} }
} }
function sortLogEntryByDate($a, $b) { function sortLogEntriesByDate(&$a, &$b) {
return ($a['start'] === $b['start']) ? 0 : ($a['start'] < $b['start']) ? -1 : 1; $astart = LSldap::getAttr($a['attrs'], 'reqStart');
$bstart = LSldap::getAttr($b['attrs'], 'reqStart');
return ($astart === $bstart) ? 0 : ($astart < $bstart) ? -1 : 1;
} }
function getEntryAccessLog($dn) { function getEntryAccessLog($dn, $start_date=null) {
$data = LSldap::search( $filter = Net_LDAP2_Filter::create('reqDn', 'equals', $dn);
Net_LDAP2_Filter::create('reqDn', 'equals', $dn), if ($start_date) {
$date_filter = Net_LDAP2_Filter::create('reqStart', 'greaterOrEqual', $start_date);
$filter = Net_LDAP2_Filter::combine('and', array($filter, $date_filter));
}
$entries = LSldap::search(
$filter,
LS_ACCESSLOG_BASEDN, LS_ACCESSLOG_BASEDN,
array( array(
'attributes' => array( 'attributes' => array(
'reqDN',
'reqStart', 'reqStart',
'reqEnd', 'reqEnd',
'reqAuthzID', 'reqAuthzID',
@ -124,22 +157,33 @@ function getEntryAccessLog($dn) {
'reqMessage', 'reqMessage',
'reqMod', 'reqMod',
'reqOld', 'reqOld',
'reqNewRDN',
'reqNewSuperior',
), ),
) )
); );
if (!is_array($data)) { if (!is_array($entries)) {
return; return;
} }
usort($entries, 'sortLogEntriesByDate');
$logs = array(); $logs = array();
foreach($data as $entry) { $new_dn = null;
foreach($entry['attrs'] as $attr => $values) { $rename_date = null;
$entry['attrs'][$attr] = ensureIsArray($values); foreach($entries as $entry) {
}
mapAccessLogEntry($entry); mapAccessLogEntry($entry);
$logs[] = $entry; $logs[] = $entry;
if (isset($entry['new_dn'])) {
$new_dn = $entry['new_dn'];
$rename_date = LSldap::formatDate($entry['start']);
break;
} }
usort($logs, 'sortLogEntryByDate'); }
return array_reverse($logs); if ($new_dn) {
$next_logs = getEntryAccessLog($new_dn, $rename_date);
if (is_array($next_logs))
$logs = array_merge($logs, $next_logs);
}
return $start_date?$logs:array_reverse($logs);
} }
function getEntryAccessLogPage($dn, $page = false, $nbByPage = 30) { function getEntryAccessLogPage($dn, $page = false, $nbByPage = 30) {
@ -183,11 +227,136 @@ function showObjectAccessLogs($obj) {
'action' => 'view', 'action' => 'view',
); );
LStemplate::assign('LSview_actions', $LSview_actions); LStemplate::assign('LSview_actions', $LSview_actions);
LStemplate::addCSSFile('showObjectAccessLogs.css');
LSsession::setTemplate('showObjectAccessLogs.tpl'); LSsession::setTemplate('showObjectAccessLogs.tpl');
LSsession::displayTemplate(); LSsession::displayTemplate();
exit(); exit();
} }
function onEntryUpdated($data) {
$now = LSldap::formatDate();
$dn = "reqStart=$now,".LS_ACCESSLOG_BASEDN;
$new_entry = $data['original_entry']->isNew();
$attrs = array(
'reqStart' => array($now),
'reqEnd' => array($now),
'reqType' => array($new_entry?"add":"modify"),
'reqSession' => array("1024"),
'reqAuthzID' => array(LSsession :: get('authenticated_user_dn')),
'reqDN' => array($data["dn"]),
'reqResult' => array("0"),
);
// Compute modifications
$mods = array();
$olds = array();
if ($new_entry)
foreach(ensureIsArray($data['entry']->getValue('objectClass', 'all')) as $value)
$mods[] = "objectClass:+ $value";
foreach($data['changes'] as $attr => $values) {
if (strtolower($attr) == 'userpassword')
foreach(array_keys($values) as $idx)
$values[$idx] = hashPasswordForLogs($values[$idx]);
if ($values) {
foreach($values as $value)
$mods[] = $new_entry?"$attr:+ $value":"$attr:= $value";
}
else if (!$new_entry) {
$mods[] = "$attr:=";
}
if (!$new_entry)
foreach(ensureIsArray($data['original_entry']->getValue($attr, 'all')) as $value)
$olds[] = "$attr: $value";
}
if (!$mods) return true;
$attrs['reqMod'] = $mods;
if ($olds)
$attrs['reqOld'] = $olds;
LSldap::getNewEntry($dn, array($new_entry?'auditAdd':'auditModify'), $attrs, true);
}
function onEntryUserPasswordUpdated($data) {
$now = LSldap::formatDate();
$dn = "reqStart=$now,".LS_ACCESSLOG_BASEDN; $mods = array();
// Compute modifications
$mods = array();
// Retreive fresh entry to retreive hased/stored password
$attrs = LSldap :: getAttrs($data['dn'], null, array('userPassword'));
$new_passwords = $data["new_passwords"];
if ($attrs)
$new_passwords = LSldap :: getAttr($attrs, 'userPassword', true);
if ($new_passwords) {
foreach($new_passwords as $password)
$mods[] = "userPassword:= ".hashPasswordForLogs($password);
}
else
$mods[] = "userPassword:=";
$attrs = array(
'reqStart' => array($now),
'reqEnd' => array($now),
'reqType' => array("modify"),
'reqSession' => array("1024"),
'reqAuthzID' => array(LSsession :: get('authenticated_user_dn')),
'reqDN' => array($data["dn"]),
'reqResult' => array("0"),
'reqMod' => $mods,
);
LSldap::getNewEntry($dn, array('auditModify'), $attrs, true);
}
function onEntryMoved($data) {
$now = LSldap::formatDate();
$dn = "reqStart=$now,".LS_ACCESSLOG_BASEDN;
$attrs = array(
'reqStart' => array($now),
'reqEnd' => array($now),
'reqType' => array("modrdn"),
'reqSession' => array("1024"),
'reqAuthzID' => array(LSsession :: get('authenticated_user_dn')),
'reqDN' => array($data["old"]),
'reqNewRDN' => array(getRdn($data["new"])),
'reqResult' => array("0"),
);
$new_superior = parentDn($data["new"]);
$old_superior = parentDn($data["old"]);
if ($new_superior != $old_superior)
$attrs['reqNewSuperior'] = array($new_superior);
LSldap::getNewEntry($dn, array('auditModRDN'), $attrs, true);
}
function onEntryDeleted($data) {
$now = LSldap::formatDate();
$dn = "reqStart=$now,".LS_ACCESSLOG_BASEDN;
$attrs = array(
'reqStart' => array($now),
'reqEnd' => array($now),
'reqType' => array("delete"),
'reqSession' => array("1024"),
'reqAuthzID' => array(LSsession :: get('authenticated_user_dn')),
'reqDN' => array($data["dn"]),
'reqResult' => array("0"),
);
LSldap::getNewEntry($dn, array('auditDelete'), $attrs, true);
}
function hashPasswordForLogs($password) {
if (preg_match('/^{[^}]+}.*/', $password))
// Already hashed
return $password;
if(defined('PASSWORD_ARGON2I'))
return '{ARGON2}'.password_hash($password, PASSWORD_ARGON2I);
if(defined('MHASH_SHA512') && function_exists('mhash') && function_exists('mhash_keygen_s2k')) {
mt_srand( (double) microtime() * 1000000 );
$salt = mhash_keygen_s2k(MHASH_SHA512, $password, substr( pack( "h*", md5( mt_rand() ) ), 0, 8 ), 4 );
return "{SSHA512}".base64_encode(mhash($mhash_type, $password.$salt).$salt);
}
return '[not logged]';
}
if (php_sapi_name() !== 'cli') { if (php_sapi_name() !== 'cli') {
return true; return true;
} }

View file

@ -88,46 +88,54 @@ class LScli extends LSlog_staticLoggerClass {
* Show usage message * Show usage message
* *
* @param string|false $error Error message to display before usage message (optional, default: false) * @param string|false $error Error message to display before usage message (optional, default: false)
* @param string $extra_args Optional argument use to compute error message using sprintf (if provided)
* @return void * @return void
**/ **/
public static function usage($error=false) { public static function usage($error=false, ...$extra_args) {
global $argv; global $argv;
if ($error) if ($error) {
if ($extra_args)
$error = call_user_func_array('sprintf', array_merge(array($error), $extra_args));
echo "$error\n\n"; echo "$error\n\n";
}
echo "Usage : ".basename($argv[0])." [-h] [-qdC] command\n"; printf(_(
echo " -h Show this message\n"; "Usage: %s [-h] [-qdC] command
echo " -q|--quiet Quiet mode: nothing log on console (but keep other logging handler)\n"; -h Show this message
echo " -d|--debug Debug mode (set log level to DEBUG, default: WARNING)\n"; -q|--quiet Quiet mode: nothing log on console (but keep other logging handler)
echo " -v|--verbose Verbose mode (set log level to INFO, default: WARNING)\n"; -d|--debug Debug mode (set log level to DEBUG, default: WARNING)
echo " --trace Trace mode (set log level to TRACE, default: WARNING)\n"; -v|--verbose Verbose mode (set log level to INFO, default: WARNING)
echo " -C|--console Log on console with same log level as other handlers (otherwise, log only errors)\n"; --trace Trace mode (set log level to TRACE, default: WARNING)
echo " -S|--ldap-server Connect to a specific LDAP server: you could specify a LDAP\n"; -C|--console Log on console with same log level as other handlers (otherwise, log only errors)
echo " server by its declaration order in configuration (default:\n"; -S|--ldap-server Connect to a specific LDAP server: you could specify a LDAP
echo " first one).\n"; server by its declaration order in configuration (default:
echo " -L|--load-class Load specific class to permit access to its CLI commands\n"; first one).
echo " -A|--load-addons Load specific addon to permit access to its CLI commands\n"; -L|--load-class Load specific class to permit access to its CLI commands
echo " command Command to run\n"; -A|--load-addons Load specific addon to permit access to its CLI commands
echo "\n"; command Command to run
echo "Available commands :\n";
Available commands:
"), basename($argv[0]));
foreach (self :: $commands as $command => $info) { foreach (self :: $commands as $command => $info) {
if (self :: $current_command and self :: $current_command != $command) if (self :: $current_command and self :: $current_command != $command)
continue; continue;
echo " $command : ".$info['short_desc']."\n"; echo " $command: ".__($info['short_desc'])."\n";
echo " ".basename($argv[0])." $command ".($info['usage_args']?$info['usage_args']:'')."\n"; echo " ".basename($argv[0])." $command ".($info['usage_args']?__($info['usage_args']):'')."\n";
if ($info['long_desc']) { if ($info['long_desc']) {
if (is_array($info['long_desc'])) if (is_array($info['long_desc'])) {
$info['long_desc'] = array_map('__', $info['long_desc']);
$info['long_desc'] = implode("\n", $info['long_desc']); $info['long_desc'] = implode("\n", $info['long_desc']);
echo "\n ".str_replace("\n", "\n ", wordwrap($info['long_desc']))."\n"; }
echo "\n ".str_replace("\n", "\n ", wordwrap(__($info['long_desc'])))."\n";
} }
echo "\n"; echo "\n";
} }
if (empty(self :: $commands)) if (empty(self :: $commands))
echo " Currently no available command is declared.\n"; echo " "._("Currently no available command is declared.")."\n";
exit(($error?1:0)); exit($error?1:0);
} }
/** /**
@ -186,7 +194,7 @@ class LScli extends LSlog_staticLoggerClass {
$i++; $i++;
$ldap_server_id = intval($argv[$i]); $ldap_server_id = intval($argv[$i]);
if(!LSsession :: setLdapServer($ldap_server_id)) if(!LSsession :: setLdapServer($ldap_server_id))
self :: usage("Fail to select LDAP server #$ldap_server_id."); self :: usage(_("Fail to select LDAP server #%s."), $ldap_server_id);
break; break;
case '--sub-dn': case '--sub-dn':
$i++; $i++;
@ -197,14 +205,14 @@ class LScli extends LSlog_staticLoggerClass {
$i++; $i++;
$class = $argv[$i]; $class = $argv[$i];
if(!LSsession :: loadLSclass($class)) if(!LSsession :: loadLSclass($class))
self :: usage("Fail to load class '$class'."); self :: usage(_("Fail to load class '%s'."), $class);
break; break;
case '-A': case '-A':
case '--load-addon': case '--load-addon':
$i++; $i++;
$addon = $argv[$i]; $addon = $argv[$i];
if(!LSsession :: loadLSaddon($addon)) if(!LSsession :: loadLSaddon($addon))
self :: usage("Fail to load addon '$addon'."); self :: usage(_("Fail to load addon '%s'."), $addon);
break; break;
case '--': case '--':
$command_args = array_merge($command_args, array_slice($argv, $i)); $command_args = array_merge($command_args, array_slice($argv, $i));
@ -215,7 +223,8 @@ class LScli extends LSlog_staticLoggerClass {
$command_args[] = $argv[$i]; $command_args[] = $argv[$i];
else else
self :: usage( self :: usage(
getFData(_("Invalid parameter \"%{parameter}\".\nNote: Command's parameter/argument must be place after the command."), $argv[$i]) _("Invalid parameter \"%s\".\nNote: Command's parameter/argument must be place after the command."),
$argv[$i]
); );
} }
} }
@ -249,7 +258,7 @@ class LScli extends LSlog_staticLoggerClass {
if ($ldap_server_subDn) { if ($ldap_server_subDn) {
self :: need_ldap_con(); self :: need_ldap_con();
if(!LSsession :: setSubDn($ldap_server_subDn)) if(!LSsession :: setSubDn($ldap_server_subDn))
self :: usage("Fail to select sub DN '$ldap_server_subDn'."); self :: usage(_("Fail to select sub DN '%s'."), $ldap_server_subDn);
} }
if (!$command) { if (!$command) {
@ -259,7 +268,7 @@ class LScli extends LSlog_staticLoggerClass {
// Select LDAP server (if not already done with -S/--ldap-server parameter) // Select LDAP server (if not already done with -S/--ldap-server parameter)
if ($ldap_server_id === false && !LSsession :: setLdapServer(0)) if ($ldap_server_id === false && !LSsession :: setLdapServer(0))
self :: log_fatal('Fail to select first LDAP server.'); self :: log_fatal(_('Fail to select first LDAP server.'));
// Run command // Run command
self :: run_command($command, $command_args); self :: run_command($command, $command_args);
@ -379,12 +388,12 @@ class LScli extends LSlog_staticLoggerClass {
**/ **/
public static function confirm($question=null) { public static function confirm($question=null) {
if (is_null($question)) if (is_null($question))
$question = "Are you sure?"; $question = _("Are you sure?");
echo "\n$question Type 'yes' to continue: "; printf(_("\n%s Type 'yes' to continue: "), $question);
$handle = fopen ("php://stdin","r"); $handle = fopen ("php://stdin","r");
$line = fgets($handle); $line = fgets($handle);
if(trim($line) != 'yes'){ if(trim($line) != _('yes')){
echo "User cancel\n"; echo _("User cancel\n");
return false; return false;
} }
echo "\n"; echo "\n";
@ -456,7 +465,7 @@ class LScli extends LSlog_staticLoggerClass {
$ldap_server_id = intval($comp_words[$i]); $ldap_server_id = intval($comp_words[$i]);
self :: unquote_word($ldap_server_id); self :: unquote_word($ldap_server_id);
if(!LSsession :: setLdapServer($ldap_server_id)) if(!LSsession :: setLdapServer($ldap_server_id))
self :: usage("Fail to select LDAP server #$ldap_server_id."); self :: usage(_("Fail to select LDAP server #%s."), $ldap_server_id);
} }
break; break;
case '--sub-dn': case '--sub-dn':
@ -476,7 +485,7 @@ class LScli extends LSlog_staticLoggerClass {
self :: unquote_word($ldap_server_subDn); self :: unquote_word($ldap_server_subDn);
self :: need_ldap_con(); self :: need_ldap_con();
if(!LSsession :: setSubDn($ldap_server_subDn)) if(!LSsession :: setSubDn($ldap_server_subDn))
self :: usage("Fail to select sub DN '$ldap_server_subDn'."); self :: usage(_("Fail to select sub DN '%s'.", $ldap_server_subDn));
break; break;
case '-L': case '-L':
case '--load-class': case '--load-class':
@ -491,7 +500,7 @@ class LScli extends LSlog_staticLoggerClass {
$class = $comp_words[$i]; $class = $comp_words[$i];
self :: unquote_word($class); self :: unquote_word($class);
if(!LSsession :: loadLSclass($class)) if(!LSsession :: loadLSclass($class))
self :: usage("Fail to load class '$class'."); self :: usage(_("Fail to load class '%s'."), $class);
break; break;
case '-A': case '-A':
case '--load-addon': case '--load-addon':
@ -506,7 +515,7 @@ class LScli extends LSlog_staticLoggerClass {
$addon = $comp_words[$i]; $addon = $comp_words[$i];
self :: unquote_word($addon); self :: unquote_word($addon);
if(!LSsession :: loadLSaddon($addon)) if(!LSsession :: loadLSaddon($addon))
self :: usage("Fail to load addon '$addon'."); self :: usage(_("Fail to load addon '%s'."), $addon);
break; break;
default: default:
if (!in_array($comp_words[$i], $opts)) { if (!in_array($comp_words[$i], $opts)) {
@ -524,7 +533,7 @@ class LScli extends LSlog_staticLoggerClass {
asort($subDns); asort($subDns);
$subDn = key($subDns); $subDn = key($subDns);
if(!LSsession :: setSubDn($subDn)) if(!LSsession :: setSubDn($subDn))
self :: usage("Fail to select sub DN '$subDn'."); self :: usage(_("Fail to select sub DN '%s'."), $subDn);
$opts[] = '--sub-dn'; $opts[] = '--sub-dn';
} }
} }
@ -852,7 +861,7 @@ ___("LScli : The CLI command '%{command}' handler is not callable.")
LScli :: add_command( LScli :: add_command(
'bash_autocomplete', 'bash_autocomplete',
array('LScli', 'bash_autocomplete'), array('LScli', 'bash_autocomplete'),
'Handle BASH completion', ___('Handle BASH completion'),
'[arg num to autocomplete] -- [command args]', '[arg num to autocomplete] -- [command args]',
null, null,
false false

View file

@ -279,12 +279,16 @@ function _cli_interactive_ask($context, $msg) {
} }
// Format question // Format question
$empty_action = ($copyoriginalvalue?'copy original message':'pass'); $question ="\"$msg\"\n\n => "._("Please enter translated string");
$question ="\"$msg\"\n\n => Please enter translated string"; $empty_action = (
$copyoriginalvalue?
_("or leave empty to copy original message"):
_("or leave empty to pass")
);
$question .= " (i"; $question .= " (i";
if (!$copyoriginalvalue) if (!$copyoriginalvalue)
$question .= "/c"; $question .= "/c";
$question .= "/q/? or leave empty to $empty_action): "; $question .= "/e/q/? $empty_action): ";
while (true) { while (true) {
if ($context) if ($context)
@ -303,14 +307,46 @@ function _cli_interactive_ask($context, $msg) {
case 'C': // Copy case 'C': // Copy
if (!$copyoriginalvalue) if (!$copyoriginalvalue)
return $msg; return $msg;
case 'e':
case 'E': // Open editor
$tmp_file = tempnam(sys_get_temp_dir(), 'ldapsaisie_translation');
if ($tmp_file === false) {
fwrite(STDERR, _('Fail to create temporary file.'));
break;
}
$fd = fopen($tmp_file, 'w');
if ($fd === false) {
fwrite(STDERR, _('Fail to open temporary file.'));
break;
}
$nb_lines = 1;
if ($context) {
fwrite($fd, "# $context\n");
$nb_lines++;
}
$lines = explode("\n", $msg);
$nb_lines += count($lines);
fwrite($fd, "# ".implode("\n# ", $lines)."\n\n");
fclose($fd);
system("editor +$nb_lines $tmp_file > `tty`", $exit_code);
$result = array();
foreach(file($tmp_file) as $line)
if (!preg_match('/^# /', $line) || $result)
$result[] = preg_replace("/\n$/", "", $line);
unlink($tmp_file);
return implode("\n", $result);
case '?': // Help message case '?': // Help message
fwrite(STDERR, "Available choices:\n"); fwrite(STDERR, _("Available choices:\n"));
fwrite(STDERR, " - i: ignore this message\n"); fwrite(STDERR, _(" - i: ignore this message\n"));
if (!$copyoriginalvalue) if (!$copyoriginalvalue)
fwrite(STDERR, " - c: copy original message\n"); fwrite(STDERR, _(" - c: copy original message\n"));
fwrite(STDERR, " - q: quit interactive mode and ignore all following untranslated messages\n"); fwrite(STDERR, _(" - e: translate message in text editor\n"));
fwrite(STDERR, " - ?: Show this message\n"); fwrite(STDERR, _(" - q: quit interactive mode and ignore all following untranslated messages\n"));
fwrite(STDERR, "Or leave empty to $empty_action.\n"); fwrite(STDERR, _(" - ?: Show this message\n"));
if ($copyoriginalvalue)
fwrite(STDERR, _("Or leave empty to copy original message.\n"));
else
fwrite(STDERR, _("Or leave empty to pass.\n"));
break; break;
case "": // Empty case "": // Empty
// On copy orignal value mode, return $msg // On copy orignal value mode, return $msg
@ -719,9 +755,9 @@ function cli_generate_lang_file($command_args) {
$i++; $i++;
$without = strtolower($command_args[$i]); $without = strtolower($command_args[$i]);
if (!in_array($without, $available_withouts)) if (!in_array($without, $available_withouts))
LScli :: usage("Invalid -W/--without parameter. Must be one of the following values : '".implode("','", $available_withouts)."'."); LScli :: usage(_("Invalid -W/--without parameter. Must be one of the following values: %s.'"), implode("','", $available_withouts));
elseif ($only) elseif ($only)
LScli :: usage("You could not use only -W/--without parameter combined with -O/--only parameter."); LScli :: usage(_("You could not use -W/--without parameter combined with -O/--only parameter."));
$withouts[] = $without; $withouts[] = $without;
break; break;
@ -729,12 +765,12 @@ function cli_generate_lang_file($command_args) {
case '-O': case '-O':
$i++; $i++;
if ($only) if ($only)
LScli :: usage("You could specify only on -O/--only parameter."); LScli :: usage(_("You could specify only one -O/--only parameter."));
$only = strtolower($command_args[$i]); $only = strtolower($command_args[$i]);
if (!in_array($only, $available_onlys)) if (!in_array($only, $available_onlys))
LScli :: usage("Invalid -O/--only parameter. Must be one of the following values : '".implode("','", $available_onlys)."'."); LScli :: usage(_("Invalid -O/--only parameter. Must be one of the following values: %s.'"), implode("','", $available_onlys));
elseif ($withouts) elseif ($withouts)
LScli :: usage("You could not use only -O/--only parameter combined with -W/--without parameter."); LScli :: usage(_("You could not use -W/--without parameter combined with -O/--only parameter."));
break; break;
case '-I': case '-I':
@ -766,7 +802,7 @@ function cli_generate_lang_file($command_args) {
$encoding = $parse_lang[1]; $encoding = $parse_lang[1];
} }
else { else {
LScli :: usage("Invalid --lang parameter. Must be compose in format : [lang].[encoding]"); LScli :: usage(_("Invalid -l/--lang parameter. Must be compose in format : [lang].[encoding]"));
} }
break; break;
@ -781,7 +817,7 @@ function cli_generate_lang_file($command_args) {
$i++; $i++;
$format = strtolower($command_args[$i]); $format = strtolower($command_args[$i]);
if (!in_array($format, $available_formats)) { if (!in_array($format, $available_formats)) {
LScli :: usage("Invalid -f/--format parameter. Must be one of the following values : '".implode("','", $available_formats)."'."); LScli :: usage(_("Invalid -f/--format parameter. Must be one of the following values: %s.'"), implode("','", $available_formats));
} }
break; break;
@ -805,7 +841,7 @@ function cli_generate_lang_file($command_args) {
if (is_file($path)) if (is_file($path))
$load_files[] = $path; $load_files[] = $path;
else else
LScli :: usage($command_args[$i]." : Invalid parameter or lang file to load."); LScli :: usage(_("%s: Invalid parameter or lang file to load."), $command_args[$i]);
} }
} }
@ -1097,29 +1133,32 @@ function cli_generate_lang_file_args_autocompleter($comp_words, $comp_word_num,
LScli :: add_command( LScli :: add_command(
'generate_lang_file', 'generate_lang_file',
'cli_generate_lang_file', 'cli_generate_lang_file',
'Generate lang.php file', ___('Generate lang.php file'),
'l [lang] [-o output.file] [file1] [file2] [-h] [options]', '-l [lang] [-o output.file] [file1] [file2] [-h] [options]',
array( array(
" -W/--without Disable specified messages. Must be one of", ___(
" the following values :", " -W/--without Disable specified messages. Must be one of
the following values:"),
" - ".implode("\n - ", $available_withouts), " - ".implode("\n - ", $available_withouts),
" -O/--only Only handle specified messages. Must be one", ___(
" of the following values :", " -O/--only Only handle specified messages. Must be one
of the following values :"),
" - ".implode("\n - ", $available_onlys), " - ".implode("\n - ", $available_onlys),
" -I/--include-upstream Include upstream code to message lookup", ___(
" -c/--copy-original-value Copy original value as translated value when", " -I/--include-upstream Include upstream code to message lookup
" no translated value exists", -c/--copy-original-value Copy original value as translated value when
" -i/--interactive Interactive mode : ask user to enter", no translated value exists
" translated on each translation needed", -i/--interactive Interactive mode : ask user to enter
" -a/--additional-file-format Additional file format output", translated on each translation needed
" -l/--lang Load the specify lang", -a/--additional-file-format Additional file format output
" Format: [lang].[encoding]", -l/--lang Language of the translation
" -o/--output Output file (default: stdout)", Format: [lang].[encoding]
" -f/--format Output file format : php or pot", -o/--output Output file (default: stdout)
" (default: php)", -f/--format Output file format : php or pot
" -K/--keep-unused Keep unused translations in resulting file", (default: php)
" -F/--fix-utf8 Try to load and fix broken UTF-8 characters in", -K/--keep-unused Keep unused translations in resulting file
" existing lang files." -F/--fix-utf8 Try to load and fix broken UTF-8 characters
in existing lang files.")
), ),
false, // This command does not need LDAP connection false, // This command does not need LDAP connection
'cli_generate_lang_file_args_autocompleter' 'cli_generate_lang_file_args_autocompleter'
@ -1210,16 +1249,16 @@ function cli_generate_ldapsaisie_pot($command_args) {
LScli :: add_command( LScli :: add_command(
'generate_ldapsaisie_pot', 'generate_ldapsaisie_pot',
'cli_generate_ldapsaisie_pot', 'cli_generate_ldapsaisie_pot',
'Generate ldapsaisie.pot files :', ___('Generate POT files:'),
null, null,
array( array(
"This command generate 3 POT files:", ___("This command generate 3 POT files:"),
" - ".LS_I18N_DIR_PATH."/ldapsaisie-main.pot", " - ".LS_I18N_DIR_PATH."/ldapsaisie-main.pot",
" => contains messages from PHP files", ___(" => contains messages from PHP files"),
" - ".LS_I18N_DIR_PATH."/ldapsaisie-templates.pot", " - ".LS_I18N_DIR_PATH."/ldapsaisie-templates.pot",
" => contains messages from templates files", ___(" => contains messages from templates files"),
" - ".LS_I18N_DIR_PATH."/ldapsaisie.pot", " - ".LS_I18N_DIR_PATH."/ldapsaisie.pot",
" => contains all messages", ___(" => contains all messages"),
), ),
false // This command does not need LDAP connection false // This command does not need LDAP connection
); );

View file

@ -51,6 +51,14 @@ class LSldap extends LSlog_staticLoggerClass {
*/ */
private static $cnx = NULL; private static $cnx = NULL;
/**
* Registered events
* @see self::addEvent()
* @see self::fireEvent()
* @var array
*/
private static $_events = array();
/** /**
* Set configuration * Set configuration
* *
@ -81,12 +89,16 @@ class LSldap extends LSlog_staticLoggerClass {
if ($config) { if ($config) {
self :: setConfig($config); self :: setConfig($config);
} }
if (!self :: fireEvent('connecting'))
return false;
self :: $cnx = Net_LDAP2::connect(self :: $config); self :: $cnx = Net_LDAP2::connect(self :: $config);
if (Net_LDAP2::isError(self :: $cnx)) { if (Net_LDAP2::isError(self :: $cnx)) {
self :: fireEvent('connection_failure', array('error' => self :: $cnx -> getMessage()));
LSerror :: addErrorCode('LSldap_01',self :: $cnx -> getMessage()); LSerror :: addErrorCode('LSldap_01',self :: $cnx -> getMessage());
self :: $cnx = NULL; self :: $cnx = NULL;
return false; return false;
} }
self :: fireEvent('connected');
return true; return true;
} }
@ -112,12 +124,19 @@ class LSldap extends LSlog_staticLoggerClass {
$config = self :: $config; $config = self :: $config;
$config['binddn'] = $dn; $config['binddn'] = $dn;
$config['bindpw'] = $pwd; $config['bindpw'] = $pwd;
if (!self :: fireEvent('reconnecting', array('dn' => $dn)))
return false;
self :: $cnx = Net_LDAP2::connect($config); self :: $cnx = Net_LDAP2::connect($config);
if (Net_LDAP2::isError(self :: $cnx)) { if (Net_LDAP2::isError(self :: $cnx)) {
self :: fireEvent(
'reconnection_failure',
array('dn' => $dn, 'error' => self :: $cnx -> getMessage())
);
LSerror :: addErrorCode('LSldap_01', self :: $cnx -> getMessage()); LSerror :: addErrorCode('LSldap_01', self :: $cnx -> getMessage());
self :: $cnx = NULL; self :: $cnx = NULL;
return false; return false;
} }
self :: fireEvent('reconnected', array('dn' => $dn));
return true; return true;
} }
@ -134,6 +153,8 @@ class LSldap extends LSlog_staticLoggerClass {
if (!self :: $cnx) { if (!self :: $cnx) {
self :: connect(); self :: connect();
} }
if (!self :: fireEvent('setting_authz_proxy', array('dn' => $dn)))
return false;
$result = self :: $cnx -> setOption( $result = self :: $cnx -> setOption(
'LDAP_OPT_SERVER_CONTROLS', 'LDAP_OPT_SERVER_CONTROLS',
array ( array (
@ -147,9 +168,11 @@ class LSldap extends LSlog_staticLoggerClass {
// Also check user exists to validate the connection with // Also check user exists to validate the connection with
// authz proxy control. // authz proxy control.
if ($result !== True || !self :: exists($dn)) { if ($result !== True || !self :: exists($dn)) {
self :: fireEvent('setting_authz_proxy_failure', array('dn' => $dn));
LSerror :: addErrorCode('LSldap_09'); LSerror :: addErrorCode('LSldap_09');
return False; return False;
} }
self :: fireEvent('set_authz_proxy', array('dn' => $dn));
return True; return True;
} }
@ -163,8 +186,11 @@ class LSldap extends LSlog_staticLoggerClass {
* @return void * @return void
*/ */
public static function close() { public static function close() {
if (!self :: fireEvent('closing'))
return;
self :: $cnx -> done(); self :: $cnx -> done();
self :: $cnx = null; self :: $cnx = null;
self :: fireEvent('closed');
} }
/** /**
@ -306,6 +332,27 @@ class LSldap extends LSlog_staticLoggerClass {
); );
} }
/**
* Format a Datetime object as LDAP date string
*
* @param DateTime|null $datetime The DateTime object to format (optional, default: now)
* @param bool $utc Force UTC timezone (optional, default: false)
*
* @return string|false LDAP date string, or false
*/
public static function formatDate($datetime=null, $utc=true) {
if (is_null($datetime))
$datetime = new DateTime('now');
elseif (!$datetime instanceof DateTime)
return false;
$datetime -> setTimezone(timezone_open($utc?'utc':date_default_timezone_get()));
$datetime_string = $datetime -> format('YmdHis.uO');
// Replace +0000 or -0000 end by Z
$datetime_string = preg_replace('/[\+\-]0000$/', 'Z', $datetime_string);
return $datetime_string;
}
/** /**
* Check if an attribute exists in specified attributes collection * Check if an attribute exists in specified attributes collection
* *
@ -333,12 +380,13 @@ class LSldap extends LSlog_staticLoggerClass {
* @param string $name Name of a attribute * @param string $name Name of a attribute
* @param boolean $multiple true if we must return array * @param boolean $multiple true if we must return array
* *
* @return mixed Found value (or array of values) or null * @return ($multiple is True ? array<int,string> : string|null) Found value (or array of values) or null
*/ */
public static function getAttr($attrs, $name, $multiple=false) { public static function getAttr($attrs, $name, $multiple=false) {
$name = strtolower($name); $name = strtolower($name);
foreach ($attrs as $k => $v) { foreach ($attrs as $k => $v) {
if (strtolower($k) === $name) { if (strtolower($k) === $name) {
$v = ensureIsArray($v);
return $multiple ? $v : $v[0]; return $multiple ? $v : $v[0];
} }
} }
@ -456,12 +504,12 @@ class LSldap extends LSlog_staticLoggerClass {
* *
* @param string $object_type The object type * @param string $object_type The object type
* @param string $dn DN of the LDAP object * @param string $dn DN of the LDAP object
* @param array $change Array of object attributes changes * @param array $changes Array of object attributes changes
* *
* @return boolean True if object was updated, False otherwise. * @return boolean True if object was updated, False otherwise.
*/ */
public static function update($object_type, $dn, $change) { public static function update($object_type, $dn, $changes) {
self :: log_trace("update($object_type, $dn): change=".varDump($change)); self :: log_trace("update($object_type, $dn): change=".varDump($changes));
// Retrieve current LDAP entry // Retrieve current LDAP entry
$entry = self :: getEntry($object_type, $dn); $entry = self :: getEntry($object_type, $dn);
@ -470,10 +518,18 @@ class LSldap extends LSlog_staticLoggerClass {
return false; return false;
} }
if (
!self :: fireEvent(
'updating',
array('object_type' => $object_type, 'dn' => $dn, 'entry' => &$entry, 'changes' => $changes)
)
)
return false;
// Distinguish drop attributes from change attributes // Distinguish drop attributes from change attributes
$changed_attrs = array(); $changed_attrs = array();
$dropped_attrs = array(); $dropped_attrs = array();
foreach($change as $attrName => $attrVal) { foreach($changes as $attrName => $attrVal) {
$drop = true; $drop = true;
if (is_array($attrVal)) { if (is_array($attrVal)) {
foreach($attrVal as $val) { foreach($attrVal as $val) {
@ -507,6 +563,9 @@ class LSldap extends LSlog_staticLoggerClass {
} }
} }
// Keep original entry (to provide to hooks)
$original_entry = clone $entry;
// Handle attributes changes (if need) // Handle attributes changes (if need)
if ($changed_attrs) { if ($changed_attrs) {
@ -521,10 +580,26 @@ class LSldap extends LSlog_staticLoggerClass {
} }
if (Net_LDAP2::isError($ret)) { if (Net_LDAP2::isError($ret)) {
self :: fireEvent(
'update_failure',
array(
'object_type' => $object_type, 'dn' => $dn,
'original_entry' => &$original_entry, 'entry' => &$entry,
'changes' => $changed_attrs, 'error' => $ret->getMessage()
)
);
LSerror :: addErrorCode('LSldap_05',$dn); LSerror :: addErrorCode('LSldap_05',$dn);
LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage()); LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage());
return false; return false;
} }
self :: fireEvent(
'updated',
array(
'object_type' => $object_type, 'dn' => $dn,
'original_entry' => &$original_entry, 'entry' => &$entry,
'changes' => $changed_attrs
)
);
} }
elseif ($entry -> isNew()) { elseif ($entry -> isNew()) {
self :: log_error("update($object_type, $dn): no changed attribute but it's a new entry..."); self :: log_error("update($object_type, $dn): no changed attribute but it's a new entry...");
@ -560,10 +635,26 @@ class LSldap extends LSlog_staticLoggerClass {
// Check result // Check result
if (Net_LDAP2::isError($ret)) { if (Net_LDAP2::isError($ret)) {
self :: fireEvent(
'update_failure',
array(
'object_type' => $object_type, 'dn' => $dn,
'original_entry' => &$original_entry, 'entry' => &$entry,
'changes' => $replace_attrs, 'error' => $ret->getMessage()
)
);
LSerror :: addErrorCode('LSldap_06'); LSerror :: addErrorCode('LSldap_06');
LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage()); LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage());
return false; return false;
} }
self :: fireEvent(
'updated',
array(
'object_type' => $object_type, 'dn' => $dn,
'original_entry' => &$original_entry, 'entry' => &$entry,
'changes' => $replace_attrs
)
);
} }
return true; return true;
} }
@ -606,11 +697,15 @@ class LSldap extends LSlog_staticLoggerClass {
* @return boolean True if object was removed, False otherwise. * @return boolean True if object was removed, False otherwise.
*/ */
public static function remove($dn) { public static function remove($dn) {
if (!self :: fireEvent('removing', array('dn' => $dn)))
return false;
$ret = self :: $cnx -> delete($dn,array('recursive' => true)); $ret = self :: $cnx -> delete($dn,array('recursive' => true));
if (Net_LDAP2::isError($ret)) { if (Net_LDAP2::isError($ret)) {
self :: fireEvent('remove_failure', array('dn' => $dn, 'error' => $ret->getMessage()));
LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage()); LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage());
return false; return false;
} }
self :: fireEvent('removed', array('dn' => $dn));
return true; return true;
} }
@ -623,12 +718,19 @@ class LSldap extends LSlog_staticLoggerClass {
* @return boolean True if object was moved, False otherwise. * @return boolean True if object was moved, False otherwise.
*/ */
public static function move($old, $new) { public static function move($old, $new) {
if (!self :: fireEvent('moving', array('old' => $old, 'new' => $new)))
return false;
$ret = self :: $cnx -> move($old, $new); $ret = self :: $cnx -> move($old, $new);
if (Net_LDAP2::isError($ret)) { if (Net_LDAP2::isError($ret)) {
self :: fireEvent(
'move_failure',
array('old' => $old, 'new' => $new, 'error' => $ret->getMessage())
);
LSerror :: addErrorCode('LSldap_07'); LSerror :: addErrorCode('LSldap_07');
LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage()); LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage());
return false; return false;
} }
self :: fireEvent('moved', array('old' => $old, 'new' => $new));
return true; return true;
} }
@ -720,22 +822,63 @@ class LSldap extends LSlog_staticLoggerClass {
_('It is too soon to change the password'), _('It is too soon to change the password'),
_('This password was recently used and cannot be used again'), _('This password was recently used and cannot be used again'),
); );
self :: log_debug("update($object_type, $dn): update entry for userPassword"); self :: log_debug("updateUserPassword($object_type, $dn): update entry userPassword attribute");
$changes = array('userPassword' => self :: getAttr($changed_attrs, 'userPassword', true));
if (
!self :: fireEvent(
'user_password_updating',
array(
'object_type' => $object_type, 'dn' => $dn,
'new_passwords' => $changes['userPassword']
)
)
)
return false;
$ldap = self :: $cnx->getLink(); $ldap = self :: $cnx->getLink();
$attr = array('userPassword' => self :: getAttr($changed_attrs, 'userPassword'));
$ctrlRequest = array(array('oid' => LDAP_CONTROL_PASSWORDPOLICYREQUEST)); $ctrlRequest = array(array('oid' => LDAP_CONTROL_PASSWORDPOLICYREQUEST));
$r = ldap_mod_replace_ext($ldap, $dn, $attr, $ctrlRequest); $r = ldap_mod_replace_ext($ldap, $dn, $changes, $ctrlRequest);
if ($r && ldap_parse_result($ldap, $r, $errcode, $matcheddn, $errmsg, $ref, $ctrlResponse)) { if ($r && ldap_parse_result($ldap, $r, $errcode, $matcheddn, $errmsg, $ref, $ctrlResponse)) {
if ($errcode !== 0 && isset($ctrlResponse[LDAP_CONTROL_PASSWORDPOLICYRESPONSE])) { if ($errcode !== 0 && isset($ctrlResponse[LDAP_CONTROL_PASSWORDPOLICYRESPONSE])) {
LSerror :: addErrorCode('LSldap_10', $ppolicyErrorMsg[$ctrlResponse[LDAP_CONTROL_PASSWORDPOLICYRESPONSE]['value']['error']]); self :: fireEvent(
'user_password_update_failure',
array(
'object_type' => $object_type, 'dn' => $dn,
'error' => $ppolicyErrorMsg[
$ctrlResponse[LDAP_CONTROL_PASSWORDPOLICYRESPONSE]['value']['error']
]
)
);
LSerror :: addErrorCode(
'LSldap_10',
$ppolicyErrorMsg[$ctrlResponse[LDAP_CONTROL_PASSWORDPOLICYRESPONSE]['value']['error']]
);
return false; return false;
} }
// If everything OK, remove userPassword to prevent it from being processed by Net_LDAP2 // Password updated
self :: fireEvent(
'user_password_updated',
array(
'object_type' => $object_type, 'dn' => $dn,
'new_passwords' => $changes['userPassword']
)
);
// Remove userPassword to prevent it from being processed by update()
unset($changed_attrs['userPassword']); unset($changed_attrs['userPassword']);
} else { }
else {
self :: fireEvent(
'user_password_update_failure',
array(
'object_type' => $object_type, 'dn' => $dn,
'error' => ldap_errno($ldap) !== 0?ldap_error($ldap):'unknown'
)
);
if (ldap_errno($ldap) !== 0) { if (ldap_errno($ldap) !== 0) {
LSerror :: addErrorCode('LSldap_10', ldap_error($ldap)); LSerror :: addErrorCode('LSldap_10', ldap_error($ldap));
} else { }
else {
LSerror :: addErrorCode('LSldap_11'); LSerror :: addErrorCode('LSldap_11');
} }
return false; return false;
@ -755,6 +898,66 @@ class LSldap extends LSlog_staticLoggerClass {
private static function getConfig($param, $default=null, $cast=null) { private static function getConfig($param, $default=null, $cast=null) {
return LSconfig :: get($param, $default, $cast, self :: $config); return LSconfig :: get($param, $default, $cast, self :: $config);
} }
/**
* Registered an action on a specific event
*
* @param string $event The event name
* @param callable $callable The callable to run on event
* @param array $params Paremeters that will be pass to the callable
*
* @return void
*/
public static function addEvent($event, $callable, $params=NULL) {
self :: $_events[$event][] = array(
'callable' => $callable,
'params' => is_array($params)?$params:array(),
);
}
/**
* Run triggered actions on specific event
*
* @param string $event Event name
* @param mixed $data Event data
*
* @return boolean True if all triggered actions succefully runned, false otherwise
*/
public static function fireEvent($event, $data=null) {
$return = true;
// Binding via addEvent
if (isset(self :: $_events[$event]) && is_array(self :: $_events[$event])) {
foreach (self :: $_events[$event] as $e) {
if (is_callable($e['callable'])) {
try {
call_user_func_array(
$e['callable'],
array_merge(
array($data), $e['params']
)
);
}
catch(Exception $er) {
LSerror :: addErrorCode(
'LSldap_13',
array('callable' => format_callable($e['callable']), 'event' => $event)
);
$return = false;
}
}
else {
LSerror :: addErrorCode(
'LSldap_12',
array('callable' => format_callable($e['callable']), 'event' => $event)
);
$return = false;
}
}
}
return $return;
}
} }
/* /*
@ -793,3 +996,9 @@ LSerror :: defineError('LSldap_10',
LSerror :: defineError('LSldap_11', LSerror :: defineError('LSldap_11',
___("LSldap: Unknown LDAP error while updating user password") ___("LSldap: Unknown LDAP error while updating user password")
); );
LSerror :: defineError('LSldap_12',
___("LSldap: Fail to execute trigger %{callable} on event %{event} : is not callable.")
);
LSerror :: defineError('LSldap_13',
___("LSldap: Error during the execution of the trigger %{callable} on event %{event}.")
);

View file

@ -497,13 +497,6 @@ class LSsession {
) )
)) ))
return False; return False;
LStemplate :: addHelpInfo(
'LSdefault',
array(
'copy_to_clipboard' => _('Copy to clipboard'),
'copied' => _('Copied!'),
)
);
return True; return True;
} }
return False; return False;

View file

@ -416,6 +416,13 @@ class LStemplate extends LSlog_staticLoggerClass {
**/ **/
public static function assignCommonVars() { public static function assignCommonVars() {
// JS config // JS config
LStemplate :: addHelpInfo(
'LSdefault',
array(
'copy_to_clipboard' => _('Copy to clipboard'),
'copied' => _('Copied!'),
)
);
LStemplate :: assign('LSjsConfig', base64_encode(json_encode(self :: $JSconfigParams))); LStemplate :: assign('LSjsConfig', base64_encode(json_encode(self :: $JSconfigParams)));
// JS files // JS files

View file

@ -405,6 +405,35 @@ function sumDn($dn1,$dn2) {
return $basedn; return $basedn;
} }
/**
* Extract and retreive RDN from DN
* @param string $dn
* @param bool $with_attr If true, include the RDN attribute name (optional, default: true)
* @return string|null|false The parent object DN, null if no parent or false in case of error
*/
function getRdn($dn, $with_attr=true) {
$parts = ldap_explode_dn($dn, 0);
if (!is_array($parts) || !isset($parts['count'])) return false;
if ($with_attr)
return $parts[0];
$equal_pos = strpos($parts[0], '=');
if ($equal_pos !== false)
return substr($parts[0], $equal_pos+1);
return $parts[0];
}
/**
* Retreive parent object DN
* @param string $dn
* @return string|null|false The parent object DN, null if no parent or false in case of error
*/
function parentDn($dn) {
$parts = ldap_explode_dn($dn, 0);
if (!is_array($parts) || !isset($parts['count'])) return false;
if ($parts['count'] == 1) return null;
return implode(',', array_slice($parts, 2));
}
function checkEmail($value,$domain=NULL,$checkDns=true) { function checkEmail($value,$domain=NULL,$checkDns=true) {
$log = LSlog :: get_logger('checkEmail'); $log = LSlog :: get_logger('checkEmail');
$regex = '/^((\"[^\"\f\n\r\t\v\b]+\")|([\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+(\.[\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+)*))@((\[(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))\])|(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))|((([A-Za-z0-9\-])+\.)+[A-Za-z\-]+))$/'; $regex = '/^((\"[^\"\f\n\r\t\v\b]+\")|([\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+(\.[\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+)*))@((\[(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))\])|(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))|((([A-Za-z0-9\-])+\.)+[A-Za-z\-]+))$/';
@ -781,3 +810,29 @@ function timestamp2LdapDate($value, $timezone=null, $format=null) {
return dateTime2LdapDate($datetime, $timezone, $format); return dateTime2LdapDate($datetime, $timezone, $format);
return false; return false;
} }
/**
* Generate an UUID
* @return string
*/
function generate_uuid() {
return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
// 32 bits for "time_low"
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
// 16 bits for "time_mid"
mt_rand( 0, 0xffff ),
// 16 bits for "time_hi_and_version",
// four most significant bits holds version number 4
mt_rand( 0, 0x0fff ) | 0x4000,
// 16 bits, 8 bits for "clk_seq_hi_res",
// 8 bits for "clk_seq_low",
// two most significant bits holds zero and one for variant DCE1.1
mt_rand( 0, 0x3fff ) | 0x8000,
// 48 bits for "node"
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
);
}

View file

@ -76,6 +76,12 @@ var LSdefault = new Class({
this.initializeLang(); this.initializeLang();
document.getElements('.copyable').each(function(el) { document.getElements('.copyable').each(function(el) {
if (el.hasClass('copyable-no-btn')) {
el.addEvent('click', this.onCopyBtnClick.bind(this, {btn: null, element: el}));
el.setStyle('cursor', 'copy');
this.addHelpInfo(el, 'LSdefault', 'copy_to_clipboard');
return;
}
var btn = new Element('img'); var btn = new Element('img');
btn.setProperties({ btn.setProperties({
src: this.imagePath('copy') src: this.imagePath('copy')

File diff suppressed because it is too large Load diff

View file

@ -217,7 +217,7 @@ msgid "Download"
msgstr "" msgstr ""
#: includes/addons/LSaddons.showSupportInfo.php:78 #: includes/addons/LSaddons.showSupportInfo.php:78
#: includes/addons/LSaddons.accesslog.php:181 #: includes/addons/LSaddons.accesslog.php:225
#: includes/addons/LSaddons.showTechInfo.php:117 #: includes/addons/LSaddons.showTechInfo.php:117
msgid "Go back" msgid "Go back"
msgstr "" msgstr ""
@ -350,8 +350,8 @@ msgstr ""
msgid "Increment" msgid "Increment"
msgstr "" msgstr ""
#: includes/addons/LSaddons.accesslog.php:176 #: includes/addons/LSaddons.accesslog.php:220
#: includes/class/class.LSsession.php:1862 includes/routes.php:157 #: includes/class/class.LSsession.php:1855 includes/routes.php:157
#: includes/routes.php:470 templates/default/select.tpl:29 #: includes/routes.php:470 templates/default/select.tpl:29
msgid "Refresh" msgid "Refresh"
msgstr "" msgstr ""
@ -390,15 +390,15 @@ msgstr ""
msgid "SUPANN: Fail to load nomenclature %{nomenclature}." msgid "SUPANN: Fail to load nomenclature %{nomenclature}."
msgstr "" msgstr ""
#: includes/addons/LSaddons.supann.php:376 #: includes/addons/LSaddons.supann.php:447
msgid "Entity %{id} (unrecognized)" msgid "Entity %{id} (unrecognized)"
msgstr "" msgstr ""
#: includes/addons/LSaddons.supann.php:448 #: includes/addons/LSaddons.supann.php:519
msgid "Godfather %{dn} (unrecognized)" msgid "Godfather %{dn} (unrecognized)"
msgstr "" msgstr ""
#: includes/addons/LSaddons.supann.php:575 #: includes/addons/LSaddons.supann.php:646
#: includes/class/class.LSformElement_select.php:58 #: includes/class/class.LSformElement_select.php:58
#: includes/class/class.LSformElement_select_object.php:108 #: includes/class/class.LSformElement_select_object.php:108
msgid "%{value} (unrecognized value)" msgid "%{value} (unrecognized value)"
@ -740,7 +740,7 @@ msgid ""
msgstr "" msgstr ""
#: includes/class/class.LSformElement.php:262 #: includes/class/class.LSformElement.php:262
#: templates/default/showObjectAccessLogs.tpl:28 #: templates/default/showObjectAccessLogs.tpl:29
msgid "Attribute" msgid "Attribute"
msgstr "" msgstr ""
@ -918,6 +918,149 @@ msgstr ""
msgid "Clear selected date." msgid "Clear selected date."
msgstr "" msgstr ""
#: includes/class/class.LSlang.php:282
msgid "Please enter translated string"
msgstr ""
#: includes/class/class.LSlang.php:285
msgid "or leave empty to copy original message"
msgstr ""
#: includes/class/class.LSlang.php:286
msgid "or leave empty to pass"
msgstr ""
#: includes/class/class.LSlang.php:314
msgid "Fail to create temporary file."
msgstr ""
#: includes/class/class.LSlang.php:319
msgid "Fail to open temporary file."
msgstr ""
#: includes/class/class.LSlang.php:339
msgid "Available choices:\n"
msgstr ""
#: includes/class/class.LSlang.php:340
msgid " - i: ignore this message\n"
msgstr ""
#: includes/class/class.LSlang.php:342
msgid " - c: copy original message\n"
msgstr ""
#: includes/class/class.LSlang.php:343
msgid " - e: translate message in text editor\n"
msgstr ""
#: includes/class/class.LSlang.php:344
msgid ""
" - q: quit interactive mode and ignore all following untranslated messages\n"
msgstr ""
#: includes/class/class.LSlang.php:345
msgid " - ?: Show this message\n"
msgstr ""
#: includes/class/class.LSlang.php:347
msgid "Or leave empty to copy original message.\n"
msgstr ""
#: includes/class/class.LSlang.php:349
msgid "Or leave empty to pass.\n"
msgstr ""
#: includes/class/class.LSlang.php:758
#, php-format
msgid ""
"Invalid -W/--without parameter. Must be one of the following values: %s.'"
msgstr ""
#: includes/class/class.LSlang.php:760 includes/class/class.LSlang.php:773
msgid ""
"You could not use -W/--without parameter combined with -O/--only parameter."
msgstr ""
#: includes/class/class.LSlang.php:768
msgid "You could specify only one -O/--only parameter."
msgstr ""
#: includes/class/class.LSlang.php:771
#, php-format
msgid "Invalid -O/--only parameter. Must be one of the following values: %s.'"
msgstr ""
#: includes/class/class.LSlang.php:805
msgid ""
"Invalid -l/--lang parameter. Must be compose in format : [lang].[encoding]"
msgstr ""
#: includes/class/class.LSlang.php:820
#, php-format
msgid ""
"Invalid -f/--format parameter. Must be one of the following values: %s.'"
msgstr ""
#: includes/class/class.LSlang.php:844
#, php-format
msgid "%s: Invalid parameter or lang file to load."
msgstr ""
#: includes/class/class.LSlang.php:1136
msgid "Generate lang.php file"
msgstr ""
#: includes/class/class.LSlang.php:1140
msgid ""
" -W/--without Disable specified messages. Must be one of\n"
" the following values:"
msgstr ""
#: includes/class/class.LSlang.php:1144
msgid ""
" -O/--only Only handle specified messages. Must be one\n"
" of the following values :"
msgstr ""
#: includes/class/class.LSlang.php:1148
msgid ""
" -I/--include-upstream Include upstream code to message lookup\n"
" -c/--copy-original-value Copy original value as translated value when\n"
" no translated value exists\n"
" -i/--interactive Interactive mode : ask user to enter\n"
" translated on each translation needed\n"
" -a/--additional-file-format Additional file format output\n"
" -l/--lang Language of the translation\n"
" Format: [lang].[encoding]\n"
" -o/--output Output file (default: stdout)\n"
" -f/--format Output file format : php or pot\n"
" (default: php)\n"
" -K/--keep-unused Keep unused translations in resulting file\n"
" -F/--fix-utf8 Try to load and fix broken UTF-8 characters\n"
" in existing lang files."
msgstr ""
#: includes/class/class.LSlang.php:1252
msgid "Generate POT files:"
msgstr ""
#: includes/class/class.LSlang.php:1255
msgid "This command generate 3 POT files:"
msgstr ""
#: includes/class/class.LSlang.php:1257
msgid " => contains messages from PHP files"
msgstr ""
#: includes/class/class.LSlang.php:1259
msgid " => contains messages from templates files"
msgstr ""
#: includes/class/class.LSlang.php:1261
msgid " => contains all messages"
msgstr ""
#: includes/class/class.LStemplate.php:160 #: includes/class/class.LStemplate.php:160
msgid "LStemplate : compile directory is not writable (dir : %{dir})" msgid "LStemplate : compile directory is not writable (dir : %{dir})"
msgstr "" msgstr ""
@ -944,38 +1087,46 @@ msgid ""
"upgrade documentation to adapt your templates." "upgrade documentation to adapt your templates."
msgstr "" msgstr ""
#: includes/class/class.LStemplate.php:478 #: includes/class/class.LStemplate.php:422
msgid "Copy to clipboard"
msgstr ""
#: includes/class/class.LStemplate.php:423
msgid "Copied!"
msgstr ""
#: includes/class/class.LStemplate.php:485
msgid "Smarty - An exception occured displaying template '%{template}'" msgid "Smarty - An exception occured displaying template '%{template}'"
msgstr "" msgstr ""
#: includes/class/class.LStemplate.php:498 #: includes/class/class.LStemplate.php:505
msgid "Smarty - An exception occured fetching template '%{template}'" msgid "Smarty - An exception occured fetching template '%{template}'"
msgstr "" msgstr ""
#: includes/class/class.LStemplate.php:514 #: includes/class/class.LStemplate.php:521
#: includes/class/class.LStemplate.php:528 #: includes/class/class.LStemplate.php:535
msgid "A fatal error occured. If problem persist, please contact support." msgid "A fatal error occured. If problem persist, please contact support."
msgstr "" msgstr ""
#: includes/class/class.LStemplate.php:524 #: includes/class/class.LStemplate.php:531
msgid "<h1>Loop detected displaying this error:</h1><pre>%{error}</pre>" msgid "<h1>Loop detected displaying this error:</h1><pre>%{error}</pre>"
msgstr "" msgstr ""
#: includes/class/class.LStemplate.php:527 #: includes/class/class.LStemplate.php:534
msgid "A fatal error occured." msgid "A fatal error occured."
msgstr "" msgstr ""
#: includes/class/class.LStemplate.php:724 #: includes/class/class.LStemplate.php:731
msgid "LStemplate : Template %{file} not found." msgid "LStemplate : Template %{file} not found."
msgstr "" msgstr ""
#: includes/class/class.LStemplate.php:727 #: includes/class/class.LStemplate.php:734
msgid "" msgid ""
"LStemplate : Fail to execute trigger %{callable} on event %{event} : is not " "LStemplate : Fail to execute trigger %{callable} on event %{event} : is not "
"callable." "callable."
msgstr "" msgstr ""
#: includes/class/class.LStemplate.php:730 #: includes/class/class.LStemplate.php:737
msgid "" msgid ""
"LStemplate : Error during the execution of the trigger %{callable} on event " "LStemplate : Error during the execution of the trigger %{callable} on event "
"%{event}." "%{event}."
@ -1123,165 +1274,157 @@ msgstr ""
msgid "Clear" msgid "Clear"
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:503 #: includes/class/class.LSsession.php:1640
msgid "Copy to clipboard"
msgstr ""
#: includes/class/class.LSsession.php:504
msgid "Copied!"
msgstr ""
#: includes/class/class.LSsession.php:1647
msgid "Connection" msgid "Connection"
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:1667 #: includes/class/class.LSsession.php:1660
msgid "Recovery of your credentials" msgid "Recovery of your credentials"
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:1676 #: includes/class/class.LSsession.php:1669
msgid "Please fill the identifier field to proceed recovery procedure" msgid "Please fill the identifier field to proceed recovery procedure"
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:1681 #: includes/class/class.LSsession.php:1674
msgid "" msgid ""
"An email has been sent to %{mail}. Please follow the instructions on it." "An email has been sent to %{mail}. Please follow the instructions on it."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:1690 #: includes/class/class.LSsession.php:1683
msgid "Your new password has been sent to %{mail}." msgid "Your new password has been sent to %{mail}."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:2952 templates/default/select.tpl:20 #: includes/class/class.LSsession.php:2945 templates/default/select.tpl:20
#: templates/default/recoverpassword.tpl:17 templates/default/login.tpl:16 #: templates/default/recoverpassword.tpl:17 templates/default/login.tpl:16
msgid "Level" msgid "Level"
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3120 #: includes/class/class.LSsession.php:3113
msgid "LSsession : The constant '%{const}' is not defined." msgid "LSsession : The constant '%{const}' is not defined."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3123 #: includes/class/class.LSsession.php:3116
msgid "" msgid ""
"LSsession : The addon '%{addon}' support is uncertain. Verify system " "LSsession : The addon '%{addon}' support is uncertain. Verify system "
"compatibility and the add-on configuration." "compatibility and the add-on configuration."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3126 #: includes/class/class.LSsession.php:3119
msgid "" msgid ""
"LSsession : LDAP server's configuration data are invalid. Can't connect." "LSsession : LDAP server's configuration data are invalid. Can't connect."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3129 #: includes/class/class.LSsession.php:3122
msgid "LSsession : Failed to load LSobject type '%{type}' : unknon type." msgid "LSsession : Failed to load LSobject type '%{type}' : unknon type."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3132 #: includes/class/class.LSsession.php:3125
msgid "LSsession : Failed to load LSclass '%{class}'." msgid "LSsession : Failed to load LSclass '%{class}'."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3135 #: includes/class/class.LSsession.php:3128
msgid "LSsession : Login or password incorrect." msgid "LSsession : Login or password incorrect."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3138 #: includes/class/class.LSsession.php:3131
msgid "LSsession : Impossible to identify you : Duplication of identities." msgid "LSsession : Impossible to identify you : Duplication of identities."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3141 #: includes/class/class.LSsession.php:3134
msgid "LSsession : Can't load class of authentification (%{class})." msgid "LSsession : Can't load class of authentification (%{class})."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3144 #: includes/class/class.LSsession.php:3137
msgid "LSsession : Can't connect to LDAP server." msgid "LSsession : Can't connect to LDAP server."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3147 #: includes/class/class.LSsession.php:3140
msgid "LSsession : Impossible to authenticate you." msgid "LSsession : Impossible to authenticate you."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3150 #: includes/class/class.LSsession.php:3143
msgid "LSsession : Your are not authorized to do this action." msgid "LSsession : Your are not authorized to do this action."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3153 #: includes/class/class.LSsession.php:3146
msgid "LSsession : Some informations are missing to display this page." msgid "LSsession : Some informations are missing to display this page."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3156 #: includes/class/class.LSsession.php:3149
msgid "" msgid ""
"LSsession : The function '%{function}' of the custom action " "LSsession : The function '%{function}' of the custom action "
"'%{customAction}' does not exists or is not configured." "'%{customAction}' does not exists or is not configured."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3159 #: includes/class/class.LSsession.php:3152
msgid "LSsession : Fail to retrieve user's LDAP credentials from LSauth." msgid "LSsession : Fail to retrieve user's LDAP credentials from LSauth."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3162 #: includes/class/class.LSsession.php:3155
msgid "" msgid ""
"LSsession : Fail to reconnect to LDAP server with user's LDAP credentials." "LSsession : Fail to reconnect to LDAP server with user's LDAP credentials."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3165 #: includes/class/class.LSsession.php:3158
msgid "LSsession : No import/export format define for this object type." msgid "LSsession : No import/export format define for this object type."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3168 #: includes/class/class.LSsession.php:3161
msgid "" msgid ""
"LSsession : Error during creation of list of levels. Contact administrators. " "LSsession : Error during creation of list of levels. Contact administrators. "
"(Code : %{code})" "(Code : %{code})"
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3171 #: includes/class/class.LSsession.php:3164
msgid "LSsession : The password recovery is disabled for this LDAP server." msgid "LSsession : The password recovery is disabled for this LDAP server."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3174 #: includes/class/class.LSsession.php:3167
msgid "" msgid ""
"LSsession : Some informations are missing to recover your password. Contact " "LSsession : Some informations are missing to recover your password. Contact "
"administrators." "administrators."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3177 #: includes/class/class.LSsession.php:3170
msgid "" msgid ""
"LSsession : Error during password recovery. Contact administrators.(Step : " "LSsession : Error during password recovery. Contact administrators.(Step : "
"%{step})" "%{step})"
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3180 #: includes/class/class.LSsession.php:3173
msgid "" msgid ""
"LSsession : The function '%{func}' configured for the view '%{view}' of the " "LSsession : The function '%{func}' configured for the view '%{view}' of the "
"LSaddon '%{addon}' is not declared in the LSaddon file." "LSaddon '%{addon}' is not declared in the LSaddon file."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3183 #: includes/class/class.LSsession.php:3176
msgid "LSsession : Failed to load resource file '%{file}'." msgid "LSsession : Failed to load resource file '%{file}'."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3186 #: includes/class/class.LSsession.php:3179
msgid "" msgid ""
"LSsession : The function '%{func}' configured for the view '%{view}' of the " "LSsession : The function '%{func}' configured for the view '%{view}' of the "
"LSaddon '%{addon}' doesn't exist." "LSaddon '%{addon}' doesn't exist."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3189 #: includes/class/class.LSsession.php:3182
msgid "LSsession : invalid related object's DN pass in parameter." msgid "LSsession : invalid related object's DN pass in parameter."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3192 #: includes/class/class.LSsession.php:3185
msgid "" msgid ""
"LSsession : the LSaddon %{addon} keep using old-style addon view URL. Please " "LSsession : the LSaddon %{addon} keep using old-style addon view URL. Please "
"upgrade it." "upgrade it."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3195 #: includes/class/class.LSsession.php:3188
msgid "" msgid ""
"LSsession : You have been redirect from an old-style URL %{url}. Please " "LSsession : You have been redirect from an old-style URL %{url}. Please "
"upgrade this link." "upgrade this link."
msgstr "" msgstr ""
#: includes/class/class.LSsession.php:3198 #: includes/class/class.LSsession.php:3191
msgid "" msgid ""
"LSsession : You always seem to use %{old} in your custom code: Please " "LSsession : You always seem to use %{old} in your custom code: Please "
"upgrade it and use %{new}.<pre>\n" "upgrade it and use %{new}.<pre>\n"
@ -1294,199 +1437,199 @@ msgstr ""
msgid "Invalid file type (%{type})." msgid "Invalid file type (%{type})."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:566 #: includes/class/class.LSldapObject.php:577
msgid "The attribute %{attr} is not valid." msgid "The attribute %{attr} is not valid."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3139 #: includes/class/class.LSldapObject.php:3150
msgid "LSldapObject : Object type unknown." msgid "LSldapObject : Object type unknown."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3142 #: includes/class/class.LSldapObject.php:3153
msgid "LSldapObject : Update form is not defined for the object %{obj}." msgid "LSldapObject : Update form is not defined for the object %{obj}."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3145 #: includes/class/class.LSldapObject.php:3156
msgid "LSldapObject : No form exists for the object %{obj}." msgid "LSldapObject : No form exists for the object %{obj}."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3148 #: includes/class/class.LSldapObject.php:3159
msgid "" msgid ""
"LSldapObject : The function %{func} to validate the attribute %{attr} the " "LSldapObject : The function %{func} to validate the attribute %{attr} the "
"object %{obj} is unknow." "object %{obj} is unknow."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3151 #: includes/class/class.LSldapObject.php:3162
msgid "" msgid ""
"LSldapObject : Configuration data are missing to validate the attribute " "LSldapObject : Configuration data are missing to validate the attribute "
"%{attr} of the object %{obj}." "%{attr} of the object %{obj}."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3155 #: includes/class/class.LSldapObject.php:3166
msgid "" msgid ""
"LSldapObject : The function %{func} to be executed on the object event " "LSldapObject : The function %{func} to be executed on the object event "
"%{event} doesn't exist." "%{event} doesn't exist."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3158 #: includes/class/class.LSldapObject.php:3169
msgid "" msgid ""
"LSldapObject : The %{func} execution on the object event %{event} failed." "LSldapObject : The %{func} execution on the object event %{event} failed."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3162 #: includes/class/class.LSldapObject.php:3173
msgid "" msgid ""
"LSldapObject : Class %{class}, which method %{meth} to be executed on the " "LSldapObject : Class %{class}, which method %{meth} to be executed on the "
"object event %{event}, doesn't exist." "object event %{event}, doesn't exist."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3165 #: includes/class/class.LSldapObject.php:3176
msgid "" msgid ""
"LSldapObject : Method %{meth} within %{class} class to be executed on object " "LSldapObject : Method %{meth} within %{class} class to be executed on object "
"event %{event}, doesn't exist." "event %{event}, doesn't exist."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3168 #: includes/class/class.LSldapObject.php:3179
msgid "" msgid ""
"LSldapObject : Error during execute %{meth} method within %{class} class, to " "LSldapObject : Error during execute %{meth} method within %{class} class, to "
"be executed on object event %{event}." "be executed on object event %{event}."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3172 #: includes/class/class.LSldapObject.php:3183
msgid "" msgid ""
"LSldapObject : Some configuration data of the object type %{obj} are missing " "LSldapObject : Some configuration data of the object type %{obj} are missing "
"to generate the DN of the new object." "to generate the DN of the new object."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3175 #: includes/class/class.LSldapObject.php:3186
msgid "" msgid ""
"LSldapObject : The attibute %{attr} of the object is not yet defined. Can't " "LSldapObject : The attibute %{attr} of the object is not yet defined. Can't "
"generate DN." "generate DN."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3178 #: includes/class/class.LSldapObject.php:3189
msgid "LSldapObject : Without DN, the object could not be changed." msgid "LSldapObject : Without DN, the object could not be changed."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3181 #: includes/class/class.LSldapObject.php:3192
msgid "" msgid ""
"LSldapObject : The attribute %{attr_depend} depending on the attribute " "LSldapObject : The attribute %{attr_depend} depending on the attribute "
"%{attr} doesn't exist." "%{attr} doesn't exist."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3184 #: includes/class/class.LSldapObject.php:3195
msgid "LSldapObject : Error during deleting the object %{objectname}." msgid "LSldapObject : Error during deleting the object %{objectname}."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3188 #: includes/class/class.LSldapObject.php:3199
msgid "" msgid ""
"LSldapObject : Error during actions to be executed before renaming the objet." "LSldapObject : Error during actions to be executed before renaming the objet."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3191 #: includes/class/class.LSldapObject.php:3202
msgid "" msgid ""
"LSldapObject : Error during actions to be executed after renaming the objet." "LSldapObject : Error during actions to be executed after renaming the objet."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3195 #: includes/class/class.LSldapObject.php:3206
msgid "" msgid ""
"LSldapObject : Error during actions to be executed before deleting the objet." "LSldapObject : Error during actions to be executed before deleting the objet."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3198 #: includes/class/class.LSldapObject.php:3209
msgid "" msgid ""
"LSldapObject : Error during actions to be executed after deleting the objet." "LSldapObject : Error during actions to be executed after deleting the objet."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3202 #: includes/class/class.LSldapObject.php:3213
msgid "" msgid ""
"LSldapObject : Error during the actions to be executed before creating the " "LSldapObject : Error during the actions to be executed before creating the "
"object." "object."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3205 #: includes/class/class.LSldapObject.php:3216
msgid "" msgid ""
"LSldapObject : Error during the actions to be executed after creating the " "LSldapObject : Error during the actions to be executed after creating the "
"object. It was created anyway." "object. It was created anyway."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3209 #: includes/class/class.LSldapObject.php:3220
msgid "" msgid ""
"LSldapObject : The function %{func} to be executed before creating the " "LSldapObject : The function %{func} to be executed before creating the "
"object doesn't exist." "object doesn't exist."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3212 #: includes/class/class.LSldapObject.php:3223
msgid "" msgid ""
"LSldapObject : Error executing the function %{func} to be execute after " "LSldapObject : Error executing the function %{func} to be execute after "
"deleting the object." "deleting the object."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3215 #: includes/class/class.LSldapObject.php:3226
msgid "" msgid ""
"LSldapObject : The function %{func} to be executed after deleting the object " "LSldapObject : The function %{func} to be executed after deleting the object "
"doesn't exist." "doesn't exist."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3218 #: includes/class/class.LSldapObject.php:3229
msgid "" msgid ""
"LSldapObject : Error executing the function %{func} to be execute after " "LSldapObject : Error executing the function %{func} to be execute after "
"creating the object." "creating the object."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3222 #: includes/class/class.LSldapObject.php:3233
msgid "" msgid ""
"LSldapObject : %{func} function, to be executed on object event %{event}, " "LSldapObject : %{func} function, to be executed on object event %{event}, "
"doesn't exist." "doesn't exist."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3225 #: includes/class/class.LSldapObject.php:3236
msgid "" msgid ""
"LSldapObject : Error during the execution of %{func} function on object " "LSldapObject : Error during the execution of %{func} function on object "
"event %{event}." "event %{event}."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3229 #: includes/class/class.LSldapObject.php:3240
msgid "" msgid ""
"LSldapObject : %{meth} method, to be executed on object event %{event}, " "LSldapObject : %{meth} method, to be executed on object event %{event}, "
"doesn't exist." "doesn't exist."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3232 #: includes/class/class.LSldapObject.php:3243
msgid "" msgid ""
"LSldapObject : Error during execution of %{meth} method on object event " "LSldapObject : Error during execution of %{meth} method on object event "
"%{event}." "%{event}."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3235 #: includes/class/class.LSldapObject.php:3246
msgid "LSldapObject : Error during generate LDAP filter for %{LSobject}." msgid "LSldapObject : Error during generate LDAP filter for %{LSobject}."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3239 #: includes/class/class.LSldapObject.php:3250
msgid "" msgid ""
"LSldapObject : Error during execution of the custom action %{customAction} " "LSldapObject : Error during execution of the custom action %{customAction} "
"on %{objectname}." "on %{objectname}."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3243 #: includes/class/class.LSldapObject.php:3254
msgid "LSldapObject : Fail to retrieve container DN." msgid "LSldapObject : Fail to retrieve container DN."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3246 #: includes/class/class.LSldapObject.php:3257
msgid "" msgid ""
"LSldapObject : The function %{func} to generate container DN is not callable." "LSldapObject : The function %{func} to generate container DN is not callable."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3249 #: includes/class/class.LSldapObject.php:3260
msgid "LSldapObject : Error during generating container DN : %{error}" msgid "LSldapObject : Error during generating container DN : %{error}"
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3252 #: includes/class/class.LSldapObject.php:3263
msgid "" msgid ""
"LSldapObject : An LDAP object with the same DN as generated for this new one " "LSldapObject : An LDAP object with the same DN as generated for this new one "
"already exists. Please verify your configuration." "already exists. Please verify your configuration."
msgstr "" msgstr ""
#: includes/class/class.LSldapObject.php:3257 #: includes/class/class.LSldapObject.php:3268
msgid "" msgid ""
"LSrelation : Some parameters are missing in the call of methods to handle " "LSrelation : Some parameters are missing in the call of methods to handle "
"standard relations (Method : %{meth})." "standard relations (Method : %{meth})."
@ -1516,86 +1659,98 @@ msgid ""
"(begining by '/' caracter)." "(begining by '/' caracter)."
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:713 #: includes/class/class.LSldap.php:815
msgid "The password expired" msgid "The password expired"
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:714 #: includes/class/class.LSldap.php:816
msgid "The account is locked" msgid "The account is locked"
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:715 #: includes/class/class.LSldap.php:817
msgid "The password was reset and must be changed" msgid "The password was reset and must be changed"
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:716 #: includes/class/class.LSldap.php:818
msgid "It is not possible to modify the password" msgid "It is not possible to modify the password"
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:717 #: includes/class/class.LSldap.php:819
msgid "The old password must be supplied" msgid "The old password must be supplied"
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:718 #: includes/class/class.LSldap.php:820
msgid "The password does not meet the quality requirements" msgid "The password does not meet the quality requirements"
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:719 #: includes/class/class.LSldap.php:821
msgid "The password is too short" msgid "The password is too short"
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:720 #: includes/class/class.LSldap.php:822
msgid "It is too soon to change the password" msgid "It is too soon to change the password"
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:721 #: includes/class/class.LSldap.php:823
msgid "This password was recently used and cannot be used again" msgid "This password was recently used and cannot be used again"
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:764 #: includes/class/class.LSldap.php:967
msgid "LSldap: Error during the LDAP server connection (%{msg})." msgid "LSldap: Error during the LDAP server connection (%{msg})."
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:767 #: includes/class/class.LSldap.php:970
msgid "LSldap: Error during the LDAP search (%{msg})." msgid "LSldap: Error during the LDAP search (%{msg})."
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:770 #: includes/class/class.LSldap.php:973
msgid "LSldap: Object type unknown." msgid "LSldap: Object type unknown."
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:773 #: includes/class/class.LSldap.php:976
msgid "LSldap: Error while fetching the LDAP entry." msgid "LSldap: Error while fetching the LDAP entry."
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:776 #: includes/class/class.LSldap.php:979
msgid "LSldap: Error while changing the LDAP entry (DN : %{dn})." msgid "LSldap: Error while changing the LDAP entry (DN : %{dn})."
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:779 #: includes/class/class.LSldap.php:982
msgid "LSldap: Error while deleting empty attributes." msgid "LSldap: Error while deleting empty attributes."
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:782 #: includes/class/class.LSldap.php:985
msgid "LSldap: Error while changing the DN of the object." msgid "LSldap: Error while changing the DN of the object."
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:785 #: includes/class/class.LSldap.php:988
msgid "LSldap: LDAP server base DN not configured." msgid "LSldap: LDAP server base DN not configured."
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:788 #: includes/class/class.LSldap.php:991
msgid "LSldap: Fail to set authz proxy option on LDAP server connection." msgid "LSldap: Fail to set authz proxy option on LDAP server connection."
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:791 #: includes/class/class.LSldap.php:994
msgid "LSldap: Error while changing the user password: %{msg}." msgid "LSldap: Error while changing the user password: %{msg}."
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:794 #: includes/class/class.LSldap.php:997
msgid "LSldap: Unknown LDAP error while updating user password" msgid "LSldap: Unknown LDAP error while updating user password"
msgstr "" msgstr ""
#: includes/class/class.LSldap.php:1000
msgid ""
"LSldap: Fail to execute trigger %{callable} on event %{event} : is not "
"callable."
msgstr ""
#: includes/class/class.LSldap.php:1003
msgid ""
"LSldap: Error during the execution of the trigger %{callable} on event "
"%{event}."
msgstr ""
#: includes/class/class.LSformRule_ldapSearchURI.php:59 #: includes/class/class.LSformRule_ldapSearchURI.php:59
msgid "Invalid LDAP server URI (%{uri})" msgid "Invalid LDAP server URI (%{uri})"
msgstr "" msgstr ""
@ -2403,20 +2558,100 @@ msgstr ""
msgid "Role" msgid "Role"
msgstr "" msgstr ""
#: includes/class/class.LScli.php:218 #: includes/class/class.LScli.php:104
#, php-format
msgid "" msgid ""
"Invalid parameter \"%{parameter}\".\n" "Usage: %s [-h] [-qdC] command\n"
" -h Show this message\n"
" -q|--quiet Quiet mode: nothing log on console (but keep other "
"logging handler)\n"
" -d|--debug Debug mode (set log level to DEBUG, default: WARNING)\n"
" -v|--verbose Verbose mode (set log level to INFO, default: WARNING)\n"
" --trace Trace mode (set log level to TRACE, default: WARNING)\n"
" -C|--console Log on console with same log level as other handlers "
"(otherwise, log only errors)\n"
" -S|--ldap-server Connect to a specific LDAP server: you could specify a "
"LDAP\n"
" server by its declaration order in configuration "
"(default:\n"
" first one).\n"
" -L|--load-class Load specific class to permit access to its CLI "
"commands\n"
" -A|--load-addons Load specific addon to permit access to its CLI "
"commands\n"
" command Command to run\n"
"\n"
" Available commands:\n"
msgstr ""
#: includes/class/class.LScli.php:136
msgid "Currently no available command is declared."
msgstr ""
#: includes/class/class.LScli.php:197 includes/class/class.LScli.php:468
#, php-format
msgid "Fail to select LDAP server #%s."
msgstr ""
#: includes/class/class.LScli.php:208 includes/class/class.LScli.php:503
#, php-format
msgid "Fail to load class '%s'."
msgstr ""
#: includes/class/class.LScli.php:215 includes/class/class.LScli.php:518
#, php-format
msgid "Fail to load addon '%s'."
msgstr ""
#: includes/class/class.LScli.php:226
#, php-format
msgid ""
"Invalid parameter \"%s\".\n"
"Note: Command's parameter/argument must be place after the command." "Note: Command's parameter/argument must be place after the command."
msgstr "" msgstr ""
#: includes/class/class.LScli.php:843 #: includes/class/class.LScli.php:261 includes/class/class.LScli.php:488
#: includes/class/class.LScli.php:536
#, php-format
msgid "Fail to select sub DN '%s'."
msgstr ""
#: includes/class/class.LScli.php:271
msgid "Fail to select first LDAP server."
msgstr ""
#: includes/class/class.LScli.php:391
msgid "Are you sure?"
msgstr ""
#: includes/class/class.LScli.php:392
#, php-format
msgid ""
"\n"
"%s Type 'yes' to continue: "
msgstr ""
#: includes/class/class.LScli.php:395 templates/default/import.tpl:26
#: templates/default/import.tpl:32
msgid "yes"
msgstr ""
#: includes/class/class.LScli.php:396
msgid "User cancel\n"
msgstr ""
#: includes/class/class.LScli.php:852
msgid "LScli : The CLI command '%{command}' already exists." msgid "LScli : The CLI command '%{command}' already exists."
msgstr "" msgstr ""
#: includes/class/class.LScli.php:846 #: includes/class/class.LScli.php:855
msgid "LScli : The CLI command '%{command}' handler is not callable." msgid "LScli : The CLI command '%{command}' handler is not callable."
msgstr "" msgstr ""
#: includes/class/class.LScli.php:864
msgid "Handle BASH completion"
msgstr ""
#: includes/class/class.LSioFormatCSV.php:294 #: includes/class/class.LSioFormatCSV.php:294
msgid "LSioFormatCSV: function fputcsv is not available." msgid "LSioFormatCSV: function fputcsv is not available."
msgstr "" msgstr ""
@ -2797,7 +3032,7 @@ msgstr ""
msgid "Nb / page :" msgid "Nb / page :"
msgstr "" msgstr ""
#: templates/default/showObjectAccessLogs.tpl:70 #: templates/default/showObjectAccessLogs.tpl:71
msgid "No access log found for this object." msgid "No access log found for this object."
msgstr "" msgstr ""
@ -2817,7 +3052,7 @@ msgstr ""
msgid "Object classes" msgid "Object classes"
msgstr "" msgstr ""
#: templates/default/showObjectAccessLogs.tpl:31 #: templates/default/showObjectAccessLogs.tpl:30
msgid "Old value(s)" msgid "Old value(s)"
msgstr "" msgstr ""
@ -2826,7 +3061,7 @@ msgid "Only validate data"
msgstr "" msgstr ""
#: templates/default/showObjectAccessLogs.tpl:11 #: templates/default/showObjectAccessLogs.tpl:11
#: templates/default/showObjectAccessLogs.tpl:29 #: templates/default/showObjectAccessLogs.tpl:28
msgid "Operation" msgid "Operation"
msgstr "" msgstr ""
@ -2930,18 +3165,14 @@ msgstr ""
msgid "Valid" msgid "Valid"
msgstr "" msgstr ""
#: templates/default/showObjectAccessLogs.tpl:30 #: templates/default/showObjectAccessLogs.tpl:31
msgid "Value" msgid "Value"
msgstr "" msgstr ""
#: templates/default/showObjectAccessLogs.tpl:75 #: templates/default/showObjectAccessLogs.tpl:76
msgid "event(s) found for this object." msgid "event(s) found for this object."
msgstr "" msgstr ""
#: templates/default/import.tpl:27 templates/default/import.tpl:33 #: templates/default/import.tpl:27 templates/default/import.tpl:33
msgid "no" msgid "no"
msgstr "" msgstr ""
#: templates/default/import.tpl:26 templates/default/import.tpl:32
msgid "yes"
msgstr ""

View file

@ -3,7 +3,7 @@
<h1>{$pagetitle}</h1> <h1>{$pagetitle}</h1>
{include file='ls:LSview_actions.tpl'} {include file='ls:LSview_actions.tpl'}
<table class='LStable'> <table class='LStable objectAccessLogs'>
<thead> <thead>
<tr> <tr>
<th>{tr msg="Date"}</th> <th>{tr msg="Date"}</th>
@ -22,23 +22,23 @@
<td class="center">{$log.result}{if $log.message} <img class='LStips' src="{img name='help'}" alt="?" title='{$log.message|escape:quotes}'/>{/if}</td> <td class="center">{$log.result}{if $log.message} <img class='LStips' src="{img name='help'}" alt="?" title='{$log.message|escape:quotes}'/>{/if}</td>
<td> <td>
{if $log.mods} {if $log.mods}
<table style='margin: auto'> <table class="mods">
<thead> <thead>
<tr> <tr>
<th>{tr msg="Attribute"}</th> <th class="col-op">{tr msg="Operation"}</th>
<th>{tr msg="Operation"}</th> <th class="col-attr">{tr msg="Attribute"}</th>
<th>{tr msg="Value"}</th> <th class="col-value">{tr msg="Old value(s)"}</th>
<th>{tr msg="Old value(s)"}</th> <th class="col-value">{tr msg="Value"}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{foreach $log.mods as $attr => $info} {foreach $log.mods as $attr => $info}
<tr> <tr>
<td class="center" {if count($info.changes)>1}rowspan={$info.changes|count}{/if}>{$attr}</td> <td class="col-op center" {if count($info.changes)>1}rowspan={$info.changes|count}{/if}>{$info.changes.0.op|escape:htmlall}</td>
<td class="center">{$info.changes.0.op|escape:htmlall}</td> <td class="col-attr center" {if count($info.changes)>1}rowspan={$info.changes|count}{/if}>{$attr}</td>
<td>{$info.changes.0.value|escape:htmlall}</td> <td class="col-value" {if count($info.changes)>1}rowspan={$info.changes|count}{/if}>
<td {if count($info.changes)>1}rowspan={$info.changes|count}{/if}>
{if $info.old_values} {if $info.old_values}
<div class="copyable copyable-no-btn">
{if count($info.old_values) == 1} {if count($info.old_values) == 1}
{$info.old_values[0]|escape:'htmlall'} {$info.old_values[0]|escape:'htmlall'}
{else} {else}
@ -48,14 +48,15 @@
{/foreach} {/foreach}
</ul> </ul>
{/if} {/if}
</div>
{/if} {/if}
</td> </td>
<td class="col-value"><div class="copyable copyable-no-btn">{$info.changes.0.value|escape:htmlall}</div></td>
</tr> </tr>
{if count($info.changes) > 1} {if count($info.changes) > 1}
{section name=change loop=$info.changes step=1 start=1} {section name=change loop=$info.changes step=1 start=1}
<tr> <tr>
<td>{$info.changes[change].op|escape:htmlall}</td> <td class="col-value"><div class="copyable copyable-no-btn">{$info.changes[change].value|escape:htmlall}</div></td>
<td>{$info.changes[change].value|escape:htmlall}</td>
</tr> </tr>
{/section} {/section}
{/if} {/if}