'pathauto')); } /** * Implements hook_help(). */ function pathauto_help($path, $arg) { switch ($path) { case 'admin/help#pathauto': module_load_include('inc', 'pathauto'); $output = '
' . t('Provides a mechanism for modules to automatically generate aliases for the content they manage.') . '
'; $output .= '' . t('Bulk generation will only generate URL aliases for items that currently have no aliases. This is typically used when installing Pathauto on a site that has existing un-aliased content that needs to be aliased in bulk.') . '
'; return $output; } } /** * Implements hook_permission(). */ function pathauto_permission() { return array( 'administer pathauto' => array( 'title' => t('Administer pathauto'), 'description' => t('Allows a user to configure patterns for automated aliases and bulk delete URL-aliases.'), ), 'notify of path changes' => array( 'title' => t('Notify of Path Changes'), 'description' => t('Determines whether or not users are notified.'), ), ); } /** * Implements hook_menu(). */ function pathauto_menu() { $items['admin/config/search/path/patterns'] = array( 'title' => 'Patterns', 'page callback' => 'drupal_get_form', 'page arguments' => array('pathauto_patterns_form'), 'access arguments' => array('administer pathauto'), 'type' => MENU_LOCAL_TASK, 'weight' => 10, 'file' => 'pathauto.admin.inc', ); $items['admin/config/search/path/settings'] = array( 'title' => 'Settings', 'page callback' => 'drupal_get_form', 'page arguments' => array('pathauto_settings_form'), 'access arguments' => array('administer pathauto'), 'type' => MENU_LOCAL_TASK, 'weight' => 20, 'file' => 'pathauto.admin.inc', ); $items['admin/config/search/path/update_bulk'] = array( 'title' => 'Bulk generate', 'page callback' => 'drupal_get_form', 'page arguments' => array('pathauto_bulk_update_form'), 'access arguments' => array('administer url aliases'), 'type' => MENU_LOCAL_TASK, 'weight' => 30, 'file' => 'pathauto.admin.inc', ); $items['admin/config/search/path/delete_bulk'] = array( 'title' => 'Delete aliases', 'page callback' => 'drupal_get_form', 'page arguments' => array('pathauto_admin_delete'), 'access arguments' => array('administer url aliases'), 'type' => MENU_LOCAL_TASK, 'weight' => 40, 'file' => 'pathauto.admin.inc', ); return $items; } /** * Load an URL alias pattern by entity, bundle, and language. * * @param $entity * An entity (e.g. node, taxonomy, user, etc.) * @param $bundle * A bundle (e.g. content type, vocabulary ID, etc.) * @param $language * A language code, defaults to the LANGUAGE_NONE constant. */ function pathauto_pattern_load_by_entity($entity, $bundle = '', $language = LANGUAGE_NONE) { $patterns = &drupal_static(__FUNCTION__, array()); $pattern_id = "$entity:$bundle:$language"; if (!isset($patterns[$pattern_id])) { $variables = array(); if ($language != LANGUAGE_NONE) { $variables[] = "pathauto_{$entity}_{$bundle}_{$language}_pattern"; } if ($bundle) { $variables[] = "pathauto_{$entity}_{$bundle}_pattern"; } $variables[] = "pathauto_{$entity}_pattern"; foreach ($variables as $variable) { if ($pattern = trim(variable_get($variable, ''))) { break; } } $patterns[$pattern_id] = $pattern; } return $patterns[$pattern_id]; } /** * Delete multiple URL aliases. * * Intent of this is to abstract a potential path_delete_multiple() function * for Drupal 7 or 8. * * @param $pids * An array of path IDs to delete. */ function pathauto_path_delete_multiple($pids) { foreach ($pids as $pid) { path_delete(array('pid' => $pid)); } } /** * Delete an URL alias and any of its sub-paths. * * Given a source like 'node/1' this function will delete any alias that have * that specific source or any sources that match 'node/1/%'. * * @param $source * An string with a source URL path. */ function pathauto_path_delete_all($source) { $sql = "SELECT pid FROM {url_alias} WHERE source = :source OR source LIKE :source_wildcard"; $pids = db_query($sql, array(':source' => $source, ':source_wildcard' => $source . '/%'))->fetchCol(); if ($pids) { pathauto_path_delete_multiple($pids); } } /** * Delete an entity URL alias and any of its sub-paths. * * This function also checks to see if the default entity URI is different from * the current entity URI and will delete any of the default aliases. * * @param $entity_type * A string with the entity type. * @param $entity * An entity object. * @param $default_uri * The optional default uri path for the entity. */ function pathauto_entity_path_delete_all($entity_type, $entity, $default_uri = NULL) { $uri = entity_uri($entity_type, $entity); pathauto_path_delete_all($uri['path']); if (isset($default_uri) && $uri['path'] != $default_uri) { pathauto_path_delete_all($default_uri); } } /** * Implements hook_field_attach_rename_bundle(). * * Respond to machine name changes for pattern variables. */ function pathauto_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) { $variables = db_select('variable', 'v') ->fields('v', array('name')) ->condition('name', db_like("pathauto_{$entity_type}_{$bundle_old}_") . '%', 'LIKE') ->execute() ->fetchCol(); foreach ($variables as $variable) { $value = variable_get($variable, ''); variable_del($variable); $variable = strtr($variable, array("{$entity_type}_{$bundle_old}" => "{$entity_type}_{$bundle_new}")); variable_set($variable, $value); } } /** * Implements hook_field_attach_delete_bundle(). * * Respond to sub-types being deleted, their patterns can be removed. */ function pathauto_field_attach_delete_bundle($entity_type, $bundle) { $variables = db_select('variable', 'v') ->fields('v', array('name')) ->condition('name', db_like("pathauto_{$entity_type}_{$bundle}_") . '%', 'LIKE') ->execute() ->fetchCol(); foreach ($variables as $variable) { variable_del($variable); } } /** * Implements hook_field_attach_form(). * * Add the automatic alias form elements to an existing path form fieldset. */ function pathauto_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) { list($id, , $bundle) = entity_extract_ids($entity_type, $entity); if (!isset($form['path'])) { // This entity must be supported by core's path.module first. // @todo Investigate removing this and supporting all fieldable entities. return; } else { // Taxonomy terms do not have an actual fieldset for path settings. // Merge in the defaults. $form['path'] += array( '#type' => 'fieldset', '#title' => t('URL path settings'), '#collapsible' => TRUE, '#collapsed' => empty($form['path']['alias']), '#group' => 'additional_settings', '#attributes' => array( 'class' => array('path-form'), ), '#access' => user_access('create url aliases') || user_access('administer url aliases'), '#weight' => 30, '#tree' => TRUE, '#element_validate' => array('path_form_element_validate'), ); } $pattern = pathauto_pattern_load_by_entity($entity_type, $bundle, $langcode); if (empty($pattern)) { return; } if (!isset($entity->path['pathauto'])) { if (!empty($id)) { module_load_include('inc', 'pathauto'); $uri = entity_uri($entity_type, $entity); $pathauto_alias = pathauto_create_alias($entity_type, 'return', $uri['path'], array($entity_type => $entity), $bundle, $langcode); if ($pathauto_alias === FALSE) { // If Pathauto is not going to be able to generate an alias, then we // should not bother to show the checkbox since it wouldn't do anything. // Note that if a pattern does apply, but all the tokens currently // evaluate to empty strings, then $pathauto_alias would equal null and // not false. return; } else { $path = drupal_get_path_alias($uri['path'], $langcode); $entity->path['pathauto'] = ($path != $uri['path'] && $path == $pathauto_alias); } } else { $entity->path['pathauto'] = TRUE; } } // Add JavaScript that will disable the path textfield when the automatic // alias checkbox is checked. $form['path']['alias']['#states']['!enabled']['input[name="path[pathauto]"]'] = array('checked' => TRUE); // Override path.module's vertical tabs summary. $form['path']['#attached']['js'] = array( 'vertical-tabs' => drupal_get_path('module', 'pathauto') . '/pathauto.js' ); $form['path']['pathauto'] = array( '#type' => 'checkbox', '#title' => t('Generate automatic URL alias'), '#default_value' => $entity->path['pathauto'], '#description' => t('Uncheck this to create a custom alias below.'), '#weight' => -1, ); // Add a shortcut link to configure URL alias patterns. if (drupal_valid_path('admin/config/search/path/patterns')) { $form['path']['pathauto']['#description'] .= ' ' . l(t('Configure URL alias patterns.'), 'admin/config/search/path/patterns'); } if ($entity->path['pathauto'] && !empty($entity->old_alias) && empty($entity->path['alias'])) { $form['path']['alias']['#default_value'] = $entity->old_alias; $entity->path['alias'] = $entity->old_alias; } // For Pathauto to remember the old alias and prevent the Path module from // deleting it when Pathauto wants to preserve it. if (!empty($entity->path['alias'])) { $form['path']['old_alias'] = array( '#type' => 'value', '#value' => $entity->path['alias'], ); } } /** * Implements hook_entity_load(). */ function pathauto_entity_load($entities, $entity_type) { // Statically cache which entity types have data in the pathauto_state // table to avoid unnecessary queries for entities that would not have any // data anyway. static $loadable_types; if (!isset($loadable_types)) { $loadable_types = &drupal_static(__FUNCTION__); if (!isset($loadable_types)) { // Prevent errors if pathauto_update_7006() has not yet been run. if (!db_table_exists('pathauto_state')) { $loadable_types = array(); } else { $loadable_types = db_query("SELECT DISTINCT entity_type FROM {pathauto_state}")->fetchCol(); } } } // Check if this entity type has loadable records. if (!in_array($entity_type, $loadable_types)) { return; } $states = pathauto_entity_state_load_multiple($entity_type, array_keys($entities)); foreach ($states as $id => $state) { if (!isset($entities[$id]->path)) { $entities[$id]->path = array(); } if (is_array($entities[$id]->path) && !isset($entities[$id]->path['pathauto'])) { $entities[$id]->path['pathauto'] = $state; } } } /** * Implements hook_entity_presave(). */ function pathauto_entity_presave($entity, $entity_type) { if (isset($entity->path['pathauto']) && is_array($entity->path)) { // We must set an empty alias string for the path to prevent saving an // alias. $entity->path += array('alias' => ''); } // About to be saved (before insert/update) if (!empty($entity->path['pathauto']) && isset($entity->path['old_alias']) && $entity->path['alias'] == '' && $entity->path['old_alias'] != '') { /** * There was an old alias, but when pathauto_perform_alias was checked * the javascript disabled the textbox which led to an empty value being * submitted. Restoring the old path-value here prevents the Path module * from deleting any old alias before Pathauto gets control. */ $entity->path['alias'] = $entity->path['old_alias']; } // Help prevent errors with progromatically creating entities by defining // path['alias'] as an empty string. // @see http://drupal.org/node/1328180 // @see http://drupal.org/node/1576552 if (isset($entity->path['pathauto']) && !isset($entity->path['alias'])) { $entity->path['alias'] = ''; } } /** * Implements hook_entity_insert(). */ function pathauto_entity_insert($entity, $entity_type) { if (isset($entity->path['pathauto'])) { pathauto_entity_state_save($entity_type, $entity, $entity->path['pathauto']); } } /** * Implements hook_entity_update(). */ function pathauto_entity_update($entity, $entity_type) { if (isset($entity->path['pathauto'])) { pathauto_entity_state_save($entity_type, $entity, $entity->path['pathauto']); } } /** * Implements hook_entity_delete(). */ function pathauto_entity_delete($entity, $entity_type) { if (isset($entity->path['pathauto'])) { pathauto_entity_state_delete($entity_type, $entity); } } /** * Load a pathauto state for an entity. * * @param string $entity_type * An entity type. * @param int $entity_id * An entity ID. * * @return bool * A value that evaluates to TRUE if Pathauto should control this entity's * path. A value that evaluates to FALSE if Pathauto should not manage the * entity's path. */ function pathauto_entity_state_load($entity_type, $entity_id) { $pathauto_state = pathauto_entity_state_load_multiple($entity_type, array($entity_id)); return !empty($pathauto_state) ? reset($pathauto_state) : FALSE; } /** * Load a pathauto state for multiple entities. * * @param string $entity_type * The entity type. * @param int[] $entity_ids * The array of entity IDs. * * @return bool[] * An array of Pathauto states keyed by entity ID. */ function pathauto_entity_state_load_multiple($entity_type, $entity_ids) { return db_query("SELECT entity_id, pathauto FROM {pathauto_state} WHERE entity_type = :entity_type AND entity_id IN (:entity_ids)", array(':entity_type' => $entity_type, ':entity_ids' => $entity_ids))->fetchAllKeyed(); } /** * Save the pathauto state for an entity. * * @param string $entity_type * The entity type. * @param object $entity * The entity object. * @param bool $pathauto_state * A value that evaluates to TRUE means that Pathauto should keep controlling * this entity's path in the future. A value that evaluates to FALSE means * that Pathauto should not manage the entity's path. */ function pathauto_entity_state_save($entity_type, $entity, $pathauto_state) { list($entity_id) = entity_extract_ids($entity_type, $entity); db_merge('pathauto_state') ->key(array( 'entity_type' => $entity_type, 'entity_id' => $entity_id, )) ->fields(array( 'pathauto' => $pathauto_state ? 1 : 0, )) ->execute(); drupal_static_reset('pathauto_entity_load'); } /** * Delete the pathauto state for an entity. * * @param string $entity_type * The entity type. * @param object $entity * The entity object. */ function pathauto_entity_state_delete($entity_type, $entity) { list($entity_id) = entity_extract_ids($entity_type, $entity); db_delete('pathauto_state') ->condition('entity_type', $entity_type) ->condition('entity_id', $entity_id) ->execute(); drupal_static_reset('pathauto_entity_load'); } /** * Implements hook_action_info(). */ function pathauto_action_info() { $info['pathauto_node_update_action'] = array( 'type' => 'node', 'label' => t('Update node alias'), 'configurable' => FALSE, 'triggers' => array(), ); $info['pathauto_taxonomy_term_update_action'] = array( 'type' => 'taxonomy_term', 'label' => t('Update taxonomy term alias'), 'configurable' => FALSE, 'triggers' => array(), ); $info['pathauto_user_update_action'] = array( 'type' => 'user', 'label' => t('Update user alias'), 'configurable' => FALSE, 'triggers' => array(), ); return $info; } /** * Returns the language code of the given entity. * * Backward compatibility layer to ensure that installations running an older * version of core where entity_language() is not available do not break. * * @param string $entity_type * An entity type. * @param object $entity * An entity object. * @param bool $check_language_property * A boolean if TRUE, will attempt to fetch the language code from * $entity->language if the entity_language() function failed or does not * exist. Default is TRUE. */ function pathauto_entity_language($entity_type, $entity, $check_language_property = TRUE) { $langcode = NULL; if (function_exists('entity_language')) { $langcode = entity_language($entity_type, $entity); } elseif ($check_language_property && !empty($entity->language)) { $langcode = $entity->language; } return !empty($langcode) ? $langcode : LANGUAGE_NONE; } function pathauto_is_alias_reserved($alias, $source, $langcode = LANGUAGE_NONE) { foreach (module_implements('pathauto_is_alias_reserved') as $module) { $result = module_invoke($module, 'pathauto_is_alias_reserved', $alias, $source, $langcode); if (!empty($result)) { // As soon as the first module says that an alias is in fact reserved, // then there is no point in checking the rest of the modules. return TRUE; } } return FALSE; } /** * Implements hook_pathauto_is_alias_reserved() on behalf of path.module. */ function path_pathauto_is_alias_reserved($alias, $source, $langcode) { // For language neutral content, we need to make sure the alias doesn't // collide with any existing aliases. For localized content, just make sure // it doesn't collide with same language or language neutral aliases. $query = db_select('url_alias', 'ua') ->fields('ua', array('pid')) ->condition('source', $source, '<>') ->condition('alias', $alias); if ($langcode != LANGUAGE_NONE) { $query->condition('language', array($langcode, LANGUAGE_NONE), 'IN'); } return $query->execute()->rowCount() > 0; } /** * Implements hook_pathauto_is_alias_reserved(). */ function pathauto_pathauto_is_alias_reserved($alias, $source, $langcode) { module_load_include('inc', 'pathauto'); return _pathauto_path_is_callback($alias); } if (!function_exists('path_field_extra_fields')) { /** * Implements hook_field_extra_fields() on behalf of path.module. * * Add support for the 'URL path settings' to be re-ordered by the user on the * 'Manage Fields' tab of content types and vocabularies. */ function path_field_extra_fields() { $info = array(); foreach (node_type_get_types() as $node_type) { if (!isset($info['node'][$node_type->type]['form']['path'])) { $info['node'][$node_type->type]['form']['path'] = array( 'label' => t('URL path settings'), 'description' => t('Path module form elements'), 'weight' => 30, ); } } if (module_exists('taxonomy')) { $vocabularies = taxonomy_get_vocabularies(); foreach ($vocabularies as $vocabulary) { if (!isset($info['taxonomy_term'][$vocabulary->machine_name]['form']['path'])) { $info['taxonomy_term'][$vocabulary->machine_name]['form']['path'] = array( 'label' => t('URL path settings'), 'description' => t('Path module form elements'), 'weight' => 30, ); } } } return $info; } } /** * @name pathauto_node Pathauto integration for the core node module. * @{ */ /** * Implements hook_path_alias_types() on behalf of node module. */ function node_path_alias_types() { return array('node/' => t('Content')); } /** * Implements hook_pathauto() on behalf of node module. */ function node_pathauto($op) { if ($op == 'settings') { $settings = array(); $settings['module'] = 'node'; $settings['token_type'] = 'node'; $settings['groupheader'] = t('Content paths'); $settings['patterndescr'] = t('Default path pattern (applies to all content types with blank patterns below)'); $settings['patterndefault'] = 'content/[node:title]'; $settings['batch_update_callback'] = 'node_pathauto_bulk_update_batch_process'; $settings['batch_file'] = drupal_get_path('module', 'pathauto') . '/pathauto.pathauto.inc'; $languages = array(); if (module_exists('locale')) { $languages = array(LANGUAGE_NONE => t('language neutral')) + locale_language_list('name'); } foreach (node_type_get_names() as $node_type => $node_name) { if (count($languages) && variable_get('language_content_type_' . $node_type, 0)) { $settings['patternitems'][$node_type] = t('Default path pattern for @node_type (applies to all @node_type content types with blank patterns below)', array('@node_type' => $node_name)); foreach ($languages as $lang_code => $lang_name) { $settings['patternitems'][$node_type . '_' . $lang_code] = t('Pattern for all @language @node_type paths', array('@node_type' => $node_name, '@language' => $lang_name)); } } else { $settings['patternitems'][$node_type] = t('Pattern for all @node_type paths', array('@node_type' => $node_name)); } } return (object) $settings; } } /** * Implements hook_node_insert(). */ function pathauto_node_insert($node) { // @todo Remove the next line when http://drupal.org/node/1025870 is fixed. unset($node->uri); pathauto_node_update_alias($node, 'insert'); } /** * Implements hook_node_update(). */ function pathauto_node_update($node) { pathauto_node_update_alias($node, 'update'); } /** * Implements hook_node_delete(). */ function pathauto_node_delete($node) { pathauto_entity_path_delete_all('node', $node, "node/{$node->nid}"); } /** * Implements hook_form_BASE_FORM_ID_alter(). * * Add the Pathauto settings to the node form. */ function pathauto_form_node_form_alter(&$form, &$form_state) { $node = $form_state['node']; $langcode = pathauto_entity_language('node', $node); pathauto_field_attach_form('node', $node, $form, $form_state, $langcode); } /** * Implements hook_node_operations(). */ function pathauto_node_operations() { $operations['pathauto_update_alias'] = array( 'label' => t('Update URL alias'), 'callback' => 'pathauto_node_update_alias_multiple', 'callback arguments' => array('bulkupdate', array('message' => TRUE)), ); return $operations; } /** * Update the URL aliases for an individual node. * * @param $node * A node object. * @param $op * Operation being performed on the node ('insert', 'update' or 'bulkupdate'). * @param $options * An optional array of additional options. */ function pathauto_node_update_alias(stdClass $node, $op, array $options = array()) { // Skip processing if the user has disabled pathauto for the node. if (isset($node->path['pathauto']) && empty($node->path['pathauto']) && empty($options['force'])) { return FALSE; } $options += array('language' => pathauto_entity_language('node', $node)); // Skip processing if the node has no pattern. if (!pathauto_pattern_load_by_entity('node', $node->type, $options['language'])) { return FALSE; } module_load_include('inc', 'pathauto'); $uri = entity_uri('node', $node); return pathauto_create_alias('node', $op, $uri['path'], array('node' => $node), $node->type, $options['language']); } /** * Update the URL aliases for multiple nodes. * * @param $nids * An array of node IDs. * @param $op * Operation being performed on the nodes ('insert', 'update' or * 'bulkupdate'). * @param $options * An optional array of additional options. */ function pathauto_node_update_alias_multiple(array $nids, $op, array $options = array()) { $options += array('message' => FALSE); $nodes = node_load_multiple($nids); foreach ($nodes as $node) { pathauto_node_update_alias($node, $op, $options); } if (!empty($options['message'])) { drupal_set_message(format_plural(count($nids), 'Updated URL alias for 1 node.', 'Updated URL aliases for @count nodes.')); } } /** * Update action wrapper for pathauto_node_update_alias(). */ function pathauto_node_update_action($node, $context = array()) { pathauto_node_update_alias($node, 'bulkupdate', array('message' => TRUE)); } /** * @} End of "name pathauto_node". */ /** * @name pathauto_taxonomy Pathauto integration for the core taxonomy module. * @{ */ /** * Implements hook_path_alias_types() on behalf of taxonomy module. */ function taxonomy_path_alias_types() { return array('taxonomy/term/' => t('Taxonomy terms')); } /** * Implements hook_pathauto() on behalf of taxonomy module. */ function taxonomy_pathauto($op) { if ($op == 'settings') { $settings = array(); $settings['module'] = 'taxonomy_term'; $settings['token_type'] = 'term'; $settings['groupheader'] = t('Taxonomy term paths'); $settings['patterndescr'] = t('Default path pattern (applies to all vocabularies with blank patterns below)'); $settings['patterndefault'] = '[term:vocabulary]/[term:name]'; $settings['batch_update_callback'] = 'taxonomy_pathauto_bulk_update_batch_process'; $settings['batch_file'] = drupal_get_path('module', 'pathauto') . '/pathauto.pathauto.inc'; $vocabularies = taxonomy_get_vocabularies(); if (count($vocabularies)) { $settings['patternitems'] = array(); foreach ($vocabularies as $vid => $vocabulary) { if ($vid == variable_get('forum_nav_vocabulary', '')) { // Skip the forum vocabulary. continue; } $settings['patternitems'][$vocabulary->machine_name] = t('Pattern for all %vocab-name paths', array('%vocab-name' => $vocabulary->name)); } } return (object) $settings; } } /** * Implements hook_taxonomy_term_insert(). */ function pathauto_taxonomy_term_insert($term) { pathauto_taxonomy_term_update_alias($term, 'insert'); } /** * Implements hook_taxonomy_term_update(). */ function pathauto_taxonomy_term_update($term) { pathauto_taxonomy_term_update_alias($term, 'update', array('alias children' => TRUE)); } /** * Implements hook_taxonomy_term_delete(). */ function pathauto_taxonomy_term_delete($term) { pathauto_entity_path_delete_all('taxonomy_term', $term, "taxonomy/term/{$term->tid}"); } /** * Implements hook_form_FORM_ID_alter(). * * Add the Pathauto settings to the taxonomy term form. */ function pathauto_form_taxonomy_form_term_alter(&$form, $form_state) { $term = $form_state['term']; $langcode = pathauto_entity_language('taxonomy_term', $term); pathauto_field_attach_form('taxonomy_term', $term, $form, $form_state, $langcode); } /** * Update the URL aliases for an individual taxonomy term. * * @param $term * A taxonomy term object. * @param $op * Operation being performed on the term ('insert', 'update' or 'bulkupdate'). * @param $options * An optional array of additional options. */ function pathauto_taxonomy_term_update_alias(stdClass $term, $op, array $options = array()) { // Skip processing if the user has disabled pathauto for the term. if (isset($term->path['pathauto']) && empty($term->path['pathauto']) && empty($options['force'])) { return FALSE; } $module = 'taxonomy_term'; if ($term->vid == variable_get('forum_nav_vocabulary', '')) { if (module_exists('forum')) { $module = 'forum'; } else { return FALSE; } } // Check that the term has its bundle, which is the vocabulary's machine name. if (!isset($term->vocabulary_machine_name)) { $vocabulary = taxonomy_vocabulary_load($term->vid); $term->vocabulary_machine_name = $vocabulary->machine_name; } $options += array( 'alias children' => FALSE, 'language' => pathauto_entity_language('taxonomy_term', $term), ); // Skip processing if the term has no pattern. if (!pathauto_pattern_load_by_entity($module, $term->vocabulary_machine_name)) { return FALSE; } module_load_include('inc', 'pathauto'); $uri = entity_uri('taxonomy_term', $term); $result = pathauto_create_alias($module, $op, $uri['path'], array('term' => $term), $term->vocabulary_machine_name, $options['language']); if (!empty($options['alias children'])) { // For all children generate new aliases. unset($options['language']); foreach (taxonomy_get_children($term->tid, $term->vid) as $subterm) { pathauto_taxonomy_term_update_alias($subterm, $op, $options); } } return $result; } /** * Update the URL aliases for multiple taxonomy terms. * * @param $tids * An array of term IDs. * @param $op * Operation being performed on the nodes ('insert', 'update' or * 'bulkupdate'). * @param $options * An optional array of additional options. */ function pathauto_taxonomy_term_update_alias_multiple(array $tids, $op, array $options = array()) { $options += array('message' => FALSE); $terms = taxonomy_term_load_multiple($tids); foreach ($terms as $term) { pathauto_taxonomy_term_update_alias($term, $op, $options); } if (!empty($options['message'])) { drupal_set_message(format_plural(count($tids), 'Updated URL alias for 1 term.', 'Updated URL aliases for @count terms.')); } } /** * Update action wrapper for pathauto_taxonomy_term_update_alias(). */ function pathauto_taxonomy_term_update_action($term, $context = array()) { pathauto_taxonomy_term_update_alias($term, 'bulkupdate', array('message' => TRUE)); } /** * @} End of "name pathauto_taxonomy". */ /** * @name pathauto_forum Pathauto integration for the core forum module. * @{ */ /** * Implements hook_path_alias_types() on behalf of forum module. */ function forum_path_alias_types() { return array('forum/' => t('Forums')); } /** * Implements hook_pathauto() for forum module. */ function forum_pathauto($op) { if ($op == 'settings') { $settings = array(); $settings['module'] = 'forum'; $settings['token_type'] = 'term'; $settings['groupheader'] = t('Forum paths'); $settings['patterndescr'] = t('Pattern for forums and forum containers'); $settings['patterndefault'] = '[term:vocabulary]/[term:name]'; $settings['batch_update_callback'] = 'forum_pathauto_bulk_update_batch_process'; $settings['batch_file'] = drupal_get_path('module', 'pathauto') . '/pathauto.pathauto.inc'; return (object) $settings; } } /** * @} End of "name pathauto_forum". */ /** * @name pathauto_user Pathauto integration for the core user and blog modules. * @{ */ /** * Implements hook_path_alias_types() on behalf of user module. */ function user_path_alias_types() { return array('user/' => t('Users')); } /** * Implements hook_pathauto() on behalf of user module. */ function user_pathauto($op) { if ($op == 'settings') { $settings = array(); $settings['module'] = 'user'; $settings['token_type'] = 'user'; $settings['groupheader'] = t('User paths'); $settings['patterndescr'] = t('Pattern for user account page paths'); $settings['patterndefault'] = 'users/[user:name]'; $settings['batch_update_callback'] = 'user_pathauto_bulk_update_batch_process'; $settings['batch_file'] = drupal_get_path('module', 'pathauto') . '/pathauto.pathauto.inc'; return (object) $settings; } } /** * Implements hook_user_insert(). */ function pathauto_user_insert(&$edit, $account, $category) { pathauto_user_update_alias($account, 'insert'); } /** * Implements hook_user_update(). */ function pathauto_user_update(&$edit, $account, $category) { pathauto_user_update_alias($account, 'update'); } /** * Implements hook_user_delete(). */ function pathauto_user_delete($account) { pathauto_entity_path_delete_all('user', $account, "user/{$account->uid}"); pathauto_path_delete_all("blog/{$account->uid}"); } /** * Implements hook_user_operations(). */ function pathauto_user_operations() { $operations['pathauto_update_alias'] = array( 'label' => t('Update URL alias'), 'callback' => 'pathauto_user_update_alias_multiple', 'callback arguments' => array('bulkupdate', array('message' => TRUE)), ); return $operations; } /** * Update the URL aliases for an individual user account. * * @param $account * A user account object. * @param $op * Operation being performed on the account ('insert', 'update' or * 'bulkupdate'). * @param $options * An optional array of additional options. */ function pathauto_user_update_alias(stdClass $account, $op, array $options = array()) { // Skip processing if the user has disabled pathauto for the account. if (isset($account->path['pathauto']) && empty($account->path['pathauto']) && empty($options['force'])) { return FALSE; } $options += array( 'alias blog' => module_exists('blog'), // $user->language is not the user entity language, thus we need to skip // the property fallback check. 'language' => pathauto_entity_language('user', $account, FALSE), ); // Skip processing if the account has no pattern. if (!pathauto_pattern_load_by_entity('user', '', $options['language'])) { return FALSE; } module_load_include('inc', 'pathauto'); $uri = entity_uri('user', $account); $return = pathauto_create_alias('user', $op, $uri['path'], array('user' => $account), NULL, $options['language']); // Because blogs are also associated with users, also generate the blog paths. if (!empty($options['alias blog'])) { pathauto_blog_update_alias($account, $op, $options); } return $return; } /** * Update the URL aliases for multiple user accounts. * * @param $uids * An array of user account IDs. * @param $op * Operation being performed on the accounts ('insert', 'update' or * 'bulkupdate'). * @param $options * An optional array of additional options. */ function pathauto_user_update_alias_multiple(array $uids, $op, array $options = array()) { $options += array('message' => FALSE); $accounts = user_load_multiple($uids); foreach ($accounts as $account) { pathauto_user_update_alias($account, $op, $options); } if (!empty($options['message'])) { drupal_set_message(format_plural(count($uids), 'Updated URL alias for 1 user account.', 'Updated URL aliases for @count user accounts.')); } } /** * Update action wrapper for pathauto_user_update_alias(). */ function pathauto_user_update_action($account, $context = array()) { pathauto_user_update_alias($account, 'bulkupdate', array('message' => TRUE)); } /** * @} End of "name pathauto_user". */ /** * @name pathauto_blog Pathauto integration for the core blog module. * @{ */ /** * Implements hook_path_alias_types() on behalf of blog module. */ function blog_path_alias_types() { return array('blog/' => t('User blogs')); } /** * Implements hook_pathauto() on behalf of blog module. */ function blog_pathauto($op) { if ($op == 'settings') { $settings = array(); $settings['module'] = 'blog'; $settings['token_type'] = 'user'; $settings['groupheader'] = t('Blog paths'); $settings['patterndescr'] = t('Pattern for blog page paths'); $settings['patterndefault'] = 'blogs/[user:name]'; $settings['batch_update_callback'] = 'blog_pathauto_bulk_update_batch_process'; $settings['batch_file'] = drupal_get_path('module', 'pathauto') . '/pathauto.pathauto.inc'; return (object) $settings; } } /** * Update the blog URL aliases for an individual user account. * * @param $account * A user account object. * @param $op * Operation being performed on the blog ('insert', 'update' or * 'bulkupdate'). * @param $options * An optional array of additional options. */ function pathauto_blog_update_alias(stdClass $account, $op, array $options = array()) { // Skip processing if the blog has no pattern. if (!pathauto_pattern_load_by_entity('blog')) { return FALSE; } $options += array( 'language' => LANGUAGE_NONE, ); module_load_include('inc', 'pathauto'); if (node_access('create', 'blog', $account)) { return pathauto_create_alias('blog', $op, "blog/{$account->uid}", array('user' => $account), NULL, $options['language']); } else { pathauto_path_delete_all("blog/{$account->uid}"); } } /** * @} End of "name pathauto_blog". */ /** * Implements hook_features_pipe_COMPONENT_alter(). */ function pathauto_features_pipe_node_alter(&$pipe, $data, $export) { foreach ($data as $node_type) { $pipe['variable'][] = "pathauto_node_{$node_type}_pattern"; if (module_exists('locale')) { $langcodes = array_keys(locale_language_list('name')); $langcodes[] = LANGUAGE_NONE; foreach ($langcodes as $langcode) { $pipe['variable'][] = "pathauto_node_{$node_type}_{$langcode}_pattern"; } } } } /** * Implements hook_features_pipe_COMPONENT_alter(). */ function pathauto_features_pipe_taxonomy_alter(&$pipe, $data, $export) { foreach ($data as $vocabulary) { $pipe['variable'][] = "pathauto_taxonomy_term_{$vocabulary}_pattern"; } }