? ghop118_1.patch
? ghop118_2.patch
Index: states.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/workflow_ng/states/Attic/states.install,v
retrieving revision 1.1.2.1
diff -u -p -r1.1.2.1 states.install
--- states.install	24 Aug 2007 13:43:30 -0000	1.1.2.1
+++ states.install	28 Dec 2007 17:46:45 -0000
@@ -23,6 +23,15 @@ function states_install() {
         state varchar(127),
         PRIMARY KEY (uid, machine)
       ) /*!40100 DEFAULT CHARACTER SET UTF8 */ ");
+      db_query("CREATE TABLE if not exists {states_custom} (
+        sid int unsigned NOT NULL auto_increment,
+        label varchar(127),
+        entity_type int unsigned NOT NULL default '0',
+        entity_filter longtext NOT NULL,
+        states longtext NOT NULL,
+        init_state varchar(127),
+        PRIMARY KEY (sid)
+      ) /*!40100 DEFAULT CHARACTER SET UTF8 */ ");
       break;
     case 'pgsql':
       db_query("CREATE TABLE {node_state} (
@@ -39,6 +48,48 @@ function states_install() {
         state varchar(127),
         PRIMARY KEY (uid, machine)
       )");
+      db_query("CREATE TABLE {states_custom} (
+        sid serial CHECK (sid >= 0),
+        label varchar(127),
+        entity_type int_unsigned NOT NULL default '0',
+        entity_filter text NOT NULL,
+        states text NOT NULL,
+        init_state varchar(127),
+        PRIMARY KEY (sid)
+      )");
+      break;
+    default:
+      break;
+  }
+}
+
+/*
+ * Due to the addition of web interface configurable states, a new table is required
+ */
+function states_update_5001() {
+  switch ($GLOBALS['db_type']) {
+    case 'mysqli':
+    case 'mysql':
+      db_query("CREATE TABLE if not exists {states_custom} (
+        sid int unsigned NOT NULL auto_increment,
+        label varchar(127),
+        entity_type int unsigned NOT NULL default '0',
+        entity_filter longtext NOT NULL,
+        states longtext NOT NULL,
+        init_state varchar(127),
+        PRIMARY KEY (sid)
+      ) /*!40100 DEFAULT CHARACTER SET UTF8 */ ");
+      break;
+    case 'pgsql':
+      db_query("CREATE TABLE {states_custom} (
+        sid serial CHECK (sid >= 0),
+        label varchar(127),
+        entity_type int_unsigned NOT NULL default '0',
+        entity_filter text NOT NULL,
+        states text NOT NULL,
+        init_state varchar(127),
+        PRIMARY KEY (sid)
+      )");
       break;
     default:
       break;
@@ -51,5 +102,6 @@ function states_install() {
 function states_uninstall() {
   db_query("DROP TABLE {node_state}");
   db_query("DROP TABLE {users_state}");
+  db_query("DROP TABLE {states_custom}");
   variable_del('state_machines');
 }
Index: states.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/workflow_ng/states/Attic/states.module,v
retrieving revision 1.1.2.6
diff -u -p -r1.1.2.6 states.module
--- states.module	25 Dec 2007 23:08:58 -0000	1.1.2.6
+++ states.module	27 Dec 2007 20:03:51 -0000
@@ -13,6 +13,7 @@ if (module_exists('views')) {
 if (module_exists('workflow_ng')) {
   include_once(drupal_get_path('module', 'states') .'/states_workflow_ng.inc');
 }
+include_once(drupal_get_path('module', 'states') .'/states_admin.inc');
 
 /*
  * Returns all defined state machines
Index: states_admin.inc
===================================================================
RCS file: states_admin.inc
diff -N states_admin.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ states_admin.inc	28 Dec 2007 20:19:51 -0000
@@ -0,0 +1,626 @@
+<?php
+// $$
+
+/**
+ * @file
+ * Provides an admin interface to list/create/edit state machines.
+ */
+
+// Entity types.
+define('STATES_ADMIN_ENTITY_NODE', 0);
+define('STATES_ADMIN_ENTITY_USER', 1);
+
+// State creation / editing steps
+define('STATES_ADMIN_STEP_BASIC'  , 1); // label textbox, entity type dropdown, allow any state checkbox
+define('STATES_ADMIN_STEP_STATES' , 2); // states list
+define('STATES_ADMIN_STEP_OPTIONS', 3); // initial state downdown/textbox, allow any entity checkbox
+define('STATES_ADMIN_STEP_FILTERS', 4); // node type / user role checkboxes
+define('STATES_ADMIN_STEP_REVIEW' , 5); // confirmation of previous steps
+
+// Admin interface path
+define('STATES_ADMIN_UI_PATH', 'admin/build/state-machines');
+ 
+/**
+ * Implementation of hook_states
+ * Lists the states created through the admin interface
+ */
+ function states_states() {
+  $machines = array();
+  $result = db_query('SELECT * FROM {states_custom}');
+  while ($row = db_fetch_object($result)) {
+    $state = array(
+      '#label' => $row->label,
+      '#states' => unserialize($row->states),
+      'can_edit' => TRUE,
+      'custom_sid' => $row->sid,
+    );
+    if ($row->init_state != '') {
+      $state['#init_state'] = $row->init_state;
+    }
+    if ($row->entity_type == STATES_ADMIN_ENTITY_NODE) {
+      $state['#entity'] = 'node';
+      if ($row->entity_filter !== '') {
+        $state['#types'] = unserialize($row->entity_filter);
+      }
+    }
+    else if ($row->entity_type == STATES_ADMIN_ENTITY_USER) {
+      $state['#entity'] = 'user';
+      if ($row->entity_filter !== '') {
+        $state['#roles'] = unserialize($row->entity_filter);
+      }
+    }
+    $machines['custom_state_'. $row->sid] = $state;
+  }
+  return $machines;
+}
+
+/**
+ * Implementation of hook_perm().
+ */
+function states_perm() {
+  return array('administer state');
+}
+
+/**
+ * Implementation of hook_menu().
+ */
+function states_menu($may_cache) {
+  $items = array();
+  if ($may_cache) {
+    $items[] = array(
+      'path' => STATES_ADMIN_UI_PATH,
+      'title' => t('Custom state machines'),
+      'description' => t('View, edit, create and delete custom state machines.'),
+      'callback' => 'drupal_get_form',
+      'callback arguments' => array('states_admin_overview', NULL),
+      'access' => user_access('administer state'),
+      'type' => MENU_NORMAL_ITEM,
+    );
+    $items[] = array(
+      'path' => STATES_ADMIN_UI_PATH .'/0/list',
+      'title' => t('Overview'),
+      'weight' => -5,
+      'type' => MENU_DEFAULT_LOCAL_TASK,
+    );
+    $items[] = array(
+      'path' => STATES_ADMIN_UI_PATH .'/0/add',
+      'title' => t('Create a new state machine'),
+      'type' => MENU_LOCAL_TASK,
+      'callback' => 'drupal_get_form',
+      'callback arguments' => array('states_admin_create'),
+      'access' => user_access('administer state'),
+    );
+  }
+  else if (arg(0) == 'admin' && arg(1) == 'build' && arg(2) == 'state-machines') {
+    $machines = states_get_machines();
+    if (isset($machines[arg(3)]) && $machines[arg(3)]['can_edit'] == TRUE) {
+      $label = $machines[arg(3)]['#label'];
+      $items[] = array(
+        //this item lets the menu system create a nicer breadcrumb
+        'path' => STATES_ADMIN_UI_PATH .'/'. arg(3),
+        'title' => check_plain($label),
+        'type' => MENU_CALLBACK,
+      );
+      $items[] = array(
+        'path' => STATES_ADMIN_UI_PATH .'/'. arg(3) .'/edit',
+        'title' => t('Edit'),
+        'type' => MENU_CALLBACK,
+        'callback' => 'drupal_get_form',
+        'callback arguments' => array('states_admin_edit', arg(3)),
+        'access' => user_access('administer state'),
+      );
+      $items[] = array(
+        'path' => STATES_ADMIN_UI_PATH .'/'. arg(3) .'/delete',
+        'title' => t('Delete'),
+        'type' => MENU_CALLBACK,
+        'callback' => 'drupal_get_form',
+        'callback arguments' => array('states_admin_delete', arg(3)),
+        'access' => user_access('administer state'),
+      );
+    }
+  }
+  return $items;
+}
+
+function states_admin_overview() {
+  drupal_set_title("State machines overview");
+  states_clear_machine_cache();
+  
+  $form['nodes_header'] = array('#value' => '<h3>'. t('Content state machines'). '</h3>');
+  $form['nodes'] = states_admin_overview_table('node');
+  $form['users_header'] = array('#value' => '<h3>'. t('User state machines'). '</h3>');
+  $form['users'] = states_admin_overview_table('user');
+  $form['#redirect'] = FALSE;
+  return $form;
+}
+
+function states_admin_overview_table($entity) {
+  $machines = states_get_machines();
+  
+  $header = array(t('Label'), t('States'), t('Initial state'), t('Attribute name'), t($entity=='node'?'Content types':'User roles') ,t('Operations'));
+  
+  $rows = array();
+  foreach ($machines as $name => $machine) {
+    if ($machine['#entity'] == $entity) {
+      $states = t('(any)');
+      if ($machine['#states'] != '*') {
+        $states = theme('item_list', $machine['#states']);
+      }
+      $filter = t('(any)');
+      if ($entity == 'node' && isset($machine['#types'])) {
+        $filter = array();
+        $node_names = node_get_types('names');
+        foreach ($machine['#types'] as $value) {
+          $filter[] = $node_names[$value];
+        }
+        $filter = theme('item_list', $filter);
+      }
+      else if ($entity == 'user' && isset($machine['#roles'])) {
+        $filter = array();
+        $role_names = user_roles();
+        foreach ($machine['#roles'] as $value) {
+          $filter[] = $role_names[$value];
+        }
+        $filter = theme('item_list', $filter);
+      }
+      $ops = array();
+      if (isset($machine['can_edit']) && $machine['can_edit'] == TRUE) {
+        $path = STATES_ADMIN_UI_PATH .'/'. $name;
+        $ops[] = l(t('edit'), $path .'/edit');
+        $ops[] = l(t('delete'), $path .'/delete');
+      }
+      $rows[] = array(
+        check_plain($machine['#label']),
+        $states,
+        isset($machine['#init_state']) ? check_plain($machine['#init_state']) : t('(none)'),
+        check_plain(isset($machine['#attribute_name']) ? isset($machine['#attribute_name']) : $name),
+        $filter,
+        implode(' ', $ops),
+      );
+    }
+  }
+
+  if (count($rows)) {
+    return array('#value' => theme('table', $header, $rows, array('class' => 'state-configurations')));
+  }
+
+  return array('#value' => t('None.'));
+}
+
+function states_admin_trim_value(&$value) {
+  $value = trim($value);
+}
+
+/**
+ * Helper function for states_admin_create. Addresses a bug in the D5 Forms API;
+ * multistep forms do not honour default values for checkboxes.
+ */
+function states_admin_checkboxes_multistep_fix($checkboxes) {
+  foreach ($checkboxes as &$checkbox) {
+    if (is_array($checkbox) && $checkbox['#type'] == 'checkbox' && isset($checkbox['#default_value']) && $checkbox['#default_value'] != 0) {
+      $checkbox['#attributes']['checked'] = 'checked';
+    }
+  }
+  return $checkboxes;
+}
+
+/**
+ * The custom state creation / editing multistep form
+ */
+function states_admin_create($form_values = NULL) {
+  if (!isset($form_values)) {
+    $step = 1;
+    $form_values = array(
+      'step' => $step,
+      // Default values for the new machine:
+      'label' => '',
+      'entity' => STATES_ADMIN_ENTITY_NODE,
+      'any_state' => 0,
+      'state_list' => array(),
+      'initial_state' => 0,
+      'any_entity' => 0,
+      'filters' => array(),
+    );
+  }
+  else {
+    // Merge the saved values into the form_values
+    $saved = unserialize($form_values['saved']);
+    unset($form_values['saved']); // remove the saved values from the form values so they aren't in both parts of the merge
+    if (isset($form_values['unset'])) {
+      // To allow checkboxes to be unticked, they have to be zeroed in the saved values, as they only appear in the new values if they are ticked
+      $saved_unset = $form_values['unset'];
+      unset($form_values['unset']);
+      $saved[$saved_unset] = 0;
+    }
+    $form_values = array_merge($saved, $form_values);
+    
+    $from_step = $form_values['step'];
+    
+    // The state list is changed from a newline delimited string into an array. It is the only value not left as-is until the
+    // form submission. This is because it is much more useful to us as an array.
+    if ($from_step == STATES_ADMIN_STEP_STATES) {
+      $states = explode("\n", $form_values['state_list']);
+      array_walk($states, 'states_admin_trim_value');
+      $form_values['state_list'] = $states;
+    }
+       
+    // Changing the any_state option will invalidate the initial_state option and the state_list
+    if (($saved['any_state'] == 1) != ($form_values['any_state'] == 1)) {
+      if ($form_values['any_state'] == 1) {
+        $form_values['state_list'] = '*';
+        $form_values['initial_state'] = '';
+      }
+      else {
+        $form_values['state_list'] = array();
+        $form_values['initial_state'] = 0;
+      }
+    }
+    
+    // Changing the entity type option will invalidate the filters option
+    if (($saved['entity'] == STATES_ADMIN_ENTITY_NODE) != ($form_values['entity'] == STATES_ADMIN_ENTITY_NODE)) {
+      $form_values['filters'] = array();
+    }
+    
+    if ($form_values['op'] == t('Back')) {
+      $step = $from_step - 1;
+      if ($step == STATES_ADMIN_STEP_FILTERS && $form_values['any_entity'] == 1) {
+        // The filters step is skipped if any entity is allowed
+        $step = STATES_ADMIN_STEP_OPTIONS;
+      }
+      if ($step == STATES_ADMIN_STEP_STATES && $form_values['any_state'] == 1) {
+        // The state list step is skipped if any state is allowed
+        $step = STATES_ADMIN_STEP_BASIC;
+      }
+    }
+    else {
+      $step = $from_step + 1;
+      if ($step == STATES_ADMIN_STEP_STATES && $form_values['any_state'] == 1) {
+        // The state list step is skipped if any state is allowed
+        $step = STATES_ADMIN_STEP_OPTIONS;
+      }
+      if ($step == STATES_ADMIN_STEP_FILTERS && $form_values['any_entity'] == 1) {
+        // The filters step is skipped if any entity is allowed
+        $step = STATES_ADMIN_STEP_REVIEW;
+      }
+    }
+    $form_values['step'] = $step;
+  }
+  
+  $form['saved'] = array(
+    '#type' => 'hidden',
+    '#value' => serialize($form_values),
+  );
+
+  switch ($step) {
+    case STATES_ADMIN_STEP_BASIC:
+      $form['label'] = array(
+        '#type' => 'textfield',
+        '#title' => t('Label'),
+        '#maxlength' => 127,
+        '#description' =>t('Choose an appropriate label for the new state machine.'),
+        '#required' => TRUE,
+        '#default_value' => $form_values['label'],
+      );
+      $form['entity'] = array(
+        '#type' => 'select',
+        '#title' => t('Entity'),
+        '#options' => array(
+          STATES_ADMIN_ENTITY_NODE => t('Content'),
+          STATES_ADMIN_ENTITY_USER => t('User'),
+        ),
+        '#description' => t('Select the type of entity you want the state machine to track.'),
+        '#required' => TRUE,
+        '#default_value' => $form_values['entity'],
+      );
+      $form['any_state'] = array(
+        '#type' => 'checkbox',
+        '#title' => t('Allow any state'),
+        '#default_value' => $form_values['any_state'],
+        '#description' => t('Allows the state machine to have any state, rather than requiring one from a defined pool.'),
+      );
+      $form['unset'] = array(
+        '#type' => 'hidden',
+        '#value' => 'any_state',
+      );
+      $form = states_admin_checkboxes_multistep_fix($form); // Workaround FAPI checkbox multistep bug
+      break;
+
+    case STATES_ADMIN_STEP_STATES:
+      $form['state_list'] = array(
+        '#type' => 'textarea',
+        '#title' => t('Machine states'),
+        '#description' => t('List the possible states of the state machine, one per line.'),
+        '#required' => TRUE,
+        '#default_value' => implode("\n", $form_values['state_list']),
+      );
+      break;
+      
+    case STATES_ADMIN_STEP_OPTIONS:     
+      if ($form_values['any_state'] == 1) {
+        $form['initial_state'] = array(
+          '#title' => t('Initial state'),
+          '#type' => 'textfield',
+          '#maxlength' => 127,
+          '#description' => t('Choose a initial state for entities, or leave blank to leave entities with no state.'),
+          '#default_value' => $form_values['initial_state'],
+        );
+      }
+      else {
+        $states = $form_values['state_list'];
+        array_unshift($states, t('(none)'));
+        $form['initial_state'] = array(
+          '#title' => t('Initial state'),
+          '#type' => 'select',
+          '#options' => $states,
+          '#description' => t('Choose a initial state for entities.'),
+          '#required' => TRUE,
+          '#default_value' => $form_values['initial_state'],
+        );
+      }
+      $form['any_entity'] = array(
+        '#type' => 'checkbox',
+        '#default_value' => $form_values['any_entity'],
+      );
+      $form['unset'] = array(
+        '#type' => 'hidden',
+        '#value' => 'any_entity',
+      );
+      if ($form_values['entity'] == STATES_ADMIN_ENTITY_NODE) {
+        $form['any_entity']['#title'] = t('Allow any content type');
+        $form['any_entity']['#description'] = t('Allows the state machine to work on all content types, rather than ones from a defined pool.');
+      }
+      else {
+        $form['any_entity']['#title'] = t('Allow any user role');
+        $form['any_entity']['#description'] = t('Allows the state machine to work on all user roles, rather than ones from a defined pool.');
+      }
+      $form = states_admin_checkboxes_multistep_fix($form); // Workaround FAPI checkbox multistep bug
+      break;
+      
+    case STATES_ADMIN_STEP_FILTERS:     
+      $form['filters'] = array(
+        '#type' => 'checkboxes',
+        '#process' => array('expand_checkboxes' => array(), 'states_admin_checkboxes_multistep_fix' => array()), // Workaround FAPI checkbox multistep bug
+        '#default_value' => $form_values['filters'],
+      );
+      if ($form_values['entity'] == STATES_ADMIN_ENTITY_NODE) {
+        $form['filters']['#title'] = t('Permitted content types');
+        $form['filters']['#description'] = t('The state machine is only valid for the checked content types.');
+        $form['filters']['#options'] = node_get_types('names');
+      }
+      else {
+        $form['filters']['#title'] = t('Permitted user roles');
+        $form['filters']['#description'] = t('The state machine is only valid for the checked user roles.');
+        $form['filters']['#options'] = user_roles();
+      }
+      break;
+      
+    case STATES_ADMIN_STEP_REVIEW:     
+      $form['review_label'] = array(
+        '#prefix' => '<h3>'. t('You are about to create the following state machine:') .'</h3>',
+        '#type' => 'item',
+        '#title' => t('Label'),
+        '#value' => $form_values['label'],
+      );
+      if (isset($machine)) {
+        $form['review_label']['#prefix'] = '<h3>'. t('You are about to update the state machine to the following:') .'</h3>';
+      } 
+      
+      $form['review_entity'] = array(
+        '#type' => 'item',
+        '#title' => t('Entity'),
+        '#value' => $form_values['entity'] == STATES_ADMIN_ENTITY_NODE ? t('Node') : t('User'),
+      );
+      
+      $form['review_states'] = array(
+        '#type' => 'item',
+        '#title' => t('States'),
+        '#value' => t('(any)'),
+      );
+      if ($form_values['any_state'] != 1) {
+        $form['review_states']['#value'] = theme('item_list', $form_values['state_list']);
+      }
+      
+      $form['review_initial'] = array(
+        '#type' => 'item',
+        '#title' => t('Initial state'),
+        '#value' => t('(none)'),
+      );
+      if ($form_values['any_state'] == 1) {
+        if ($form_values['initial_state'] != '') {
+          $form['review_initial']['#value'] = $form_values['initial_state'];
+        }
+      }
+      else {
+        if ($form_values['initial_state'] > 0) {
+          $form['review_initial']['#value'] = $states[$form_values['initial_state'] - 1];
+        }
+      }
+      
+      $form['review_filter'] = array(
+        '#type' => 'item',
+        '#value' => t('(any)'),
+      );
+      if ($form_values['entity'] == STATES_ADMIN_ENTITY_NODE) {
+        $form['review_filter']['#title'] = t('Content types');
+        if ($form_values['any_entity'] != 1) {
+          $node_names = node_get_types('names');
+          $node_list = $form_values['filters'];
+          foreach ($node_list as &$value) {
+            $value = $node_names[$value];
+          }
+          $form['review_filter']['#value'] = theme('item_list', $node_list);
+        }
+      }
+      else {
+        $form['review_filter']['#title'] = t('User roles');
+        if ($form_values['any_entity'] != 1) {
+          $role_names = user_roles();
+          $role_list = $form_values['filters'];
+          foreach ($role_list as &$value) {
+            $value = $role_names[$value];
+          }
+          $form['review_filter']['#value'] = theme('item_list', $role_list);
+        }
+      }
+      
+      break;
+  }
+  if ($step != 1) {
+    $form['back'] = array(
+      '#type' => 'submit',
+      '#value' => t('Back'),
+    );
+  }
+  if ($step == STATES_ADMIN_STEP_REVIEW) {
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => isset($machine) ? t('Update') : t('Create'),
+    );
+  }
+  else {
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => t('Next'),
+    );
+  }
+  $form['#redirect'] = FALSE;
+  $form['#multistep'] = TRUE;
+  return $form;
+}
+
+function states_admin_create_validate($form_id, $form_values) {
+  $saved = unserialize($form_values['saved']);
+  unset($form_values['saved']);
+  $form_values = array_merge($saved, $form_values);
+  $step = $form_values['step'];
+  switch ($step) {
+    case STATES_ADMIN_STEP_STATES:
+      $states = explode("\n", $form_values['state_list']);
+      array_walk($states, 'states_admin_trim_value');
+      foreach ($states as $state) {
+        if (strlen($state) > 127) {
+          form_set_error('state_list', t('The state "!state" exceeds the maximum length of 127.', array('!state' => $state)));
+        }
+      }      
+      $states_count = array_count_values($states);
+      foreach ($states_count as $state => $count) {
+        if ($count != 1) {
+          form_set_error('state_list', t('The state "!state" appears more than once.', array('!state' => $state)));
+        }
+      }
+      break;
+  }
+}
+
+function states_admin_create_submit($form_id, $form_values, $sid_to_update = NULL) {
+  $saved = unserialize($form_values['saved']);
+  unset($form_values['saved']);
+  $form_values = array_merge($saved, $form_values);
+  $step = $form_values['step'];
+  
+  if ($step == STATES_ADMIN_STEP_REVIEW && $form_values['op'] != t('Back')) {
+    $label = $form_values['label'];
+    
+    $entity_type = $form_values['entity'];
+    
+    $entity_filter = '';
+    if ($form_values['any_entity'] != 1) {
+      $entity_filter = serialize(array_keys(unserialize($form_values['filters'])));
+    }
+    
+    $states = '*';
+    if ($form_values['any_state'] != 1) {
+      $states = $form_values['state_list'];
+    }
+    
+    $init_state = '';
+    if ($form_values['any_state'] == 1) {
+      $init_state = $form_values['initial_state'];
+    }
+    else {
+      if ($form_values['initial_state'] > 0) {
+        $init_state = $states[$form_values['initial_state'] - 1];
+      }
+    }
+    
+    if (isset($sid_to_update)) {
+      db_query("UPDATE {states_custom} SET label = '%s', entity_type = %d, entity_filter = '%s', states = '%s', init_state = '%s' WHERE sid = %d",
+        $label, $entity_type, $entity_filter, serialize($states), $init_state, $sid_to_update);
+      drupal_set_message(t('State machine updated'));
+    }
+    else
+    {
+      db_query("INSERT INTO {states_custom} (label, entity_type, entity_filter, states, init_state) VALUES ('%s', %d, '%s', '%s', '%s')",
+        $label, $entity_type, $entity_filter, serialize($states), $init_state); 
+      drupal_set_message(t('New state machine created'));
+    }
+    
+    drupal_goto(STATES_ADMIN_UI_PATH);
+  }
+}
+
+function states_admin_edit($machine_name, $form_values = NULL) {
+  if (!isset($form_values)) {
+    $machines = states_get_machines();
+    $machine = $machines[$machine_name];
+       
+    $form_values = array(
+      'step' => 0,
+      'saved' => serialize(array()),
+      'label' => $machine['#label'],
+      'entity' => $machine['#entity'] == 'node' ? STATES_ADMIN_ENTITY_NODE : STATES_ADMIN_ENTITY_USER,
+      'any_state' => $machine['#states'] == '*' ? 1 : 0,
+      'state_list' => $machine['#states'],
+      'any_entity' => (isset($machine['#types']) || isset($machine['#roles'])) ? 0 : 1,
+    );
+    if ($machine['#states'] == '*') {
+      $form_values['initial_state'] = isset($machine['#init_state']) ? $machine['#init_state'] : '';
+    }
+    else {
+      $form_values['initial_state'] = isset($machine['#init_state']) ? array_search($machine['#init_state'], $machine['#states']) : FALSE;
+      if ($form_values['initial_state'] === FALSE) {
+        $form_values['initial_state'] = 0;
+      }
+      else {
+        $form_values['initial_state'] = $form_values['initial_state'] + 1;
+      }
+    }
+    if ($machine['#entity'] == 'node' && isset($machine['#types'])) {
+      $form_values['filter'] = array_combine(array_values($machine['#types']), array_values($machine['#types']));
+    }
+    else if ($machine['#entity'] == 'user' && isset($machine['#roles'])) {
+      $form_values['filter'] = array_combine(array_values($machine['#roles']), array_values($machine['#roles']));
+    }
+  }
+  return states_admin_create($form_values);
+}
+
+function states_admin_edit_validate($form_id, $form_values) {
+  return states_admin_create_validate($form_id, $form_values);
+}
+
+function states_admin_edit_submit($form_id, $form_values) {
+  $machines = states_get_machines();
+  $sid_to_update = $machines[arg(3)]['custom_sid'];
+  return states_admin_create_submit($form_id, $form_values, $sid_to_update);
+}
+
+function states_admin_delete($machine_name) {
+  $machines = states_get_machines();
+  $machine = $machines[$machine_name];
+  
+  $form = array();
+  return confirm_form($form,
+    t('Are you sure you want to delete the state machine "%machine"?', array('%machine' => $machine['#label'])),
+    array('path' => STATES_ADMIN_UI_PATH),
+    t('This action cannot be undone.'), t('Delete'), t('Cancel')
+  );
+}
+
+function states_admin_delete_submit($form_id, $form_values) {
+  $machines = states_get_machines();
+  $sid_to_delete = $machines[arg(3)]['custom_sid'];
+  db_query("DELETE FROM {states_custom} WHERE sid = %d", $sid_to_delete);
+  drupal_set_message(t('State machine deleted'));
+  return STATES_ADMIN_UI_PATH;
+}
