--- autoassignrole-admin.inc	2011-10-24 19:03:44.000000000 +0200
+++ autoassignrole-admin.inc	2011-12-02 14:53:01.000000000 +0100
@@ -89,6 +89,7 @@
       'registration' => $setting->registration,
     );
   }
+  $defaults += _autoassignrole_roles_defaults(module_invoke_all('autoassignrole_settings_defaults'));
   foreach ($roles as $k => $v) {
     $active = (isset($defaults[$k]['display']) ? 1 : 0);
     $form['autoassignrole_settings_page'][$k] = array(
--- autoassignrole.features.inc	1970-01-01 01:00:00.000000000 +0100
+++ autoassignrole.features.inc	2011-12-02 14:53:01.000000000 +0100
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * @file autoassignrole.features.inc
+ * Provides integration with the Features module.
+ */
+
+/**
+ * Implementation of hook_features_export() for settings.
+ */
+
+function autoassignrole_settings_features_export($data, &$export, $module_name = '') {
+  foreach ($data as $component) {
+    $export['features']['autoassignrole_settings'][$component] = $component;
+  }
+}
+
+function autoassignrole_settings_features_export_options() {
+  $options = array();
+  foreach (_autoassignrole_load_settings() as $setting_name => $setting_value) {
+    $skip = FALSE;
+    if (preg_match('/^path_(\d*)/i', $setting_name, $matches)) {
+      if ($matches[1]) {
+        $roles = user_roles();
+        $setting_title = 'path: ' . $roles[(int) $matches[1]];
+      }
+    }
+    else {
+      $setting_title = $setting_name;
+    }
+    if (preg_match('/^path_(\D[^_]*)_(\d*)/i', $setting_name, $matches)) {
+      $skip = TRUE;
+    }
+    if ($skip === FALSE) {
+      $options[$setting_name] = $setting_title;
+    }
+  }
+  return $options;
+}
+
+function autoassignrole_settings_features_export_render($module_name, $data, $export = NULL) {
+  $code = array();
+  $code[] = '  $autoassignrole_settings = array();';
+  foreach ($data as $name) {
+    if (preg_match('/^path_(\d*)/i', $name, $matches)) {
+      $this_rid = (int) $matches[1];
+      if ($this_rid) {
+        $code[] = "  \$autoassignrole_settings['roles'][{$this_rid}] = " . features_var_export(_autoassignrole_get_path_settings($this_rid)) . ';';
+      }
+    }
+    else {
+      $this_setting = _autoassignrole_get_settings($name);
+      // user_roles and auto_roles need to be saved serialized
+      // It's what _autoassignrole_get_settings() expects.
+      if (is_array($this_setting)) {
+        $this_setting = serialize($this_setting);
+      }
+      $code[] = "  \$autoassignrole_settings['{$name}'] = " . features_var_export($this_setting) . ';';
+    }
+  }
+  $code[] = "  return \$autoassignrole_settings;";
+  $code = implode("\n", $code);
+  return array('autoassignrole_settings_defaults' => $code);
+}
+
+function autoassignrole_settings_features_revert($module_name) {
+  foreach (_autoassignrole_load_settings() as $setting_name => $value) {
+    $skip = FALSE;
+    if (preg_match('/^path_(\d*)/i', $setting_name, $matches)) {
+      if ($matches[1]) {
+        db_query("DELETE FROM {autoassignrole_page} WHERE rid=%d", (int) $matches[1]);
+      }
+      $skip = TRUE;
+    }
+    elseif (preg_match('/^path_(\D[^_]*)_(\d*)/i', $setting_name, $matches)) {
+      // Nothing to do.
+    }
+    else {
+      // We NULL out rather than deleting these because the module depends
+      // on them being there and can't re-insert them by itself.
+      db_query("UPDATE {autoassignrole} SET value = NULL WHERE arid = '%s'", array($setting_name));
+    }
+  }
+}
+
--- autoassignrole.module	2011-09-14 15:17:35.000000000 +0200
+++ autoassignrole.module	2011-12-02 14:59:20.000000000 +0100
@@ -16,6 +16,10 @@
  * @return array
  */
 function autoassignrole_menu() {
+  static $settings_default;
+  if (!isset($settings_default)) {
+    $settings_default = module_invoke_all('autoassignrole_settings_defaults');
+  }
   $items = array();
     $items['admin/user/autoassignrole/autocomplete/node'] = array(
       'title' => 'Node autocomplete',
@@ -38,91 +42,14 @@
   // path based role assignments that are listed as a menu item
   $result = db_query("SELECT rid, path, display, title, weight, menu FROM {autoassignrole_page}");
   while ($r = db_fetch_object($result)) {
-    switch ($r->display) {
-      case 0:
-        $items[$r->path] = array(
-          'title' => check_plain($r->title),
-          'page arguments' => array($r->rid),
-          'page callback' => 'autoassignrole_path',
-          'access callback' => '_autoassignrole_path_access',
-          'file' => 'autoassignrole-path.inc',
-          'weight' => $r->weight,
-          'type' => MENU_NORMAL_ITEM,
-          'menu_name' => $r->menu,
-        );
-        $items[$r->path .'/register'] = array(
-          'title' => 'Create new account',
-          'page arguments' => array($r->rid),
-          'page callback' => 'autoassignrole_path',
-          'access callback' => '_autoassignrole_path_access',
-          'file' => 'autoassignrole-path.inc',
-          'type' => MENU_DEFAULT_LOCAL_TASK,
-        );
-        $items[$r->path .'/login'] = array(
-          'title' => 'Log in',
-          'access callback' => 'user_is_anonymous',
-          'type' => MENU_LOCAL_TASK,
-          'page callback' => '_autoassignrole_user_login',
-          'file' => 'autoassignrole-path.inc',
-        );
-
-        $items[$r->path .'/password'] = array(
-          'title' => 'Request new password',
-          'access callback' => 'user_is_anonymous',
-          'type' => MENU_LOCAL_TASK,
-          'page callback' => '_autoassignrole_user_password',
-          'file' => 'autoassignrole-path.inc',
-        );
-        break;
-      // tabs on user registration pages
-      case 1:
-        $items['user/'. $r->path] = array(
-          'title' => check_plain($r->title),
-          'page arguments' => array($r->rid),
-          'page callback' => 'autoassignrole_path',
-          'access callback' => '_autoassignrole_path_access',
-          'file' => 'autoassignrole-path.inc',
-          'type' => MENU_LOCAL_TASK,
-          'weight' => $r->weight
-        );
-        break;
-      // no menu items
-      case 2:
-        $items[$r->path] = array(
-          'title' => check_plain($r->title),
-          'page arguments' => array($r->rid),
-          'page callback' => 'autoassignrole_path',
-          'access callback' => '_autoassignrole_path_access',
-          'file' => 'autoassignrole-path.inc',
-          'weight' => $r->weight,
-          'type' => MENU_CALLBACK,
-        );
-        $items[$r->path .'/register'] = array(
-          'title' => 'Create new account',
-          'page arguments' => array($r->rid),
-          'page callback' => 'autoassignrole_path',
-          'access callback' => '_autoassignrole_path_access',
-          'file' => 'autoassignrole-path.inc',
-          'type' => MENU_DEFAULT_LOCAL_TASK,
-        );
-        $items[$r->path .'/login'] = array(
-          'title' => 'Log in',
-          'access callback' => 'user_is_anonymous',
-          'type' => MENU_LOCAL_TASK,
-          'page callback' => '_autoassignrole_user_login',
-          'file' => 'autoassignrole-path.inc',
-        );
-
-        $items[$r->path .'/password'] = array(
-          'title' => 'Request new password',
-          'access callback' => 'user_is_anonymous',
-          'type' => MENU_LOCAL_TASK,
-          'page callback' => '_autoassignrole_user_password',
-          'file' => 'autoassignrole-path.inc',
-        );
-        break;
+    $items = array_merge($items, _autoassignrole_path_menu_generate($r));
+  }
+  // Account for any default path configurations not overridden in the DB
+  foreach ($settings_default['roles'] as $rid => $path_settings) {
+    $db_path_settings = _autoassignrole_get_path_settings($rid, TRUE);
+    if (empty($db_path_settings)) {
+      $items = array_merge($items, _autoassignrole_path_menu_generate((object) _autoassignrole_get_path_settings($rid)));
     }
-
   }
   return $items;
 }
@@ -139,6 +66,96 @@
   return FALSE;
 }
 
+function _autoassignrole_path_menu_generate($path_settings) {
+  $items = array();
+  $path_settings->rid = (string) $path_settings->rid;
+  switch ($path_settings->display) {
+    case 0:
+      $items[$path_settings->path] = array(
+        'title' => check_plain($path_settings->title),
+        'page arguments' => array($path_settings->rid),
+        'page callback' => 'autoassignrole_path',
+        'access callback' => '_autoassignrole_path_access',
+        'file' => 'autoassignrole-path.inc',
+        'weight' => $path_settings->weight,
+        'type' => MENU_NORMAL_ITEM,
+        'menu_name' => $path_settings->menu,
+      );
+      $items[$path_settings->path .'/register'] = array(
+        'title' => 'Create new account',
+        'page arguments' => array($path_settings->rid),
+        'page callback' => 'autoassignrole_path',
+        'access callback' => '_autoassignrole_path_access',
+        'file' => 'autoassignrole-path.inc',
+        'type' => MENU_DEFAULT_LOCAL_TASK,
+      );
+      $items[$path_settings->path .'/login'] = array(
+        'title' => 'Log in',
+        'access callback' => 'user_is_anonymous',
+        'type' => MENU_LOCAL_TASK,
+        'page callback' => '_autoassignrole_user_login',
+        'file' => 'autoassignrole-path.inc',
+      );
+
+      $items[$path_settings->path .'/password'] = array(
+        'title' => 'Request new password',
+        'access callback' => 'user_is_anonymous',
+        'type' => MENU_LOCAL_TASK,
+        'page callback' => '_autoassignrole_user_password',
+        'file' => 'autoassignrole-path.inc',
+      );
+      break;
+    // tabs on user registration pages
+    case 1:
+      $items['user/'. $path_settings->path] = array(
+        'title' => check_plain($path_settings->title),
+        'page arguments' => array($path_settings->rid),
+        'page callback' => 'autoassignrole_path',
+        'access callback' => '_autoassignrole_path_access',
+        'file' => 'autoassignrole-path.inc',
+        'type' => MENU_LOCAL_TASK,
+        'weight' => $path_settings->weight
+      );
+      break;
+    // no menu items
+    case 2:
+      $items[$path_settings->path] = array(
+        'title' => check_plain($path_settings->title),
+        'page arguments' => array($path_settings->rid),
+        'page callback' => 'autoassignrole_path',
+        'access callback' => '_autoassignrole_path_access',
+        'file' => 'autoassignrole-path.inc',
+        'weight' => $path_settings->weight,
+        'type' => MENU_CALLBACK,
+      );
+      $items[$path_settings->path .'/register'] = array(
+        'title' => 'Create new account',
+        'page arguments' => array($path_settings->rid),
+        'page callback' => 'autoassignrole_path',
+        'access callback' => '_autoassignrole_path_access',
+        'file' => 'autoassignrole-path.inc',
+        'type' => MENU_DEFAULT_LOCAL_TASK,
+      );
+      $items[$path_settings->path .'/login'] = array(
+        'title' => 'Log in',
+        'access callback' => 'user_is_anonymous',
+        'type' => MENU_LOCAL_TASK,
+        'page callback' => '_autoassignrole_user_login',
+        'file' => 'autoassignrole-path.inc',
+      );
+
+      $items[$path_settings->path .'/password'] = array(
+        'title' => 'Request new password',
+        'access callback' => 'user_is_anonymous',
+        'type' => MENU_LOCAL_TASK,
+        'page callback' => '_autoassignrole_user_password',
+        'file' => 'autoassignrole-path.inc',
+      );
+      break;
+  }
+  return $items;
+}
+
 /**
  * Implementation of hook_perm().
  * @return array
@@ -151,6 +168,10 @@
  * Implementation of hook_user().
  */
 function autoassignrole_user($op, &$edit, &$account, $category = NULL) {
+  static $settings_default;
+  if (!isset($settings_default)) {
+    $settings_default = module_invoke_all('autoassignrole_settings_defaults');
+  }
   switch ($op) {
     case 'insert':
       // If this is an administrator creating the account only use auto_assign if
@@ -187,8 +208,16 @@
         $roles = user_roles(TRUE);
         $user_roles = _autoassignrole_get_settings('user_roles');
         $path_roles = db_query("SELECT rid from {autoassignrole_page}");
+        $processed_rids = array();
         while ($path_role = db_fetch_object($path_roles)) {
           unset($roles[$path_role->rid]);
+          $processed_rids = $path_role->rid;
+        }
+        // Also unset any default values not yet dealt with
+        foreach ($settings_default['roles'] as $default_rid => $path_settings) {
+          if (!in_array($default_rid, $processed_rids)) {
+            unset($roles[$default_rid]);
+          }
         }
         foreach ($roles as $k => $r) {
           if (!in_array($k, $user_roles, TRUE)) {
@@ -261,10 +290,18 @@
  *
  */
 function _autoassignrole_get_settings($value) {
+  static $settings_default;
+  if (!isset($settings_default)) {
+    $settings_default = module_invoke_all('autoassignrole_settings_defaults');
+  }
   // if we are dealing with a variable in the format of path_field_rid get
   // variables from {autoassignrole_page}
   if (preg_match('/^path_(\D[^_]*)_(\d*)/i', $value, $matches)) {
     $result = db_fetch_array(db_query("SELECT rid, display, path, title, description, format, weight FROM {autoassignrole_page} WHERE rid = %d", $matches[2]));
+    // Use default value if nothing in database and default value exists
+    if ($result === FALSE && !empty($settings_default['roles'][(int) $matches[2]])) {
+      $result = $settings_default['roles'][$matches[2]];
+    }
     if (count($result) > 1 && $matches[1] == 'active') {
       return 1;
     }
@@ -282,9 +319,18 @@
   }
 
   if (preg_match('/^path_(\d*)/i', $value, $matches)) {
-  $result = db_fetch_array(db_query("SELECT path FROM {autoassignrole_page} WHERE rid = %d", $matches[1]));
-    if (isset($result['path'])) {
-      return $result['path'];
+    if ($matches[1]) {
+      $result = db_fetch_array(db_query("SELECT path FROM {autoassignrole_page} WHERE rid = %d", $matches[1]));
+      // Use default value if nothing in database and default value exists
+      if ($result === FALSE && !empty($settings_default['roles'][(int) $matches[1]])) {
+        $result = $settings_default['roles'][(int) $matches[1]];
+      }
+      if (isset($result['path'])) {
+        return $result['path'];
+      }
+      else {
+        return FALSE;
+      }
     }
     else {
       return FALSE;
@@ -293,12 +339,27 @@
 
   // if we are not dealing with {autoassignrole_page} then get variables from
   // {autoassignrole}
+  /* @todo: Add static variable after verifying no code paths rely on
+    variables that were just updated in the DB (or add $reset parameter)
+    to rebuild the static cache. */
   $settings = array();
   $result = db_query("SELECT arid, value from {autoassignrole}");
   while ($s = db_fetch_object($result)) {
     $settings[$s->arid] = $s->value;
   }
 
+  unset($settings['roles']); // We're done with this.
+
+  // Merge any default settings; database values take precedence.
+  foreach ($settings_default as $default_arid => $default_value) {
+    if ($default_arid == 'roles') {
+      continue;
+    }
+    if (is_null($settings[$default_arid]) || !isset($settings[$default_arid])) {
+      $settings[$default_arid] = $default_value;
+    }
+  }
+
   // break apart auto_roles[x] and user_roles[x]
   if (preg_match('/^(\D[^_]*_\D[^_]*)\[(\d*)]/i', $value, $matches)) {
     $values = unserialize($settings[$matches[1]]);
@@ -332,6 +393,39 @@
   }
 }
 
+/**
+ * Retrieve settings for a specific role's registration path configuration
+ */
+function _autoassignrole_get_path_settings($rid, $skip_defaults = FALSE) {
+  static $settings_default;
+  if (!isset($settings_default)) {
+    $settings_default = module_invoke_all('autoassignrole_settings_defaults');
+  }
+  $result = db_query("SELECT rid, display, path, title, description, format, weight, menu, registration
+    FROM {autoassignrole_page} WHERE rid=%d", array($rid));
+  if ($row = db_fetch_object($result)) {
+    return array(
+      'rid' => $row->rid,
+      'display' => $row->display,
+      'path' => $row->path,
+      'title' => $row->title,
+      'description' => $row->description,
+      'format' => $row->format,
+      'weight' => $row->weight,
+      'menu' => $row->menu,
+      'registration' => $row->registration,
+    );
+  }
+  // Fall back on default values if nothing in database and the caller
+  // did not request we skip this part.
+  elseif (isset($settings_default['roles'][$rid]) && $skip_defaults === FALSE) {
+    return array('rid' => (string) $rid) + $settings_default['roles'][$rid];
+  }
+  else {
+    return array();
+  }
+}
+
 function _autoassignrole_path($rid, $value) {
   static $pages = array();
   if (count($pages) == 0) {
@@ -514,6 +608,10 @@
  *  array An array of roles
  */
 function autoassignrole_get_roles($op = 'all', $path = array()) {
+  static $settings_default;
+  if (!isset($settings_default)) {
+    $settings_default = module_invoke_all('autoassignrole_settings_defaults');
+  }
   $roles = user_roles();
   $aar_roles = array();
 
@@ -521,7 +619,13 @@
     case 'all':
       // Select all auto and user selectable roles
       $sql = "SELECT value FROM {autoassignrole} WHERE arid = 'auto_roles' OR arid = 'user_roles'";
+      // The above code is on crack and seems deprecated anyway, so what follows
+      // is a lazy implementation of default value support.
       $result = db_fetch_object(db_query($sql));
+      if ((!isset($result) || is_null($result)) && isset($settings_default['auto_roles'])) {
+        $result = new stdClass();
+        $result->value = $settings_default['auto_roles'];
+      }
       $aar_roles = unserialize($result->value);
 
       // Select all path assignable roles
@@ -532,9 +636,15 @@
       }
       break;
     case 'auto':
+      // @todo: Make this use _autoassignrole_get_settings('auto_roles');
       // Select all auto roles
       $sql = "SELECT value FROM {autoassignrole} WHERE arid = 'auto_roles'";
       $result = db_fetch_object(db_query($sql));
+      // Fall back on default values
+      if ((!isset($result) || is_null($result)) && isset($settings_default['auto_roles'])) {
+        $result = new stdClass();
+        $result->value = $settings_default['auto_roles'];
+      }
       $aar_roles = unserialize($result->value);
       break;
     case 'path':
@@ -552,9 +662,15 @@
       }
       break;
     case 'user':
+      // @todo: Make this use _autoassignrole_get_settings('user_roles');
       // Select all user selectable roles
       $sql = "SELECT value FROM {autoassignrole} WHERE arid = 'user_roles'";
       $result = db_fetch_object(db_query($sql));
+      // Fall back on default values
+      if ((!isset($result) || is_null($result)) && isset($settings_default['user_roles'])) {
+        $result = new stdClass();
+        $result->value = $settings_default['user_roles'];
+      }
       $aar_roles = unserialize($result->value);
       break;
   }
@@ -576,12 +692,33 @@
  * the replacement registration path.
  */
 function autoassignrole_menu_alter(&$items) {
+  static $settings_default;
+  if (!isset($settings_default)) {
+    $settings_default = module_invoke_all('autoassignrole_settings_defaults');
+  }
   $row = db_fetch_object(db_query("SELECT rid, path, display, title, weight, menu, registration FROM {autoassignrole_page} WHERE registration = 1"));
+  // Fall back on default value
+  foreach ($settings_default['roles'] as $rid => $path_settings) {
+    // Skip these settings if they are set in the database
+    $db_path_settings = _autoassignrole_get_path_settings($rid, TRUE);
+    if (empty($db_path_settings)) {
+      if ($path_settings['registration']) {
+        $row = new stdClass();
+        $row->registration = TRUE;
+        break;
+      }
+    }
+  }
   if (isset($row->registration)) {
     $path = $row->path;
   }
   else {
     $row = db_fetch_object(db_query("SELECT arid, value FROM {autoassignrole} WHERE arid = '%s'", 'node_user_register'));
+    // Fall back on default values
+    if ((is_null($row->value) || !isset($row->value)) && isset($settings_default['node_user_register'])) {
+      $row = new stdClass();
+      $row->value = $settings_default['node_user_register'];
+    }
     if (!empty($row->value)) {
       $path = 'node/'. $row->value;
     }
@@ -592,3 +729,102 @@
     $items['user/register']['type'] = MENU_LOCAL_TASK;
   }
 }
+
+/**
+ * Implementation of hook_features_api().
+ */
+function autoassignrole_features_api() {
+  return array(
+    'autoassignrole_settings' => array(
+      'name' => t('Auto Assign Role settings'),
+      'default_hook' => 'autoassignrole_settings_defaults',
+      'default_file' => FEATURES_DEFAULTS_INCLUDED_COMMON,
+      'features_source' => TRUE,
+      'file' => drupal_get_path('module', 'autoassignrole') . '/autoassignrole.features.inc',
+    ),
+  );
+}
+
+/**
+ * Load all settings into a single array. The keys match those used on
+ *   administrative forms.
+ * @return array An array of all values. Values belonging to role-specific
+ * configurations will be in the 'roles' sub-array and global settings will
+ * be in the 'global' array.
+ */
+function _autoassignrole_load_settings() {
+  static $settings_default;
+  if (!isset($settings_default)) {
+    $settings_default = module_invoke_all('autoassignrole_settings_defaults');
+  }
+  // Load general settings first
+  $settings = array();
+  $result = db_query("SELECT arid from {autoassignrole}");
+  while ($s = db_fetch_object($result)) {
+    $settings[$s->arid] = _autoassignrole_get_settings($s->arid);
+  }
+  $autoassignrole_settings = $settings;
+
+  // Now load each configured role's path settings
+  $role_settings = array();
+  $roles = autoassignrole_get_roles('path');
+  // Merge in roles from default path settings
+  foreach ($settings_default['roles'] as $rid => $path_settings) {
+    $all_roles = user_roles();
+    $roles += array((int) $rid => $all_roles[$rid]);
+  }
+  foreach ($roles as $rid => $role) {
+    /* @todo: This could use drupal_get_schema('autoassignrole') and some
+      array_keys magic to be more dynamic, but I didn't want to add
+      any surprise behavior at this point. */
+    $path_keys = array('path',
+      'path_title',
+      'path_description',
+      'path_display',
+      'path_format',
+      'path_weight',
+      'path_menu',
+      'path_registration',
+    );
+    foreach ($path_keys as $path_key) {
+      $role_settings["{$path_key}_{$rid}"] = _autoassignrole_get_settings("{$path_key}_{$rid}");
+    }
+  }
+  $autoassignrole_settings = array_merge($autoassignrole_settings, $role_settings);
+  return $autoassignrole_settings;
+}
+
+/** 
+ * Converts an array of autoassignrole_settings_defaults()-compatible
+ *   variable names into the format expected by administrative forms.
+ * This function only deals with role assignments by path, not global
+ *  settings.
+ * return array $settings An array, keyed by role ID, of
+ *   configuration for each active role's registration page.
+ */
+function _autoassignrole_roles_defaults($defaults) {
+  $settings = array();
+  foreach ($defaults as $key => $value) {
+    // If the array key matches the pattern for role-specific
+    // configuration, add it to the relevant array.
+
+    if ($key == 'roles') {
+      $settings += $value;
+    }
+  }
+  return $settings;
+}
+
+function _autoassignrole_roles_defaults_default_keys() {
+  return array(
+    'display',
+    'path',
+    'title',
+    'description',
+    'format',
+    'weight',
+    'menu',
+    'registration',
+  );
+}
+
--- autoassignrole-path.inc	2009-09-21 21:21:18.000000000 +0200
+++ autoassignrole-path.inc	2011-12-02 14:53:01.000000000 +0100
@@ -12,7 +12,18 @@
 require_once drupal_get_path('module', 'user') .'/user.pages.inc';
 
 function autoassignrole_path($rid) {
+  static $settings_default;
+  if (!isset($settings_default)) {
+    $settings_default = module_invoke_all('autoassignrole_settings_defaults');
+  }
   $page = db_fetch_object(db_query("SELECT description, format FROM {autoassignrole_page} WHERE rid = %d", $rid));
+  if ($page === FALSE) {
+    $page = new stdClass();
+    list($page->description, $page->format) = array(
+      $settings_default['roles'][$rid]['description'],
+      $settings_default['roles'][$rid]['format'],
+    );
+  }
   $output = check_markup($page->description, $page->format, FALSE);
   $output .= drupal_get_form('user_register');
   return $output;
