Merge remote-tracking branch 'origin/develop' into 6_2_1

Conflicts:
	lam-packaging/debian/changelog
	lam/HISTORY
	lam/VERSION
pull/45/head
Roland Gruber 6 years ago
commit f0b68d2b1a

@ -26,28 +26,28 @@ fi
function minify {
local dir="$1"
echo "Minify JS files in $dir"
local outFile=$dir/100_lam.${VERSION}.min.js
local files=`ls $dir/*.js`
local jsFiles=""
for file in $files; do
closure-compiler --charset UTF-8 --js $file --js_output_file ${file}-MIN
rm $file
mv ${file}-MIN $file
# add final new line to supress Debian warnings
echo "" >> $file
jsFiles="$jsFiles --js $file"
done
closure-compiler --charset UTF-8 $jsFiles --js_output_file $outFile
rm $files
# add final new line to supress Debian warnings
echo "" >> $outFile
}
# minifies the CSS files
function minifyCSS {
local dir="$1"
echo "Minify CSS files in $dir"
local outFile=$dir/100_lam.${VERSION}.min.css
local files=`ls $dir/*.css`
for file in $files; do
cleancss -o ${file}-MIN $file
rm $file
mv ${file}-MIN $file
# add final new line to supress Debian warnings
echo "" >> $file
done
cat $files | cleancss -o $outFile
rm $files
# add final new line to supress Debian warnings
echo "" >> $outFile
}
echo "Getting files..."

@ -1,3 +1,9 @@
ldap-account-manager (6.3.DEV-1) unstable; urgency=medium
* new upstream release
-- Roland Gruber <post@rolandgruber.de> Wed, 13 Dec 2017 16:47:31 +0200
ldap-account-manager (6.2.1-1) unstable; urgency=medium
* new upstream release

@ -1,22 +1,24 @@
#!/bin/bash
set -e
files=`ls templates/lib/*.js`
for file in $files; do
closure-compiler --charset UTF-8 --js $file --js_output_file ${file}-MIN
rm $file
mv ${file}-MIN $file
outFile=templates/lib/100_lam.${SOURCE_DATE_EPOCH}.min.js
if [ ! -e $outFile ]; then
files=`ls templates/lib/*.js`
jsFiles=""
for file in $files; do
jsFiles="$jsFiles --js $file"
done
closure-compiler --charset UTF-8 $jsFiles --js_output_file $outFile
rm $files
# add final new line to supress Debian warnings
echo "" >> $file
done
echo "" >> $outFile
fi
files=`ls style/*.css`
for file in $files; do
cleancss -o ${file}-MIN $file
rm $file
mv ${file}-MIN $file
outFile=style/100_lam.${SOURCE_DATE_EPOCH}.min.css
if [ ! -e $outFile ]; then
cat $files | cleancss -o ${outFile}
rm $files
# add final new line to supress Debian warnings
echo "" >> $file
done
echo "" >> $outFile
fi

@ -1,3 +1,10 @@
March 2018 6.3
- Server profile: added option if referential integrity overlay is active to sip cleanup actions
- LAM Pro:
-> Support custom structural object classes with new custom type
-> Support dynamic lists
04.02.2018 6.2.1
- Fixed bugs:
-> Login page not working when no server profile exists (44)

@ -1 +1 @@
6.2.1
6.3.DEV

@ -0,0 +1,4 @@
<pdf type="bind" filename="printLogo.jpg" headline="Custom entry" foldingmarks="no">
<section name="_main_dn">
</section>
</pdf>

@ -15,9 +15,11 @@ time.
* lib/modules/aliasEntry.inc
* lib/modules/automount.inc
* lib/modules/bindDLZ.inc
* lib/modules/customBaseType.inc
* lib/modules/customFields.inc
* lib/modules/customScripts.inc
* lib/modules/device.inc
* lib/modules/dynamicList.inc
* lib/modules/groupOfNames.inc
* lib/modules/groupOfNamesUser.inc
* lib/modules/groupOfUniqueNames.inc
@ -48,8 +50,9 @@ time.
* lib/modules/zarafaServer.inc
* lib/modules/zarafaUser.inc
* lib/types/alias.inc
* lib/types/bind.inc
* lib/types/automountType.inc
* lib/types/bind.inc
* lib/types/customType.inc
* lib/types/gon.inc
* lib/types/nisObjectType.inc
* lib/types/nsview.inc

@ -60,6 +60,19 @@ This is a list of API changes for all LAM releases.
<br>
<h2>6.2 -&gt; 6.3</h2>
<ul>
<li>Module API
<ul>
<li>get_configOptions(): $allScopes contains type ids instead of account types</li>
<li>check_configOptions(): first parameter contains type ids instead of account types</li>
</ul>
</li>
</ul>
<h2>6.1 -&gt; 6.2</h2>
<ul>
<li>No major API changes</li>
</ul>
<h2>6.0 -&gt; 6.1</h2>
<ul>
<li>module API</li>
@ -616,4 +629,4 @@ The class variable "triggered_messages" in baseModule was removed.<br>
<br>
</body></html>
</body></html>

@ -368,24 +368,49 @@
<para><emphasis role="bold">Advanced options</emphasis></para>
<para>Sometimes, you may not want to display the server address on the
login page. In this case you can setup a display name here (e.g.
"Production").</para>
<para>Display name: Sometimes, you may not want to display the server
address on the login page. In this case you can setup a display name
here (e.g. "Production").</para>
<para>By default LAM will not follow LDAP referrals. This is ok for
most installations. If you use LDAP referrals please activate the
referral option in advanced settings.</para>
<para>Follow referrals: By default LAM will not follow LDAP referrals.
This is ok for most installations. If you use LDAP referrals please
activate the referral option in advanced settings.</para>
<para>Paged results should be activated only if you encounter any
problems regarding size limits on Active Directory. LAM will then
query LDAP to return results in chunks of 999 entries.</para>
<para>Paged results: Paged results should be activated only if you
encounter any problems regarding size limits on Active Directory. LAM
will then query LDAP to return results in chunks of 999
entries.</para>
<para>Referential integrity overlay: Activate this checkbox if you
have any server side extension for referential integrity in place. In
this case the server will cleanup references to LDAP entries that are
deleted.</para>
<para>The following actions are skipped in this case:</para>
<itemizedlist>
<listitem>
<para>Users: group of (unique) names: memberships are not deleted
when user is deleted</para>
</listitem>
<listitem>
<para>Users: organizational roles: role assignments are not
deleted when user is deleted</para>
</listitem>
<listitem>
<para>Groups: groupOf(Unique)Names: memberships are not deleted
when group is deleted</para>
</listitem>
</itemizedlist>
<literallayout>
</literallayout>
<para>LAM is translated to many different languages. Here you can
select the default language for this server profile. The language
setting may be overriden at the LAM login page.</para>
setting may be overridden at the LAM login page.</para>
<para>Please also set your time zone here.</para>

@ -2519,6 +2519,82 @@
</screenshot>
</section>
<section>
<title>Dynamic lists (LAM Pro)</title>
<para><ulink
url="http://www.openldap.org/doc/admin24/overlays.html#Dynamic%20Lists">Dynamic
lists</ulink> allow you to create LDAP entries that populate the value
of an attribute via LDAP query. This is e.g. used to create groups that
contain all users in a certain DN.</para>
<para>Please note that this functionality requires configuration on your
LDAP server. E.g. on OpenLDAP you need to activate the "dynlist" overlay
and need to specify attribute mappings.</para>
<para><emphasis role="bold">Configuration</emphasis></para>
<para>Add a new group account type and set a unique label for it.</para>
<para><inlinemediaobject>
<imageobject>
<imagedata fileref="images/mod_dynamicList1.png"/>
</imageobject>
</inlinemediaobject></para>
<para>Do not forget to set proper "List attributes" to be shown on the
overview page of all dynamic lists.</para>
<para><inlinemediaobject>
<imageobject>
<imagedata fileref="images/mod_dynamicList2.png"/>
</imageobject>
</inlinemediaobject></para>
<para>On tab "Modules" please add the dynamic lists module.</para>
<para><inlinemediaobject>
<imageobject>
<imagedata fileref="images/mod_dynamicList4.png"/>
</imageobject>
</inlinemediaobject></para>
<para>On tab "Module settings" you can now configure your dynamic lists.
Here you setup the used object class, RDN attribute, query attribute and
list attribute (the one that is populated via query).</para>
<para>In case you have different types of dynamic lists you can simply
redo the steps above to create more group types.</para>
<para><inlinemediaobject>
<imageobject>
<imagedata fileref="images/mod_dynamicList3.png"/>
</imageobject>
</inlinemediaobject></para>
<para/>
<para><emphasis role="bold">Usage</emphasis></para>
<para>When you login to LAM you will see your new dynamic lists
tab.</para>
<para><inlinemediaobject>
<imageobject>
<imagedata fileref="images/mod_dynamicList5.png"/>
</imageobject>
</inlinemediaobject></para>
<para>For each list you can manage the name and query string. LAM also
displays which entries are auto-populated to the list.</para>
<para><inlinemediaobject>
<imageobject>
<imagedata fileref="images/mod_dynamicList6.png"/>
</imageobject>
</inlinemediaobject></para>
</section>
<section>
<title>PyKota</title>
@ -4733,7 +4809,72 @@ OK (10 msec)</programlisting>
</screenshot>
</section>
<section>
<section id="mod_customTypes">
<title>Custom types (LAM Pro)</title>
<para>This account type allows you to manage any type of LDAP entries.
This is e.g. needed if you define your own structural object classes or
LAM does not yet provide a module for a structural object class.</para>
<para>Always use this together with <link
linkend="mod_customFields">Custom fields</link> to specify the LDAP
attributes.</para>
<para><emphasis role="bold">Configuration</emphasis></para>
<para>Add a custom account type in your server profile (you can also add
multiple if needed).</para>
<para><inlinemediaobject>
<imageobject>
<imagedata fileref="images/mod_customBaseType1.png"/>
</imageobject>
</inlinemediaobject></para>
<para>Then specify the root DN where the entries should be stored. Also
provide the attributes to show in list view and a unique label for your
entries.</para>
<para><inlinemediaobject>
<imageobject>
<imagedata fileref="images/mod_customBaseType2.png"/>
</imageobject>
</inlinemediaobject></para>
<para>On tab modules add the custom type module. You will also need the
<link linkend="mod_customFields">Custom fields</link> module to manage the
attributes.</para>
<para><inlinemediaobject>
<imageobject>
<imagedata fileref="images/mod_customBaseType3.png"/>
</imageobject>
</inlinemediaobject></para>
<para>Finally, switch to tab Module settings. Here you need to specify the
structural object class. Also configure the <link
linkend="mod_customFields">Custom fields</link> module to manage all your
attributes.</para>
<para><inlinemediaobject>
<imageobject>
<imagedata fileref="images/mod_customBaseType4.png"/>
</imageobject>
</inlinemediaobject></para>
<para><emphasis role="bold">Manage your entries</emphasis></para>
<para>You can now login to LAM and will see one tab for each configured
custom type.</para>
<para><inlinemediaobject>
<imageobject>
<imagedata fileref="images/mod_customBaseType5.png"/>
</imageobject>
</inlinemediaobject></para>
</section>
<section id="mod_customFields">
<title>Custom fields (LAM Pro)</title>
<para>This module allows you to manage LDAP attributes that are not
@ -4781,7 +4922,8 @@ OK (10 msec)</programlisting>
<itemizedlist>
<listitem>
<para>structural object classes</para>
<para>structural object classes (supported by <link
linkend="mod_customTypes">Custom types</link>)</para>
</listitem>
<listitem>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 815 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

@ -194,6 +194,8 @@ $helpArray = array (
"Text" => _('Please select the template for the new server profile. You can either select an existing server profile or use one of the built-in templates.')),
"268" => array ("Headline" => _('Display name'),
"Text" => _('This name is shown on the login page as server name. Defaults to server address if empty.')),
"269" => array ("Headline" => _('Referential integrity overlay'),
"Text" => _('Activate this checkbox if you have any server side extension for referential integrity in place. LAM will then skip cleanup tasks like deletion of group memberships on account deletion.')),
"270" => array ("Headline" => _('Bind user and password'),
"Text" => _('Please enter the DN and password to use for all jobs.')),
"271" => array ("Headline" => _('Database type'),

@ -4,7 +4,7 @@ $Id$
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2003 - 2006 Tilo Lutz
2009 - 2017 Roland Gruber
2009 - 2018 Roland Gruber
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -1094,7 +1094,14 @@ function extractRDNAttribute($dn) {
*/
function extractRDNValue($dn) {
if (empty($dn)) return null;
$parts = explode("=", substr($dn, 0, strpos($dn, ',')));
if (strpos($dn, '=') === false) {
return $dn;
}
$dnWork = $dn;
if (strpos($dnWork, ',') !== false) {
$dnWork = substr($dn, 0, strpos($dnWork, ','));
}
$parts = explode("=", $dnWork);
return $parts[1];
}
@ -1567,7 +1574,7 @@ function printJsIncludes($prefix) {
$jsFiles = array();
$jsEntry = $jsDir->read();
while ($jsEntry !== false) {
if (substr($jsEntry, strlen($jsEntry) - 3, 3) == '.js') {
if ((substr($jsEntry, strlen($jsEntry) - 3, 3) == '.js') || (substr($jsEntry, strlen($jsEntry) - 4, 4) == '.php')) {
$jsFiles[] = $jsEntry;
}
$jsEntry = $jsDir->read();
@ -1578,6 +1585,11 @@ function printJsIncludes($prefix) {
}
}
/**
* LAM exception with title and message.
*
* @author Roland Gruber
*/
class LAMException extends Exception {
private $title;

@ -0,0 +1,35 @@
<?php
namespace LAM\FOOTER;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2018 Roland Gruber
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* Footer part of page which closes the tab content area etc.
*
* @package main
* @author Roland Gruber
*/
?>
</div>
<br>
</body>
</html>

@ -0,0 +1,206 @@
<?php
namespace LAM\HEADER;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2018 Roland Gruber
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* Head part of page which includes links to lists etc.
*
* @package main
* @author Roland Gruber
*/
$headerPrefix = "";
if (is_file("login.php")) {
$headerPrefix = "..";
}
elseif (is_file("../../templates/login.php")) {
$headerPrefix = "../..";
}
elseif (is_file("../../../templates/login.php")) {
$headerPrefix = "../../..";
}
/** tool definitions */
include_once($headerPrefix . "/lib/tools.inc");
$pro = '';
if (isLAMProVersion()) {
$pro = ' Pro';
}
// HTML header and title
echo $_SESSION['header'];
$title = "LDAP Account Manager" . $pro . " (" . str_replace(array('ldap://', 'ldaps://'), array('', ''), $_SESSION['config']->get_ServerURL()) . ")";
printHeaderContents($title, $headerPrefix);
echo "</head><body class=\"admin\">\n";
// include all JavaScript files
printJsIncludes($headerPrefix);
// get tool list
$availableTools = getTools();
$toolSettings = $_SESSION['config']->getToolSettings();
// sort tools
$toSort = array();
foreach ($availableTools as $toolClass) {
$myTool = new $toolClass();
if ($myTool->getRequiresWriteAccess() && !checkIfWriteAccessIsAllowed()) {
continue;
}
if ($myTool->getRequiresPasswordChangeRights() && !checkIfPasswordChangeIsAllowed()) {
continue;
}
// check visibility
if (!$myTool->isVisible()) {
continue;
}
// check if hidden by config
$toolName = substr($toolClass, strrpos($toolClass, '\\') + 1);
if (isset($toolSettings['tool_hide_' . $toolName]) && ($toolSettings['tool_hide_' . $toolName] == 'true')) {
continue;
}
$toSort[$toolClass] = $myTool->getPosition();
}
asort($toSort);
$tools = array();
foreach ($toSort as $key => $value) {
$tools[] = new $key();
}
?>
<table border=0 width="100%" class="lamHeader ui-corner-all">
<tr>
<td align="left" height="30" class="nowrap">
<a class="lamLogo" href="https://www.ldap-account-manager.org/" target="new_window">
<span class="hide-on-tablet">&nbsp;</span>
<span class="hide-on-mobile">
LDAP Account Manager
<?php
echo $pro . " - " . LAMVersion();
?>
</span>
</a>
</td>
<td align="left" height="30" class="nowrap">
<span class="hide-on-mobile">
<?php
$userData = $_SESSION['ldap']->decrypt_login();
echo '&nbsp;&nbsp;<small title="' . $userData[0] . '">';
printf('(' . _('Logged in as: %s') . ')', extractRDNValue($userData[0]));
$userData = null;
echo '</small>';
?>
</span>
</td>
<td align="right" height=30 width="100%">
<ul id="dropmenu" class="dropmenu">
<li><a href="<?php echo $headerPrefix; ?>/templates/logout.php" target="_top"><img class="align-middle" height="16" width="16" alt="logout" src="<?php echo $headerPrefix; ?>/graphics/exit.png"><span class="hide-on-mobile padding0">&nbsp;<?php echo _("Logout") ?></span></a></li>
<?php
if (is_dir(dirname(__FILE__) . '/../docs/manual')) {
?>
<li>
<a target="_blank" href="<?php echo $headerPrefix; ?>/docs/manual/index.html"><img class="align-middle" width="16" height="16" alt="help" src="<?php echo $headerPrefix; ?>/graphics/help.png"><span class="hide-on-mobile padding0">&nbsp;<?php echo _("Help") ?>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></a>
</li>
<?php
}
if (sizeof($tools) > 0) {
?>
<li>
<a href="<?php echo $headerPrefix; ?>/templates/tools.php"><img class="align-middle" height="16" width="16" alt="tools" src="<?php echo $headerPrefix; ?>/graphics/tools.png"><span class="hide-on-mobile padding0">&nbsp;<?php echo _("Tools") ?></span></a>
<ul>
<?php
foreach ($tools as $tool) {
$subTools = $tool->getSubTools();
echo '<li title="' . $tool->getDescription() . '">';
$link = $headerPrefix . '/templates/' . $tool->getLink();
echo '<a href="' . $link . "\">\n";
echo '<img class="max16" height="16px" width="16px" alt="" src="' . $headerPrefix . '/graphics/' . $tool->getImageLink() . '"> ' . $tool->getName();
echo "</a>\n";
if (sizeof($subTools) > 0) {
echo "<ul>\n";
foreach ($subTools as $subTool) {
echo "<li title=\"" . $subTool->description . "\">\n";
echo "<a href=\"" . $headerPrefix . '/templates/' . $subTool->link . "\">\n";
echo '<img class="max16" width="16px" height="16px" alt="" src="' . $headerPrefix . '/graphics/' . $subTool->image . '"> ' . $subTool->name;
echo "</a>\n";
echo "</li>\n";
}
echo "</ul>\n";
}
echo "</li>\n";
}
?>
</ul>
</li>
<?php
}
if ($_SESSION['config']->get_Suffix('tree') != "") {
?>
<li>
<a href="<?php echo $headerPrefix; ?>/templates/tree/treeViewContainer.php"><img class="align-middle" height="16" width="16" alt="tree" src="<?php echo $headerPrefix; ?>/graphics/process.png"><span class="hide-on-mobile padding0">&nbsp;<?php echo _("Tree view") ?></span></a>
</li>
<?php
}
?>
</ul>
</td>
</tr>
</table>
<script type="text/javascript">
jQuery(document).ready(function() {
jQuery('#dropmenu').dropmenu({
effect : 'slide',
nbsp : true,
timeout : 350,
speed : 'fast'
});
});
</script>
<br>
<div class="ui-tabs ui-widget ui-widget-content ui-corner-all">
<ul class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all">
<?php
printTypeTabs($headerPrefix);
?>
</ul>
<?php
function printTypeTabs($headerPrefix) {
$typeManager = new \LAM\TYPES\TypeManager();
$types = $typeManager->getConfiguredTypes();
foreach ($types as $type) {
if ($type->isHidden()) {
continue;
}
$link = '<a href="' . $headerPrefix . '/templates/lists/list.php?type=' . $type->getId() .
'" onmouseover="jQuery(this).addClass(\'tabs-hover\');" onmouseout="jQuery(this).removeClass(\'tabs-hover\');">' .
'<img height="16" width="16" alt="' . $type->getId() . '" src="' . $headerPrefix . '/graphics/' . $type->getIcon() . '">&nbsp;' .
$type->getAlias() . '</a>';
echo '<li id="tab_' . $type->getId() . '" class="ui-state-default ui-corner-top">';
echo $link;
echo "</li>\n";
}
}

@ -2,11 +2,11 @@
use \LAM\PDF\PDFLabelValue;
use \LAM\PDF\PDFTable;
use LAM\TYPES\ConfiguredType;
use function LAM\TYPES\getScopeFromTypeId;
/*
$Id$
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2003 - 2017 Roland Gruber
Copyright (C) 2003 - 2018 Roland Gruber
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -168,7 +168,9 @@ abstract class baseModule {
}
}
// load attributes
$attributeNames = $this->getManagedAttributes($typeId);
$attributeNames = array_merge($this->getManagedAttributes($typeId), $this->getManagedHiddenAttributes($typeId));
$attributeNames = array_unique($attributeNames);
$attributeNames = array_values($attributeNames);
for ($i = 0; $i < sizeof($attributeNames); $i++) {
if (isset($attributes[$attributeNames[$i]])) {
$this->attributes[$attributeNames[$i]] = $attributes[$attributeNames[$i]];
@ -424,11 +426,12 @@ abstract class baseModule {
* <br>
* <b>Example:</b> return array('or' => '(objectClass=posixAccount)', 'and' => '(!(uid=*$))')
*
* @param string $typeId account type id
* @return string LDAP filter
*
* @see baseModule::get_metaData()
*/
public function get_ldap_filter() {
public function get_ldap_filter($typeId) {
if (isset($this->meta['ldap_filter'])) return $this->meta['ldap_filter'];
else return "";
}
@ -638,7 +641,7 @@ abstract class baseModule {
* We recommend to use the module name as prefix for them (e.g. posixAccount_homeDirectory) to avoid naming conflicts.
*
* @param array $scopes account types (user, group, host)
* @param array $allScopes list of all active account modules and their scopes (module => array(scopes))
* @param array $allScopes list of all active account modules and their account type id (module => array(type id))
* @return mixed htmlElement or array of htmlElement
*
* @see baseModule::get_metaData()
@ -681,15 +684,20 @@ abstract class baseModule {
* If the input data is invalid the return value is an array that contains subarrays to build StatusMessages ('message type', 'message head', 'message text').
* <br>If no errors occured the function returns an empty array.
*
* @param array $scopes list of account types which are used
* @param array $typeIds list of account type ids which are used
* @param array $options hash array (option name => value) that contains the input. The option values are all arrays containing one or more elements.
* @return array list of error messages
*
* @see baseModule::get_metaData()
*/
public function check_configOptions($scopes, &$options) {
public function check_configOptions($typeIds, &$options) {
$messages = array();
$scopes[] = 'all'; // add checks that are independent of scope
// add checks that are independent of scope
$scopes = array('all');
foreach ($typeIds as $typeId) {
$scopes[] = getScopeFromTypeId($typeId);
}
$scopes = array_unique($scopes);
for ($s = 0; $s < sizeof($scopes); $s++) {
if (isset($this->meta['config_checks'][$scopes[$s]]) && is_array($this->meta['config_checks'][$scopes[$s]])) {
$identifiers = array_keys($this->meta['config_checks'][$scopes[$s]]);
@ -1586,11 +1594,12 @@ abstract class baseModule {
/**
* Returns a list of operational LDAP attributes which are managed by this module and need to be explicitly set for LDAP search.
*
* @param string $typeId account type id
* @return array list of hidden attributes
*
* @see baseModule::get_metaData()
*/
public function getManagedHiddenAttributes() {
public function getManagedHiddenAttributes($typeId) {
if (isset($this->meta['hiddenAttributes']) && is_array($this->meta['hiddenAttributes'])) return $this->meta['hiddenAttributes'];
else return array();
}

@ -435,6 +435,9 @@ class LAMConfig {
/** use paged results */
private $pagedResults = 'false';
/** overlay for referential integrity is activated */
private $referentialIntegrityOverlay = 'false';
/** Array of string: users with admin rights */
private $Admins;
@ -589,7 +592,7 @@ class LAMConfig {
'pwdResetAllowScreenPassword', 'pwdResetForcePasswordChange', 'pwdResetDefaultPasswordOutput',
'scriptUserName', 'scriptSSHKey', 'scriptSSHKeyPassword', 'twoFactorAuthentication', 'twoFactorAuthenticationURL',
'twoFactorAuthenticationInsecure', 'twoFactorAuthenticationLabel', 'twoFactorAuthenticationOptional',
'twoFactorAuthenticationCaption'
'twoFactorAuthenticationCaption', 'referentialIntegrityOverlay'
);
@ -799,6 +802,7 @@ class LAMConfig {
if (!in_array("useTLS", $saved)) array_push($file_array, "\n\n# enable TLS encryption\n" . "useTLS: " . $this->useTLS . "\n");
if (!in_array("followReferrals", $saved)) array_push($file_array, "\n\n# follow referrals\n" . "followReferrals: " . $this->followReferrals . "\n");
if (!in_array("pagedResults", $saved)) array_push($file_array, "\n\n# paged results\n" . "pagedResults: " . $this->pagedResults . "\n");
if (!in_array("referentialIntegrityOverlay", $saved)) array_push($file_array, "\n" . "referentialIntegrityOverlay: " . $this->referentialIntegrityOverlay . "\n");
if (!in_array("Passwd", $saved)) array_push($file_array, "\n\n# password to change these preferences via webfrontend\n" . "Passwd: " . $this->Passwd . "\n");
if (!in_array("Admins", $saved)) array_push($file_array, "\n\n# list of users who are allowed to use LDAP Account Manager\n" .
"# names have to be seperated by semicolons\n" .
@ -1034,6 +1038,33 @@ class LAMConfig {
$this->pagedResults = $pagedResults;
}
/**
* Returns if referential integrity overlay is in place.
*
* @return String true or false
*/
public function getReferentialIntegrityOverlay() {
return $this->referentialIntegrityOverlay;
}
/**
* Sets if referential integrity overlay is in place.
*
* @param String $referentialIntegrityOverlay true or false
*/
public function setReferentialIntegrityOverlay($referentialIntegrityOverlay) {
$this->referentialIntegrityOverlay = $referentialIntegrityOverlay;
}
/**
* Returns if referential integrity overlay is in place.
*
* @return bool overlay in place
*/
public function isReferentialIntegrityOverlayActive() {
return $this->referentialIntegrityOverlay === 'true';
}
/**
* Returns an array of string with all admin names
*

@ -2,7 +2,7 @@
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2010 - 2017 Roland Gruber
Copyright (C) 2010 - 2018 Roland Gruber
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -61,6 +61,8 @@ abstract class htmlElement {
protected $cssClasses = array();
/** table cell CSS classes */
protected $tableCellCssClasses = array();
/** data attributes */
private $dataAttributes = array();
/**
* Prints the HTML code for this element.
@ -155,6 +157,29 @@ abstract class htmlElement {
return $this->tableCellCssClasses;
}
/**
* Adds a data attribute.
*
* @param string $key attribute name (without "data-")
* @param string $value attribute value
*/
public function addDataAttribute($key, $value) {
$this->dataAttributes[$key] = $value;
}
/**
* Returns the data attributes as rendered string.
*
* @return string data attributes
*/
protected function getDataAttributesAsString() {
$result = '';
foreach ($this->dataAttributes as $key => $value) {
$result .= ' data-' . htmlspecialchars($key) . '="' . htmlspecialchars($value) . '"';
}
return $result;
}
}
/**
@ -1254,7 +1279,9 @@ class htmlSelect extends htmlElement {
echo '<div class="hidden">';
}
// print select box
echo '<select' . $class . $style . $name . $size . $multi . $disabled . $onchange . ' tabindex="' . $tabindex . "\">\n";
echo '<select' . $this->getDataAttributesAsString() . $class . $style
. $name . $size . $multi . $disabled . $onchange
. ' tabindex="' . $tabindex . "\">\n";
$tabindex++;
if ($this->containsOptgroups) {
foreach ($this->elements as $label => $elements) {
@ -1926,22 +1953,23 @@ class htmlInputCheckbox extends htmlElement {
// build Java script to show/hide depending fields
$onChange = '';
$script = '';
$selector = $this->getShowHideSelector();
if ((sizeof($this->tableRowsToShow) > 0) || (sizeof($this->tableRowsToHide) > 0)) {
// build onChange listener
$onChange .= 'if (jQuery(\'#' . $this->name . ':checked\').val() !== undefined) {';
for ($i = 0; $i < sizeof($this->tableRowsToShow); $i++) {
$onChange .= 'jQuery(\'#' . $this->tableRowsToShow[$i] . '\').closest(\'tr\').removeClass(\'hidden\');';
$onChange .= 'jQuery(\'#' . $this->tableRowsToShow[$i] . '\').closest(\'' . $selector . '\').removeClass(\'hidden\');';
}
for ($i = 0; $i < sizeof($this->tableRowsToHide); $i++) {
$onChange .= 'jQuery(\'#' . $this->tableRowsToHide[$i] . '\').closest(\'tr\').addClass(\'hidden\');';
$onChange .= 'jQuery(\'#' . $this->tableRowsToHide[$i] . '\').closest(\'' . $selector . '\').addClass(\'hidden\');';
}
$onChange .= '}';
$onChange .= 'else {';
for ($i = 0; $i < sizeof($this->tableRowsToShow); $i++) {
$onChange .= 'jQuery(\'#' . $this->tableRowsToShow[$i] . '\').closest(\'tr\').addClass(\'hidden\');';
$onChange .= 'jQuery(\'#' . $this->tableRowsToShow[$i] . '\').closest(\'' . $selector . '\').addClass(\'hidden\');';
}
for ($i = 0; $i < sizeof($this->tableRowsToHide); $i++) {
$onChange .= 'jQuery(\'#' . $this->tableRowsToHide[$i] . '\').closest(\'tr\').removeClass(\'hidden\');';
$onChange .= 'jQuery(\'#' . $this->tableRowsToHide[$i] . '\').closest(\'' . $selector . '\').removeClass(\'hidden\');';
}
$onChange .= '};';
// build script to set initial state
@ -1951,14 +1979,14 @@ class htmlInputCheckbox extends htmlElement {
if ($this->checked) {
$classType = 'removeClass';
}
$script .= 'jQuery(\'#' . $this->tableRowsToShow[$i] . '\').closest(\'tr\').' . $classType . '(\'hidden\');';
$script .= 'jQuery(\'#' . $this->tableRowsToShow[$i] . '\').closest(\'' . $selector . '\').' . $classType . '(\'hidden\');';
}
for ($i = 0; $i < sizeof($this->tableRowsToHide); $i++) {
$classType = 'removeClass';
if ($this->checked) {
$classType = 'addClass';
}
$script .= 'jQuery(\'#' . $this->tableRowsToHide[$i] . '\').closest(\'tr\').' . $classType . '(\'hidden\');';
$script .= 'jQuery(\'#' . $this->tableRowsToHide[$i] . '\').closest(\'' . $selector . '\').' . $classType . '(\'hidden\');';
}
$script .= '});</script>';
}
@ -2078,6 +2106,13 @@ class htmlInputCheckbox extends htmlElement {
$this->elementsToEnable = $elements;
}
/**
* Returns the CSS selector to use to find show/hide elements.
*/
protected function getShowHideSelector() {
return '.tr';
}
}
/**
@ -2273,7 +2308,7 @@ class htmlInputTextarea extends htmlElement {
/** enabled or disabled */
private $isEnabled = true;
/** specifies if LAM should display this field whith a WYSIWYG editor */
private $richEdit = false;
protected $richEdit = false;
/**
* Constructor.
@ -3574,6 +3609,8 @@ class htmlResponsiveRow extends htmlElement {
/** @var htmlResponsiveCell[] cells */
private $cells = array();
/** HTML ID */
private $id = null;
/**
* Creates a new responsive row.
@ -3591,6 +3628,15 @@ class htmlResponsiveRow extends htmlElement {
}
}
/**
* Sets the HTML id.
*
* @param string $id ID
*/
public function setId($id) {
$this->id = $id;
}
/**
* Adds a responsive cell to the row.
*
@ -3619,16 +3665,17 @@ class htmlResponsiveRow extends htmlElement {
* Adds the content as a typical label with 12/6/6 columns and CSS class "responsiveLabel".
*
* @param htmlElement $content label
* @param string $cssClasses additional CSS classes
*/
public function addLabel($content) {
$this->add($content, 12, 6, 6, 'responsiveLabel nowrap');
public function addLabel($content, $cssClasses = '') {
$this->add($content, 12, 6, 6, 'responsiveLabel nowrap ' . $cssClasses);
}
/**
* Adds the content as a typical field with 12/6/6 columns and CSS class "responsiveField".
*
* @param htmlElement $content field
* @param $cssClasses CSS class names separated by space
* @param string $cssClasses CSS class names separated by space
*/
public function addField($content, $cssClasses = '') {
$this->add($content, 12, 6, 6, 'responsiveField ' . $cssClasses);
@ -3648,7 +3695,11 @@ class htmlResponsiveRow extends htmlElement {
public function generateHTML($module, $input, $values, $restricted, &$tabindex, $scope) {
$return = array();
$cssClasses = implode(' ', $this->cssClasses);
echo '<div class="row ' . $cssClasses . '">';
$idParam = '';
if ($this->id !== null) {
$idParam = ' id="' . $this->id . '"';
}
echo '<div class="row ' . $cssClasses . '"' . $idParam . '>';
foreach ($this->cells as $cell) {
$return = array_merge($return, $cell->generateHTML($module, $input, $values, $restricted, $tabindex, $scope));
}
@ -3808,6 +3859,8 @@ class htmlResponsiveInputTextarea extends htmlInputTextarea {
private $label;
/** help ID */
private $helpID;
/** help module */
private $helpModule = null;
/** required field */
private $required = false;
/** render HTML of parent class */
@ -3821,12 +3874,18 @@ class htmlResponsiveInputTextarea extends htmlInputTextarea {
* @param int $colCount number of characters per line
* @param int $rowCount number of rows
* @param String $label descriptive label
* @param String $helpID help ID
* @param String|array $helpID help ID
*/
function __construct($name, $value, $colCount, $rowCount, $label, $helpID = null) {
parent::__construct($name, $value, $colCount, $rowCount);
$this->label = htmlspecialchars($label);
$this->helpID = $helpID;
if (is_string($helpID)) {
$this->helpID = $helpID;
}
elseif (is_array($helpID)) {
$this->helpID = $helpID[0];
$this->helpModule = $helpID[1];
}
$this->alignment = htmlElement::ALIGN_TOP;
}
@ -3850,16 +3909,20 @@ class htmlResponsiveInputTextarea extends htmlInputTextarea {
$labelGroup->addElement(new htmlImage($graphicsPath . '/required.png', 16, 16, _('required'), _('required')));
}
if (!empty($this->helpID)) {
$helpLinkLabel = new htmlHelpLink($this->helpID);
$helpLinkLabel->setCSSClasses(array('hide-on-tablet', 'margin-left5'));
$helpLinkLabel = new htmlHelpLink($this->helpID, $this->helpModule);
$helpCssClasses = array('margin-left5');
if (!$this->richEdit) {
$helpCssClasses[] = 'hide-on-tablet';
}
$helpLinkLabel->setCSSClasses($helpCssClasses);
$labelGroup->addElement($helpLinkLabel);
}
$row->add($labelGroup, 12, 6, 6, 'responsiveLabel');
// input field
$fieldGroup = new htmlGroup();
$fieldGroup->addElement($this);
if (!empty($this->helpID)) {
$helpLink = new htmlHelpLink($this->helpID);
if (!empty($this->helpID) && !$this->richEdit) {
$helpLink = new htmlHelpLink($this->helpID, $this->helpModule);
$helpLink->setCSSClasses(array('align-top', 'hide-on-mobile'));
$fieldGroup->addElement($helpLink);
}
@ -4095,6 +4158,14 @@ class htmlResponsiveInputCheckbox extends htmlInputCheckbox {
return $row->generateHTML($module, $input, $values, $restricted, $tabindex, $scope);
}
/**
* {@inheritDoc}
* @see htmlInputCheckbox::getShowHideSelector()
*/
protected function getShowHideSelector() {
return '.row';
}
}
/**
@ -4113,15 +4184,22 @@ class htmlResponsiveTable extends htmlElement {
/** widthes of the columns */
private $widths = array();
/** highlighted rows */
private $highlighted = array();
/**
* Creates the table.
*
* @param string[] $titles row titles
* @param htmlElement[][] $data data rows
* @param int[] $highlighted list of row numbers that should be highlighted (starting at 0)
*/
public function __construct($titles, $data) {
public function __construct($titles, $data, $highlighted = null) {
$this->titles = $titles;
$this->data = $data;
if (!empty($highlighted) && is_array($highlighted)) {
$this->highlighted = $highlighted;
}
}
/**
@ -4146,8 +4224,13 @@ class htmlResponsiveTable extends htmlElement {
echo '</thead>';
echo '<tbody>';
$titleCount = sizeof($this->titles);
$counter = 0;
foreach ($this->data as $row) {
echo '<tr>';
$cssClass = '';
if (in_array($counter, $this->highlighted)) {
$cssClass = ' class="bold"';
}
echo '<tr ' . $cssClass . '>';
for ($i = 0; $i < $titleCount; $i++) {
echo '<td data-label="' . $this->titles[$i] . '">';
$ids = parseHtml($module, $row[$i], $values, $restricted, $tabindex, $scope);
@ -4155,6 +4238,7 @@ class htmlResponsiveTable extends htmlElement {
echo '</td>';
}
echo '</tr>';
$counter++;
}
echo '</tbody>';
echo '</table>';

@ -101,7 +101,7 @@ function get_ldap_filter($typeId) {
$orFilter = '';
for ($i = 0; $i < sizeof($mods); $i++) {
$module = moduleCache::getModule($mods[$i], $type->getScope());
$modinfo = $module->get_ldap_filter();
$modinfo = $module->get_ldap_filter($typeId);
if (isset($modinfo['or'])) {
$filters['or'][] = $modinfo['or'];
}
@ -360,7 +360,7 @@ function getConfigOptions($scopes) {
/**
* Checks if the configuration options are valid
*
* @param array $scopes hash array (module name => array(account types))
* @param array $scopes hash array (module name => array(account type ids))
* @param array $options hash array containing all options (name => array(...))
* @return array list of error messages
*/
@ -1581,7 +1581,7 @@ class accountContainer {
$searchAttrs = array('*', '+');
foreach ($modules as $module) {
$modTmp = new $module($this->type->getScope());
$searchAttrs = array_merge($searchAttrs, $modTmp->getManagedHiddenAttributes());
$searchAttrs = array_merge($searchAttrs, $modTmp->getManagedHiddenAttributes($this->type->getId()));
}
$result = @ldap_read($_SESSION['ldap']->server(), escapeDN($dn), escapeDN($search), $searchAttrs, 0, 0, 0, LDAP_DEREF_NEVER);
if (!$result) {

@ -36,3 +36,5 @@
/zarafaUser.inc
/locking389ds.inc
/kopano*.inc
/dynamicList.inc
/customBaseType.inc

@ -245,9 +245,8 @@ class asteriskAccount extends baseModule implements passwordService {
),
);
// self service options
$selfServiceContainer = new htmlTable();
$selfServiceContainer->addElement(new htmlTableExtendedInputField(_('Asterisk realm'), 'asteriskAccount_AsteriskRealm', null));
$selfServiceContainer->addElement(new htmlHelpLink('AsteriskRealm', get_class($this)));
$selfServiceContainer = new htmlResponsiveRow();
$selfServiceContainer->add(new htmlResponsiveInputField(_('Asterisk realm'), 'asteriskAccount_AsteriskRealm', null, array('AsteriskRealm', get_class($this))), 12);
$return['selfServiceSettings'] = $selfServiceContainer;
// profile options
$profileContainer = new htmlTable();

@ -497,21 +497,11 @@ class imapAccess extends baseModule {
}
/**
* Checks input values of module settings.
*
* Calling this method does not require the existence of an enclosing {@link accountContainer}.<br>
* <br>
* If the input data is invalid the return value is an array that contains subarrays to build StatusMessages ('message type', 'message head', 'message text').
* <br>If no errors occured the function returns an empty array.
*
* @param array $scopes list of account types which are used
* @param array $options hash array (option name => value) that contains the input. The option values are all arrays containing one or more elements.
* @return array list of error messages
*
* @see baseModule::get_metaData()
*/
public function check_configOptions($scopes, &$options) {
$errors = parent::check_configOptions($scopes, $options);
* {@inheritDoc}
* @see baseModule::check_configOptions()
*/
public function check_configOptions($typeIds, &$options) {
$errors = parent::check_configOptions($typeIds, $options);
if ($options['ImapAccess_ImapAdminPasswordSelect'][0] == 'config') {
if (empty($options['ImapAccess_ImapAdminPassword'][0])) {
$errors[] = $this->messages['config'][2];

@ -2433,18 +2433,13 @@ class inetOrgPerson extends baseModule implements passwordService {
* @return htmlElement meta HTML object
*/
public function getSelfServiceSettings($profile) {
$container = new htmlTable();
$container->addElement(new htmlSubTitle(_('Photo')), true);
$photoTable = new htmlTable();
$photoTable->colspan = 2;
$container = new htmlResponsiveRow();
$container->add(new htmlSubTitle(_('Photo')), 12);
if (extension_loaded('imagick')) {
$photoTable->addElement(new htmlTableExtendedInputField(_('Maximum width (px)'), 'inetOrgPerson_jpegPhoto_maxWidth'));
$photoTable->addElement(new htmlHelpLink('crop', get_class($this)), true);
$photoTable->addElement(new htmlTableExtendedInputField(_('Maximum height (px)'), 'inetOrgPerson_jpegPhoto_maxHeight'));
$photoTable->addElement(new htmlHelpLink('crop', get_class($this)), true);
$container->add(new htmlResponsiveInputField(_('Maximum width (px)'), 'inetOrgPerson_jpegPhoto_maxWidth', null, array('crop', get_class($this))), 12);
$container->add(new htmlResponsiveInputField(_('Maximum height (px)'), 'inetOrgPerson_jpegPhoto_maxHeight', null, array('crop', get_class($this))), 12);
}
$photoTable->addElement(new htmlTableExtendedInputField(_('Maximum file size (kB)'), 'inetOrgPerson_jpegPhoto_maxSize'), true);
$container->addElement($photoTable, true);
$container->add(new htmlResponsiveInputField(_('Maximum file size (kB)'), 'inetOrgPerson_jpegPhoto_maxSize'), 12);
return $container;
}

@ -1,5 +1,6 @@
<?php
use \LAM\TYPES\TypeManager;
use function LAM\TYPES\getScopeFromTypeId;
/*
$Id$
@ -178,16 +179,11 @@ class posixAccount extends baseModule implements passwordService {
// possible self service read-only fields
$return['selfServiceReadOnlyFields'] = array('cn', 'loginShell');
// self service configuration settings
$selfServiceContainer = new htmlTable();
$selfServiceContainer->addElement(new htmlTableExtendedSelect('posixAccount_pwdHash', getSupportedHashTypes(),
array('SSHA'), _("Password hash type")));
$selfServiceContainer->addElement(new htmlHelpLink('pwdHash', get_class($this)), true);
$selfServiceContainer->addElement(new htmlTableExtendedInputTextarea('posixAccount_shells', implode("\r\n", $this->getShells()), 30, 4, _('Login shells')));
$loginShellsHelp = new htmlHelpLink('loginShells', get_class($this));
$loginShellsHelp->alignment = htmlElement::ALIGN_TOP;
$selfServiceContainer->addElement($loginShellsHelp, true);
$selfServiceContainer->addElement(new htmlTableExtendedInputCheckbox('posixAccount_useOldPwd', false, _('Password change with old password')));
$selfServiceContainer->addElement(new htmlHelpLink('useOldPwd', get_class($this)), true);
$selfServiceContainer = new htmlResponsiveRow();
$selfServiceContainer->add(new htmlResponsiveSelect('posixAccount_pwdHash', getSupportedHashTypes(),
array('SSHA'), _("Password hash type"), array('pwdHash', get_class($this))), 12);
$selfServiceContainer->add(new htmlResponsiveInputTextarea('posixAccount_shells', implode("\r\n", $this->getShells()), 30, 4, _('Login shells'), array('loginShells', get_class($this))), 12);
$selfServiceContainer->add(new htmlResponsiveInputCheckbox('posixAccount_useOldPwd', false, _('Password change with old password'), array('useOldPwd', get_class($this))), 12);
$return['selfServiceSettings'] = $selfServiceContainer;
}
// profile checks
@ -908,6 +904,11 @@ class posixAccount extends baseModule implements passwordService {
for ($i = 0; $i < sizeof($groups); $i++) {
$return[$groups[$i]['dn']]['remove']['memberUid'][] = $this->attributes['uid'][0];
}
// stop here if referential integrity overlay is active
$config = $this->getAccountContainer()->get_type()->getTypeManager()->getConfig();
if ($config->isReferentialIntegrityOverlayActive()) {
return $return;
}
// remove from group of names
$dn = $this->getAccountContainer()->dn_orig;
$gons = searchLDAPByFilter('(|(member=' . $dn . ')(uniqueMember=' . $dn . '))', array('member', 'uniqueMember'), array('group', 'gon'));
@ -2031,15 +2032,15 @@ class posixAccount extends baseModule implements passwordService {
}
/**
* Checks input values of module settings.
*
* @param array $scopes list of account types which are used
* @param array $options hash array containing the settings (array('option' => array('value')))
* @return array list of error messages
*/
function check_configOptions($scopes, &$options) {
* {@inheritDoc}
* @see baseModule::check_configOptions()
*/
function check_configOptions($typeIds, &$options) {
$return = array();
// user settings
$scopes = array();
foreach ($typeIds as $typeId) {
$scopes[] = getScopeFromTypeId($typeId);
} // user settings
if (in_array('user', $scopes)) {
if ($options['posixAccount_uidGeneratorUsers'][0] == 'range') {
// min/maxUID are required, check if they are numeric
@ -3244,7 +3245,7 @@ class posixAccount extends baseModule implements passwordService {
$wc = substr($wildcards[0][$i], 1, strlen($wildcards[0][$i]) - 2);
$value = '';
if (isset($attributes[$wc][0]) && !empty($attributes[$wc][0])) {
$value = $attributes[$wc][0][0];
$value = $this->cleanSuggestionPart($attributes[$wc][0][0]);
}
$format = str_replace('@' . $wc . '@', $value, $format);
}
@ -3256,16 +3257,26 @@ class posixAccount extends baseModule implements passwordService {
$wc = substr($wildcards[0][$i], 1, strlen($wildcards[0][$i]) - 2);
$value = '';
if (isset($attributes[$wc][0])) {
$value = $attributes[$wc][0];
$value = $this->cleanSuggestionPart($attributes[$wc][0]);
}
$format = str_replace('%' . $wc . '%', $value, $format);
}
}
$format = str_replace(array_keys($this->umlautReplacements), array_values($this->umlautReplacements), strtolower($format));
$format = str_replace(array(' ', '_', '-'), array('', '', ''), $format);
return $format;
}
/**
* Cleans a string that is injected in user name suggestion.
*
* @param string $part injected part
* @return string cleaned by removing umlauts, spaces, dashes and underscores
*/
private function cleanSuggestionPart($part) {
$result = str_replace(array_keys($this->umlautReplacements), array_values($this->umlautReplacements), strtolower($part));
$result = str_replace(array(' ', '_', '-'), array('', '', ''), $result);
return $result;
}