'. 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_admin_edit' => array( + 'arguments' => array('form' => NULL), + ), + ); +} /** * Implementation of hook_menu(). @@ -43,15 +54,41 @@ function path_menu() { 'access arguments' => array('administer url aliases'), ); $items['admin/build/path/edit'] = array( - 'title' => 'Edit alias', - 'page callback' => 'drupal_get_form', + 'title' => t('Edit alias'), 'page arguments' => array('path_admin_edit'), 'type' => MENU_CALLBACK, ); - $items['admin/build/path/delete'] = array( + if (module_exists('locale')) { + $items['admin/build/path/edit/all/%path'] = array( + 'title' => 'All languages', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('path_admin_edit', 4, 5), + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -1, + ); + foreach (locale_language_list('name') as $key => $language) { + $items['admin/build/path/edit/'. $key .'/%path'] = array( + 'title' => '!language', + 'page callback' => 'drupal_get_form', + 'title arguments' => array('!language' => $language), + 'page arguments' => array('path_admin_edit', 4, 5), + 'type' => MENU_LOCAL_TASK, + ); + } + } + else { + $items['admin/build/path/edit/%/%path'] = array( + 'title' => 'All languages', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('path_admin_edit', 4, 5), + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -1, + ); + } + $items['admin/build/path/delete/%path'] = array( 'title' => 'Delete alias', 'page callback' => 'drupal_get_form', - 'page arguments' => array('path_admin_delete_confirm'), + 'page arguments' => array('path_admin_delete_confirm', 4), 'type' => MENU_CALLBACK, ); $items['admin/build/path/list'] = array( @@ -71,28 +108,199 @@ 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($pid = 0) { - if ($pid) { - $alias = path_load($pid); - drupal_set_title(check_plain($alias['dst'])); - $output = path_form($alias); +function path_admin_edit(&$form_state, $language = '', $path = array()) { + $language = isset($language) && module_exists('locale') ? ($language == 'all' ? '' : $language) : NULL; + if (!empty($path['pid'])) { + $aliases = path_load(NULL, $path['src'], $language); + $add_form = FALSE; + } + else { + $aliases = array(); + $add_form = TRUE; + } + + $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' => !empty($path['src']) ? $path['src'] : '', + '#maxlength' => 64, + '#size' => 45, + '#required' => TRUE, + '#attributes' => $add_form ? array() : array('disabled' => 'disabled'), + '#description' => $add_form ? t('Specify the existing path which you wish to alias. For example: node/28, taxonomy/term/12.') : '', + '#field_prefix' => $field_prefix, + ); + $options[0] = ''; + + if (!empty($aliases)) { + // Disable system path field when editing. + $form['alias'][0]['#attributes'] = array('disabled' => 'disabled'); + + 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, + '#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 { - $output = path_form(); + // 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 alias by which the above 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, + '#default_value' => isset($active_alias) ? $active_alias : 0, + ); - return $output; + // This will be a hidden value unless locale module is enabled + $form['language'] = array( + '#type' => 'value', + '#value' => !empty($language) ? $language : '', + '#access' => arg(3) == 'add', + ); + + $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(array('data' => t('Redirect to'), 'style' => 'white-space: nowrap;'), t('Aliases')); + if (!empty($form['operations'])) { + $header[] = t('Operations'); + } + $rows = array(); + foreach (element_children($form['alias']) as $key) { + $rows[] = array( + array('data' => drupal_render($form['active'][$key]), 'align' => 'center'), + 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_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.')); + } + + if (!empty($form_values['alias'])) { + $locale_exists = module_exists('locale'); + foreach ($form_values['alias'] as $pid => $alias) { + if (empty($alias)) { + if ($active == $pid) { + form_set_error('alias]['. $pid, t('You cannot set an empty field as active.')); + } + } + else { + $query = "SELECT COUNT(dst) FROM {url_alias} WHERE pid != %d AND dst = '%s'"; + $query .= $locale_exists ? " AND language = '%s'" : ''; + if (db_result(db_query($query, $pid, $alias, $language))) { + $error = $locale_exists ? t('The alias %alias is already in use in this language.', array('%alias' => $alias)) : t('The alias %alias is already in use.', array('%alias' => $alias)); + form_set_error('alias]['. $pid, $error); + } + } + } + } +} + +/** + * 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.')); + + $pid = db_result(db_query("SELECT MAX(pid) FROM {url_alias} WHERE src = '%s'", $src)); + $language = !empty($language) ? $language : 'all'; + $form_state['redirect'] = 'admin/build/path' . (!empty($pid) ? "/edit/$language/$pid" : ''); + + return; } /** * Menu callback; confirms deleting an URL alias **/ -function path_admin_delete_confirm($pid) { - $path = path_load($pid); +function path_admin_delete_confirm(&$form_state, $path) { if (user_access('administer url aliases')) { - $form['pid'] = array('#type' => 'value', '#value' => $pid); + $form['pid'] = array('#type' => 'value', '#value' => $path['pid']); $output = confirm_form($form, t('Are you sure you want to delete path alias %title?', array('%title' => $path['dst'])), isset($_GET['destination']) ? $_GET['destination'] : 'admin/build/path'); @@ -105,24 +313,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); @@ -141,13 +342,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. @@ -158,10 +362,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) { @@ -171,48 +373,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 @@ -240,20 +400,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 set as the active alias. 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 set as the active 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; } @@ -304,12 +472,15 @@ 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 $count = db_result(db_query("SELECT COUNT(*) FROM {url_alias} WHERE language != ''")); - $multilanguage = (module_exists('locale') || $count); + $multilanguage = module_exists('locale'); if ($keys) { // Replace wildcards with MySQL/PostgreSQL wildcards. @@ -321,7 +492,7 @@ function path_admin_overview($keys = NUL } $header = array( array('data' => t('Alias'), 'field' => 'dst', 'sort' => 'asc'), - array('data' => t('System'), 'field' => 'src'), + array('data' => t('System path'), 'field' => 'src'), array('data' => t('Operations'), 'colspan' => '2') ); if ($multilanguage) { @@ -334,7 +505,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->pid", 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]; @@ -357,54 +529,77 @@ 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 the $form_state argument. + 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' => '