Create path alias for hook_menu URLS based on node information I'm looking for guidance on how to dynamically create a path alias for dynamic paths that I create in a module using hook menu. For example, I have a module that defines a path such as subsite/template2/% where % expects the nid of a node. For that specific node, however, I want to create the alias based on the title of the node.

So if the title at subsite/node/5 is "Community Gardens" I want the path alias of subsite/template2/% to become subsite/community-gardens. I'm pretty certain this is possible, I'm just not sure how to do it. It even gets more complicated because there are sub tabs that need to be aliased but I figure if I can figure out the first part I can puzzle out the rest. (Using version 7)
For this I would use the pathauto module, which I am assuming you already have installed.

It provides a bunch of hooks to allow you to integrate with pathauto for your own needs.

For documentation, see the API.txt file in the pathauto module (from what I can tell though that file seems a little out of date).

You can look at working examples of its usage to give you an idea how to make your own.
To see these look in the pathauto.pathauto.inc file in the pathauto module.

Here is an example of what you might do (I haven't tested it but it should be close to what you need if it isn't exactly):

This is for the install file:

/**
 * Implements hook_install().
 */
function MODULENAME_install() {
  // Set the default value of the pathauto pattern.
  variable_set('pathauto_subsite_pattern', 'subsite/[node:title]');
}

/**
 * Implements hook_uninstall().
 */
function MODULENAME_uninstall() {
  // Remove the pathauto pattern.
  variable_del('pathauto_subsite_pattern');
}


This is for the module file (note you can move some of this out into an include file if you prefer):

/**
 * The internal path pattern for subsites.
 */
define('MODULENAME_SUBSITE_INTERNAL_PATH', 'subsite/template2/');

/**
 * Implements hook_path_alias_types().
 */
function MODULENAME_path_alias_types() {
  $objects = array();
  $objects[MODULENAME_SUBSITE_INTERNAL_PATH] = t('Subsites');
  return $objects;
}

/**
 * Implements hook_pathauto().
 */
function MODULENAME_pathauto($op) {
  switch ($op) {
    case 'settings':
      $settings = array();
      $settings['module'] = 'MODULENAME';
      $settings['token_type'] = 'node';
      $settings['groupheader'] = t('Subsite paths');
      $settings['patterndescr'] = t('Pattern for subsite');
      $settings['patterndefault'] = 'subsite/[node:title]';
      $settings['batch_update_callback'] = 'MODULENAME_subsite_pathauto_bulk_update_batch_process';
      return (object) $settings;
    default:
      break;
  }
}

/**
 * Batch processing callback; Generate aliases for subsites.
 */
function MODULENAME_subsite_pathauto_bulk_update_batch_process(&amp;$context) {
  if (!isset($context['sandbox']['current'])) {
    $context['sandbox']['count'] = 0;
    $context['sandbox']['current'] = 0;
  }

  $query = db_select('node', 'n');
  $query-&gt;leftJoin('url_alias', 'ua', "CONCAT(MODULENAME_SUBSITE_INTERNAL_PATH, n.nid) = ua.source");
  $query-&gt;addField('n', 'nid');
  $query-&gt;isNull('ua.source');
  $query-&gt;condition('n.nid', $context['sandbox']['current'], '&gt;');
  $query-&gt;orderBy('n.nid');
  $query-&gt;addTag('pathauto_bulk_update');
  $query-&gt;addMetaData('entity', 'node');

  // Get the total amount of items to process.
  if (!isset($context['sandbox']['total'])) {
    $context['sandbox']['total'] = $query-&gt;countQuery()-&gt;execute()-&gt;fetchField();

    // If there are no nodes to update, the stop immediately.
    if (!$context['sandbox']['total']) {
      $context['finished'] = 1;
      return;
    }
  }

  $query-&gt;range(0, 25);
  $nids = $query-&gt;execute()-&gt;fetchCol();

  MODULENAME_subsite_update_alias_multiple($nids, 'bulkupdate');
  $context['sandbox']['count'] += count($nids);
  $context['sandbox']['current'] = max($nids);
  $context['message'] = t('Updated subsute alias for node @nid.', array('@nid' =&gt; end($nids)));

  if ($context['sandbox']['count'] != $context['sandbox']['total']) {
    $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total'];
  }
}

/**
 * Update the subsite 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 MODULENAME_subsite_update_alias_multiple(array $nids, $op, array $options = array()) {
  $options += array('message' =&gt; FALSE);

  $nodes = node_load_multiple($nids);
  foreach ($nodes as $node) {
    MODULENAME_subsite_update_alias($node, $op, $options);
  }

  if (!empty($options['message'])) {
    drupal_set_message(format_plural(count($nids), 'Updated URL alias for 1 subsite.', 'Updated URL aliases for @count subsites.'));
  }
}

/**
 * Update the sibsite 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 MODULENAME_subsite_update_alias(stdClass $node, $op, array $options = array()) {
  // Skip processing if the subsite has no pattern.
  if (!pathauto_pattern_load_by_entity('subsite')) {
    return;
  }

  module_load_include('inc', 'pathauto');
  pathauto_create_alias('test', $op, MODULENAME_SUBSITE_INTERNAL_PATH . $node-&gt;nid, array('node' =&gt; $node));
}

/**
 * Implements hook_node_insert().
 */
function MODULENAME_node_insert($node) {
  // Insert subsite alias.
  MODULENAME_subsite_update_alias($node, 'insert');
}

/**
 * Implements hook_node_update().
 */
function MODULENAME_node_update($node) {
  // Update subsite alias.
  MODULENAME_subsite_update_alias($node, 'update');
}

/**
 * Implements hook_node_delete().
 */
function MODULENAME_node_delete($node) {
  // Delete subsite aliases.
  pathauto_path_delete_all(MODULENAME_SUBSITE_INTERNAL_PATH . $node-&gt;nid);
}

/**
 * Implements hook_node_operations().
 */
function MODULENAME_node_operations() {
  // This will add an operation to update subsite aliases to the content
  // administration page.
  $operations['MODULENAME_subsite_update_alias'] = array(
    'label' =&gt; t('Update subsite URL alias'),
    'callback' =&gt; 'MODULENAME_subsite_update_alias_multiple',
    'callback arguments' =&gt; array('bulkupdate', array('message' =&gt; TRUE)),
  );
  return $operations;
}


This will use a default path alias pattern of subsite/[node:title] but allow you to configure it on the path auto settings page.

It will update the alias when a node is added, edited or deleted.

It will allow bulk updating of path aliases via the bulk update area of the path auto settings pages.

It will allow bulk updating of aliases via the options on the content administration page.

It would be trivial to add a views bulk operations action too.