From 5b9023a88ce3789619634614a89c5f8049392dcb Mon Sep 17 00:00:00 2001 From: Ben Klang Date: Thu, 17 Dec 2009 14:59:25 -0500 Subject: [PATCH] Other half of the changes from Daniel Collins (missed from the last commit) --- vilma/lib/Driver.php | 76 +++++++++ vilma/lib/Driver/qmailldap.php | 357 +++++++++++++++++++++++++++++++++------ vilma/templates/users/index.html | 6 + vilma/users/delete.php | 35 +++- vilma/users/index.php | 48 +++++- 5 files changed, 459 insertions(+), 63 deletions(-) diff --git a/vilma/lib/Driver.php b/vilma/lib/Driver.php index 240dc50b6..14b4f858a 100644 --- a/vilma/lib/Driver.php +++ b/vilma/lib/Driver.php @@ -6,6 +6,7 @@ * did not receive this file, see http://cvs.horde.org/co.php/vilma/LICENSE. * * @author Marko Djukic + * @author Daniel Collins * @package Vilma */ class Vilma_Driver { @@ -94,6 +95,7 @@ class Vilma_Driver { */ function getAddressInfo($address, $type = 'all') { + Horde::logMessage("Get Addresses Called for $domain with type $type and key $key", __FILE__, __LINE__, PEAR_LOG_DEBUG); $domain = Vilma::stripDomain($address); $addresses = $this->getAddresses($domain, $type); foreach($addresses as $addrinfo) { @@ -156,6 +158,80 @@ class Vilma_Driver { return $status; } + /* Saves or creates alias records for a given user. + * + * @param array info The info used to store the information. + * Required fields are: + * 'address' => The destination address (used for LDAP ID lookup) + * 'alias_address' => The alias to create or the new data for the modified entry + * 'alias' => The alias we are modifying, if we are modifying an existing one. + */ + function saveAlias(&$info) + { + Horde::logMessage("saveAlias called with info: " . print_r($info, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); + $result = $this->_saveAlias($info); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + return true; + } + + /* Deletes alias records for a given user. + * + * @param array info The info used to store the information. + * Required fields are: + * 'address' => The destination address (used for LDAP ID lookup) + * 'alias' => The alias we are deleting. + */ + function deleteAlias(&$info) + { + Horde::logMessage("deleteAlias called with info: " . print_r($info, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); + $result = $this->_deleteAlias($info); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + return true; + } + + /* Saves or creates forward records for a given user. + * + * @param array info The info used to store the information. + * Required fields are: + * 'address' => The destination address (used for LDAP ID lookup) + * 'forward_address' => The forward to create or the new data for the modified entry + * 'forward' => The forward we are modifying, if we are modifying an existing one. + */ + function saveForward(&$info) + { + Horde::logMessage("saveForward called with info: " . print_r($info, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); + $result = $this->_saveForward($info); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + return true; + } + + /* Deletes forward records for a given user. + * + * @param array info The info used to store the information. + * Required fields are: + * 'address' => The destination address (used for LDAP ID lookup) + * 'forward' => The forward we are deleting. + */ + function deleteForward(&$info) + { + Horde::logMessage("deleteForward called with info: " . print_r($info, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); + $result = $this->_deleteForwrd($info); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + return true; + } + function saveUser(&$info) { $create = false; diff --git a/vilma/lib/Driver/qmailldap.php b/vilma/lib/Driver/qmailldap.php index 9dea67b15..3ff99f28c 100644 --- a/vilma/lib/Driver/qmailldap.php +++ b/vilma/lib/Driver/qmailldap.php @@ -114,6 +114,7 @@ class Vilma_Driver_qmailldap extends Vilma_Driver { function getAddresses($domain, $type = 'all', $key = 'user_name', $direction = 0) { + Horde::logMessage("Get Addresses Called for $domain with type $type and key $key", __FILE__, __LINE__, PEAR_LOG_DEBUG); $addresses = array(); if ($type == 'all' || $type == 'user') { $users = $this->_getUsers($domain); @@ -483,6 +484,7 @@ class Vilma_Driver_qmailldap extends Vilma_Driver { */ function getAddressInfo($address, $type = 'all') { + Horde::logMessage("Get Addresses Called for $address with type $type and key $key", __FILE__, __LINE__, PEAR_LOG_DEBUG); if ($type != 'alias') { return parent::getAddressInfo($address, $type); } else { @@ -493,7 +495,7 @@ class Vilma_Driver_qmailldap extends Vilma_Driver { // Add each objectClass from parameters $filter .= '(objectClass=' . $objectclass . ')'; } - $filter .= '(mailAlternateAddress= ' . $address . ')'; + $filter .= '(mailAlternateAddress=' . $address . ')'; $filter .= ')'; // End filter Horde::logMessage($filter, __FILE__, __LINE__, PEAR_LOG_DEBUG); $res = @ldap_search($this->_ldap, $this->_ldapparams['basedn'], $filter); @@ -668,7 +670,7 @@ class Vilma_Driver_qmailldap extends Vilma_Driver { return PEAR::raiseError(_("Unable to acquire handle on DN. Aborting delete operation.")); } else if($res['count'] !== 1) { return PEAR::raiseError(_("More than one DN returned. Aborting delete operation.")); - } + } return $res; } @@ -716,65 +718,322 @@ class Vilma_Driver_qmailldap extends Vilma_Driver { } /** - * Modifies alias data on the backend. + * Modifies alias data on the backend. See Driver::saveAlias() for parameter info. * * @param mixed $info The alias, or an array containing the alias and supporting data. - * @param string $mode The operation requested: add, update, delete. * * @return mixed True on success or PEAR error otherwise. */ - function _savealias($info,$mode = null) + function _saveAlias($info) { - if ($mode == 'delete') { - $address = $info; - - $user_info = $this->searchForAliases($address); - $aliasesList = $user_info[0]['mailalternateaddress']; - if (is_a($user_info, 'PEAR_Error') || ($res['count'] === 0) ) { - return PEAR::raiseError(_("Error reading address information from backend.")); - } - $addrinfo = $this->getAddressInfo($address); - if (is_a($addrinfo, 'PEAR_Error')) { - return $addrinfo; + Horde::logMessage("_saveAlias called with info: " . print_r($info, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); + $address = $info['address']; + if (!empty($info['alias'])) { + $alias = $info['alias']; + $create = false; + } else { + $create = true; + } // if + $alias_address = $info['alias_address']; + + $user_res = $this->searchForUser($address); + if (is_a($user_res, 'PEAR_Error') || ($res['count'] === 0) ) { + return PEAR::raiseError(_("Error reading address information from backend.")); + } // if + $user = $user_res[0]; + + // Retrieve the current MAA values + if (array_key_exists('mailalternateaddress', $user_res[0])) { + $maa = $user['mailalternateaddress']; + unset($maa['count']); + } else { + $maa = array(); + } // if + + Horde::logMessage("Resource contains: " . print_r($maa, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); + + $update = false; + $oldmaa = $maa; + if ($create) { + // Verify that it does not already exist + if (in_array($alias_address, $maa) === false) { + // Not there, we create it + // return PEAR::raiseError(_("We would create a new entry here.")); + $maa[] = $alias_address; + // usort($maa, "compareEmailSort"); + sort($maa); + $update = true; + } else { + // Already exists, throw a notification + return PEAR::raiseError(_("That alias already exists!")); + } // if + + } else { + if ($alias == $alias_address) { + /* do nothing */; + } else { + $key = array_search($alias, $maa); + if ($key > 0 || $key === 0) { + $maa[$key] = $alias_address; + // usort($maa, "compareEmailSort"); + sort($maa); + $update = true; + } else { + return PEAR::raiseError(sprintf(_("Existing entry \"%s\" could not be found: " . print_r($key, true)), $alias)); + } // if + } + } // if + + + if ($update) { + $dn = $user['dn']; + Horde::logMessage("UPDATING: $dn \nOld MAA: " . print_r($oldmaa, true) . "\nNew MAA: " . print_r($maa, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); + // return PEAR::raiseError(sprintf(_("Update Code Not Written."), $alias)); + if ($this->_ldap) { + // bind with appropriate dn to give update access + $res = ldap_bind($this->_ldap, $this->_ldapparams['binddn'], + $this->_ldapparams['bindpw']); + if (!$res) { + return PEAR::raiseError(_("Unable to bind to the LDAP server. Check authentication credentials.")); } - $type = $addrinfo['type']; - $addrinfo = $this->getAddressInfo($address,$type); - if (is_a($addrinfo, 'PEAR_Error')) { - return $addrinfo; + $entry["mailAlternateAddress"] = $maa; + + $res = @ldap_modify($this->_ldap, $dn, $entry); + if ($res === false) { + return PEAR::raiseError(sprintf(_("Error modifying account: %s"), @ldap_error($this->_ldap))); + } else { + return TRUE; + } // if + } // if + } // if + + return true; + } + + function _deleteAlias($info) + { + Horde::logMessage("_deleteAlias called with info: " . print_r($info, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); + $address = $info['address']; + $alias = $info['alias']; + + $user_res = $this->searchForUser($address); + if (is_a($user_res, 'PEAR_Error') || ($res['count'] === 0) ) { + return PEAR::raiseError(_("Error reading address information from backend.")); + } // if + $user = $user_res[0]; + + // Retrieve the current MAA values + if (array_key_exists('mailalternateaddress', $user_res[0])) { + $maa = $user['mailalternateaddress']; + unset($maa['count']); + } else { + $maa = array(); + } // if + + Horde::logMessage("Resource contains: " . print_r($maa, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); + + $update = false; + $oldmaa = $maa; + $key = array_search($alias, $maa); + if ($key > 0 || $key === 0) { + unset($maa[$key]); + sort($maa); + $update = true; + } else { + /* skip */; + } // if + + if ($update) { + $dn = $user['dn']; + Horde::logMessage("UPDATING: $dn \nOld MAA: " . print_r($oldmaa, true) . "\nNew MAA: " . print_r($maa, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); + // return PEAR::raiseError(sprintf(_("Update Code Not Written."), $alias)); + if ($this->_ldap) { + // bind with appropriate dn to give update access + $res = ldap_bind($this->_ldap, $this->_ldapparams['binddn'], + $this->_ldapparams['bindpw']); + if (!$res) { + return PEAR::raiseError(_("Unable to bind to the LDAP server. Check authentication credentials.")); } - $objectClassData = null; - if(isset($user_info[0]['objectclass'])) { - $objectClassData = $user_info[0]['objectclass']; + $entry["mailAlternateAddress"] = $maa; + + $res = @ldap_modify($this->_ldap, $dn, $entry); + if ($res === false) { + return PEAR::raiseError(sprintf(_("Error modifying account: %s"), @ldap_error($this->_ldap))); + } else { + return TRUE; + } // if + } // if + } // if + + return true; + } + + /** + * Modifies forward data on the backend. See Driver::saveForward() for parameter info. + * + * @param mixed $info An array containing the alias and supporting data. + * + * @return mixed True on success or PEAR error otherwise. + */ + function _saveForward($info) + { + Horde::logMessage("_saveForward called with info: " . print_r($info, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); + $address = $info['address']; + if (!empty($info['forward'])) { + $forward = $info['forward']; + $create = false; + } else { + $create = true; + } // if + $forward_address = $info['forward_address']; + + $user_res = $this->searchForUser($address); + if (is_a($user_res, 'PEAR_Error') || ($res['count'] === 0) ) { + return PEAR::raiseError(_("Error reading address information from backend.")); + } // if + $user = $user_res[0]; + + // Retrieve the current MAA values + if (array_key_exists('mailforwardingaddress', $user_res[0])) { + $mfa = $user['mailforwardingaddress']; + unset($mfa['count']); + } else { + $mfa = array(); + } // if + + Horde::logMessage("Resource contains: " . print_r($mfa, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); + + $update = false; + $oldmfa = $mfa; + if ($create) { + // Verify that it does not already exist + if (in_array($forward_address, $mfa) === false) { + // Not there, we create it + // return PEAR::raiseError(_("We would create a new entry here.")); + $mfa[] = $forward_address; + // usort($mfa, "compareEmailSort"); + sort($mfa); + $update = true; + } else { + // Already exists, throw a notification + return PEAR::raiseError(sprintf(_("That forward, \"%s\", already exists!"), $forward_address)); + } // if + + } else { + if ($forward == $forward_address) { + /* do nothing */; + } else { + $key = array_search($forward, $mfa); + if ($key > 0 || $key === 0) { + $mfa[$key] = $forward_address; + // usort($mfa, "compareEmailSort"); + sort($mfa); + $update = true; + } else { + return PEAR::raiseError(sprintf(_("Existing entry \"%s\" could not be found: " . print_r($key, true)), $forward)); + } // if + } + } // if + + + if ($update) { + $dn = $user['dn']; + Horde::logMessage("UPDATING: $dn \nOld MFA: " . print_r($oldmfa, true) . "\nNew MFA: " . print_r($mfa, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); + // return PEAR::raiseError(sprintf(_("Update Code Not Written."), $alias)); + if ($this->_ldap) { + // bind with appropriate dn to give update access + $res = ldap_bind($this->_ldap, $this->_ldapparams['binddn'], + $this->_ldapparams['bindpw']); + if (!$res) { + return PEAR::raiseError(_("Unable to bind to the LDAP server. Check authentication credentials.")); } - if ($this->_ldap) { - // bind with appropriate dn to give update access - $res = ldap_bind($this->_ldap, $this->_ldapparams['binddn'], - $this->_ldapparams['bindpw']); - if (!$res) { - return PEAR::raiseError(_("Unable to bind to the LDAP server. Check authentication credentials.")); - } - $tmp = array(); - $key = null; - foreach($aliasesList as $key => $val) { - if($val != $address) { - array_push($tmp,$val); - } - } - $entry["mailalternateaddress"] = $tmp; - - $rdn = 'mail=' . $addrinfo['destination']; - $dn = $rdn . ',' . $this->_ldapparams['basedn']; - $res = @ldap_modify($this->_ldap, $dn, $entry); - if ($res === false) { - return PEAR::raiseError(sprintf(_("Error modifying account: %s"), @ldap_error($this->_ldap))); - } else { - return TRUE; - } + $entry["mailForwardingAddress"] = $mfa; + + $res = @ldap_modify($this->_ldap, $dn, $entry); + if ($res === false) { + return PEAR::raiseError(sprintf(_("Error modifying account: %s"), @ldap_error($this->_ldap))); + } else { + return TRUE; + } // if + } // if + } // if + + return true; + } + + /** + * Deletes forward data on the backend. See Driver::deleteForward() for parameter info. + * + * @param mixed $info An array containing the forward and supporting data. + * + * @return mixed True on success or PEAR error otherwise. + */ + function _deleteForward($info) + { + Horde::logMessage("_deleteForward called with info: " . print_r($info, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); + $address = $info['address']; + $forward = $info['forward']; + + $user_res = $this->searchForUser($address); + if (is_a($user_res, 'PEAR_Error') || ($res['count'] === 0) ) { + return PEAR::raiseError(_("Error reading address information from backend.")); + } // if + $user = $user_res[0]; + + // Retrieve the current MFA values + if (array_key_exists('mailforwardingaddress', $user_res[0])) { + $mfa = $user['mailforwardingaddress']; + unset($mfa['count']); + } else { + $mfa = array(); + } // if + + Horde::logMessage("Resource contains: " . print_r($mfa, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); + + $update = false; + $oldmfa = $mfa; + $key = array_search($forward, $mfa); + if ($key > 0 || $key === 0) { + unset($mfa[$key]); + sort($mfa); + $update = true; + } else { + /* skip */; + } // if + + if ($update) { + $dn = $user['dn']; + Horde::logMessage("UPDATING: $dn \nOld MFA: " . print_r($oldmfa, true) . "\nNew MFA: " . print_r($mfa, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); + // return PEAR::raiseError(sprintf(_("Update Code Not Written."), $alias)); + if ($this->_ldap) { + // bind with appropriate dn to give update access + $res = ldap_bind($this->_ldap, $this->_ldapparams['binddn'], + $this->_ldapparams['bindpw']); + if (!$res) { + return PEAR::raiseError(_("Unable to bind to the LDAP server. Check authentication credentials.")); } - } + $entry["mailForwardingAddress"] = $mfa; + + $res = @ldap_modify($this->_ldap, $dn, $entry); + if ($res === false) { + return PEAR::raiseError(sprintf(_("Error modifying account: %s"), @ldap_error($this->_ldap))); + } else { + return TRUE; + } // if + } // if + } // if - return PEAR::raiseError(_("Unable to save user information.")); + return true; } + + /* Sorting function to sort aliases, forwards, and accounts by domain name first, + * then by user component. + */ + function compareEmailSort($a, $b) { + $a_comp = split("@", $a); + $b_comp = split("@", $b); + // not finished. + } function _saveUser(&$info) { diff --git a/vilma/templates/users/index.html b/vilma/templates/users/index.html index f4dad378d..68a2d93e4 100644 --- a/vilma/templates/users/index.html +++ b/vilma/templates/users/index.html @@ -29,6 +29,12 @@ + + +Alias + + + +Forward + diff --git a/vilma/users/delete.php b/vilma/users/delete.php index 80c9c166f..d44ca5fd8 100644 --- a/vilma/users/delete.php +++ b/vilma/users/delete.php @@ -80,14 +80,14 @@ $domain = $vilma_driver->getDomainByName($domain); $vars->set('domain', $domain); $vars->set('mode', 'edit'); -$form = new Horde_Form($vars, _("Delete User")); +$form = new Horde_Form($vars, _("Delete " . $type)); /* Set up the form. */ $form->setButtons(array(_("Delete"), _("Do not delete"))); //$form->addHidden($user_id, 'user_id', 'text', false); $form->addHidden($address['address'], 'address', 'text', false); $form->addHidden($section, 'section', 'text', false); -$desc = "Delete user \"%s\""; +$desc = "Delete $type \"%s\""; $sub = " and all dependencies?"; $tot = $aliasesCount + $groupsCount + $forwardsCount; if($tot > 0) { @@ -109,15 +109,38 @@ if ($vars->get('submitbutton') == _("Delete")) { if($type == 'alias') { if ($form->validate($vars)) { $form->getInfo($vars, $info); - $delete = $vilma_driver->_savealias($address['address'],'delete'); + $deleteInfo = array(); + $deleteInfo['address'] = $address['destination']; + $deleteInfo['alias'] = $user_id; + $delete = $vilma_driver->deleteAlias($deleteInfo); if (is_a($delete, 'PEAR_Error')) { Horde::logMessage($delete, __FILE__, __LINE__, PEAR_LOG_ERR); - $notification->push(sprintf(_("Error deleting user. %s."), $delete->getMessage()), 'horde.error'); + $notification->push(sprintf(_("Error deleting alias. %s."), $delete->getMessage()), 'horde.error'); + $url = Util::addParameter(Horde::applicationUrl('users/index.php'), 'domain_id', $domain['id'], false); + header('Location: ' . $url); + exit; + } else { + $notification->push(_("Alias deleted."), 'horde.success'); + $url = Util::addParameter(Horde::applicationUrl('users/index.php'), 'domain_id', $domain['id'], false); + header('Location: ' . $url); + exit; + } + } + } elseif ($type == 'forward') { + if ($form->validate($vars)) { + $form->getInfo($vars, $info); + $deleteInfo = array(); + $deleteInfo['address'] = $address['destination']; + $deleteInfo['forward'] = $user_id; + $delete = $vilma_driver->deleteForward($deleteInfo); + if (is_a($delete, 'PEAR_Error')) { + Horde::logMessage($delete, __FILE__, __LINE__, PEAR_LOG_ERR); + $notification->push(sprintf(_("Error deleting forward. %s."), $delete->getMessage()), 'horde.error'); $url = Horde_Util::addParameter(Horde::applicationUrl('users/index.php'), 'domain_id', $domain['id'], false); header('Location: ' . $url); exit; } else { - $notification->push(_("User deleted."), 'horde.success'); + $notification->push(_("Forward deleted."), 'horde.success'); $url = Horde_Util::addParameter(Horde::applicationUrl('users/index.php'), 'domain_id', $domain['id'], false); header('Location: ' . $url); exit; @@ -135,7 +158,7 @@ if ($vars->get('submitbutton') == _("Delete")) { header('Location: ' . $url); exit; } else { - $notification->push(_("User deleted."), 'horde.success'); + $notification->push(_("$type deleted."), 'horde.success'); $url = Horde_Util::addParameter(Horde::applicationUrl('users/index.php'), 'domain_id', $domain['id'], false); header('Location: ' . $url); exit; diff --git a/vilma/users/index.php b/vilma/users/index.php index e0808975e..a3438b7d2 100644 --- a/vilma/users/index.php +++ b/vilma/users/index.php @@ -51,12 +51,34 @@ $types = Vilma::getUserMgrTypes(); foreach ($addresses as $i => $address) { $type = $address['type']; $id = $address['id']; - $url = Horde::applicationUrl('users/edit.php'); - $url = Horde_Util::addParameter($url, 'address', $id); + if($type === 'alias') { - $addresses[$i]['edit_url'] = ''; + $url = Horde::applicationUrl('users/editAlias.php'); + $url = Util::addParameter($url, 'alias', $id); + $url = Util::addParameter($url, 'section', $section); + $addresses[$i]['edit_url'] = $url; + $addresses[$i]['add_alias_url'] = false; + $addresses[$i]['add_forward_url'] = false; + } elseif($type === 'forward') { + $url = Horde::applicationUrl('users/editForward.php'); + $url = Util::addParameter($url, 'forward', $id); + $url = Util::addParameter($url, 'section', $section); + $addresses[$i]['edit_url'] = $url; + $addresses[$i]['add_alias_url'] = false; + $addresses[$i]['add_forward_url'] = false; } else { - $addresses[$i]['edit_url'] = Horde_Util::addParameter($url, 'section', $section); + $url = Horde::applicationUrl('users/edit.php'); + $url = Util::addParameter($url, 'address', $id); + $url = Util::addParameter($url, 'section', $section); + $addresses[$i]['edit_url'] = $url; + $url = Horde::applicationURL('users/editAlias.php'); + $url = Util::addParameter($url, 'address', $id); + $url = Util::addParameter($url, 'section', $section); + $addresses[$i]['add_alias_url'] = $url; + $url = Horde::applicationURL('users/editForward.php'); + $url = Util::addParameter($url, 'address', $id); + $url = Util::addParameter($url, 'section', $section); + $addresses[$i]['add_forward_url'] = $url; } $url = Horde::applicationUrl('users/delete.php'); $currentAddress = $address['address']; @@ -68,12 +90,22 @@ foreach ($addresses as $i => $address) { $addresses[$i]['del_url'] = Horde_Util::addParameter($url, 'section', $section); //$url = Horde::applicationUrl('users/edit.php'); //$addresses[$i]['view_url'] = Horde_Util::addParameter($url, 'address', $address['user_name']); - $url = Horde::applicationUrl('users/edit.php'); - $url = Horde_Util::addParameter($url, 'address', $id); + if($type === 'alias') { - $addresses[$i]['view_url'] = ''; + $url = Horde::applicationUrl('users/editAlias.php'); + $url = Util::addParameter($url, 'alias', $id); + $url = Util::addParameter($url, 'section', $section); + $addresses[$i]['view_url'] = $url; + } elseif ($type === 'forward') { + $url = Horde::applicationUrl('users/editForward.php'); + $url = Util::addParameter($url, 'forward', $id); + $url = Util::addParameter($url, 'section', $section); + $addresses[$i]['view_url'] = $url; } else { - $addresses[$i]['view_url'] = Horde_Util::addParameter($url, 'section', $section); + $url = Horde::applicationUrl('users/edit.php'); + $url = Util::addParameter($url, 'address', $id); + $url = Util::addParameter($url, 'section', $section); + $addresses[$i]['view_url'] = $url; } $addresses[$i]['type'] = $types[$address['type']]['singular']; $addresses[$i]['status'] = $vilma_driver->getUserStatus($address); -- 2.11.0