Index: wp-admin/includes/misc.php
===================================================================
--- wp-admin/includes/misc.php	(revision 17567)
+++ wp-admin/includes/misc.php	(working copy)
@@ -593,4 +593,84 @@
 </fieldset>
 <?php
 }
+
+/**
+ * Process settings submitted by the Settings API.
+ *
+ * @since 3.2.0
+ *
+ * @global array $whitelist_options A whitelist of permitted settings fields
+ * @param string $option_page The id of the panel that submitted the settings
+ */
+function process_settings( $option_page ) {
+	global $whitelist_options;
+	
+	if ( 'options' == $option_page && !isset( $_POST['option_page'] ) ) { // This is for back compat and will eventually be removed.
+		$unregistered = true;
+		check_admin_referer( 'update-options' );
+	} else {
+		$unregistered = false;
+		check_admin_referer( $option_page . '-options' );
+	}
+
+	if ( !isset( $whitelist_options[ $option_page ] ) )
+		wp_die( __( 'Error: options page not found.' ) );
+
+	if ( 'options' == $option_page ) {
+		if ( is_multisite() && ! is_super_admin() )
+			wp_die( __( 'You do not have sufficient permissions to modify unregistered settings for this site.' ) );
+		$options = explode( ',', stripslashes( $_POST[ 'page_options' ] ) );
+	} else {
+		$options = $whitelist_options[ $option_page ];
+	}
+
+	// Handle custom date/time formats
+	if ( 'general' == $option_page ) {
+		if ( !empty($_POST['date_format']) && isset($_POST['date_format_custom']) && '\c\u\s\t\o\m' == stripslashes( $_POST['date_format'] ) )
+			$_POST['date_format'] = $_POST['date_format_custom'];
+		if ( !empty($_POST['time_format']) && isset($_POST['time_format_custom']) && '\c\u\s\t\o\m' == stripslashes( $_POST['time_format'] ) )
+			$_POST['time_format'] = $_POST['time_format_custom'];
+		// Map UTC+- timezones to gmt_offsets and set timezone_string to empty.
+		if ( !empty($_POST['timezone_string']) && preg_match('/^UTC[+-]/', $_POST['timezone_string']) ) {
+			$_POST['gmt_offset'] = $_POST['timezone_string'];
+			$_POST['gmt_offset'] = preg_replace('/UTC\+?/', '', $_POST['gmt_offset']);
+			$_POST['timezone_string'] = '';
+		}
+	}
+
+	if ( $options ) {
+		foreach ( $options as $option ) {
+			if ( $unregistered )
+				_deprecated_argument( 'options.php', '2.7', sprintf( __( 'The <code>%1$s</code> setting is unregistered. Unregistered settings are deprecated. See http://codex.wordpress.org/Settings_API' ), $option, $option_page ) );
+
+			$option = trim($option);
+			$value = null;
+			if ( isset($_POST[$option]) )
+				$value = $_POST[$option];
+			if ( !is_array($value) )
+				$value = trim($value);
+			$value = stripslashes_deep($value);
+			
+			if ( is_network_admin() )
+				update_site_option( $option, $value );
+			else
+				update_option( $option, $value );
+		}
+	}
+
+	/**
+	 *  Handle settings errors and return to the originating page
+	 */
+	// If no settings errors were registered add a general 'updated' message.
+	if ( !count( get_settings_errors() ) )
+		add_settings_error('general', 'settings_updated', __('Settings saved.'), 'updated');
+	set_transient('settings_errors', get_settings_errors(), 30);
+
+	/**
+	 * Redirect back to the settings page that was submitted
+	 */
+	$goback = add_query_arg( 'settings-updated', 'true',  wp_get_referer() );
+	wp_redirect( $goback );
+	exit;
+}
 ?>
Index: wp-admin/includes/template.php
===================================================================
--- wp-admin/includes/template.php	(revision 17567)
+++ wp-admin/includes/template.php	(working copy)
@@ -1137,6 +1137,30 @@
 }
 
 /**
+ * Echoes the output of get_settings_form_action()
+ *
+ * @since 3.2.0
+ */
+function settings_form_action() {
+	echo get_settings_form_action();
+}
+
+/**
+ * Gets the proper form action for a settings screen
+ *
+ * On a single Site Admin, settings should be saved to options.php. On the Network Admin, settings
+ * are saved by edit.php?action=pluginoptions.
+ *
+ * @since 3.2.0
+ * 
+ * @return string $action The form action
+ */
+function get_settings_form_action() {
+	$action = is_network_admin() ? network_admin_url( 'edit.php?action=pluginoptions' ) : admin_url( 'options.php' );
+	return apply_filters( 'settings_form_action', $action );
+}
+
+/**
  * Prints out all settings sections added to a particular settings page
  *
  * Part of the Settings API. Use this in a settings page callback function
Index: wp-admin/network/edit.php
===================================================================
--- wp-admin/network/edit.php	(revision 17567)
+++ wp-admin/network/edit.php	(working copy)
@@ -165,6 +165,13 @@
 		exit();
 	break;
 
+	case 'pluginoptions':
+		wp_reset_vars (array( 'option_page' ) );
+		$whitelist_options = apply_filters( 'whitelist_options', array() );
+
+		process_settings( $option_page );
+	break;
+
 	case 'updateblog':
 		// No longer used.
 	break;
Index: wp-admin/options.php
===================================================================
--- wp-admin/options.php	(revision 17567)
+++ wp-admin/options.php	(working copy)
@@ -97,69 +97,7 @@
  * If $_GET['action'] == 'update' we are saving settings sent from a settings page
  */
 if ( 'update' == $action ) {
-	if ( 'options' == $option_page && !isset( $_POST['option_page'] ) ) { // This is for back compat and will eventually be removed.
-		$unregistered = true;
-		check_admin_referer( 'update-options' );
-	} else {
-		$unregistered = false;
-		check_admin_referer( $option_page . '-options' );
-	}
-
-	if ( !isset( $whitelist_options[ $option_page ] ) )
-		wp_die( __( 'Error: options page not found.' ) );
-
-	if ( 'options' == $option_page ) {
-		if ( is_multisite() && ! is_super_admin() )
-			wp_die( __( 'You do not have sufficient permissions to modify unregistered settings for this site.' ) );
-		$options = explode( ',', stripslashes( $_POST[ 'page_options' ] ) );
-	} else {
-		$options = $whitelist_options[ $option_page ];
-	}
-
-	// Handle custom date/time formats
-	if ( 'general' == $option_page ) {
-		if ( !empty($_POST['date_format']) && isset($_POST['date_format_custom']) && '\c\u\s\t\o\m' == stripslashes( $_POST['date_format'] ) )
-			$_POST['date_format'] = $_POST['date_format_custom'];
-		if ( !empty($_POST['time_format']) && isset($_POST['time_format_custom']) && '\c\u\s\t\o\m' == stripslashes( $_POST['time_format'] ) )
-			$_POST['time_format'] = $_POST['time_format_custom'];
-		// Map UTC+- timezones to gmt_offsets and set timezone_string to empty.
-		if ( !empty($_POST['timezone_string']) && preg_match('/^UTC[+-]/', $_POST['timezone_string']) ) {
-			$_POST['gmt_offset'] = $_POST['timezone_string'];
-			$_POST['gmt_offset'] = preg_replace('/UTC\+?/', '', $_POST['gmt_offset']);
-			$_POST['timezone_string'] = '';
-		}
-	}
-
-	if ( $options ) {
-		foreach ( $options as $option ) {
-			if ( $unregistered )
-				_deprecated_argument( 'options.php', '2.7', sprintf( __( 'The <code>%1$s</code> setting is unregistered. Unregistered settings are deprecated. See http://codex.wordpress.org/Settings_API' ), $option, $option_page ) );
-
-			$option = trim($option);
-			$value = null;
-			if ( isset($_POST[$option]) )
-				$value = $_POST[$option];
-			if ( !is_array($value) )
-				$value = trim($value);
-			$value = stripslashes_deep($value);
-			update_option($option, $value);
-		}
-	}
-
-	/**
-	 *  Handle settings errors and return to options page
-	 */
-	// If no settings errors were registered add a general 'updated' message.
-	if ( !count( get_settings_errors() ) )
-		add_settings_error('general', 'settings_updated', __('Settings saved.'), 'updated');
-	set_transient('settings_errors', get_settings_errors(), 30);
-
-	/**
-	 * Redirect back to the settings page that was submitted
-	 */
-	$goback = add_query_arg( 'settings-updated', 'true',  wp_get_referer() );
-	wp_redirect( $goback );
-	exit;
+	process_settings( $option_page );
 }
 
 include('./admin-header.php'); ?>
