'GET', 'callback' => array( $this, 'get' ), 'permission_callback' => array( RestModule::class, 'require_manage_woocommerce' ), ), array( 'methods' => 'PUT', 'callback' => array( $this, 'put' ), 'permission_callback' => array( RestModule::class, 'require_manage_woocommerce' ), ), ) ); } public function get(): \WP_REST_Response { return new \WP_REST_Response( self::payload() ); } /** * @return \WP_REST_Response|\WP_Error */ public function put( \WP_REST_Request $request ) { $patch = $request->get_json_params(); $error = self::validate( $patch ); if ( null !== $error ) { return $error; } $patch = (array) $patch; if ( array_key_exists( 'pixel_id', $patch ) ) { Options::set_pixel_id( (string) $patch['pixel_id'] ); unset( $patch['pixel_id'] ); } if ( array() !== $patch ) { Options::update_settings( $patch ); } return new \WP_REST_Response( self::payload() ); } /** * Booleans only, known keys only. Anything else is a 400 and nothing is written. * * @param mixed $patch Decoded JSON body. */ public static function validate( $patch ): ?\WP_Error { if ( ! is_array( $patch ) || array() === $patch ) { return self::invalid( __( 'Send a JSON object with pixel_enabled, injection and/or pixel_id.', 'cortexcommerce' ) ); } foreach ( $patch as $key => $value ) { if ( 'pixel_enabled' === $key ) { if ( ! is_bool( $value ) ) { return self::invalid( __( 'pixel_enabled must be true or false.', 'cortexcommerce' ) ); } continue; } if ( 'injection' === $key ) { if ( ! is_array( $value ) || array() === $value ) { return self::invalid( __( 'injection must be an object of flags.', 'cortexcommerce' ) ); } foreach ( $value as $flag => $on ) { if ( ! in_array( $flag, Options::INJECTION_KEYS, true ) || ! is_bool( $on ) ) { /* translators: %s: the flag name. */ return self::invalid( sprintf( __( 'Unknown or non-boolean injection flag: %s', 'cortexcommerce' ), (string) $flag ) ); } } continue; } if ( 'pixel_id' === $key ) { if ( ! is_string( $value ) || 1 !== preg_match( Options::PIXEL_ID_PATTERN, $value ) ) { return self::invalid( __( 'pixel_id must be 12 letters and digits.', 'cortexcommerce' ) ); } continue; } /* translators: %s: the key name. */ return self::invalid( sprintf( __( 'Unknown config key: %s', 'cortexcommerce' ), (string) $key ) ); } return null; } /** * @return array */ public static function payload(): array { $settings = Options::settings(); return array( 'tenant_id' => Options::tenant_id(), 'pixel_id' => Options::pixel_id(), 'store_host' => Config::store_host(), 'pixel_enabled' => $settings['pixel_enabled'], 'injection' => $settings['injection'], 'seo_plugin' => SeoAdapters::detect()->id(), // Since 1.1.0 the plugin pushes product changes itself (contract §5c) instead of WooCommerce webhooks. 'webhooks' => 'push', 'versions' => array( 'plugin' => Config::VERSION, 'wp' => (string) get_bloginfo( 'version' ), 'wc' => defined( 'WC_VERSION' ) ? (string) WC_VERSION : '', 'php' => PHP_VERSION, ), ); } private static function invalid( string $message ): \WP_Error { return new \WP_Error( 'cortexcommerce_invalid_config', $message, array( 'status' => 400 ) ); } }