? files ? patches Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.644 diff -u -p -r1.644 common.inc --- includes/common.inc 26 May 2007 10:54:12 -0000 1.644 +++ includes/common.inc 26 May 2007 23:47:29 -0000 @@ -191,44 +191,6 @@ function drupal_get_feeds($delimiter = " */ /** - * Parse an array into a valid urlencoded query string. - * - * @param $query - * The array to be processed e.g. $_GET - * @param $exclude - * The array filled with keys to be excluded. Use parent[child] to exclude nested items. - * @param $urlencode - * If TRUE, the keys and values are both urlencoded. - * @param $parent - * Should not be passed, only used in recursive calls - * @return - * urlencoded string which can be appended to/as the URL query string - */ -function drupal_query_string_encode($query, $exclude = array(), $parent = '') { - $params = array(); - - foreach ($query as $key => $value) { - $key = drupal_urlencode($key); - if ($parent) { - $key = $parent .'['. $key .']'; - } - - if (in_array($key, $exclude)) { - continue; - } - - if (is_array($value)) { - $params[] = drupal_query_string_encode($value, $exclude, $key); - } - else { - $params[] = $key .'='. drupal_urlencode($value); - } - } - - return implode('&', $params); -} - -/** * Prepare a destination query string for use in combination with * drupal_goto(). Used to direct the user back to the referring page * after completing a form. By default the current URL is returned. @@ -254,69 +216,6 @@ function drupal_get_destination() { } /** - * Send the user to a different Drupal page. - * - * This issues an on-site HTTP redirect. The function makes sure the redirected - * URL is formatted correctly. - * - * Usually the redirected URL is constructed from this function's input - * parameters. However you may override that behavior by setting a - * destination in either the $_REQUEST-array (i.e. by using - * the query string of an URI) or the $_REQUEST['edit']-array (i.e. by - * using a hidden form field). This is used to direct the user back to - * the proper page after completing a form. For example, after editing - * a post on the 'admin/content/node'-page or after having logged on using the - * 'user login'-block in a sidebar. The function drupal_get_destination() - * can be used to help set the destination URL. - * - * It is advised to use drupal_goto() instead of PHP's header(), because - * drupal_goto() will append the user's session ID to the URI when PHP is - * compiled with "--enable-trans-sid". - * - * This function ends the request; use it rather than a print theme('page') - * statement in your menu callback. - * - * @param $path - * A Drupal path or a full URL. - * @param $query - * The query string component, if any. - * @param $fragment - * The destination fragment identifier (named anchor). - * @param $http_response_code - * Valid values for an actual "goto" as per RFC 2616 section 10.3 are: - * - 301 Moved Permanently (the recommended value for most redirects) - * - 302 Found (default in Drupal and PHP, sometimes used for spamming search - * engines) - * - 303 See Other - * - 304 Not Modified - * - 305 Use Proxy - * - 307 Temporary Redirect (an alternative to "503 Site Down for Maintenance") - * Note: Other values are defined by RFC 2616, but are rarely used and poorly - * supported. - * @see drupal_get_destination() - */ -function drupal_goto($path = '', $query = NULL, $fragment = NULL, $http_response_code = 302) { - if (isset($_REQUEST['destination'])) { - extract(parse_url(urldecode($_REQUEST['destination']))); - } - else if (isset($_REQUEST['edit']['destination'])) { - extract(parse_url(urldecode($_REQUEST['edit']['destination']))); - } - - $url = url($path, array('query' => $query, 'fragment' => $fragment, 'absolute' => TRUE)); - - // Before the redirect, allow modules to react to the end of the page request. - module_invoke_all('exit', $url); - - header('Location: '. $url, TRUE, $http_response_code); - - // The "Location" header sends a REDIRECT status code to the http - // daemon. In some cases this can go wrong, so we make sure none - // of the code below the drupal_goto() call gets executed when we redirect. - exit(); -} - -/** * Generates a site off-line message */ function drupal_site_offline() { @@ -1142,122 +1041,6 @@ function format_date($timestamp, $type = */ /** - * Generate a URL from a Drupal menu path. Will also pass-through existing URLs. - * - * @param $path - * The Drupal path being linked to, such as "admin/content/node", or an existing URL - * like "http://drupal.org/". - * @param $options - * An associative array of additional options, with the following keys: - * 'query' - * A query string to append to the link, or an array of query key/value - * properties. - * 'fragment' - * A fragment identifier (or named anchor) to append to the link. - * Do not include the '#' character. - * 'absolute' (default FALSE) - * Whether to force the output to be an absolute link (beginning with - * http:). Useful for links that will be displayed outside the site, such - * as in an RSS feed. - * 'alias' (default FALSE) - * Whether the given path is an alias already. - * @return - * a string containing a URL to the given path. - * - * When creating links in modules, consider whether l() could be a better - * alternative than url(). - */ -function url($path = NULL, $options = array()) { - // Merge in defaults - $options += array( - 'fragment' => '', - 'query' => '', - 'absolute' => FALSE, - 'alias' => FALSE, - ); - - // May need language dependant rewriting if language.inc is present - if (function_exists('language_url_rewrite')) { - language_url_rewrite($path, $options); - } - if ($options['fragment']) { - $options['fragment'] = '#'. $options['fragment']; - } - if (is_array($options['query'])) { - $options['query'] = drupal_query_string_encode($options['query']); - } - - // Return an external link if $path contains an allowed absolute URL. - // Only call the slow filter_xss_bad_protocol if $path contains a ':' before any / ? or #. - $colonpos = strpos($path, ':'); - if ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && filter_xss_bad_protocol($path, FALSE) == check_plain($path)) { - // Split off the fragment - if (strpos($path, '#') !== FALSE) { - list($path, $old_fragment) = explode('#', $path, 2); - if (isset($old_fragment) && !$options['fragment']) { - $options['fragment'] = '#'. $old_fragment; - } - } - // Append the query - if ($options['query']) { - $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . $options['query']; - } - // Reassemble - return $path . $options['fragment']; - } - - global $base_url; - static $script; - static $clean_url; - - if (!isset($script)) { - // On some web servers, such as IIS, we can't omit "index.php". So, we - // generate "index.php?q=foo" instead of "?q=foo" on anything that is not - // Apache. - $script = (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') === FALSE) ? 'index.php' : ''; - } - - // Cache the clean_url variable to improve performance. - if (!isset($clean_url)) { - $clean_url = (bool)variable_get('clean_url', '0'); - } - - $base = $options['absolute'] ? $base_url .'/' : base_path(); - - // The special path '' links to the default front page. - if (!empty($path) && $path != '') { - if (!$options['alias']) { - $path = drupal_get_path_alias($path); - } - $path = drupal_urlencode($path); - if (!$clean_url) { - if ($options['query']) { - return $base . $script .'?q='. $path .'&'. $options['query'] . $options['fragment']; - } - else { - return $base . $script .'?q='. $path . $options['fragment']; - } - } - else { - if ($options['query']) { - return $base . $path .'?'. $options['query'] . $options['fragment']; - } - else { - return $base . $path . $options['fragment']; - } - } - } - else { - if ($options['query']) { - return $base . $script .'?'. $options['query'] . $options['fragment']; - } - else { - return $base . $options['fragment']; - } - } -} - -/** * Format an attribute string to insert in a tag. * * @param $attributes @@ -1788,34 +1571,6 @@ function drupal_to_js($var) { } /** - * Wrapper around urlencode() which avoids Apache quirks. - * - * Should be used when placing arbitrary data in an URL. Note that Drupal paths - * are urlencoded() when passed through url() and do not require urlencoding() - * of individual components. - * - * Notes: - * - For esthetic reasons, we do not escape slashes. This also avoids a 'feature' - * in Apache where it 404s on any path containing '%2F'. - * - mod_rewrite's unescapes %-encoded ampersands and hashes when clean URLs - * are used, which are interpreted as delimiters by PHP. These characters are - * double escaped so PHP will still see the encoded version. - * - * @param $text - * String to encode - */ -function drupal_urlencode($text) { - if (variable_get('clean_url', '0')) { - return str_replace(array('%2F', '%26', '%23'), - array('/', '%2526', '%2523'), - urlencode($text)); - } - else { - return str_replace('%2F', '/', urlencode($text)); - } -} - -/** * Ensure the private key variable used to generate tokens is set. * * @return Index: includes/menu.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/menu.inc,v retrieving revision 1.168 diff -u -p -r1.168 menu.inc --- includes/menu.inc 26 May 2007 10:54:12 -0000 1.168 +++ includes/menu.inc 26 May 2007 23:47:37 -0000 @@ -293,7 +293,7 @@ function menu_get_item($path = NULL) { } if ($item->access) { $item->map = $map; - $item->page_arguments = array_merge(menu_unserialize($item->page_arguments, $map), array_slice($parts, $item->number_parts)); + $item->page_arguments = array_merge(menu_unserialize($item->page_arguments, $map), array_slice($map, $item->number_parts)); } } $items[$path] = $item; Index: includes/path.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/path.inc,v retrieving revision 1.15 diff -u -p -r1.15 path.inc --- includes/path.inc 26 Mar 2007 01:32:22 -0000 1.15 +++ includes/path.inc 26 May 2007 23:47:38 -0000 @@ -14,12 +14,314 @@ * Initialize the $_GET['q'] variable to the proper normal path. */ function drupal_init_path() { - if (!empty($_GET['q'])) { - $_GET['q'] = drupal_get_normal_path(trim($_GET['q'], '/')); + if (empty($_GET['q'])) { + $src = drupal_get_normal_path(variable_get('site_frontpage', 'node')); } else { - $_GET['q'] = drupal_get_normal_path(variable_get('site_frontpage', 'node')); + $src = drupal_get_normal_path(trim($_GET['q'], '/')); } + + $dst = drupal_get_path_alias($src, '', FALSE); + if ($_GET['q'] != $dst) { + $get = $_GET; + unset($get['q']); + drupal_goto($dst, $get ? $get : NULL, NULL, 301); + } + else { + $_GET['q'] = $src; + } +} + +/** + * Send the user to a different Drupal page. + * + * This issues an on-site HTTP redirect. The function makes sure the redirected + * URL is formatted correctly. + * + * Usually the redirected URL is constructed from this function's input + * parameters. However you may override that behavior by setting a + * destination in either the $_REQUEST-array (i.e. by using + * the query string of an URI) or the $_REQUEST['edit']-array (i.e. by + * using a hidden form field). This is used to direct the user back to + * the proper page after completing a form. For example, after editing + * a post on the 'admin/content/node'-page or after having logged on using the + * 'user login'-block in a sidebar. The function drupal_get_destination() + * can be used to help set the destination URL. + * + * It is advised to use drupal_goto() instead of PHP's header(), because + * drupal_goto() will append the user's session ID to the URI when PHP is + * compiled with "--enable-trans-sid". + * + * This function ends the request; use it rather than a print theme('page') + * statement in your menu callback. + * + * @param $path + * A Drupal path or a full URL. + * @param $query + * The query string component, if any. + * @param $fragment + * The destination fragment identifier (named anchor). + * @param $http_response_code + * Valid values for an actual "goto" as per RFC 2616 section 10.3 are: + * - 301 Moved Permanently (the recommended value for most redirects) + * - 302 Found (default in Drupal and PHP, sometimes used for spamming search + * engines) + * - 303 See Other + * - 304 Not Modified + * - 305 Use Proxy + * - 307 Temporary Redirect (an alternative to "503 Site Down for Maintenance") + * Note: Other values are defined by RFC 2616, but are rarely used and poorly + * supported. + * @see drupal_get_destination() + */ +function drupal_goto($path = '', $query = NULL, $fragment = NULL, $http_response_code = 302) { + if (isset($_REQUEST['destination'])) { + extract(parse_url(urldecode($_REQUEST['destination']))); + } + else if (isset($_REQUEST['edit']['destination'])) { + extract(parse_url(urldecode($_REQUEST['edit']['destination']))); + } + + $url = url($path, array('query' => $query, 'fragment' => $fragment, 'absolute' => TRUE)); + + // Before the redirect, allow modules to react to the end of the page request. + //module_invoke_all('exit', $url); + + header('Location: '. $url, TRUE, $http_response_code); + + // The "Location" header sends a REDIRECT status code to the http + // daemon. In some cases this can go wrong, so we make sure none + // of the code below the drupal_goto() call gets executed when we redirect. + exit(); +} + +/** + * Generate a URL from a Drupal menu path. Will also pass-through existing URLs. + * + * @param $path + * The Drupal path being linked to, such as "admin/content/node", or an existing URL + * like "http://drupal.org/". + * @param $options + * An associative array of additional options, with the following keys: + * 'query' + * A query string to append to the link, or an array of query key/value + * properties. + * 'fragment' + * A fragment identifier (or named anchor) to append to the link. + * Do not include the '#' character. + * 'absolute' (default FALSE) + * Whether to force the output to be an absolute link (beginning with + * http:). Useful for links that will be displayed outside the site, such + * as in an RSS feed. + * 'alias' (default FALSE) + * Whether the given path is an alias already. + * @return + * a string containing a URL to the given path. + * + * When creating links in modules, consider whether l() could be a better + * alternative than url(). + */ +function url($path = NULL, $options = array()) { + // Merge in defaults + $options += array( + 'fragment' => '', + 'query' => '', + 'absolute' => FALSE, + 'alias' => FALSE, + ); + + // May need language dependant rewriting if language.inc is present + if (function_exists('language_url_rewrite')) { + language_url_rewrite($path, $options); + } + if ($options['fragment']) { + $options['fragment'] = '#'. $options['fragment']; + } + if (is_array($options['query'])) { + $options['query'] = drupal_query_string_encode($options['query']); + } + + // Return an external link if $path contains an allowed absolute URL. + // Only call the slow filter_xss_bad_protocol if $path contains a ':' before any / ? or #. + $colonpos = strpos($path, ':'); + if ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && filter_xss_bad_protocol($path, FALSE) == check_plain($path)) { + // Split off the fragment + if (strpos($path, '#') !== FALSE) { + list($path, $old_fragment) = explode('#', $path, 2); + if (isset($old_fragment) && !$options['fragment']) { + $options['fragment'] = '#'. $old_fragment; + } + } + // Append the query + if ($options['query']) { + $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . $options['query']; + } + // Reassemble + return $path . $options['fragment']; + } + + global $base_url; + static $script; + static $clean_url; + + if (!isset($script)) { + // On some web servers, such as IIS, we can't omit "index.php". So, we + // generate "index.php?q=foo" instead of "?q=foo" on anything that is not + // Apache. + $script = (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') === FALSE) ? 'index.php' : ''; + } + + // Cache the clean_url variable to improve performance. + if (!isset($clean_url)) { + $clean_url = (bool)variable_get('clean_url', '0'); + } + + $base = $options['absolute'] ? $base_url .'/' : base_path(); + + // The special path '' links to the default front page. + if (!empty($path) && $path != '') { + if (!$options['alias']) { + $path = drupal_get_path_alias($path); + } + $path = drupal_urlencode($path); + if (!$clean_url) { + if ($options['query']) { + return $base . $script .'?q='. $path .'&'. $options['query'] . $options['fragment']; + } + else { + return $base . $script .'?q='. $path . $options['fragment']; + } + } + else { + if ($options['query']) { + return $base . $path .'?'. $options['query'] . $options['fragment']; + } + else { + return $base . $path . $options['fragment']; + } + } + } + else { + if ($options['query']) { + return $base . $script .'?'. $options['query'] . $options['fragment']; + } + else { + return $base . $options['fragment']; + } + } +} + +/** + * Wrapper around urlencode() which avoids Apache quirks. + * + * Should be used when placing arbitrary data in an URL. Note that Drupal paths + * are urlencoded() when passed through url() and do not require urlencoding() + * of individual components. + * + * Notes: + * - For esthetic reasons, we do not escape slashes. This also avoids a 'feature' + * in Apache where it 404s on any path containing '%2F'. + * - mod_rewrite's unescapes %-encoded ampersands and hashes when clean URLs + * are used, which are interpreted as delimiters by PHP. These characters are + * double escaped so PHP will still see the encoded version. + * + * @param $text + * String to encode + */ +function drupal_urlencode($text) { + if (variable_get('clean_url', '0')) { + return str_replace(array('%2F', '%26', '%23'), + array('/', '%2526', '%2523'), + urlencode($text)); + } + else { + return str_replace('%2F', '/', urlencode($text)); + } +} + +/** + * Parse an array into a valid urlencoded query string. + * + * @param $query + * The array to be processed e.g. $_GET + * @param $exclude + * The array filled with keys to be excluded. Use parent[child] to exclude nested items. + * @param $urlencode + * If TRUE, the keys and values are both urlencoded. + * @param $parent + * Should not be passed, only used in recursive calls + * @return + * urlencoded string which can be appended to/as the URL query string + */ +function drupal_query_string_encode($query, $exclude = array(), $parent = '') { + $params = array(); + + foreach ($query as $key => $value) { + $key = drupal_urlencode($key); + if ($parent) { + $key = $parent .'['. $key .']'; + } + + if (in_array($key, $exclude)) { + continue; + } + + if (is_array($value)) { + $params[] = drupal_query_string_encode($value, $exclude, $key); + } + else { + $params[] = $key .'='. drupal_urlencode($value); + } + } + + return implode('&', $params); +} + +/** + * Processes an HTML attribute value and ensures it does not contain an URL + * with a disallowed protocol (e.g. javascript:) + * + * @param $string + * The string with the attribute value. + * @param $decode + * Whether to decode entities in the $string. Set to FALSE if the $string + * is in plain text, TRUE otherwise. Defaults to TRUE. + * @return + * Cleaned up and HTML-escaped version of $string. + */ +function filter_xss_bad_protocol($string, $decode = TRUE) { + static $allowed_protocols; + if (!isset($allowed_protocols)) { + $allowed_protocols = array_flip(variable_get('filter_allowed_protocols', array('http', 'https', 'ftp', 'news', 'nntp', 'telnet', 'mailto', 'irc', 'ssh', 'sftp', 'webcal'))); + } + + // Get the plain text representation of the attribute value (i.e. its meaning). + if ($decode) { + $string = decode_entities($string); + } + + // Iteratively remove any invalid protocol found. + + do { + $before = $string; + $colonpos = strpos($string, ':'); + if ($colonpos > 0) { + // We found a colon, possibly a protocol. Verify. + $protocol = substr($string, 0, $colonpos); + // If a colon is preceded by a slash, question mark or hash, it cannot + // possibly be part of the URL scheme. This must be a relative URL, + // which inherits the (safe) protocol of the base document. + if (preg_match('![/?#]!', $protocol)) { + break; + } + // Per RFC2616, section 3.2.3 (URI Comparison) scheme comparison must be case-insensitive + // Check if this is a disallowed protocol. + if (!isset($allowed_protocols[strtolower($protocol)])) { + $string = substr($string, $colonpos + 1); + } + } + } while ($before != $string); + return check_plain($string); } /** @@ -43,7 +345,7 @@ function drupal_init_path() { * Either a Drupal system path, an aliased path, or FALSE if no path was * found. */ -function drupal_lookup_path($action, $path = '', $path_language = '') { +function drupal_lookup_path($action, $path = '', $path_language = '', $cached = TRUE) { global $language; // $map is an array with language keys, holding arrays of Drupal paths to alias relations static $map = array(), $no_src = array(), $count; @@ -61,12 +363,14 @@ function drupal_lookup_path($action, $pa } elseif ($count > 0 && $path != '') { if ($action == 'alias') { - if (isset($map[$path_language][$path])) { + if (isset($map[$path_language][$path]) && $cached) { return $map[$path_language][$path]; } // Get the most fitting result falling back with alias without language - $alias = db_result(db_query("SELECT dst FROM {url_alias} WHERE src = '%s' AND language IN('%s', '') ORDER BY language DESC", $path, $path_language)); - $map[$path_language][$path] = $alias; + $alias = db_result(db_query("SELECT dst FROM {url_alias} WHERE src = '%s' AND active = 1 AND language IN('%s', '') ORDER BY language DESC", $path, $path_language)); + if (!empty($alias)) { + $map[$path_language][$path] = $alias; + } return $alias; } // Check $no_src for this $path in case we've already determined that there @@ -105,9 +409,9 @@ function drupal_lookup_path($action, $pa * An aliased path if one was found, or the original path if no alias was * found. */ -function drupal_get_path_alias($path, $path_language = '') { +function drupal_get_path_alias($path, $path_language = '', $cached = TRUE) { $result = $path; - if ($alias = drupal_lookup_path('alias', $path, $path_language)) { + if ($alias = drupal_lookup_path('alias', $path, $path_language, $cached)) { $result = $alias; } if (function_exists('custom_url_rewrite')) { Index: modules/filter/filter.module =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.module,v retrieving revision 1.174 diff -u -p -r1.174 filter.module --- modules/filter/filter.module 20 May 2007 16:44:35 -0000 1.174 +++ modules/filter/filter.module 26 May 2007 23:47:48 -0000 @@ -1504,53 +1504,6 @@ function _filter_xss_attributes($attr) { } /** - * Processes an HTML attribute value and ensures it does not contain an URL - * with a disallowed protocol (e.g. javascript:) - * - * @param $string - * The string with the attribute value. - * @param $decode - * Whether to decode entities in the $string. Set to FALSE if the $string - * is in plain text, TRUE otherwise. Defaults to TRUE. - * @return - * Cleaned up and HTML-escaped version of $string. - */ -function filter_xss_bad_protocol($string, $decode = TRUE) { - static $allowed_protocols; - if (!isset($allowed_protocols)) { - $allowed_protocols = array_flip(variable_get('filter_allowed_protocols', array('http', 'https', 'ftp', 'news', 'nntp', 'telnet', 'mailto', 'irc', 'ssh', 'sftp', 'webcal'))); - } - - // Get the plain text representation of the attribute value (i.e. its meaning). - if ($decode) { - $string = decode_entities($string); - } - - // Iteratively remove any invalid protocol found. - - do { - $before = $string; - $colonpos = strpos($string, ':'); - if ($colonpos > 0) { - // We found a colon, possibly a protocol. Verify. - $protocol = substr($string, 0, $colonpos); - // If a colon is preceded by a slash, question mark or hash, it cannot - // possibly be part of the URL scheme. This must be a relative URL, - // which inherits the (safe) protocol of the base document. - if (preg_match('![/?#]!', $protocol)) { - break; - } - // Per RFC2616, section 3.2.3 (URI Comparison) scheme comparison must be case-insensitive - // Check if this is a disallowed protocol. - if (!isset($allowed_protocols[strtolower($protocol)])) { - $string = substr($string, $colonpos + 1); - } - } - } while ($before != $string); - return check_plain($string); -} - -/** * @} End of "Standard filters". */ Index: modules/locale/locale.module =================================================================== RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v retrieving revision 1.174 diff -u -p -r1.174 locale.module --- modules/locale/locale.module 22 May 2007 07:42:37 -0000 1.174 +++ modules/locale/locale.module 26 May 2007 23:47:51 -0000 @@ -224,6 +224,7 @@ function locale_form_alter(&$form, $form '#title' => t('Language'), '#options' => array('' => t('All languages')) + locale_language_list('name'), '#default_value' => $form['language']['#value'], + '#attributes' => !empty($form['edit_mode']['#value']) ? array('disabled' => 'disabled') : array(), '#weight' => -10, '#description' => t('Path aliases added for languages take precedence over path aliases added for all languages for the same Drupal path.'), ); Index: modules/path/path.module =================================================================== RCS file: /cvs/drupal/drupal/modules/path/path.module,v retrieving revision 1.119 diff -u -p -r1.119 path.module --- modules/path/path.module 16 May 2007 13:45:16 -0000 1.119 +++ modules/path/path.module 26 May 2007 23:47:52 -0000 @@ -31,6 +31,17 @@ function path_help($section) { case 'admin/build/path/add': return '

'. t('Enter the path you wish to create the alias for, followed by the name of the new alias.') .'

'; } +} + +/** + * Implementation of hook_theme() + */ +function path_theme() { + return array( + 'path_form' => array( + 'arguments' => array('form' => NULL), + ), + ); } /** @@ -74,11 +85,18 @@ function path_menu() { /** * Menu callback; handles pages for creating and editing URL aliases. */ -function path_admin_edit($pid = 0) { - if ($pid) { - $alias = path_load($pid); - drupal_set_title(check_plain($alias['dst'])); - $output = path_form($alias); +function path_admin_edit($language = '') { + $args = func_get_args(); + unset($args[0]); + // Unset the first language argument. + + $src = implode('/', $args); + + if (!empty($src)) { + $language = (isset($language) && $language != 'all') ? $language : ''; + $aliases = path_load('', $src, $language); + drupal_set_title(check_plain($src)); + $output = path_form($src, $language, $aliases); } else { $output = path_form(); @@ -124,7 +142,7 @@ function path_admin_delete($pid = 0) { /** * Set an aliased path for a given Drupal path, preventing duplicates. */ -function path_set_alias($path = NULL, $alias = NULL, $pid = NULL, $language = '') { +function path_set_alias($path = NULL, $alias = NULL, $pid = NULL, $active = '', $language = '') { if ($path && !$alias) { // Delete based on path db_query("DELETE FROM {url_alias} WHERE src = '%s' AND language = '%s'", $path, $language); @@ -145,11 +163,14 @@ function path_set_alias($path = NULL, $a if ($alias_count == 0) { if ($pid) { // Existing path changed data - db_query("UPDATE {url_alias} SET src = '%s', dst = '%s', language = '%s' WHERE pid = %d", $path, $alias, $language, $pid); + db_query("UPDATE {url_alias} SET src = '%s', dst = '%s', active = %d, language = '%s' WHERE pid = %d", $path, $alias, $active, $language, $pid); } else { // No such alias yet in this language - db_query("INSERT INTO {url_alias} (src, dst, language) VALUES ('%s', '%s', '%s')", $path, $alias, $language); + if ($active) { + db_query("UPDATE {url_alias} SET active = '' WHERE src = '%s'", $path); + } + db_query("INSERT INTO {url_alias} (src, dst, active, language) VALUES ('%s', '%s', %d, '%s')", $path, $alias, $active, $language); } } // The alias exists. @@ -160,10 +181,9 @@ function path_set_alias($path = NULL, $a } else { // This will delete the path that alias was originally pointing to. - path_set_alias(NULL, $alias, NULL, $language); + path_set_alias(NULL, $alias, NULL, $active, $language); // This will remove the current aliases of the path. - path_set_alias($path, NULL, NULL, $language); - path_set_alias($path, $alias, NULL, $language); + path_set_alias($path, $alias, NULL, $active, $language); } } if ($alias_count == 0 || $path_count == 0) { @@ -175,43 +195,155 @@ function path_set_alias($path = NULL, $a /** * Return a form for editing or creating an individual URL alias. */ -function path_form($edit = array('src' => '', 'dst' => '', 'language' => '', 'pid' => NULL)) { +function path_form($src = '', $language = '', $aliases = array()) { $form['#submit'][] = 'path_form_submit'; $form['#validate'][] = 'path_form_validate'; - $form['#alias'] = $edit; - - $form['src'] = array( + $form['#theme'] = 'path_form'; + $form['aliases']['#tree'] = TRUE; + + $active_alias = 'src'; + $options['src'] = ''; + $form['aliases']['src'] = array( '#type' => 'textfield', '#title' => t('Existing system path'), - '#default_value' => $edit['src'], + '#default_value' => $src, '#maxlength' => 64, '#size' => 45, '#description' => t('Specify the existing path you wish to alias. For example: node/28, forum/1, taxonomy/term/1+2.'), '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=') ); - $form['dst'] = array( + + $options['new'] = ''; + $form['aliases']['new'] = array( '#type' => 'textfield', - '#title' => t('Path alias'), - '#default_value' => $edit['dst'], + '#title' => t('New alias'), '#maxlength' => 64, - '#size' => 45, - '#description' => t('Specify an alternative path by which this data can be accessed. For example, type "about" when writing an about page. Use a relative path and don\'t add a trailing slash or the URL alias won\'t work.'), + '#size' => 45, + '#weight' => 1, + '#description' => t('Specify a new alternative path by which this data can be accessed. For example: about, faq, etc.'), '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=') ); + if (!empty($aliases)) { + $count = 1; + foreach ($aliases as $alias) { + $active_alias = !empty($alias['active']) ? $alias['pid'] : $active_alias; + $options[$alias['pid']] = ''; + $form['aliases'][$alias['pid']] = array( + '#type' => 'textfield', + '#title' => t('Alias !number', array('!number' => ($count++))), + '#default_value' => $alias['dst'], + '#maxlength' => 64, + '#size' => 45, + '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=') + ); + } + } + else { + $active_alias = 'new'; + } + $form['active'] = array( + '#type' => 'radios', + '#options' => $options, + '#default_value' => $active_alias, + ); + + if (!empty($aliases)) { + $attributes = array('disabled' => 'disabled'); + } + else { + $attributes = array(); + } + // This will be a hidden value unless locale module is enabled $form['language'] = array( '#type' => 'value', - '#value' => $edit['language'] + '#value' => $language, + '#attributes' => $attributes ); - if ($edit['pid']) { - $form['pid'] = array('#type' => 'hidden', '#value' => $edit['pid']); - $form['submit'] = array('#type' => 'submit', '#value' => t('Update alias')); + + if (!empty($aliases)) { + $form['edit_mode'] = array('#type' => 'value', '#value' => TRUE); + $form['submit'] = array('#type' => 'submit', '#value' => t('Update')); } else { $form['submit'] = array('#type' => 'submit', '#value' => t('Create new alias')); } return $form; +} + +/** + * Theme the path add/edit form. + */ +function theme_path_form($form) { + $header = array(t('Active'), t('Alias')); + $rows = array(); + uasort($form['aliases'], '_element_sort'); + foreach (element_children($form['aliases']) as $key) { + $rows[] = array( + drupal_render($form['active'][$key]), + drupal_render($form['aliases'][$key]), + ); + } + $output = theme('table', $header, $rows); + $output .= drupal_render($form); + return $output; +} + +/** + * Verify that a new URL alias is valid + */ +function path_form_validate(&$form_values) { + $active = $form_values['active']; + if ($active == 'new' && empty($form_values['aliases'][$active])) { + form_set_error('aliases][new', t('You can not set an empty field as an active alias.')); + } + + // Language is only set if locale module is enabled, otherwise save for all languages. + $language = isset($form_values['language']) ? $form_values['language'] : ''; + + foreach ($form_values['aliases'] as $pid => $alias) { + if ($alias && db_result(db_query("SELECT COUNT(dst) FROM {url_alias} WHERE pid != %d AND dst = '%s' AND language = '%s'", $pid, $alias, $language))) { + form_set_error('aliases]['. $pid, t('The alias %alias is already in use in this language.', array('%alias' => $alias))); + } + } +} + +/** + * Save a new URL alias to the database. + */ +function path_form_submit($form_values, $form, &$form_state) { + $src = $form_values['aliases']['src']; + $new = $form_values['aliases']['new']; + + // Language is only set if locale module is enabled, otherwise save for all languages. + $language = isset($form_values['language']) ? $form_values['language'] : ''; + + unset($form_values['aliases']['src'], $form_values['aliases']['new']); + // Unset source and new alias, preparing a list of existing aliases. + + if (!empty($form_values['aliases'])) { + foreach ($form_values['aliases'] as $pid => $alias) { + if (!empty($alias)) { + path_set_alias($src, $alias, $pid, ($pid == $form_values['active']), $language); + } + else { + // Delete based on alias + db_query("DELETE FROM {url_alias} WHERE pid = %d", $pid); + drupal_clear_path_cache(); + } + } + } + + if (!empty($new)) { + path_set_alias($src, $new, NULL, ($form_values['active'] == 'new'), $language); + } + + menu_rebuild(); + drupal_set_message(t('The aliases have been saved.')); + + //$form_state['redirect'] = 'admin/build/path'; + return; } /** @@ -307,7 +439,10 @@ function path_perm() { * When filter key passed, perform a standard search on the given key, * and return the list of matching URL aliases. */ -function path_admin_overview($keys = NULL) { +function path_admin_overview() { + $args = func_get_args(); + $keys = implode('/', $args); + // Add the filter form above the overview table. $output = drupal_get_form('path_admin_filter_form', $keys); // Enable language column if locale is enabled or if we have any alias with language @@ -337,7 +472,8 @@ function path_admin_overview($keys = NUL $rows = array(); $destination = drupal_get_destination(); while ($data = db_fetch_object($result)) { - $row = array(check_plain($data->dst), check_plain($data->src), l(t('edit'), "admin/build/path/edit/$data->pid", array('query' => $destination)), l(t('delete'), "admin/build/path/delete/$data->pid", array('query' => $destination))); + $language = !empty($data->language) ? $data->language : 'all' ; + $row = array(check_plain($data->dst), check_plain($data->src), l(t('edit'), "admin/build/path/edit/$language/$data->src", array('query' => $destination)), l(t('delete'), "admin/build/path/delete/$data->pid", array('query' => $destination))); if ($multilanguage) { $row[4] = $row[3]; $row[3] = $row[2]; @@ -360,71 +496,65 @@ function path_admin_overview($keys = NUL /** * Fetch a specific URL alias from the database. */ -function path_load($pid) { - return db_fetch_array(db_query('SELECT * FROM {url_alias} WHERE pid = %d', $pid)); -} - -/** - * Verify that a new URL alias is valid - */ -function path_form_validate($form_values, $form, &$form_state) { - $src = $form_values['src']; - $dst = $form_values['dst']; - $pid = isset($form_values['pid']) ? $form_values['pid'] : 0; - // Language is only set if locale module is enabled, otherwise save for all languages. - $language = isset($form_values['language']) ? $form_values['language'] : ''; - - if (db_result(db_query("SELECT COUNT(dst) FROM {url_alias} WHERE pid != %d AND dst = '%s' AND language = '%s'", $pid, $dst, $language))) { - form_set_error('dst', t('The alias %alias is already in use in this language.', array('%alias' => $dst))); +function path_load($pid, $src = '', $language = '') { + if (!empty($pid)) { + return db_fetch_array(db_query('SELECT * FROM {url_alias} WHERE pid = %d', $pid)); + } + else if (!empty($src)) { + $result = db_query("SELECT * FROM {url_alias} WHERE src = '%s' AND language = '%s'", $src, $language); + while ($row = db_fetch_array($result)) { + $output[] = $row; + } + return $output; } } /** - * Save a new URL alias to the database. - */ -function path_form_submit($form_values, $form, &$form_state) { - // Language is only set if locale module is enabled - path_set_alias($form_values['src'], $form_values['dst'], isset($form_values['pid']) ? $form_values['pid'] : 0, isset($form_values['language']) ? $form_values['language'] : ''); - - drupal_set_message(t('The alias has been saved.')); - $form_state['redirect'] = 'admin/build/path'; - return; -} - -/** * Return a form to filter URL aliases. */ -function path_admin_filter_form($keys = '') { +function path_admin_filter_form() { + $args = func_get_args(); + $keys = implode('/', $args); + $form['#attributes'] = array('class' => 'search-form'); - $form['basic'] = array('#type' => 'fieldset', - '#title' => t('Filter aliases') - ); - $form['basic']['inline'] = array('#prefix' => '
', '#suffix' => '
'); - $form['basic']['inline']['filter'] = array( + $form['inline'] = array('#prefix' => '
', '#suffix' => '
'); + $form['inline']['filter'] = array( '#type' => 'textfield', - '#title' => '', + '#title' => t('Filter aliases'), '#default_value' => $keys, '#maxlength' => 64, '#size' => 25, ); - $form['basic']['inline']['submit'] = array('#type' => 'submit', '#value' => t('Filter')); + $form['inline']['submit'] = array('#type' => 'submit', '#value' => t('Filter')); + $form['#submit'][] = 'path_admin_filter_form_submit'; return $form; } - -/** - * Process filter form submission. - */ -function path_admin_filter_form_submit($form_id, $form_values) { - return 'admin/build/path/list/'. trim($form_values['filter']); + +/** + * Implementation of hook_user. + */ +function path_user($op, $edit, $user) { + if ($op == 'delete') { + $path = "user/$user->uid"; + path_set_alias($path); + } +} + +/** + * Implementation of hook_comment. + */ +function path_comment($comment, $op) { + if ($op == 'delete') { + $path = "comment/$comment->cid"; + path_set_alias($path); + } } /** - * Helper function for grabbing filter keys. + * Process filter form submission. */ -function path_admin_filter_get_keys() { - // Extract keys as remainder of path - $path = explode('/', $_GET['q'], 5); - return count($path) == 5 ? $path[4] : ''; +function path_admin_filter_form_submit(&$form_values) { + return drupal_goto('admin/build/path/list/'. trim($form_values['filter'])); }