diff --git a/lam-packaging/debian/copyright b/lam-packaging/debian/copyright
index cab1f9509..a9b92c1c3 100644
--- a/lam-packaging/debian/copyright
+++ b/lam-packaging/debian/copyright
@@ -1,4 +1,4 @@
-This software is copyright (c) 2003 - 2024 by Roland Gruber
+This software is copyright (c) 2003 - 2025 by Roland Gruber
If you purchased a copy of LDAP Account Manager Pro then the following
files are licensed under the conditions which you accepted at purchase
@@ -17,6 +17,8 @@ time.
* lib/modules/automount.inc
* lib/modules/bindDLZ.inc
* lib/modules/bindDLZXfr.inc
+* lib/modules/bindDyndbRecord.inc
+* lib/modules/bindDyndbZone.inc
* lib/modules/customBaseType.inc
* lib/modules/customFields.inc
* lib/modules/customScripts.inc
@@ -56,6 +58,7 @@ time.
* lib/modules/rfc2307bisAutomount.inc
* lib/modules/rfc2307bisPosixGroup.inc
* lib/modules/selfRegistration.inc
+* lib/modules/simpleSecurityObject.inc
* lib/modules/sudoRole.inc
* lib/modules/uidObject.inc
* lib/modules/webauthn.inc
@@ -64,6 +67,7 @@ time.
* lib/types/alias.inc
* lib/types/automountType.inc
* lib/types/bind.inc
+* lib/types/bindDyndbType.inc
* lib/types/customType.inc
* lib/types/gon.inc
* lib/types/kopanoAddressListType.inc
diff --git a/lam/HISTORY b/lam/HISTORY
index 661b9993f..1b6a16b84 100644
--- a/lam/HISTORY
+++ b/lam/HISTORY
@@ -1,6 +1,9 @@
June 2025 9.2
+ - TAK support added
- Active Directory: allow to restore deleted entries in tree view (415)
-
+ - Fixed bugs:
+ -> Unix: profile editor for users not working (418)
+ -> Custom fields: problems with deleting facsimileTelephoneNumber (419)
13.03.2025 9.1
- Usability improvements (347, 348, 360, 403)
diff --git a/lam/copyright b/lam/copyright
index 6b0822d04..d10c3adbe 100644
--- a/lam/copyright
+++ b/lam/copyright
@@ -17,6 +17,8 @@ time.
* lib/modules/automount.inc
* lib/modules/bindDLZ.inc
* lib/modules/bindDLZXfr.inc
+* lib/modules/bindDyndbRecord.inc
+* lib/modules/bindDyndbZone.inc
* lib/modules/customBaseType.inc
* lib/modules/customFields.inc
* lib/modules/customScripts.inc
@@ -56,6 +58,7 @@ time.
* lib/modules/rfc2307bisAutomount.inc
* lib/modules/rfc2307bisPosixGroup.inc
* lib/modules/selfRegistration.inc
+* lib/modules/simpleSecurityObject.inc
* lib/modules/sudoRole.inc
* lib/modules/uidObject.inc
* lib/modules/webauthn.inc
@@ -64,6 +67,7 @@ time.
* lib/types/alias.inc
* lib/types/automountType.inc
* lib/types/bind.inc
+* lib/types/bindDyndbType.inc
* lib/types/customType.inc
* lib/types/gon.inc
* lib/types/kopanoAddressListType.inc
@@ -433,7 +437,7 @@ E:
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-F:
+F:
3-Clause BSD License
Redistribution and use in source and binary forms, with or without
diff --git a/lam/docs/manual-sources/appendix-schema.xml b/lam/docs/manual-sources/appendix-schema.xml
index d22cab80b..c369ed91e 100644
--- a/lam/docs/manual-sources/appendix-schema.xml
+++ b/lam/docs/manual-sources/appendix-schema.xml
@@ -467,7 +467,7 @@
dhcp.schema
- docs/schema/dhcp.schema
+ Part of LAM installation: docs/schema/dhcp.schema
The LDAP suffix should be set to your dhcpServer
entry.
@@ -486,7 +486,7 @@
schema.ldif
- part of bind-dyndb-ldap
+ Part of bind-dyndb-ldap
LAM Pro only
@@ -505,7 +505,7 @@
dlz.schema
- part of Bind
+ Part of Bind
DLZ patch
LAM Pro only
@@ -821,6 +821,24 @@
LAM Pro only, requires DDS extension on LDAP server
side
+
+
+
+
+
+
+
+
+ TAK
+
+ takUser
+
+ tak-*.ldif
+
+ Part of LAM installation: docs/schema/tak-*.ldif
+
+
+
diff --git a/lam/docs/manual-sources/chapter-modules.xml b/lam/docs/manual-sources/chapter-modules.xml
index cca247743..e03e61617 100644
--- a/lam/docs/manual-sources/chapter-modules.xml
+++ b/lam/docs/manual-sources/chapter-modules.xml
@@ -2510,6 +2510,56 @@ AuthorizedKeysCommandUser root
+
+
+ TAK
+
+ The TAK module
+ supports the Team Awareness Kit or Tactical Assault Kit (TAK) with the
+ Android Team Awareness Kit (ATAK).
+
+ You can define callsigns, team roles and colors for users.
+
+ LDAP schema
+
+ The module expects that TAK users use the object class "takUser"
+ and the attributes "takCallsign", "takRole" and "takColor". You can find
+ matching schema files in /usr/share/ldap-account-manager/docs/schema
+ (DEB/RPM) or docs/schema (tar.bz2). Please see the beginning of the
+ files for installation instructions.
+
+
+
+ OpenLDAP: tak-OpenLDAP.ldif
+
+
+
+ Samba 4: tak-Samba4-attributes.ldif and
+ tak-Samba4-objectClass.ldif
+
+
+
+ Windows (AD): tak-Windows.ldif
+
+
+
+ Configuration
+
+ Add the TAK module for users in your server profile:
+
+
+
+
+
+ Now you can manage the TAK attributes for users.
+
+ LAM Pro users can add these attributes to the self-service profile
+ if needed.
+
+
+
+
+
diff --git a/lam/docs/manual-sources/images/mod_tak1.png b/lam/docs/manual-sources/images/mod_tak1.png
new file mode 100644
index 000000000..5caf6d0a4
Binary files /dev/null and b/lam/docs/manual-sources/images/mod_tak1.png differ
diff --git a/lam/docs/manual-sources/images/mod_tak2.png b/lam/docs/manual-sources/images/mod_tak2.png
new file mode 100644
index 000000000..a5e97b7c0
Binary files /dev/null and b/lam/docs/manual-sources/images/mod_tak2.png differ
diff --git a/lam/docs/manual-sources/images/schema_tak.png b/lam/docs/manual-sources/images/schema_tak.png
new file mode 100644
index 000000000..0bb41fb6b
Binary files /dev/null and b/lam/docs/manual-sources/images/schema_tak.png differ
diff --git a/lam/docs/schema/tak-OpenLDAP.ldif b/lam/docs/schema/tak-OpenLDAP.ldif
new file mode 100644
index 000000000..a9d69372c
--- /dev/null
+++ b/lam/docs/schema/tak-OpenLDAP.ldif
@@ -0,0 +1,33 @@
+#
+# LDAP schema for LAM TAK functionality
+#
+# This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
+# Copyright (C) 2025 Roland Gruber
+#
+#
+# OID bases:
+# 1.3.6.1.4.1.34955 Roland Gruber Softwareentwicklung
+# 1.3.6.1.4.1.34955.1 attributes
+# 1.3.6.1.4.1.34955.2 object classes
+#
+# Installation:
+# ldapadd -x -W -H ldap://localhost -D "cn=admin,dc=company,dc=com" -f tak-OpenLDAP.ldif
+#
+# Please replace "localhost" with your LDAP server and "cn=admin,dc=company,dc=com" with your LDAP admin user (usually starts with cn=admin or cn=manager).
+#
+# In some cases you might need to import directly on the OpenLDAP server as root:
+# ldapadd -Y EXTERNAL -H ldapi:/// -f tak-OpenLDAP.ldif
+#
+# Version: 1
+#
+# Changelog:
+# 1: initial release (LAM 9.2)
+#
+
+dn: cn=tak,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: tak
+olcAttributeTypes: ( 1.3.6.1.4.1.34955.1.100 NAME 'takCallsign' DESC 'TAK callsign' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.4.1.34955.1.101 NAME 'takRole' DESC 'TAK team role' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.4.1.34955.1.102 NAME 'takColor' DESC 'TAK team color' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
+olcObjectClasses: ( 1.3.6.1.4.1.34955.2.10 NAME 'takUser' DESC 'TAK user' SUP top AUXILIARY MAY ( takCallsign $ takRole $ takColor ) MUST ( cn ) )
diff --git a/lam/docs/schema/tak-Samba4-attributes.ldif b/lam/docs/schema/tak-Samba4-attributes.ldif
new file mode 100644
index 000000000..38b93523a
--- /dev/null
+++ b/lam/docs/schema/tak-Samba4-attributes.ldif
@@ -0,0 +1,58 @@
+#
+# LDAP schema for LAM TAK functionality
+#
+# This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
+# Copyright (C) 2025 Roland Gruber
+#
+#
+# OID bases:
+# 1.3.6.1.4.1.34955 Roland Gruber Softwareentwicklung
+# 1.3.6.1.4.1.34955.1 attributes
+# 1.3.6.1.4.1.34955.2 object classes
+#
+# Please replace DOMAIN_TOP_DN with your LDAP suffix (e.g. dc=samba4,dc=test).
+# This file must be installed first.
+#
+# Installation: ldbmodify -H /var/lib/samba/private/sam.ldb tak-Samba4-attributes.ldif --option="dsdb:schema update allowed"=true
+#
+#
+# Version: 1
+# 1: initial release (LAM 9.2)
+#
+
+dn: CN=takCallsign,CN=Schema,CN=Configuration,DOMAIN_TOP_DN
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.34955.1.100
+attributeSyntax: 2.5.5.12
+oMSyntax: 64
+isSingleValued: TRUE
+rangeLower: 4
+cn: takCallsign
+name: takCallsign
+lDAPDisplayName: takCallsign
+description: TAK callsign
+
+dn: CN=takRole,CN=Schema,CN=Configuration,DOMAIN_TOP_DN
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.34955.1.101
+attributeSyntax: 2.5.5.12
+oMSyntax: 64
+isSingleValued: TRUE
+cn: takRole
+name: takRole
+lDAPDisplayName: takRole
+description: TAK team role
+
+dn: CN=takColor,CN=Schema,CN=Configuration,DOMAIN_TOP_DN
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.34955.1.102
+attributeSyntax: 2.5.5.12
+oMSyntax: 64
+isSingleValued: TRUE
+cn: takColor
+name: takColor
+LDAPDisplayName: takColor
+Description: TAK team color
diff --git a/lam/docs/schema/tak-Samba4-objectClass.ldif b/lam/docs/schema/tak-Samba4-objectClass.ldif
new file mode 100644
index 000000000..25d165988
--- /dev/null
+++ b/lam/docs/schema/tak-Samba4-objectClass.ldif
@@ -0,0 +1,36 @@
+#
+# LDAP schema for LAM TAK functionality
+#
+# This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
+# Copyright (C) 2025 Roland Gruber
+#
+#
+# OID bases:
+# 1.3.6.1.4.1.34955 Roland Gruber Softwareentwicklung
+# 1.3.6.1.4.1.34955.1 attributes
+# 1.3.6.1.4.1.34955.2 object classes
+#
+# Please replace DOMAIN_TOP_DN with your LDAP suffix (e.g. dc=samba4,dc=test).
+# This file must be installed second.
+#
+# Installation: ldbmodify -H /var/lib/samba/private/sam.ldb tak-Samba4-objectClass.ldif --option="dsdb:schema update allowed"=true
+#
+#
+# Version: 1
+# 1: initial release (LAM 9.2)
+#
+
+dn: CN=takUser,CN=Schema,CN=Configuration,DOMAIN_TOP_DN
+objectClass: top
+objectClass: classSchema
+governsID: 1.3.6.1.4.1.34955.2.10
+cn: takUser
+lDAPDisplayName: takUser
+subClassOf: top
+objectClassCategory: 3
+mustContain: cn
+mayContain: takCallsign
+mayContain: takRole
+mayContain: takColor
+description: TAK user
+possSuperiors: top
diff --git a/lam/docs/schema/tak-Windows.ldif b/lam/docs/schema/tak-Windows.ldif
new file mode 100644
index 000000000..0fba52bed
--- /dev/null
+++ b/lam/docs/schema/tak-Windows.ldif
@@ -0,0 +1,100 @@
+#
+# LDAP schema for LAM TAK functionality
+#
+# This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
+# Copyright (C) 2025 Roland Gruber
+#
+#
+# OID bases:
+# 1.3.6.1.4.1.34955 Roland Gruber Softwareentwicklung
+# 1.3.6.1.4.1.34955.1 attributes
+# 1.3.6.1.4.1.34955.2 object classes
+#
+# Please replace DOMAIN_TOP_DN with your LDAP suffix (e.g. dc=windows,dc=test).
+#
+# Installation: ldifde -v -i -f tak-Windows.ldif
+#
+#
+# Version: 1
+# 1: initial release (LAM 9.2)
+#
+
+dn: CN=takCallsign,CN=Schema,CN=Configuration,DOMAIN_TOP_DN
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.34955.1.100
+attributeSyntax: 2.5.5.12
+oMSyntax: 64
+isSingleValued: TRUE
+rangeLower: 4
+cn: takCallsign
+name: takCallsign
+lDAPDisplayName: takCallsign
+description: TAK callsign
+
+dn: CN=takRole,CN=Schema,CN=Configuration,DOMAIN_TOP_DN
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.34955.1.101
+attributeSyntax: 2.5.5.12
+oMSyntax: 64
+isSingleValued: TRUE
+cn: takRole
+name: takRole
+lDAPDisplayName: takRole
+description: TAK team role
+
+dn: CN=takColor,CN=Schema,CN=Configuration,DOMAIN_TOP_DN
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.34955.1.102
+attributeSyntax: 2.5.5.12
+oMSyntax: 64
+isSingleValued: TRUE
+cn: takColor
+name: takColor
+LDAPDisplayName: takColor
+Description: TAK team color
+
+dn:
+changetype: modify
+add: schemaUpdateNow
+schemaUpdateNow: 1
+-
+
+dn: CN=takUser,CN=Schema,CN=Configuration,DOMAIN_TOP_DN
+changetype: add
+objectClass: top
+objectClass: classSchema
+governsID: 1.3.6.1.4.1.34955.2.10
+cn: takUser
+lDAPDisplayName: takUser
+subClassOf: top
+objectClassCategory: 3
+mustContain: cn
+mayContain: takCallsign
+mayContain: takRole
+mayContain: takColor
+description: TAK user
+possSuperiors: top
+
+dn:
+changetype: modify
+add: schemaUpdateNow
+schemaUpdateNow: 1
+-
+
+dn: CN=User,CN=Schema,CN=Configuration,DOMAIN_TOP_DN
+changetype: modify
+add: auxiliaryClass
+auxiliaryClass: takUser
+-
+
+dn:
+changetype: modify
+add: schemaUpdateNow
+schemaUpdateNow: 1
+-
diff --git a/lam/lib/account.inc b/lam/lib/account.inc
index 3afa4f5c4..0a3ff74fe 100644
--- a/lam/lib/account.inc
+++ b/lam/lib/account.inc
@@ -3,7 +3,7 @@
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2003 - 2006 Tilo Lutz
- 2009 - 2024 Roland Gruber
+ 2009 - 2025 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
@@ -77,6 +77,18 @@ function in_array_ignore_case($needle, $haystack) {
return false;
}
+/**
+ * Checks if two arrays have the same content.
+ *
+ * @param array $array1 array 1
+ * @param array $array2 array 2
+ * @return bool same content
+ */
+function areArrayContentsEqual(array $array1, array $array2): bool {
+ $intersect = array_intersect($array1, $array2);
+ return ((count($array1) === count($array2)) && (count($intersect) === count($array1)));
+}
+
/**
* Sorts an array in natural order by its keys.
*
diff --git a/lam/lib/baseModule.inc b/lam/lib/baseModule.inc
index 662042a85..6e6e47e52 100644
--- a/lam/lib/baseModule.inc
+++ b/lam/lib/baseModule.inc
@@ -9,7 +9,7 @@ use LAM\PDF\PDFImage;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
- Copyright (C) 2003 - 2024 Roland Gruber
+ Copyright (C) 2003 - 2025 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
@@ -1034,7 +1034,7 @@ abstract class baseModule {
if (!empty($regex)) {
$this->checkUploadRegex($regexIDs, $rawAccounts[$position][$ids[$colName]], $message, $position, $errors);
}
- $partialAccounts[$position][$attrName] = trim($rawAccounts[$position][$ids[$colName]]);
+ $partialAccounts[$position][$attrName] = [trim($rawAccounts[$position][$ids[$colName]])];
}
// multi-value
else {
@@ -1799,7 +1799,7 @@ abstract class baseModule {
* @param array $container return value of checkSelfServiceOptions()
* @param String $name attribute name
* @param array $attributes LDAP attributes
- * @param string $fields input fields
+ * @param array $fields input fields
* @param array $readOnlyFields list of read-only fields
* @param String $validationID validation ID for get_preg()
* @param array $validationMessage validation message data (defaults to $this->messages[$name][0])
@@ -1822,13 +1822,11 @@ abstract class baseModule {
$container['add'][$ldapAttrName] = [$_POST[$fieldName]];
}
}
+ elseif ($requiredMessage !== null) {
+ $container['messages'][] = $requiredMessage;
+ }
elseif (isset($attributes[$ldapAttrName])) {
- if ($requiredMessage !== null) {
- $container['messages'][] = $requiredMessage;
- }
- else {
- $container['del'][$ldapAttrName] = $attributes[$ldapAttrName];
- }
+ $container['del'][$ldapAttrName] = $attributes[$ldapAttrName];
}
}
}
@@ -1954,8 +1952,7 @@ abstract class baseModule {
$container['messages'][] = $requiredMessage;
}
$valuesOld = $attributes[$ldapAttrName] ?? [];
- $intersect = array_intersect($valuesOld, $valuesNew);
- if ((count($valuesOld) !== count($valuesNew)) || (count($intersect) !== count($valuesOld))) {
+ if (!areArrayContentsEqual($valuesOld, $valuesNew)) {
$container['mod'][$ldapAttrName] = $valuesNew;
}
}
diff --git a/lam/lib/modules.inc b/lam/lib/modules.inc
index 02d6053d4..70e4b7ef4 100644
--- a/lam/lib/modules.inc
+++ b/lam/lib/modules.inc
@@ -1589,87 +1589,49 @@ class accountContainer {
*/
function save_module_attributes($attributes, $orig) {
$return = [];
- $toadd = [];
- $tomodify = [];
- $torem = [];
- $notchanged = [];
- // get list of all attributes
- $attr_names = array_keys($attributes);
- $orig_names = array_keys($orig);
+ $toModify = [];
+ $notChanged = [];
+ // cleanup
+ foreach ($attributes as $name => &$values) {
+ // remove empty values
+ $values = array_values($values);
+ for ($i = 0; $i < count($values); $i++) {
+ if ($values[$i] === '') {
+ unset($values[$i]);
+ }
+ }
+ $values = array_values($values);
+ // remove empty list of values
+ if (sizeof($values) === 0) {
+ unset($attributes[$name]);
+ }
+ }
// find deleted attributes (in $orig but no longer in $attributes)
- foreach ($orig_names as $value) {
- if (!isset($attributes[$value])) {
- $torem[$value] = $orig[$value];
+ foreach ($orig as $name => $value) {
+ if (!isset($attributes[$name]) || (sizeof($attributes[$name]) === 0)) {
+ $toModify[$name] = [];
}
}
// find changed attributes
- foreach ($attr_names as $name) {
- // find deleted attributes
- if (isset($orig[$name]) && is_array($orig[$name])) {
- foreach ($orig[$name] as $value) {
- if (is_array($attributes[$name])) {
- if (!in_array($value, $attributes[$name], true)
- && ($value !== null)
- && ($value !== '')) {
- $torem[$name][] = $value;
- }
- }
- elseif (($value !== null) && ($value !== '')) {
- $torem[$name][] = $value;
- }
- }
+ foreach ($attributes as $name => $value) {
+ // new attributes
+ if (!isset($orig[$name])) {
+ $toModify[$name] = $value;
}
- // find new attributes
- if (isset($attributes[$name]) && is_array($attributes[$name])) {
- foreach ($attributes[$name] as $value) {
- if (isset($orig[$name]) && is_array($orig[$name])) {
- if (!in_array($value, $orig[$name], true)
- && ($value !== null)
- && ($value !== '')) {
- $toadd[$name][] = $value;
- }
- }
- elseif (($value !== null) && ($value !== '')) {
- $toadd[$name][] = $value;
- }
- }
+ // changed attributes
+ elseif (!areArrayContentsEqual($value, $orig[$name])) {
+ $toModify[$name] = $value;
}
- // find unchanged attributes
- if (isset($orig[$name]) && is_array($orig[$name]) && is_array($attributes[$name])) {
- foreach ($attributes[$name] as $value) {
- if (($value !== null) && ($value !== '') && in_array($value, $orig[$name], true)) {
- $notchanged[$name][] = $value;
- }
- }
+ // unchanged attributes
+ else {
+ $notChanged[$name] = $value;
}
}
- // create modify with add and remove
- $attributes2 = array_keys($toadd);
- for ($i = 0; $i < count($attributes2); $i++) {
- if (isset($torem[$attributes2[$i]]) && ($toadd[$attributes2[$i]] !== []) && (count($torem[$attributes2[$i]]) > 0)) {
- // found attribute which should be modified
- $tomodify[$attributes2[$i]] = $toadd[$attributes2[$i]];
- // merge unchanged values
- if (isset($notchanged[$attributes2[$i]])) {
- $tomodify[$attributes2[$i]] = array_merge($tomodify[$attributes2[$i]], $notchanged[$attributes2[$i]]);
- unset($notchanged[$attributes2[$i]]);
- }
- // remove old add and remove commands
- unset($toadd[$attributes2[$i]]);
- unset($torem[$attributes2[$i]]);
- }
+ if ($toModify !== []) {
+ $return[$this->dn_orig]['modify'] = $toModify;
}
- if ($toadd !== []) {
- $return[$this->dn_orig]['add'] = $toadd;
- }
- if ($torem !== []) {
- $return[$this->dn_orig]['remove'] = $torem;
- }
- if ($tomodify !== []) {
- $return[$this->dn_orig]['modify'] = $tomodify;
- }
- if ($notchanged !== []) {
- $return[$this->dn_orig]['notchanged'] = $notchanged;
+ if ($notChanged !== []) {
+ $return[$this->dn_orig]['notchanged'] = $notChanged;
}
return $return;
}
diff --git a/lam/lib/modules/inetOrgPerson.inc b/lam/lib/modules/inetOrgPerson.inc
index 4d9e5d462..dcfdeb232 100644
--- a/lam/lib/modules/inetOrgPerson.inc
+++ b/lam/lib/modules/inetOrgPerson.inc
@@ -894,28 +894,6 @@ class inetOrgPerson extends baseModule implements passwordService, AccountStatus
return [];
}
$return = parent::save_attributes();
- // postalAddress, registeredAddress, facsimileTelephoneNumber and jpegPhoto need special removing
- if (isset($return[$this->getAccountContainer()->dn_orig]['remove']['postalAddress'])) {
- $return[$this->getAccountContainer()->dn_orig]['modify']['postalAddress'] = $this->attributes['postalAddress'];
- unset($return[$this->getAccountContainer()->dn_orig]['remove']['postalAddress']);
- }
- if (isset($return[$this->getAccountContainer()->dn_orig]['remove']['registeredAddress'])) {
- $return[$this->getAccountContainer()->dn_orig]['modify']['registeredAddress'] = $this->attributes['registeredAddress'];
- unset($return[$this->getAccountContainer()->dn_orig]['remove']['registeredAddress']);
- }
- if (isset($return[$this->getAccountContainer()->dn_orig]['remove']['facsimileTelephoneNumber'])) {
- $return[$this->getAccountContainer()->dn_orig]['modify']['facsimileTelephoneNumber'] = $this->attributes['facsimileTelephoneNumber'];
- unset($return[$this->getAccountContainer()->dn_orig]['remove']['facsimileTelephoneNumber']);
- }
- if (isset($return[$this->getAccountContainer()->dn_orig]['add']['facsimileTelephoneNumber'])
- && isset($this->orig['facsimileTelephoneNumber']) && (count($this->orig['facsimileTelephoneNumber']) > 0)) {
- $return[$this->getAccountContainer()->dn_orig]['modify']['facsimileTelephoneNumber'] = $this->attributes['facsimileTelephoneNumber'];
- unset($return[$this->getAccountContainer()->dn_orig]['add']['facsimileTelephoneNumber']);
- }
- if (isset($return[$this->getAccountContainer()->dn_orig]['remove']['jpegPhoto'])) {
- $return[$this->getAccountContainer()->dn_orig]['modify']['jpegPhoto'] = [];
- unset($return[$this->getAccountContainer()->dn_orig]['remove']['jpegPhoto']);
- }
// add information about clear text password
if ($this->clearTextPassword != null) {
$return[$this->getAccountContainer()->dn_orig]['info']['userPasswordClearText'][0] = $this->clearTextPassword;
@@ -2730,19 +2708,19 @@ class inetOrgPerson extends baseModule implements passwordService, AccountStatus
$attributes, $readOnlyFields, false, false, 'givenName');
$this->addSimpleSelfServiceTextField($return, 'lastName', _('Last name'), $fields,
$attributes, $readOnlyFields, true, false, 'sn');
- $this->addSimpleSelfServiceTextField($return, 'mail', _('Email address'), $fields,
+ $this->addMultiValueSelfServiceTextField($return, 'mail', _('Email address'), $fields,
$attributes, $readOnlyFields);
$this->addMultiValueSelfServiceTextField($return, 'labeledURI', _('Web site'), $fields,
$attributes, $readOnlyFields, false, false, 'labeledURI');
- $this->addSimpleSelfServiceTextField($return, 'telephoneNumber', _('Telephone number'), $fields,
+ $this->addMultiValueSelfServiceTextField($return, 'telephoneNumber', _('Telephone number'), $fields,
$attributes, $readOnlyFields, false, false, 'telephoneNumber');
- $this->addSimpleSelfServiceTextField($return, 'homePhone', _('Home telephone number'), $fields,
+ $this->addMultiValueSelfServiceTextField($return, 'homePhone', _('Home telephone number'), $fields,
$attributes, $readOnlyFields, false, false, 'homePhone');
- $this->addSimpleSelfServiceTextField($return, 'mobile', _('Mobile telephone number'), $fields,
+ $this->addMultiValueSelfServiceTextField($return, 'mobile', _('Mobile telephone number'), $fields,
$attributes, $readOnlyFields);
- $this->addSimpleSelfServiceTextField($return, 'faxNumber', _('Fax number'), $fields,
+ $this->addMultiValueSelfServiceTextField($return, 'faxNumber', _('Fax number'), $fields,
$attributes, $readOnlyFields, false, false, 'facsimileTelephoneNumber');
- $this->addSimpleSelfServiceTextField($return, 'pager', _('Pager'), $fields,
+ $this->addMultiValueSelfServiceTextField($return, 'pager', _('Pager'), $fields,
$attributes, $readOnlyFields);
$this->addMultiValueSelfServiceTextField($return, 'street', _('Street'), $fields,
$attributes, $readOnlyFields);
@@ -3200,22 +3178,22 @@ class inetOrgPerson extends baseModule implements passwordService, AccountStatus
$this->checkSimpleSelfServiceTextField($return, 'lastName', $attributes, $fields,
$readOnlyFields, 'realname', $this->messages['lastname'][0], $this->messages['lastname'][0],
'sn');
- $this->checkSimpleSelfServiceTextField($return, 'mail', $attributes, $fields,
+ $this->checkMultiValueSelfServiceTextField($return, 'mail', $attributes, $fields,
$readOnlyFields, 'email', $this->messages['mail'][0]);
$this->checkMultiValueSelfServiceTextField($return, 'labeledURI', $attributes, $fields,
$readOnlyFields, null, null, null, 'labeledURI');
- $this->checkSimpleSelfServiceTextField($return, 'telephoneNumber', $attributes, $fields,
+ $this->checkMultiValueSelfServiceTextField($return, 'telephoneNumber', $attributes, $fields,
$readOnlyFields, 'telephone', $this->messages['telephoneNumber'][0], null,
'telephoneNumber');
- $this->checkSimpleSelfServiceTextField($return, 'homePhone', $attributes, $fields,
+ $this->checkMultiValueSelfServiceTextField($return, 'homePhone', $attributes, $fields,
$readOnlyFields, 'telephone', $this->messages['homePhone'][0], null,
'homePhone');
- $this->checkSimpleSelfServiceTextField($return, 'faxNumber', $attributes, $fields,
+ $this->checkMultiValueSelfServiceTextField($return, 'faxNumber', $attributes, $fields,
$readOnlyFields, 'telephone', $this->messages['facsimileTelephoneNumber'][0], null,
'facsimileTelephoneNumber');
- $this->checkSimpleSelfServiceTextField($return, 'mobile', $attributes, $fields,
+ $this->checkMultiValueSelfServiceTextField($return, 'mobile', $attributes, $fields,
$readOnlyFields, 'telephone', $this->messages['mobile'][0]);
- $this->checkSimpleSelfServiceTextField($return, 'pager', $attributes, $fields,
+ $this->checkMultiValueSelfServiceTextField($return, 'pager', $attributes, $fields,
$readOnlyFields, 'telephone', $this->messages['pager'][0]);
$this->checkMultiValueSelfServiceTextField($return, 'street', $attributes, $fields,
$readOnlyFields, 'street', $this->messages['street'][0]);
diff --git a/lam/lib/modules/nisnetgroup.inc b/lam/lib/modules/nisnetgroup.inc
index 66d3b45ed..4722d907b 100644
--- a/lam/lib/modules/nisnetgroup.inc
+++ b/lam/lib/modules/nisnetgroup.inc
@@ -8,7 +8,7 @@ use LAM\TYPES\ConfiguredType;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
- Copyright (C) 2009 - 2024 Roland Gruber
+ Copyright (C) 2009 - 2025 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
@@ -159,32 +159,6 @@ class nisnetgroup extends baseModule {
$this->messages['domain'][0] = ['ERROR', _('Domain name'), _('Domain name is invalid!')];
}
- /**
- * Returns a list of modifications which have to be made to the LDAP account.
- *
- * @return array list of modifications
- *
This function returns an array with 3 entries:
- *
array( DN1 ('add' => array($attr), 'remove' => array($attr), 'modify' => array($attr)), DN2 .... )
- *
DN is the DN to change. It may be possible to change several DNs (e.g. create a new user and add him to some groups via attribute memberUid)
- *
"add" are attributes which have to be added to LDAP entry
- *
"remove" are attributes which have to be removed from LDAP entry
- *
"modify" are attributes which have to been modified in LDAP entry
- *
"info" are values with informational value (e.g. to be used later by pre/postModify actions)
- */
- function save_attributes() {
- $return = $this->getAccountContainer()->save_module_attributes($this->attributes, $this->orig);
- // nisNetgroupTriple needs special changing
- if (isset($return[$this->getAccountContainer()->dn_orig]['remove']['nisNetgroupTriple'])) {
- $return[$this->getAccountContainer()->dn_orig]['modify']['nisNetgroupTriple'] = $this->attributes['nisNetgroupTriple'];
- unset($return[$this->getAccountContainer()->dn_orig]['remove']['nisNetgroupTriple']);
- }
- if (isset($return[$this->getAccountContainer()->dn_orig]['add']['nisNetgroupTriple'])) {
- $return[$this->getAccountContainer()->dn_orig]['modify']['nisNetgroupTriple'] = $this->attributes['nisNetgroupTriple'];
- unset($return[$this->getAccountContainer()->dn_orig]['add']['nisNetgroupTriple']);
- }
- return $return;
- }
-
/**
* Returns the HTML meta data for the main account page.
*
diff --git a/lam/lib/modules/posixAccount.inc b/lam/lib/modules/posixAccount.inc
index d9f128979..043246e50 100644
--- a/lam/lib/modules/posixAccount.inc
+++ b/lam/lib/modules/posixAccount.inc
@@ -1382,10 +1382,13 @@ class posixAccount extends baseModule implements passwordService, AccountStatusP
/**
* Returns if groups with same name can be created.
*
+ * @param ?string $typeId type ID
* @return bool creation is possible
*/
- private function allowsToCreateGroupWithSameName(): bool {
- $typeId = $this->getAccountContainer()->get_type()->getId();
+ private function allowsToCreateGroupWithSameName(?string $typeId = null): bool {
+ if ($typeId === null) {
+ $typeId = $this->getAccountContainer()->get_type()->getId();
+ }
if (($this->get_scope() !== 'user')
|| $this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hideCreateGroup')) {
return false;
@@ -2174,7 +2177,7 @@ class posixAccount extends baseModule implements passwordService, AccountStatusP
if ($this->get_scope() == 'user') {
// primary Unix group
$primaryGroups = $groups;
- $allowToCreateGroupWithUserName = $this->allowsToCreateGroupWithSameName();
+ $allowToCreateGroupWithUserName = $this->allowsToCreateGroupWithSameName($typeId);
if ($allowToCreateGroupWithUserName) {
$primaryGroups = [_('Create group with same name') => self::CREATE_GROUP_WITH_SAME_NAME] + $primaryGroups;
}
diff --git a/lam/lib/modules/takUser.inc b/lam/lib/modules/takUser.inc
new file mode 100644
index 000000000..ede66a210
--- /dev/null
+++ b/lam/lib/modules/takUser.inc
@@ -0,0 +1,327 @@
+get_scope() === 'user';
+ }
+
+ /** possible roles */
+ private const ROLE_TYPES = ['Team Member', 'Team Leader', 'HQ', 'RTO', 'K9', 'Medic', 'Forward Observer', 'Sniper'];
+
+ /** possible colors */
+ private const COLOR_TYPES = ['Blue', 'Brown', 'Cyan', 'Dark Blue', 'Dark Green', 'Green', 'Magenta', 'Maroon', 'Orange', 'Purple', 'Red', 'Teal', 'White', 'Yellow'];
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_metaData() {
+ $return = [];
+ // icon
+ $return['icon'] = 'tak.png';
+ // alias name
+ $return["alias"] = _("TAK");
+ // LDAP filter
+ $return["ldap_filter"] = ['or' => "(objectClass=takUser)"];
+ // RDN attribute
+ $return["RDN"] = ["cn" => "normal"];
+ // module dependencies
+ $return['dependencies'] = ['depends' => [], 'conflicts' => []];
+ // managed object classes
+ $return['objectClasses'] = ['takUser'];
+ // managed attributes
+ $return['attributes'] = ['takcallsign', 'takrole', 'takcolor'
+ ];
+ // help Entries
+ $return['help'] = [
+ 'takcallsign' => [
+ "Headline" => _("Callsign"), 'attr' => 'takCallsign',
+ "Text" => _("The user's callsign to be displayed to other TAK users. It must be unique.")
+ ],
+ 'takrole' => [
+ "Headline" => _("Team role"), 'attr' => 'takRole',
+ "Text" => _("The user's role to be displayed to other TAK users.")
+ ],
+ 'takcolor' => [
+ "Headline" => _("Team color"), 'attr' => 'takColor',
+ "Text" => _("The user's team color to be displayed to other TAK users.")
+ ],
+ ];
+ // upload fields
+ $return['upload_columns'] = [
+ [
+ 'name' => 'takuser_takcallsign',
+ 'description' => _('Callsign'),
+ 'help' => 'takcallsign',
+ 'example' => 'UK-ORG-01',
+ 'required' => true,
+ 'unique' => true
+ ],
+ [
+ 'name' => 'takuser_takrole',
+ 'description' => _('Team role'),
+ 'help' => 'takrole',
+ 'default' => 'Team Member',
+ 'values' => implode(", ", self::ROLE_TYPES),
+ 'example' => 'Team Member',
+ 'required' => true
+ ],
+ [
+ 'name' => 'takuser_takcolor',
+ 'description' => _('Team color'),
+ 'help' => 'takcolor',
+ 'default' => 'Cyan',
+ 'values' => implode(", ", self::COLOR_TYPES),
+ 'example' => 'Cyan',
+ 'required' => true
+ ],
+ ];
+ // available PDF fields
+ $return['PDF_fields'] = [
+ 'takcallsign' => _('Callsign'),
+ 'takrole' => _('Team role'),
+ 'takcolor' => _('Team color'),
+ ];
+ // profile options
+ $profileContainer = new htmlResponsiveRow();
+ $profileContainer->add(new htmlResponsiveInputField(_('Callsign'), 'takuser_takcallsign', null, 'takusercallsign'));
+ $profileContainer->add(new htmlResponsiveInputField(_('Team Role'), 'takuser_takrole', null, 'takuserrole'));
+ $profileContainer->add(new htmlResponsiveInputField(_('Team Color'), 'takuser_takcolor', null, 'takusercolor'));
+ $return['profile_options'] = $profileContainer;
+ // self-service field settings
+ $return['selfServiceFieldSettings'] = [
+ 'takcallsign' => _('Callsign'),
+ 'takrole' => _('Team role'),
+ 'takcolor' => _('Team color'),
+ ];
+ return $return;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function load_Messages() {
+ $this->messages['takcallsign'][0] = ['ERROR', _('Callsign'), _('Callsign contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_')];
+ $this->messages['takcallsign'][1] = ['ERROR', _('A TAK login with this callsign already exists. Please choose a different callsign.')];
+ $this->messages['takcallsign'][2] = ['ERROR', _('Callsign'), _('This field is required.')];
+ $this->messages['takcallsign'][3] = ['ERROR', _('Account %s:') . ' takuser_takcallsign', _('Callsign contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_')];
+ $this->messages['takrole'][0] = ['ERROR', _('Account %s:') . ' takuser_takrole', _('Please enter a valid option:') . ' ' . implode(', ', self::ROLE_TYPES)];
+ $this->messages['takcolor'][0] = ['ERROR', _('Account %s:') . ' takuser_takcolor', _('Please enter a valid option:') . ' ' . implode(', ', self::COLOR_TYPES)];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function display_html_attributes() {
+ $return = new htmlResponsiveRow();
+ // takCallsign
+ $this->addSimpleInputTextField($return, 'takcallsign', _('Callsign'), true);
+ // takRole
+ $takRole = $this->attributes['takrole'][0] ?? '';
+ $return->add(new htmlResponsiveSelect('takrole', self::ROLE_TYPES, [$takRole], _('Team role'), 'takrole'));
+ //takColor
+ $takColor = $this->attributes['takcolor'][0] ?? 'Cyan';
+ $return->add(new htmlResponsiveSelect('takcolor', self::COLOR_TYPES, [$takColor], _('Team color'), 'takcolor'));
+ return $return;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function process_attributes() {
+ $return = [];
+ $this->attributes['takcallsign'][0] = $_POST['takcallsign'];
+ $this->attributes['takrole'][0] = $_POST['takrole'];
+ $this->attributes['takcolor'][0] = $_POST['takcolor'];
+ // check if callsign is filled
+ if ($_POST['takcallsign'] == '') {
+ $return[] = $this->messages['takcallsign'][2];
+ }
+ elseif (!get_preg($_POST['takcallsign'], 'username')) {
+ $return[] = $this->messages['takcallsign'][0];
+ }
+ // check if callsign is unique
+ elseif (empty($this->orig['takcallsign'][0]) || ($this->attributes['takcallsign'][0] !== $this->orig['takcallsign'][0])) {
+ $suffix = $this->getAccountContainer()->get_type()->getSuffix();
+ $search = searchLDAP($suffix, 'takcallsign=' . $_POST['takcallsign'], ['dn']);
+ if (!empty($search)) {
+ $return[] = $this->messages['takcallsign'][1];
+ }
+ }
+ return $return;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @see baseModule::build_uploadAccounts()
+ */
+ public function build_uploadAccounts($rawAccounts, $ids, &$partialAccounts, $selectedModules, &$type) {
+ $errors = [];
+ for ($i = 0; $i < count($rawAccounts); $i++) {
+ // add object class
+ if (!in_array('takUser', $partialAccounts[$i]['objectClass'])) {
+ $partialAccounts[$i]['objectClass'][] = 'takUser';
+ }
+ $this->mapSimpleUploadField($rawAccounts, $ids, $partialAccounts, $i, 'takuser_takcallsign', 'takcallsign', 'username', $this->messages['takcallsign'][3], $errors);
+ $this->mapSimpleUploadField($rawAccounts, $ids, $partialAccounts, $i, 'takuser_takrole', 'takrole', null, null, $errors);
+ if (!in_array($partialAccounts[$i]['takrole'][0], self::ROLE_TYPES)) {
+ $errors[] = array_merge($this->messages['takrole'][0], [[$i]]);
+ }
+ $this->mapSimpleUploadField($rawAccounts, $ids, $partialAccounts, $i, 'takuser_takcolor', 'takcolor', null, null, $errors);
+ if (!in_array($partialAccounts[$i]['takcolor'][0], self::COLOR_TYPES)) {
+ $errors[] = array_merge($this->messages['takcolor'][0], [[$i]]);
+ }
+ }
+ return $errors;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @see baseModule::get_pdfEntries()
+ */
+ public function get_pdfEntries($pdfKeys, $typeId) {
+ $return = [];
+ $this->addSimplePDFField($return, 'takcallsign', _('Callsign'));
+ $this->addSimplePDFField($return, 'takrole', _('Team role'));
+ $this->addSimplePDFField($return, 'takcolor', _('Team color'));
+ return $return;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function load_profile($profile) {
+ // profile mappings in meta data
+ parent::load_profile($profile);
+ // special profile options
+ if (!empty($profile['takuser_takcallsign'][0])) {
+ $this->attributes['takcallsign'][0] = $profile['takuser_takcallsign'][0];
+ }
+ if (!empty($profile['takuser_takrole'][0])) {
+ $this->attributes['takrole'][0] = $profile['takuser_takrole'][0];
+ }
+ if (!empty($profile['takuser_takcolor'][0])) {
+ $this->attributes['takcolor'][0] = $profile['takuser_takcolor'][0];
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getSelfServiceOptions($fields, $attributes, $passwordChangeOnly, $readOnlyFields) {
+ $return = [];
+ if ($passwordChangeOnly) {
+ return $return; // only password fields as long no LDAP content can be read
+ }
+ if (!in_array_ignore_case('takUser', $attributes['objectClass'])) {
+ return $return;
+ }
+ $this->addSimpleSelfServiceTextField($return, 'takcallsign', _('Callsign'), $fields, $attributes, $readOnlyFields, true);
+ if (in_array('takrole', $fields)) {
+ $role = '';
+ if (isset($attributes['takrole'][0])) {
+ $role = $attributes['takrole'][0];
+ }
+ if (in_array('takrole', $readOnlyFields)) {
+ $field = new htmlOutputText($role);
+ }
+ else {
+ $selected = [];
+ if (!empty($attributes['takrole'][0])) {
+ $selected = [$attributes['takrole'][0]];
+ }
+ $field = new htmlSelect('takUser_takrole', self::ROLE_TYPES, $selected);
+ }
+ $return['takrole'] = new htmlResponsiveRow(
+ new htmlLabel('takUser_takrole', $this->getSelfServiceLabel('takrole', _('Team role'))), $field
+ );
+ }
+ if (in_array('takcolor', $fields)) {
+ $color = '';
+ if (isset($attributes['takcolor'][0])) {
+ $color = $attributes['takcolor'][0];
+ }
+ if (in_array('takcolor', $readOnlyFields)) {
+ $field = new htmlOutputText($color);
+ }
+ else {
+ $selected = [];
+ if (!empty($attributes['takcolor'][0])) {
+ $selected = [$attributes['takcolor'][0]];
+ }
+ $field = new htmlSelect('takUser_takcolor', self::COLOR_TYPES, $selected);
+ }
+ $return['takcolor'] = new htmlResponsiveRow(
+ new htmlLabel('takUser_takcolor', $this->getSelfServiceLabel('takcolor', _('Team color'))), $field
+ );
+ }
+ return $return;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function checkSelfServiceOptions($fields, $attributes, $passwordChangeOnly, $readOnlyFields) {
+ $return = ['messages' => [], 'add' => [], 'del' => [], 'mod' => [], 'info' => []];
+ if ($passwordChangeOnly) {
+ return $return; // skip processing if only a password change is done
+ }
+ if (!in_array_ignore_case('takUser', $attributes['objectClass'])) {
+ return $return;
+ }
+ $this->checkSimpleSelfServiceTextField($return, 'takcallsign', $attributes, $fields, $readOnlyFields, 'username', $this->messages['takcallsign'][0], $this->messages['takcallsign'][2]);
+ $this->checkSimpleSelfServiceTextField($return, 'takrole', $attributes, $fields, $readOnlyFields);
+ $this->checkSimpleSelfServiceTextField($return, 'takcolor', $attributes, $fields, $readOnlyFields);
+ return $return;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getListAttributeDescriptions(ConfiguredType $type): array {
+ return [
+ "takcallsign" => _("Callsign"),
+ "takrole" => _("Team role"),
+ "takcolor" => _("Team color"),
+ ];
+ }
+
+}
diff --git a/lam/templates/upload/massBuildAccounts.php b/lam/templates/upload/massBuildAccounts.php
index 8e437f837..71af045f7 100644
--- a/lam/templates/upload/massBuildAccounts.php
+++ b/lam/templates/upload/massBuildAccounts.php
@@ -13,7 +13,7 @@ use LamTemporaryFilesManager;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
- Copyright (C) 2004 - 2024 Roland Gruber
+ Copyright (C) 2004 - 2025 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
@@ -155,7 +155,7 @@ if ($_FILES['inputfile'] && ($_FILES['inputfile']['size'] > 0)) {
$checkcolumns = [];
$columns = [];
foreach ($uploadColumns as $uploadColumn) {
- $columns = array_merge($columns, $uploadColumns);
+ $columns = array_merge($columns, $uploadColumn);
}
foreach ($columns as $column) {
if (isset($column['required']) && ($column['required'] === true)) {
diff --git a/lam/tests/lib/AccountTest.php b/lam/tests/lib/AccountTest.php
index c3bcc63e4..ac1a95d5d 100644
--- a/lam/tests/lib/AccountTest.php
+++ b/lam/tests/lib/AccountTest.php
@@ -2,7 +2,7 @@
use PHPUnit\Framework\TestCase;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
- Copyright (C) 2018 - 2024 Roland Gruber
+ Copyright (C) 2018 - 2025 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
@@ -325,4 +325,13 @@ class AccountTest extends TestCase {
$this->assertFalse(get_preg('ab?c:80', 'hostAndPort'));
}
+ function testAreArrayContentsEqual() {
+ $this->assertTrue(areArrayContentsEqual([], []));
+ $this->assertFalse(areArrayContentsEqual(['1'], []));
+ $this->assertFalse(areArrayContentsEqual([], ['1']));
+ $this->assertTrue(areArrayContentsEqual(['a', 'b', 'c'], ['c', 'b', 'a']));
+ $this->assertFalse(areArrayContentsEqual(['a', 'b', 'c'], ['a', 'c', 'd']));
+ $this->assertFalse(areArrayContentsEqual(['a', 'b', 'c'], ['a', 'c']));
+ }
+
}
diff --git a/phpstan.neon b/phpstan.neon
index 5e35a2ff9..9f05bfdc6 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -27,7 +27,6 @@ parameters:
- '#Call to an undefined method object::.*#'
- '#Parameter \#2 \$string of function explode expects string, .* given.#'
- '#Parameter \#2 \$result of function ldap_.* expects LDAP\\Result, array\|LDAP\\Result given.#'
- - '#Cannot access an offset on mixed.#'
- '#Cannot access offset .* on mixed.#'
- '#Cannot access offset .* on array\|int.#'
- '#Cannot access an offset on array\|Countable.#'
@@ -47,4 +46,3 @@ parameters:
- '#Binary operation .* between .* and .* results in an error.#'
- '#Parameter \#. .* of (function|method) .* expects .*, mixed given.#'
- '#Cannot access property .* on mixed#'
- - '#Cannot use \+\+ on mixed.#'