diff --git a/lam/HISTORY b/lam/HISTORY index 773fbf34..73aba618 100644 --- a/lam/HISTORY +++ b/lam/HISTORY @@ -1,4 +1,5 @@ December 2016 5.6 + - New mechanism to replace wildcards in user edit screen. Personal/Unix support more wildcards like "$firstname". - Windows: added support for pager, otherPager, mobile, otherMobile, company and proxyAddresses (disabled by default in server profile) - Mail routing: enable for groups and allow to add/remove the extension - LAM Pro: diff --git a/lam/docs/manual-sources/howto.xml b/lam/docs/manual-sources/howto.xml index e25333f3..1b2d319b 100644 --- a/lam/docs/manual-sources/howto.xml +++ b/lam/docs/manual-sources/howto.xml @@ -1005,6 +1005,9 @@ Have fun! Mail routing: No longer added by default. Use profile editor to activate by default for new users/groups. + + Personal: no more replacement of $user/$group on user + upload
@@ -3306,6 +3309,12 @@ mysql> GRANT ALL PRIVILEGES ON lam_cron.* TO 'lam_cron'@'localhost'; Location + + labeledURI + + Web site + + mail/rfc822Mailbox @@ -3330,6 +3339,12 @@ mysql> GRANT ALL PRIVILEGES ON lam_cron.* TO 'lam_cron'@'localhost'; Organisation + + ou + + Organizational unit + + pager @@ -3422,6 +3437,85 @@ mysql> GRANT ALL PRIVILEGES ON lam_cron.* TO 'lam_cron'@'localhost'; + + Wildcards + + This module provides the following wildcards (others may be + provided by other modules): + + + + $firstname: First name + + + + $lastname: Last name + + + + $user: User name + + + + $commonname: Common name + + + + $email: Email address + + + + You can use them in the following input fields on user edit + screen: + + + + Common name + + + + Description + + + + Mail + + + + Postal address + + + + Registered address + + + + Web site + + + + Use this when some of your data always follows the same schema. + E.g. using "$firstname $lastname" in common name field can be used + like this to get "First Last". You can set the wildcards in profile + editor so they are automatically applied for new users. + + + + + + + + + + + + + + + + + +
diff --git a/lam/docs/manual-sources/images/mod_personal5.png b/lam/docs/manual-sources/images/mod_personal5.png new file mode 100644 index 00000000..35be0271 Binary files /dev/null and b/lam/docs/manual-sources/images/mod_personal5.png differ diff --git a/lam/docs/manual-sources/images/mod_personal6.png b/lam/docs/manual-sources/images/mod_personal6.png new file mode 100644 index 00000000..e3648113 Binary files /dev/null and b/lam/docs/manual-sources/images/mod_personal6.png differ diff --git a/lam/lib/baseModule.inc b/lam/lib/baseModule.inc index b0f5a7cb..4644664f 100644 --- a/lam/lib/baseModule.inc +++ b/lam/lib/baseModule.inc @@ -1951,6 +1951,16 @@ abstract class baseModule { return ($this->moduleSettings[$optionName][0] == 'true'); } + /** + * Returns a list of wildcards that can be replaced in input fileds. + * E.g. "$firstname$" is replaced with "givenName" attribute value. + * + * @return array replacements as wildcard => value + */ + public function getWildCardReplacements() { + return array(); + } + } /** diff --git a/lam/lib/modules.inc b/lam/lib/modules.inc index f17cf003..e54bb1b1 100644 --- a/lam/lib/modules.inc +++ b/lam/lib/modules.inc @@ -2143,6 +2143,62 @@ class accountContainer { return $this->cachedOUs; } + /** + * Replaces POST data with wildcard values from modules. + * + * @param array $keyPrefixes POST keys as full name or prefix (e.g. "key" matches "key1") + */ + public function replaceWildcardsInPOST($keyPrefixes) { + $replacements = array(); + foreach ($this->module as $module) { + $replacements = array_merge($replacements, $module->getWildCardReplacements()); + } + while (true) { + if (!$this->replaceWildcards($replacements, $keyPrefixes, $_POST)) { + break; + } + } + } + + /** + * Replaces wildcards in an array. + * + * @param array $replacements replacements (key => value) + * @param array $keyPrefixes prefixes of $data array keys that should be replaced + * @param array $data data array + * @return boolean replacement done + */ + private function replaceWildcards($replacements, $keyPrefixes, &$data) { + $found = false; + foreach ($data as $key => $value) { + foreach ($keyPrefixes as $keyPrefix) { + if (strpos($key, $keyPrefix) === 0) { + $found = $this->doReplace($replacements, $data[$key]) || $found; + } + } + } + return $found; + } + + /** + * Replaces wildcards in a value. + * + * @param array $replacements replacements (key => value) + * @param String $value value to perform replacements + * @return boolean replacement done + */ + private function doReplace($replacements, &$value) { + $found = false; + foreach ($replacements as $replKey => $replValue) { + $searchString = '$' . $replKey; + if (strpos($value, $searchString) !== false) { + $found = true; + $value = str_replace($searchString, $replValue, $value); + } + } + return $found; + } + /** * Encrypts sensitive data before storing in session. * diff --git a/lam/lib/modules/inetOrgPerson.inc b/lam/lib/modules/inetOrgPerson.inc index 5e124534..15df66e0 100644 --- a/lam/lib/modules/inetOrgPerson.inc +++ b/lam/lib/modules/inetOrgPerson.inc @@ -171,6 +171,9 @@ class inetOrgPerson extends baseModule implements passwordService { 'ou', 'description', 'uid'); // profile elements $profileElements = array(); + if (!$this->isUnixActive()) { + $profileElements[] = new htmlTableExtendedInputField(_('Common name'), 'inetOrgPerson_cn', null, 'cn'); + } if (!$this->isBooleanConfigOptionSet('inetOrgPerson_hideInitials')) { $profileElements[] = new htmlTableExtendedInputField(_('Initials'), 'inetOrgPerson_initials', null, 'initials'); } @@ -243,6 +246,9 @@ class inetOrgPerson extends baseModule implements passwordService { $return['profile_options'] = $profileContainer; } // profile checks and mappings + if (!$this->isUnixActive()) { + $return['profile_mappings']['inetOrgPerson_cn'] = 'cn'; + } if (!$this->isBooleanConfigOptionSet('inetOrgPerson_hideInitials')) { $return['profile_mappings']['inetOrgPerson_initials'] = 'initials'; } @@ -740,12 +746,11 @@ class inetOrgPerson extends baseModule implements passwordService { ), 'mail' => array ( "Headline" => _("Email address"), 'attr' => 'mail', - "Text" => _("The user's email address.") . ' ' . _('You can use "$user", "$firstname" and "$lastname" as wildcards for user name, first and last name.') + "Text" => _("The user's email address.") ), 'mailList' => array ( "Headline" => _("Email address"), 'attr' => 'mail', "Text" => _("The user's email address.") . ' ' . _('Multiple values are separated by semicolon.') - . ' ' . _('You can use "$user", "$firstname" and "$lastname" as wildcards for user name, first and last name.') ), "mailPassword" => array ( "Headline" => _("Send password via mail"), @@ -934,22 +939,6 @@ class inetOrgPerson extends baseModule implements passwordService { if (!$this->getAccountContainer()->isNewAccount && !in_array('inetOrgPerson', $this->getAccountContainer()->attributes_orig['objectClass'])) { return array(); } - // replace $user in email - if (!empty($this->attributes['mail'][0])) { - $user = null; - if ($this->getAccountContainer()->getAccountModule('posixAccount') != null) { - $attrs = $this->getAccountContainer()->getAccountModule('posixAccount')->getAttributes(); - $user = $attrs['uid'][0]; - } - elseif (!empty($this->attributes['uid'][0])) { - $user = $this->attributes['uid'][0]; - } - if (!empty($user)) { - foreach ($this->attributes['mail'] as &$mail) { - $mail = str_replace('$user', $user, $mail); - } - } - } $return = $this->getAccountContainer()->save_module_attributes($this->attributes, $this->orig); // postalAddress, registeredAddress, facsimileTelephoneNumber and jpegPhoto need special removing if (isset($return[$this->getAccountContainer()->dn_orig]['remove']['postalAddress'])) { @@ -1037,7 +1026,12 @@ class inetOrgPerson extends baseModule implements passwordService { */ function process_attributes() { $errors = array(); - $replacements = array('$lastname' => 'sn', '$firstname' => 'givenName'); + $keysToReplace = array('mail', 'description', 'postalAddress', 'cn', + 'registeredAddress', 'labeledURI'); + if ($this->isUnixActive()) { + $keysToReplace[] = 'uid'; + } + $this->getAccountContainer()->replaceWildcardsInPOST($keysToReplace); // add parent object classes if ($this->getAccountContainer()->isNewAccount) { if (!in_array('organizationalPerson', $this->attributes['objectClass'])) { @@ -1069,11 +1063,6 @@ class inetOrgPerson extends baseModule implements passwordService { $this->processMultiValueInputTextField('mail', $errors, 'email'); if (!empty($this->attributes['mail'])) { foreach ($this->attributes['mail'] as &$mail) { - foreach ($replacements as $wildcard => $key) { - if (!empty($this->attributes[$key][0])) { - $mail = str_replace($wildcard, $this->attributes[$key][0], $mail); - } - } if (empty($this->orig['mail']) || !in_array($mail, $this->orig['mail'])) { if ($this->emailExists($mail)) { $msg = $this->messages['mail'][1]; @@ -2178,7 +2167,6 @@ class inetOrgPerson extends baseModule implements passwordService { */ function build_uploadAccounts($rawAccounts, $ids, &$partialAccounts, $selectedModules) { $errors = array(); - $replacements = array(); // get list of existing users $existingUsers = searchLDAPByAttribute('uid', '*', 'inetOrgPerson', array('uid'), array('user')); for ($e = 0; $e < sizeof($existingUsers); $e++) { @@ -2189,7 +2177,6 @@ class inetOrgPerson extends baseModule implements passwordService { // last name if (get_preg($rawAccounts[$i][$ids['inetOrgPerson_lastName']], 'realname')) { $partialAccounts[$i]['sn'] = trim($rawAccounts[$i][$ids['inetOrgPerson_lastName']]); - $replacements['$lastname'] = $partialAccounts[$i]['sn']; } else { $errMsg = $this->messages['lastname'][1]; @@ -2200,7 +2187,6 @@ class inetOrgPerson extends baseModule implements passwordService { if ($rawAccounts[$i][$ids['inetOrgPerson_firstName']] != "") { if (get_preg($rawAccounts[$i][$ids['inetOrgPerson_firstName']], 'realname')) { $partialAccounts[$i]['givenName'] = trim($rawAccounts[$i][$ids['inetOrgPerson_firstName']]); - $replacements['$firstname'] = $partialAccounts[$i]['givenName']; } else { $errMsg = $this->messages['givenName'][1]; @@ -2218,7 +2204,6 @@ class inetOrgPerson extends baseModule implements passwordService { } elseif (get_preg($rawAccounts[$i][$ids['inetOrgPerson_userName']], 'username')) { $partialAccounts[$i]['uid'] = $rawAccounts[$i][$ids['inetOrgPerson_userName']]; - $replacements['$user'] = $partialAccounts[$i]['uid']; } else { $errMsg = $this->messages['uid'][1]; @@ -2300,9 +2285,6 @@ class inetOrgPerson extends baseModule implements passwordService { $this->mapSimpleUploadField($rawAccounts, $ids, $partialAccounts, $i, 'inetOrgPerson_pager', 'pager', 'telephone', $this->messages['pager'][1], $errors, '/;[ ]*/'); // eMail if (isset($ids['inetOrgPerson_email']) && ($rawAccounts[$i][$ids['inetOrgPerson_email']] != "")) { - foreach ($replacements as $wildcard => $value) { - $rawAccounts[$i][$ids['inetOrgPerson_email']] = str_replace($wildcard, $value, $rawAccounts[$i][$ids['inetOrgPerson_email']]); - } $mailList = preg_split('/;[ ]*/', trim($rawAccounts[$i][$ids['inetOrgPerson_email']])); $partialAccounts[$i]['mail'] = $mailList; for ($x = 0; $x < sizeof($mailList); $x++) { @@ -3810,6 +3792,54 @@ class inetOrgPerson extends baseModule implements passwordService { return in_array('posixAccount', $modules); } + /** + * Returns a list of wildcards that can be replaced in input fileds. + * E.g. "$firstname$" is replaced with "givenName" attribute value. + * + * @return array replacements as wildcard => value + */ + public function getWildCardReplacements() { + $replacements = array(); + // first name + if (!empty($_POST['givenName'])) { + $replacements['firstname'] = $_POST['givenName']; + } + elseif (!empty($this->attributes['givenName'][0])) { + $replacements['firstname'] = $this->attributes['givenName'][0]; + } + // last name + if (!empty($_POST['sn'])) { + $replacements['lastname'] = $_POST['sn']; + } + elseif (!empty($this->attributes['sn'][0])) { + $replacements['lastname'] = $this->attributes['sn'][0]; + } + // user name + if (!$this->isUnixActive()) { + if (!empty($_POST['uid'])) { + $replacements['user'] = $_POST['uid']; + } + elseif (!empty($this->attributes['uid'][0])) { + $replacements['user'] = $this->attributes['uid'][0]; + } + } + // cn + if (!empty($_POST['cn_0'])) { + $replacements['commonname'] = $_POST['cn_0']; + } + elseif (!empty($this->attributes['cn'][0])) { + $replacements['commonname'] = $this->attributes['cn'][0]; + } + // mail + if (!empty($_POST['mail_0'])) { + $replacements['email'] = $_POST['mail_0']; + } + elseif (!empty($this->attributes['mail'][0])) { + $replacements['email'] = $this->attributes['mail'][0]; + } + return $replacements; + } + } ?> diff --git a/lam/lib/modules/posixAccount.inc b/lam/lib/modules/posixAccount.inc index 4ac81d64..04f396a4 100644 --- a/lam/lib/modules/posixAccount.inc +++ b/lam/lib/modules/posixAccount.inc @@ -471,7 +471,7 @@ class posixAccount extends baseModule implements passwordService { ), 'homeDirectory' => array( "Headline" => _("Home directory"), 'attr' => $this->getHomedirAttrName(), - "Text" => _('$user and $group will be replaced with user name and primary group name.') + "Text" => _("Please enter the path to the user's home directory.") ), 'userPassword' => array( "Headline" => _("Password"), @@ -990,6 +990,8 @@ class posixAccount extends baseModule implements passwordService { * @return array list of info/error messages */ function process_attributes() { + $keysToReplace = array('cn', 'gecos', 'homeDirectory'); + $this->getAccountContainer()->replaceWildcardsInPOST($keysToReplace); $errors = array(); if (isset($_POST['addObjectClass'])) { if (!isset($this->attributes['objectClass'])) { @@ -1160,11 +1162,6 @@ class posixAccount extends baseModule implements passwordService { if ($this->get_scope()=='host') $errors[] = $this->messages['uid'][6]; } if ($this->get_scope()=='user') { - $this->attributes[$homedirAttrName][0] = str_replace('$group', $this->getGroupName($this->attributes['gidNumber'][0]), $this->attributes[$homedirAttrName][0]); - if ($this->attributes['uid'][0] != '') { - $this->attributes[$homedirAttrName][0] = str_replace('$user', $this->attributes['uid'][0], $this->attributes[$homedirAttrName][0]); - } - if ($this->attributes[$homedirAttrName][0] != $_POST['homeDirectory']) $errors[] = array('INFO', _('Home directory'), _('Replaced $user or $group in homedir.')); // Check if Username contains only valid characters if (!get_preg($this->attributes['uid'][0], 'username')) $errors[] = $this->messages['uid'][2]; @@ -3459,6 +3456,31 @@ class posixAccount extends baseModule implements passwordService { return $this->isBooleanConfigOptionSet('posixAccount_noObjectClass'); } + /** + * Returns a list of wildcards that can be replaced in input fileds. + * E.g. "$firstname$" is replaced with "givenName" attribute value. + * + * @return array replacements as wildcard => value + */ + public function getWildCardReplacements() { + $replacements = array(); + // user name + if (!empty($_POST['uid'])) { + $replacements['user'] = $_POST['uid']; + } + elseif (!empty($this->attributes['uid'][0])) { + $replacements['user'] = $this->attributes['uid'][0]; + } + // group name + if (!empty($_POST['gidNumber'])) { + $replacements['group'] = $this->getGroupName($_POST['gidNumber']); + } + elseif (!empty($this->attributes['gidNumber'][0])) { + $replacements['group'] = $this->getGroupName($this->attributes['gidNumber'][0]); + } + return $replacements; + } + } ?>