? sites/default/settings.php Index: includes/bootstrap.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v retrieving revision 1.169 diff -u -p -r1.169 bootstrap.inc --- includes/bootstrap.inc 30 May 2007 08:08:57 -0000 1.169 +++ includes/bootstrap.inc 5 Jun 2007 03:42:26 -0000 @@ -912,6 +912,7 @@ function _drupal_bootstrap($phase) { break; case DRUPAL_BOOTSTRAP_PATH: + require_once './modules/filter/filter.module'; require_once './includes/path.inc'; // Initialize $_GET['q'] prior to loading modules and invoking hook_init(). drupal_init_path(); Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.651 diff -u -p -r1.651 common.inc --- includes/common.inc 4 Jun 2007 07:22:16 -0000 1.651 +++ includes/common.inc 5 Jun 2007 03:42:37 -0000 @@ -185,50 +185,6 @@ function drupal_get_feeds($delimiter = " } /** - * @name HTTP handling - * @{ - * Functions to properly handle HTTP responses. - */ - -/** - * 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 +210,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() { @@ -1145,122 +1038,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 @@ -2105,34 +1882,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/module.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/module.inc,v retrieving revision 1.102 diff -u -p -r1.102 module.inc --- includes/module.inc 25 May 2007 12:46:43 -0000 1.102 +++ includes/module.inc 5 Jun 2007 03:42:39 -0000 @@ -345,9 +345,11 @@ function module_implements($hook, $sort if (!isset($implementations[$hook])) { $implementations[$hook] = array(); $list = module_list(FALSE, TRUE, $sort); - foreach ($list as $module) { - if (module_hook($module, $hook)) { - $implementations[$hook][] = $module; + if (!empty($list)) { + foreach ($list as $module) { + if (module_hook($module, $hook)) { + $implementations[$hook][] = $module; + } } } } 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 5 Jun 2007 03:42:41 -0000 @@ -15,10 +15,165 @@ */ function drupal_init_path() { if (!empty($_GET['q'])) { - $_GET['q'] = drupal_get_normal_path(trim($_GET['q'], '/')); + $path = drupal_get_normal_path(trim($_GET['q'], '/')); } else { $_GET['q'] = drupal_get_normal_path(variable_get('site_frontpage', 'node')); + return; + } + + // Wipe the cached alias data. + drupal_lookup_path('wipe'); + + $active_alias = drupal_get_path_alias($path); + if ($_GET['q'] != $active_alias) { + $query = $_GET; + // Unset the drupal path query. + unset($query['q']); + + // Redirect to the active alias. + drupal_goto($path, ($query ? $query : NULL), NULL, 301); + } + else { + $_GET['q'] = $path; + } +} + +/** + * @name HTTP handling + * @{ + * Functions to properly handle HTTP responses. + */ + +/** + * 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); +} + +/** + * 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(); +} +/** + * @} End of "HTTP handling". + */ + +/** + * 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)); } } @@ -65,7 +220,7 @@ function drupal_lookup_path($action, $pa 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)); + $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)); $map[$path_language][$path] = $alias; return $alias; } @@ -140,6 +295,122 @@ function drupal_get_normal_path($path, $ } /** + * 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']; + } + } +} + +/** * Return a component of the current Drupal path. * * When viewing a page at the path "admin/content/types", for example, arg(0) Index: modules/locale/locale.module =================================================================== RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v retrieving revision 1.177 diff -u -p -r1.177 locale.module --- modules/locale/locale.module 30 May 2007 08:08:58 -0000 1.177 +++ modules/locale/locale.module 5 Jun 2007 03:42:46 -0000 @@ -223,6 +223,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' => $form['language']['#attributes'], '#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.121 diff -u -p -r1.121 path.module --- modules/path/path.module 4 Jun 2007 07:22:20 -0000 1.121 +++ modules/path/path.module 5 Jun 2007 03:42:48 -0000 @@ -34,6 +34,18 @@ function path_help($section) { } /** + * Implementation of hook_theme(). + */ +function path_theme() { + return array( + 'path_admin_edit' => array( + 'arguments' => array('form' => NULL), + ), + ); +} + + +/** * Implementation of hook_menu(). */ function path_menu() { @@ -72,19 +84,190 @@ function path_menu() { } /** - * Menu callback; handles pages for creating and editing URL aliases. + * Menu callback; handles forms for creating and editing URL aliases. + */ +function path_admin_edit(&$form_state) { + $args = func_get_args(); + $language = (isset($args[1]) && $args[1] != 'all') ? $args[1] : ''; + + // Unset the form state and language argument. + unset($args[0], $args[1]); + + $src = implode('/', $args); + if (!empty($src)) { + $aliases = path_load(NULL, $src, $language); + } + else { + $aliases = array(); + } + + $administer_access = user_access('administer url aliases'); + $create_access = user_access('create url aliases'); + $field_prefix = url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='); + $form = array(); + + $form['alias']['#tree'] = TRUE; + $form['alias'][0] = array( + '#type' => 'textfield', + '#title' => t('Existing system path'), + '#default_value' => $src, + '#maxlength' => 64, + '#size' => 45, + '#required' => TRUE, + '#attributes' => ($administer_access || $create_access) ? array() : array('disabled' => 'disabled'), + '#description' => t('Specify the existing path you wish to alias. For example: node/28, forum/1, taxonomy/term/12.'), + '#field_prefix' => $field_prefix + ); + $options[0] = ''; + + if (!empty($aliases)) { + $add_form = FALSE; + $attributes = array('disabled' => 'disabled'); + + // Disable system path field when editing. + $form['alias'][0]['#attributes'] = $attributes; + + foreach ($aliases as $alias) { + if (!empty($alias['active'])) { + $active_alias = $alias['pid']; + } + $form['alias'][$alias['pid']] = array( + '#type' => 'textfield', + '#title' => '', + '#default_value' => $alias['dst'], + '#maxlength' => 64, + '#attributes' => $administer_access ? array() : $attributes, + '#size' => 45, + '#field_prefix' => $field_prefix + ); + $form['operations'][$alias['pid']] = array( + '#value' => l(t('delete'), 'admin/build/path/delete/'. $alias['pid'], array('query' => array('destination' => $_GET['q']))) + ); + $options[$alias['pid']] = ''; + } + } + else { + $attributes = array(); + $add_form = TRUE; + // Select new path as active on add alias form. + $active_alias = -1; + } + + $form['alias'][-1] = array( + '#type' => 'textfield', + '#title' => t('New alias'), + '#maxlength' => 64, + '#size' => 45, + '#required' => $add_form, + '#weight' => 1, + '#description' => t('Specify a new path by which this data can be accessed. For example: about, faq, etc.'), + '#field_prefix' => $field_prefix + ); + $options[-1] = ''; + + $form['active'] = array( + '#type' => 'radios', + '#options' => $options, + '#required' => $add_form, + '#attributes' => $administer_access ? array() : array('disabled' => 'disabled'), + '#default_value' => isset($active_alias) ? $active_alias : 0, + ); + + // This will be a hidden value unless locale module is enabled + $form['language'] = array( + '#type' => 'value', + '#value' => $language, + '#attributes' => $attributes + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Save') + ); + + return $form; +} + +/** + * Theme the path creating and editing form. + */ +function theme_path_admin_edit($form) { + $header = array(t('Active'), t('Alias'), t('Operations')); + $rows = array(); + foreach (element_children($form['alias']) as $key) { + $rows[] = array( + drupal_render($form['active'][$key]), + drupal_render($form['alias'][$key]), + drupal_render($form['operations'][$key]), + ); + } + $output = theme('table', $header, $rows); + $output .= drupal_render($form); + return $output; +} + +/** + * Verify that a new URL alias is valid */ -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_validate($form, &$form_state) { + $form_values = $form_state['values']; + $src = !empty($form_values['alias'][0]) ? $form_values['alias'][0] : NULL; + + // Language is only set if locale module is enabled, otherwise save for all languages. + $language = isset($form_values['language']) ? $form_values['language'] : ''; + + $active = !empty($form_values['active']) ? $form_values['active'] : NULL; + if ($active == -1 && empty($form_values['alias'][$active])) { + form_set_error('alias][-1', t('You cannot set an empty field as active.')); } - else { - $output = path_form(); + + if (!empty($form_values['alias'])) { + foreach ($form_values['alias'] as $pid => $alias) { + if (empty($alias) && $active == $pid) { + form_set_error('alias]['. $pid, t('You cannot set an empty field as active.')); + } + 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('alias]['. $pid, t('The alias %alias is already in use in this language.', array('%alias' => $alias))); + } + } } +} - return $output; +/** + * Save a new URL alias to the database. + */ +function path_admin_edit_submit($form, &$form_state) { + $form_values = $form_state['values']; + $src = !empty($form_values['alias'][0]) ? $form_values['alias'][0] : NULL; + $new = !empty($form_values['alias'][-1]) ? $form_values['alias'][-1] : NULL; + + // 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['alias'][0], $form_values['alias'][-1]); + // Unset source and new alias, preparing a list of existing aliases. + + if (!empty($form_values['alias'])) { + foreach ($form_values['alias'] as $pid => $alias) { + if (!empty($alias)) { + path_set_alias($src, $alias, $pid, ($pid == $form_values['active']), $language); + } + else { + path_delete('', $pid); + } + } + drupal_clear_path_cache(); + } + + if (!empty($new)) { + path_set_alias($src, $new, NULL, ($form_values['active'] == -1), $language); + } + + menu_rebuild(); + drupal_set_message(t('The aliases have been saved.')); + + $form_state['redirect'] = 'admin/build/path/edit/'. (!empty($language) ? $language : 'all') .'/'. $src; + return; } /** @@ -107,24 +290,17 @@ function path_admin_delete_confirm($pid) **/ function path_admin_delete_confirm_submit($form, &$form_state) { if ($form_state['values']['confirm']) { - path_admin_delete($form_state['values']['pid']); + path_delete($form_state['values']['pid']); + drupal_set_message(t('The alias has been deleted.')); $form_state['redirect'] = 'admin/build/path'; return; } } /** - * Post-confirmation; delete an URL alias. - */ -function path_admin_delete($pid = 0) { - db_query('DELETE FROM {url_alias} WHERE pid = %d', $pid); - drupal_set_message(t('The alias has been deleted.')); -} - -/** * 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 = NULL, $language = '') { if ($path && !$alias) { // Delete based on path db_query("DELETE FROM {url_alias} WHERE src = '%s' AND language = '%s'", $path, $language); @@ -143,13 +319,16 @@ function path_set_alias($path = NULL, $a $alias_count = db_result(db_query("SELECT COUNT(dst) FROM {url_alias} WHERE dst = '%s' AND language = '%s'", $alias, $language)); if ($alias_count == 0) { + if ($active) { + db_query("UPDATE {url_alias} SET active = 0 WHERE src = '%s' AND language = '%s'", $path, $language); + } 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); + db_query("INSERT INTO {url_alias} (src, dst, active, language) VALUES ('%s', '%s', %d, '%s')", $path, $alias, $active, $language); } } // The alias exists. @@ -160,10 +339,8 @@ 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); - // 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(NULL, $alias, NULL, $active, $language); + path_set_alias($path, $alias, NULL, $active, $language); } } if ($alias_count == 0 || $path_count == 0) { @@ -173,48 +350,6 @@ function path_set_alias($path = NULL, $a } /** - * Return a form for editing or creating an individual URL alias. - */ -function path_form(&$form_state, $edit = array('src' => '', 'dst' => '', 'language' => '', 'pid' => NULL)) { - $form['#submit'][] = 'path_form_submit'; - $form['#validate'][] = 'path_form_validate'; - $form['#alias'] = $edit; - - $form['src'] = array( - '#type' => 'textfield', - '#title' => t('Existing system path'), - '#default_value' => $edit['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( - '#type' => 'textfield', - '#title' => t('Path alias'), - '#default_value' => $edit['dst'], - '#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.'), - '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=') - ); - // This will be a hidden value unless locale module is enabled - $form['language'] = array( - '#type' => 'value', - '#value' => $edit['language'] - ); - if ($edit['pid']) { - $form['pid'] = array('#type' => 'hidden', '#value' => $edit['pid']); - $form['submit'] = array('#type' => 'submit', '#value' => t('Update alias')); - } - else { - $form['submit'] = array('#type' => 'submit', '#value' => t('Create new alias')); - } - - return $form; -} - -/** * Implementation of hook_nodeapi(). * * Allows URL aliases for nodes to be specified at node edit time rather @@ -233,9 +368,10 @@ function path_nodeapi(&$node, $op, $arg) case 'load': $path = "node/$node->nid"; + $language = !empty($node->language) ? $node->language : ''; // We don't use drupal_get_path_alias() to avoid custom rewrite functions. - // We only care about exact aliases. - $result = db_query("SELECT dst FROM {url_alias} WHERE src = '%s'", $path); + // We only care about exact aliases whichever is the active one. + $result = db_query("SELECT dst FROM {url_alias} WHERE src = '%s' AND active = 1 AND language = '%s'", $path, $language); if (db_num_rows($result)) { $node->path = db_result($result); } @@ -243,20 +379,28 @@ function path_nodeapi(&$node, $op, $arg) case 'insert': // Don't try to insert if path is NULL. We may have already set - // the alias ahead of time. + // the alias ahead of time. This is always the preferred alais. if ($node->path) { - path_set_alias("node/$node->nid", $node->path); + path_set_alias("node/$node->nid", $node->path, NULL, TRUE); } break; case 'update': - path_set_alias("node/$node->nid", isset($node->path) ? $node->path : NULL, isset($node->pid) ? $node->pid : NULL); + $pid = isset($node->pid) ? $node->pid : NULL; + if (!empty($node->path)) { + // Update alias data. This is always the preferred alais. + path_set_alias("node/$node->nid", $node->path, $pid, TRUE); + } + else if (!empty($pid)) { + // Delete the alias if path was emptied. + path_delete($pid); + } break; case 'delete': $path = "node/$node->nid"; if (drupal_get_path_alias($path) != $path) { - path_set_alias($path); + path_delete(NULL, $path); } break; } @@ -307,7 +451,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 +484,10 @@ 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 is only available in multi-lingual aliases. "all" doesn't + // include every language. + $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,54 +510,76 @@ 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, &$form_state) { - $src = $form_state['values']['src']; - $dst = $form_state['values']['dst']; - $pid = isset($form_state['values']['pid']) ? $form_state['values']['pid'] : 0; - // Language is only set if locale module is enabled, otherwise save for all languages. - $language = isset($form_state['values']['language']) ? $form_state['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 = NULL, $src = '', $language = NULL) { + $output = ''; + if (isset($pid) && is_numeric($pid)) { + $output = db_fetch_array(db_query('SELECT * FROM {url_alias} WHERE pid = %d', $pid)); + } + else if (!empty($src)) { + // Let the user load aliases based on an empty (not every), or + // a particular language. + if ($language !== NULL) { + // Load for a specified language. e.g. 'en', 'de', or even ''. + $result = db_query("SELECT * FROM {url_alias} WHERE src = '%s' AND language = '%s' ORDER BY pid", $src, $language); + } + else { + // Load for every language. e.g. 'en', 'de', AND even ''. + $result = db_query("SELECT * FROM {url_alias} WHERE src = '%s' ORDER BY pid", $src); + } + while ($row = db_fetch_array($result)) { + $output[] = $row; + } } + + return $output; } /** - * Save a new URL alias to the database. + * Delete a specific URL alias from the database. */ -function path_form_submit($form, &$form_state) { - // Language is only set if locale module is enabled - path_set_alias($form_state['values']['src'], $form_state['values']['dst'], isset($form_state['values']['pid']) ? $form_state['values']['pid'] : 0, isset($form_state['values']['language']) ? $form_state['values']['language'] : ''); +function path_delete($pid = NULL, $src = '', $language = NULL) { + // Delete based on path ID. + if (isset($pid) && is_numeric($pid)) { + db_query('DELETE FROM {url_alias} WHERE pid = %d', $pid); + } + // Delete based on source(system) path. + else if (!empty($src)) { + // Let the user delete aliases based on an empty (not every), or + // a particular language. + if ($language !== NULL) { + // Delete for a specified language. e.g. 'en', 'de', or even ''. + db_query("DELETE FROM {url_alias} WHERE src = '%s' AND language = '%s'", $src, $language); + } + else { + // Delete for every language. e.g. 'en', 'de', AND even ''. + db_query("DELETE FROM {url_alias} WHERE src = '%s'", $src); + } + } - drupal_set_message(t('The alias has been saved.')); - $form_state['redirect'] = 'admin/build/path'; + drupal_clear_path_cache(); return; } /** * Return a form to filter URL aliases. */ -function path_admin_filter_form(&$form_state, $keys = '') { +function path_admin_filter_form(&$form_state) { + $args = func_get_args(); + unset($args[0]); + $keys = implode('/', $args); + + $form = array(); + $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')); return $form; } @@ -419,11 +591,3 @@ function path_admin_filter_form_submit($ return 'admin/build/path/list/'. trim($form_state['values']['filter']); } -/** - * Helper function for grabbing filter keys. - */ -function path_admin_filter_get_keys() { - // Extract keys as remainder of path - $path = explode('/', $_GET['q'], 5); - return count($path) == 5 ? $path[4] : ''; -} Index: modules/system/system.install =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.install,v retrieving revision 1.119 diff -u -p -r1.119 system.install --- modules/system/system.install 30 May 2007 08:08:58 -0000 1.119 +++ modules/system/system.install 5 Jun 2007 03:43:04 -0000 @@ -3339,6 +3339,20 @@ function system_update_6022() { return $ret; } + +/** + * Add 'active' column to url_alias table. + */ +function system_update_6023() { + $ret = array(); + + if (db_table_exists('url_alias')) { + db_add_field($ret, 'url_alias', 'active', array('type' => 'int', 'not null' => TRUE, 'default' => 0)); + } + + return $ret; +} + /** * @} End of "defgroup updates-5.x-to-6.x" * The next series of updates should start at 7000. Index: modules/system/system.schema =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.schema,v retrieving revision 1.4 diff -u -p -r1.4 system.schema --- modules/system/system.schema 30 May 2007 08:08:59 -0000 1.4 +++ modules/system/system.schema 5 Jun 2007 03:43:06 -0000 @@ -176,6 +176,7 @@ function system_schema() { 'pid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE), 'src' => array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''), 'dst' => array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''), + 'active' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), 'language' => array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => '') ), 'unique keys' => array('dst_language' => array('dst', 'language')),