# jetpack/16.3/jetpack_vendor/automattic/jetpack-sharing-likes/src/settings/class-post-handler.php

Jetpack – WP Security, Backup, Speed, &amp; Growth, version 16.3. 376 lines.

- Page: https://pluginprobe.com/plugins/jetpack/16.3/code/jetpack_vendor/automattic/jetpack-sharing-likes/src/settings/class-post-handler.php
- Raw: https://pluginprobe.com/plugins/jetpack/16.3/raw/jetpack_vendor/automattic/jetpack-sharing-likes/src/settings/class-post-handler.php
- Modified: 2026-09-29T02:50:08+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/jetpack/16.3/code/jetpack_vendor/automattic/jetpack-sharing-likes/src/settings/class-post-handler.php#L10-L20`.

```php
<?php
/**
 * Handles form submissions from Settings > Sharing.
 *
 * @package automattic/jetpack-sharing-likes
 */

declare( strict_types = 1 );

namespace Automattic\Jetpack\Sharing_Likes\Settings;

use Automattic\Jetpack\Modules;

/**
 * Processes the screen's form submissions.
 *
 * Each section posts its own action with its own nonce, so saving one section
 * never runs another section's handlers.
 */
final class Post_Handler {

	/**
	 * Field naming the requested action.
	 */
	private const ACTION_FIELD = 'jetpack_sharing_action';

	/**
	 * Hook the handler up.
	 */
	public static function init(): void {
		add_action( 'admin_init', array( __CLASS__, 'maybe_handle' ) );
	}

	/**
	 * Dispatch a submission, if this request is one.
	 */
	public static function maybe_handle(): void {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- identifying the screen; the nonce is verified below.
		if ( ! isset( $_GET['page'] ) || Settings_Page::SLUG !== $_GET['page'] ) {
			return;
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified per action below.
		if ( ! isset( $_POST[ self::ACTION_FIELD ] ) ) {
			return;
		}

		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified per action below.
		$action = sanitize_key( wp_unslash( $_POST[ self::ACTION_FIELD ] ) );

		$redirect = null;

		switch ( $action ) {
			case 'activate-likes':
				$redirect = self::activate_module( 'likes', Likes_Section::NONCE_ACTION );
				break;
			case 'activate-sharing':
				$redirect = self::activate_module( 'sharedaddy', Sharing_Section::NONCE_ACTION );
				break;
			case 'switch-to-block-likes':
				$redirect = self::switch_likes_to_block();
				break;
			case 'switch-to-block-sharing':
				$redirect = self::switch_sharing_to_block();
				break;
			case 'save-settings':
				$redirect = self::save_settings();
				break;
		}

		if ( null === $redirect ) {
			return;
		}

		wp_safe_redirect( $redirect );
		exit;
	}

	/**
	 * Stop producing legacy sharing buttons, so the block can take over.
	 *
	 * This is a migration, not the section's off switch: it is what the Jetpack
	 * dashboard's "Switch to the … block" button does, and it leaves the block
	 * itself untouched. Simple has no module to deactivate, so it removes every
	 * service instead, which the services list can undo.
	 *
	 * @return string URL to send the browser back to.
	 */
	private static function switch_sharing_to_block(): string {
		check_admin_referer( Sharing_Section::NONCE_ACTION );

		if ( Environment::is_simple_site() ) {
			self::remove_all_sharing_services();
		} else {
			( new Modules() )->deactivate( 'sharedaddy' );
		}

		return self::redirect_url( true );
	}

	/**
	 * The Like buttons counterpart of `switch_sharing_to_block()`.
	 *
	 * On Simple it turns off Likes and Reblogs for every post, since the legacy
	 * widget renders for either. Posts that opted in individually keep their
	 * buttons, and Comment Likes has no block to move to, so it is left alone.
	 *
	 * @return string URL to send the browser back to.
	 */
	private static function switch_likes_to_block(): string {
		check_admin_referer( Likes_Section::NONCE_ACTION );

		if ( Environment::is_simple_site() ) {
			update_option( 'disabled_likes', 1 );
			update_option( 'disabled_reblogs', 1 );
		} else {
			( new Modules() )->deactivate( 'likes' );
		}

		return self::redirect_url( true );
	}

	/**
	 * Leave sharedaddy no services to render.
	 */
	private static function remove_all_sharing_services(): void {
		// Preferred over writing the option, because wpcom hooks the state change it announces.
		if ( class_exists( 'Sharing_Service' ) ) {
			( new \Sharing_Service() )->set_blog_services( array(), array() );
			return;
		}

		update_option(
			'sharing-services',
			array(
				'visible' => array(),
				'hidden'  => array(),
			)
		);
	}

	/**
	 * Save every section that put fields on the form.
	 *
	 * Only those: the others' fields were not on the screen, and reading their
	 * absence as "off" would switch them off.
	 *
	 * @return string URL to send the browser back to.
	 */
	private static function save_settings(): string {
		check_admin_referer( Settings_Form::NONCE_ACTION );

		$sections           = Settings_Form::posted_sections();
		$comment_likes_held = true;

		// Before placement, because the services save rebuilds the global options it lives in.
		if ( in_array( Settings_Form::SECTION_SHARING, $sections, true ) ) {
			self::save_sharing_options();
		}

		if ( in_array( Settings_Form::SECTION_PLACEMENT, $sections, true ) ) {
			self::save_placement();
		}

		if ( in_array( Settings_Form::SECTION_LIKES, $sections, true ) ) {
			self::save_likes();
		}

		if ( in_array( Settings_Form::SECTION_COMMENT_LIKES, $sections, true ) && Environment::likes_supported() ) {
			$comment_likes_held = self::save_comment_likes();
		}

		// Once, from whichever section rendered `Services_Config::global_options()`; never both.
		if ( array_intersect( array( Settings_Form::SECTION_SHARING, Settings_Form::SECTION_EXTRAS ), $sections ) ) {
			self::save_global_options( $sections );
		}

		return $comment_likes_held
			? self::redirect_url( true )
			: add_query_arg( Settings_Page::COMMENT_LIKES_UNCHANGED, '1', self::redirect_url( true ) );
	}

	/**
	 * Save the rows that close the settings table, ours and then third parties'.
	 *
	 * @param string[] $sections Sections the submitted form carried fields for.
	 */
	private static function save_global_options( array $sections ): void {
		// Only the services section renders it, and `is_available()` can have turned true
		// since the form was built, so the claim decides rather than the environment.
		if ( in_array( Settings_Form::SECTION_SHARING, $sections, true ) ) {
			Sharing_Resources::save();
		}

		Twitter_Site_Tag::save();

		/** This action is documented in projects/packages/sharing-likes/src/settings/class-services-config.php */
		do_action( 'sharing_admin_update' );
	}

	/**
	 * Save the services list's own settings: button style and label.
	 */
	private static function save_sharing_options(): void {
		// The section renders only when this class is loaded, but the request can claim it regardless.
		if ( ! class_exists( 'Sharing_Service' ) ) {
			return;
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput -- verified by the caller; set_global_options() validates each field.
		$data = $_POST;

		// set_global_options() rebuilds the global array from defaults, so a payload with no `show` would clear placement.
		if ( ! isset( $data['show'] ) ) {
			$data['show'] = Placement_Section::selected_post_types();
		}

		( new \Sharing_Service() )->set_global_options( $data );
	}

	/**
	 * Save the Like buttons settings.
	 */
	private static function save_likes(): void {
		if ( 'off' === self::posted_choice( 'wpl_default' ) ) {
			update_option( 'disabled_likes', 1 );
		} else {
			delete_option( 'disabled_likes' );
		}

		if ( Environment::is_simple_site() ) {
			if ( 'off' === self::posted_choice( 'jetpack_reblogs_enabled' ) ) {
				update_option( 'disabled_reblogs', 1 );
			} else {
				delete_option( 'disabled_reblogs' );
			}
		}
	}

	/**
	 * Save the Comment Likes checkbox: the option on Simple, the module on Atomic and Jetpack sites.
	 *
	 * @return bool Whether Comment Likes now match the checkbox.
	 */
	private static function save_comment_likes(): bool {
		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified by the caller.
		$enabled = ! empty( $_POST['jetpack_comment_likes_enabled'] );

		if ( Environment::is_simple_site() ) {
			update_option( 'jetpack_comment_likes_enabled', $enabled ? 1 : 0 );
			return true;
		}

		// `deactivate()` fires its hooks even when the module was already off.
		if ( Environment::comment_likes_enabled() === $enabled ) {
			return true;
		}

		if ( $enabled ) {
			( new Modules() )->activate( 'comment-likes', false, false );
		} else {
			( new Modules() )->deactivate( 'comment-likes' );
		}

		// A host can force the module either way, and activation needs a connected owner.
		return Environment::comment_likes_enabled() === $enabled;
	}

	/**
	 * Save where the buttons appear.
	 */
	private static function save_placement(): void {
		$options = get_option( 'sharing-options' );
		if ( ! is_array( $options ) ) {
			$options = array();
		}

		// Sites carry a malformed `global` (see #6121), and writing into it in place
		// would fatal where the services save, which rebuilds it wholesale, does not.
		if ( ! isset( $options['global'] ) || ! is_array( $options['global'] ) ) {
			$options['global'] = array();
		}

		$allowed   = array_values( get_post_types( array( 'public' => true ) ) );
		$allowed[] = 'index';

		// phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput -- verified by the caller; the values are checked against an allowlist below.
		$posted = isset( $_POST['show'] ) && is_array( $_POST['show'] ) ? wp_unslash( $_POST['show'] ) : array();
		$posted = array_filter( $posted, 'is_scalar' );

		$options['global']['show'] = array_values( array_intersect( $posted, $allowed ) );

		update_option( 'sharing-options', $options );
	}

	/**
	 * One of a radio group's values, defaulting to "on" when nothing was posted.
	 *
	 * @param string $field Field name.
	 */
	private static function posted_choice( string $field ): string {
		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- callers verify before reading.
		if ( empty( $_POST[ $field ] ) ) {
			return 'on';
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- callers verify before reading.
		return sanitize_text_field( wp_unslash( $_POST[ $field ] ) );
	}

	/**
	 * Where to send the browser once a submission is handled.
	 *
	 * @param bool $show_saved_notice Whether the screen should confirm a save.
	 */
	private static function redirect_url( bool $show_saved_notice ): string {
		$url = admin_url( 'options-general.php?page=' . Settings_Page::SLUG );

		return $show_saved_notice ? $url . '&update=saved' : $url;
	}

	/**
	 * Turn a module back on, then reload the screen.
	 *
	 * Reached only from the OFF variant, where no block route exists and nothing
	 * else on the site will bring the feature back. Sites that can use the block
	 * are not offered it, matching the Jetpack dashboard.
	 *
	 * @param string $module       Module slug.
	 * @param string $nonce_action Nonce action the submitting section uses.
	 * @return string URL to send the browser back to.
	 */
	private static function activate_module( string $module, string $nonce_action ): string {
		check_admin_referer( $nonce_action );

		( new Modules() )->activate( $module, false, false );

		return self::redirect_url( false );
	}

	/**
	 * Hidden field naming the action a form is submitting.
	 *
	 * @param string $action Action name, matching a case above.
	 */
	public static function render_action_field( string $action ): void {
		printf(
			'<input type="hidden" name="%1$s" value="%2$s" />',
			esc_attr( self::ACTION_FIELD ),
			esc_attr( $action )
		);
	}

	/**
	 * Markup for a single-button form submitting one of the actions above.
	 *
	 * @param string $action       Action name, matching a case above.
	 * @param string $nonce_action Nonce action for the submitting section.
	 * @param string $label        Button label.
	 * @param bool   $primary      Whether this is the only action in its state.
	 */
	public static function render_action_form( string $action, string $nonce_action, string $label, bool $primary = true ): void {
		?>
		<form method="post" action="">
			<input type="hidden" name="<?php echo esc_attr( self::ACTION_FIELD ); ?>" value="<?php echo esc_attr( $action ); ?>" />
			<?php wp_nonce_field( $nonce_action ); ?>
			<p><button type="submit" class="<?php echo esc_attr( $primary ? 'button button-primary' : 'button' ); ?>"><?php echo esc_html( $label ); ?></button></p>
		</form>
		<?php
	}
}

```
