public function linkByPackage($app, $call, $args = array(), $extra = '')
{
$links = ($api_ob = $this->_loadApi($app)) ? $api_ob->links() : array();
/* Make sure the link is defined. */
if (!isset($links[$call])) {
throw new Horde_Exception('The link "' . $call . '" is not defined in ' . $app . '\'s API.');
}
/* Initial link value. */
$link = $links[$call];
/* Fill in html-encoded arguments. */
foreach ($args as $key => $val) {
$link = str_replace('%' . $key . '%', htmlentities($val), $link);
}
$link = $this->applicationWebPath($link, $app);
/* Replace htmlencoded arguments that haven't been specified with
an empty string (this is where the default would be substituted
in a stricter registry implementation). */
$link = preg_replace('|%.+%|U', '', $link);
/* Fill in urlencoded arguments. */
foreach ($args as $key => $val) {
$link = str_replace('|' . Horde_String::lower($key) . '|', urlencode($val), $link);
}
/* Append any extra, non-standard arguments. */
if (is_array($extra)) {
$extra_args = '';
foreach ($extra as $key => $val) {
$extra_args .= '&' . urlencode($key) . '=' . urlencode($val);
}
} else {
$extra_args = $extra;
}
$link = str_replace('|extra|', $extra_args, $link);
/* Replace html-encoded arguments that haven't been specified with
an empty string (this is where the default would be substituted
in a stricter registry implementation). */
$link = preg_replace('|\\|.+\\||U', '', $link);
return $link;
}