HEX
Server: Apache/2.4.54 (Unix) OpenSSL/1.0.2k-fips
System: Linux f17.eelserver.com 3.10.0-1160.80.1.el7.x86_64 #1 SMP Tue Nov 8 15:48:59 UTC 2022 x86_64
User: zulfiqar (1155)
PHP: 8.2.0
Disabled: mail, exec, system, popen, proc_open, shell_exec, passthru, show_source
Upload Files
File: /home/zulfiqar/public_html/wp-content/plugins/stream/connectors/class-connector-woocommerce.php
<?php
/**
 * Connector for WooCommerce
 *
 * @package WP_Stream
 */

namespace WP_Stream;

/**
 * Class - Connector_WooCommerce
 */
class Connector_Woocommerce extends Connector {
	/**
	 * Context name
	 *
	 * @var string
	 */
	public $name = 'woocommerce';

	/**
	 * Holds tracked plugin minimum version required
	 *
	 * @const string
	 */
	const PLUGIN_MIN_VERSION = '2.1.10';

	/**
	 * Actions registered for this context
	 *
	 * @var array
	 */
	public $actions = array(
		'wp_stream_record_array',
		'updated_option',
		'transition_post_status',
		'deleted_post',
		'woocommerce_order_status_changed',
		'woocommerce_attribute_added',
		'woocommerce_attribute_updated',
		'woocommerce_attribute_deleted',
		'woocommerce_tax_rate_added',
		'woocommerce_tax_rate_updated',
		'woocommerce_tax_rate_deleted',
	);

	/**
	 * Taxonomies tracked by this connector.
	 *
	 * @var array
	 */
	public $taxonomies = array(
		'product_type',
		'product_cat',
		'product_tag',
		'product_shipping_class',
		'shop_order_status',
	);

	/**
	 * Post-types tracked by this connector.
	 *
	 * @var array
	 */
	public $post_types = array(
		'product',
		'product_variation',
		'shop_order',
		'shop_coupon',
	);

	/**
	 * Is the most recently update order logged yet.
	 *
	 * @var boolean
	 */
	private $order_update_logged = false;

	/**
	 * Caches WooCommerce settings page objects.
	 *
	 * @var array
	 */
	private $settings_pages = array();

	/**
	 * Caches WooCommerce settings.
	 *
	 * @var array
	 */
	private $settings = array();

	/**
	 * Stores the WooCommerce version number.
	 *
	 * @var string|null
	 */
	private $plugin_version = null;

	/**
	 * Register connection
	 */
	public function register() {
		parent::register();

		add_filter( 'wp_stream_posts_exclude_post_types', array( $this, 'exclude_order_post_types' ) );
		add_action( 'wp_stream_comments_exclude_comment_types', array( $this, 'exclude_order_comment_types' ) );

		$this->get_woocommerce_settings_fields();
	}

	/**
	 * Check if plugin dependencies are satisfied and add an admin notice if not
	 *
	 * @return bool
	 */
	public function is_dependency_satisfied() {
		global $woocommerce;

		if ( class_exists( 'WooCommerce' ) && version_compare( $woocommerce->version, self::PLUGIN_MIN_VERSION, '>=' ) ) {
			$this->plugin_version = $woocommerce->version;
			return true;
		}

		return false;
	}

	/**
	 * Return translated context label
	 *
	 * @return string Translated context label
	 */
	public function get_label() {
		return esc_html_x( 'WooCommerce', 'woocommerce', 'stream' );
	}

	/**
	 * Return translated action labels
	 *
	 * @return array Action label translations
	 */
	public function get_action_labels() {
		return array(
			'updated' => esc_html_x( 'Updated', 'woocommerce', 'stream' ),
			'created' => esc_html_x( 'Created', 'woocommerce', 'stream' ),
			'trashed' => esc_html_x( 'Trashed', 'woocommerce', 'stream' ),
			'deleted' => esc_html_x( 'Deleted', 'woocommerce', 'stream' ),
		);
	}

	/**
	 * Return translated context labels
	 *
	 * @return array Context label translations
	 */
	public function get_context_labels() {
		$context_labels = array();

		if ( class_exists( 'Connector_Posts' ) ) {
			$posts_connector = new Connector_Posts();
			$context_labels  = array_merge(
				$context_labels,
				$posts_connector->get_context_labels()
			);
		}

		$custom_context_labels = array(
			'attributes' => esc_html_x( 'Attributes', 'woocommerce', 'stream' ),
		);

		$context_labels = array_merge(
			$context_labels,
			$custom_context_labels,
			$this->settings_pages
		);

		return apply_filters( 'wp_stream_woocommerce_contexts', $context_labels );
	}

	/**
	 * Return settings used by WooCommerce that aren't registered
	 *
	 * @return array Custom settings with translated title and page
	 */
	public function get_custom_settings() {
		$custom_settings = array(
			'woocommerce_frontend_css_colors'     => array(
				'title'   => esc_html__( 'Frontend Styles', 'stream' ),
				'page'    => 'wc-settings',
				'tab'     => 'general',
				'section' => '',
				'type'    => esc_html__( 'setting', 'stream' ),
			),
			'woocommerce_default_gateway'         => array(
				'title'   => esc_html__( 'Gateway Display Default', 'stream' ),
				'page'    => 'wc-settings',
				'tab'     => 'checkout',
				'section' => '',
				'type'    => esc_html__( 'setting', 'stream' ),
			),
			'woocommerce_gateway_order'           => array(
				'title'   => esc_html__( 'Gateway Display Order', 'stream' ),
				'page'    => 'wc-settings',
				'tab'     => 'checkout',
				'section' => '',
				'type'    => esc_html__( 'setting', 'stream' ),
			),
			'woocommerce_default_shipping_method' => array(
				'title'   => esc_html__( 'Shipping Methods Default', 'stream' ),
				'page'    => 'wc-settings',
				'tab'     => 'shipping',
				'section' => '',
				'type'    => esc_html__( 'setting', 'stream' ),
			),
			'woocommerce_shipping_method_order'   => array(
				'title'   => esc_html__( 'Shipping Methods Order', 'stream' ),
				'page'    => 'wc-settings',
				'tab'     => 'shipping',
				'section' => '',
				'type'    => esc_html__( 'setting', 'stream' ),
			),
			'shipping_debug_mode'                 => array(
				'title'   => esc_html__( 'Shipping Debug Mode', 'stream' ),
				'page'    => 'wc-status',
				'tab'     => 'tools',
				'section' => '',
				'type'    => esc_html__( 'tool', 'stream' ),
			),
			'template_debug_mode'                 => array(
				'title'   => esc_html__( 'Template Debug Mode', 'stream' ),
				'page'    => 'wc-status',
				'tab'     => 'tools',
				'section' => '',
				'type'    => esc_html__( 'tool', 'stream' ),
			),
			'uninstall_data'                      => array(
				'title'   => esc_html__( 'Remove post types on uninstall', 'stream' ),
				'page'    => 'wc-status',
				'tab'     => 'tools',
				'section' => '',
				'type'    => esc_html__( 'tool', 'stream' ),
			),
		);

		return apply_filters( 'wp_stream_woocommerce_custom_settings', $custom_settings );
	}

	/**
	 * Add action links to Stream drop row in admin list screen
	 *
	 * @filter wp_stream_action_links_{connector}
	 *
	 * @param array  $links   Previous links registered.
	 * @param Record $record  Stream record.
	 *
	 * @return array Action links
	 */
	public function action_links( $links, $record ) {
		if ( in_array( $record->context, $this->post_types, true ) && get_post( $record->object_id ) ) {
			$edit_post_link = get_edit_post_link( $record->object_id );
			if ( $edit_post_link ) {
				$posts_connector = new Connector_Posts();
				$post_type_name  = $posts_connector->get_post_type_name( get_post_type( $record->object_id ) );
				/* translators: %s: a post type singular name (e.g. "Post") */
				$links[ sprintf( esc_html_x( 'Edit %s', 'Post type singular name', 'stream' ), $post_type_name ) ] = $edit_post_link;
			}

			$permalink = get_permalink( $record->object_id );
			if ( post_type_exists( get_post_type( $record->object_id ) ) && $permalink ) {
				$links[ esc_html__( 'View', 'stream' ) ] = $permalink;
			}
		}

		$context_labels = $this->get_context_labels();
		$option_key     = $record->get_meta( 'option', true );
		$option_page    = $record->get_meta( 'page', true );
		$option_tab     = $record->get_meta( 'tab', true );
		$option_section = $record->get_meta( 'section', true );

		if ( $option_key && $option_tab ) {
			/* translators: %s a context (e.g. "Attribute") */
			$text = sprintf( esc_html__( 'Edit WooCommerce %s', 'stream' ), $context_labels[ $record->context ] );
			$url  = add_query_arg(
				array(
					'page'    => $option_page,
					'tab'     => $option_tab,
					'section' => $option_section,
				),
				admin_url( 'admin.php' ) // Not self_admin_url here, as WooCommerce doesn't exist in Network Admin.
			);

			$links[ $text ] = $url . '#wp-stream-highlight:' . $option_key;
		}

		return $links;
	}

	/**
	 * Prevent the Stream Posts connector from logging orders
	 * so that we can handle them differently here
	 *
	 * @filter wp_stream_posts_exclude_post_types
	 *
	 * @param array $post_types  Ignored post types.
	 *
	 * @return array Filtered post types
	 */
	public function exclude_order_post_types( $post_types ) {
		$post_types[] = 'shop_order';

		return $post_types;
	}

	/**
	 * Prevent the Stream Comments connector from logging status
	 * change comments on orders
	 *
	 * @filter wp_stream_comment_exclude_comment_types
	 *
	 * @param array $comment_types  Ignored post types.
	 *
	 * @return array Filtered post types
	 */
	public function exclude_order_comment_types( $comment_types ) {
		$comment_types[] = 'order_note';

		return $comment_types;
	}

	/**
	 * Log Order major status changes ( creating / updating / trashing )
	 *
	 * @action transition_post_status
	 *
	 * @param string   $new_status New status.
	 * @param string   $old_status Old status.
	 * @param \WP_Post $post       Post object.
	 */
	public function callback_transition_post_status( $new_status, $old_status, $post ) {
		// Only track orders.
		if ( 'shop_order' !== $post->post_type ) {
			return;
		}

		// Don't track customer actions.
		if ( ! is_admin() ) {
			return;
		}

		// Don't track minor status change actions.
		if ( in_array( wp_stream_filter_input( INPUT_GET, 'action' ), array( 'mark_processing', 'mark_on-hold', 'mark_completed' ), true ) || defined( 'DOING_AJAX' ) ) {
			return;
		}

		// Don't log updates when more than one happens at the same time.
		if ( $post->ID === $this->order_update_logged ) {
			return;
		}

		if ( in_array( $new_status, array( 'auto-draft', 'draft', 'inherit' ), true ) ) {
			return;
		} elseif ( 'auto-draft' === $old_status && 'publish' === $new_status ) {
			/* translators: %s: an order title (e.g. "Order #42") */
			$message = esc_html_x(
				'%s created',
				'Order title',
				'stream'
			);
			$action  = 'created';
		} elseif ( 'trash' === $new_status ) {
			/* translators: %s: an order title (e.g. "Order #42") */
			$message = esc_html_x(
				'%s trashed',
				'Order title',
				'stream'
			);
			$action  = 'trashed';
		} elseif ( 'trash' === $old_status && 'publish' === $new_status ) {
			/* translators: %s: an order title (e.g. "Order #42") */
			$message = esc_html_x(
				'%s restored from the trash',
				'Order title',
				'stream'
			);
			$action  = 'untrashed';
		} else {
			/* translators: %s: an order title (e.g. "Order #42") */
			$message = esc_html_x(
				'%s updated',
				'Order title',
				'stream'
			);
		}

		if ( empty( $action ) ) {
			$action = 'updated';
		}

		$order_id        = \WC_Order_Factory::get_order_id( $post->ID );
		$order_title     = esc_html__( 'Order number', 'stream' ) . ' ' . esc_html( $order_id );
		$order_type_name = esc_html__( 'order', 'stream' );

		$this->log(
			$message,
			array(
				'post_title'    => $order_title,
				'singular_name' => $order_type_name,
				'new_status'    => $new_status,
				'old_status'    => $old_status,
				'revision_id'   => null,
			),
			$post->ID,
			$post->post_type,
			$action
		);

		$this->order_update_logged = $post->ID;
	}

	/**
	 * Log order deletion
	 *
	 * @action deleted_post
	 *
	 * @param int $post_id  Post ID.
	 */
	public function callback_deleted_post( $post_id ) {
		$post = get_post( $post_id );

		// We check if post is an instance of WP_Post as it doesn't always resolve in unit testing.
		if ( ! ( $post instanceof \WP_Post ) || 'shop_order' !== $post->post_type ) {
			return;
		}

		// Ignore auto-drafts that are deleted by the system, see issue-293.
		if ( 'auto-draft' === $post->post_status ) {
			return;
		}

		$order_id        = \WC_Order_Factory::get_order_id( $post->ID );
		$order_title     = esc_html__( 'Order number', 'stream' ) . ' ' . esc_html( $order_id );
		$order_type_name = esc_html__( 'order', 'stream' );

		$this->log(
			/* translators: %s: an order title (e.g. "Order #42") */
			_x(
				'"%s" deleted from trash',
				'Order title',
				'stream'
			),
			array(
				'post_title'    => $order_title,
				'singular_name' => $order_type_name,
			),
			$post->ID,
			$post->post_type,
			'deleted'
		);
	}

	/**
	 * Log Order minor status changes ( pending / on-hold / failed / processing / completed / refunded / cancelled )
	 *
	 * @action woocommerce_order_status_changed
	 *
	 * @param int    $order_id         Order ID.
	 * @param string $old_order_status Old order status.
	 * @param string $new_order_status New order status.
	 */
	public function callback_woocommerce_order_status_changed( $order_id, $old_order_status, $new_order_status ) {
		// Don't track customer actions.
		if ( ! is_admin() ) {
			return;
		}

		// Don't track new statuses.
		if ( empty( $old_order_status ) ) {
			return;
		}

		if ( version_compare( $this->plugin_version, '2.2', '>=' ) ) {
			$old_status_name = wc_get_order_status_name( $old_order_status );
			$new_status_name = wc_get_order_status_name( $new_order_status );
		} else {
			$old_status      = wp_stream_is_vip() ? wpcom_vip_get_term_by( 'slug', $old_order_status, 'shop_order_status' ) : get_term_by( 'slug', $old_order_status, 'shop_order_status' );
			$new_status      = wp_stream_is_vip() ? wpcom_vip_get_term_by( 'slug', $new_order_status, 'shop_order_status' ) : get_term_by( 'slug', $new_order_status, 'shop_order_status' );
			$new_status_name = $new_status->name;
			$old_status_name = $old_status->name;
		}

		/* translators: %1$s: an order title, %2$s: order status, %3$s: another order status (e.g. "Order #42", "processing", "complete") */
		$message = esc_html_x(
			'%1$s status changed from %2$s to %3$s',
			'1. Order title, 2. Old status, 3. New status',
			'stream'
		);

		$order_id        = \WC_Order_Factory::get_order( $order_id );
		$order_title     = esc_html__( 'Order number', 'stream' ) . ' ' . esc_html( $order_id );
		$order_type_name = esc_html__( 'order', 'stream' );

		$this->log(
			$message,
			array(
				'post_title'      => $order_title,
				'old_status_name' => $old_status_name,
				'new_status_name' => $new_status_name,
				'singular_name'   => $order_type_name,
				'new_status'      => $new_order_status,
				'old_status'      => $old_order_status,
				'revision_id'     => null,
			),
			$order_id,
			'shop_order',
			'updated'
		);
	}

	/**
	 * Log adding a product attribute
	 *
	 * @action woocommerce_attribute_added
	 *
	 * @param int   $attribute_id  Attribute ID.
	 * @param array $attribute     Attribute data.
	 */
	public function callback_woocommerce_attribute_added( $attribute_id, $attribute ) {
		$this->log(
			/* translators: %s: a term name (e.g. "color") */
			_x(
				'"%s" product attribute created',
				'Term name',
				'stream'
			),
			$attribute,
			$attribute_id,
			'attributes',
			'created'
		);
	}

	/**
	 * Log updating a product attribute
	 *
	 * @action woocommerce_attribute_updated
	 *
	 * @param int   $attribute_id  Attribute ID.
	 * @param array $attribute     Attribute data.
	 */
	public function callback_woocommerce_attribute_updated( $attribute_id, $attribute ) {
		$this->log(
			/* translators: %s a term name (e.g. "color") */
			_x(
				'"%s" product attribute updated',
				'Term name',
				'stream'
			),
			$attribute,
			$attribute_id,
			'attributes',
			'updated'
		);
	}

	/**
	 * Log deleting a product attribute
	 *
	 * @action woocommerce_attribute_updated
	 *
	 * @param int    $attribute_id    Attribute ID.
	 * @param string $attribute_name  Attribute name.
	 */
	public function callback_woocommerce_attribute_deleted( $attribute_id, $attribute_name ) {
		$this->log(
			/* translators: %s: a term name (e.g. "color") */
			_x(
				'"%s" product attribute deleted',
				'Term name',
				'stream'
			),
			array(
				'attribute_name' => $attribute_name,
			),
			$attribute_id,
			'attributes',
			'deleted'
		);
	}

	/**
	 * Log adding a tax rate
	 *
	 * @action woocommerce_tax_rate_added
	 *
	 * @param int   $tax_rate_id  Tax Rate ID.
	 * @param array $tax_rate     Tax Rate data.
	 */
	public function callback_woocommerce_tax_rate_added( $tax_rate_id, $tax_rate ) {
		$this->log(
			/* translators: %4$s: a tax rate name (e.g. "GST") */
			_x(
				'"%4$s" tax rate created',
				'Tax rate name',
				'stream'
			),
			$tax_rate,
			$tax_rate_id,
			'tax',
			'created'
		);
	}

	/**
	 * Log updating a tax rate
	 *
	 * @action woocommerce_tax_rate_updated
	 *
	 * @param int   $tax_rate_id  Tax Rate ID.
	 * @param array $tax_rate     Tax Rate data.
	 */
	public function callback_woocommerce_tax_rate_updated( $tax_rate_id, $tax_rate ) {
		$tax_rate_label = \WC_Tax::get_rate_label( $tax_rate_id );

		$this->log(
			/* translators: %s: a tax rate name (e.g. "GST") */
			_x(
				'"%s" tax rate updated',
				'Tax rate name',
				'stream'
			),
			array( $tax_rate_label ),
			$tax_rate_id,
			'tax',
			'updated'
		);
	}

	/**
	 * Log deleting a tax rate
	 *
	 * @action woocommerce_tax_rate_updated
	 *
	 * @param int $tax_rate_id  Tax Rate ID.
	 */
	public function callback_woocommerce_tax_rate_deleted( $tax_rate_id ) {
		global $wpdb;

		$tax_rate_name = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT tax_rate_name FROM {$wpdb->prefix}woocommerce_tax_rates
				WHERE tax_rate_id = %s
				",
				$tax_rate_id
			)
		);

		$this->log(
			/* translators: %4$s: a tax rate name (e.g. "GST") */
			_x(
				'"%s" tax rate deleted',
				'Tax rate name',
				'stream'
			),
			array(
				'tax_rate_name' => $tax_rate_name,
			),
			$tax_rate_id,
			'tax',
			'deleted'
		);
	}

	/**
	 * Filter records and take-over our precious data
	 *
	 * @filter wp_stream_record_array
	 *
	 * @param array $recordarr  Record data to be inserted.
	 *
	 * @return array Filtered record data
	 */
	public function callback_wp_stream_record_array( $recordarr ) {
		foreach ( $recordarr as $key => $record ) {
			if ( ! isset( $record['connector'] ) || ! isset( $record['context'] ) ) {
				continue;
			}

			// Change connector::posts records.
			if ( 'posts' === $record['connector'] && in_array( $record['context'], $this->post_types, true ) ) {
				$recordarr[ $key ]['connector'] = $this->name;
			} elseif ( 'taxonomies' === $record['connector'] && in_array( $record['context'], $this->taxonomies, true ) ) {
				$recordarr[ $key ]['connector'] = $this->name;
			} elseif ( 'settings' === $record['connector'] ) {
				$option = isset( $record['meta']['option_key'] ) ? $record['meta']['option_key'] : false;

				if ( $option && isset( $this->settings[ $option ] ) ) {
					return false;
				}
			}
		}

		return $recordarr;
	}

	/**
	 * Track WooCommerce-specific option changes.
	 *
	 * @param string $option_key   Option key.
	 * @param string $old_value    Old value.
	 * @param string $value        New value.
	 */
	public function callback_updated_option( $option_key, $old_value, $value ) {
		$options = array( $option_key );

		if ( is_array( $old_value ) || is_array( $value ) ) {
			foreach ( $this->get_changed_keys( $old_value, $value ) as $field_key ) {
				$options[] = $field_key;
			}
		}

		foreach ( $options as $option ) {
			if ( ! array_key_exists( $option, $this->settings ) ) {
				continue;
			}

			$this->log(
				/* translators: %1$s: a setting name, %2$s: a setting type (e.g. "Direct Deposit", "Payment Method") */
				__( '"%1$s" %2$s updated', 'stream' ),
				array(
					'label'     => $this->settings[ $option ]['title'],
					'type'      => $this->settings[ $option ]['type'],
					'page'      => $this->settings[ $option ]['page'],
					'tab'       => $this->settings[ $option ]['tab'],
					'section'   => $this->settings[ $option ]['section'],
					'option'    => $option,
					'old_value' => maybe_serialize( $old_value ),
					'value'     => maybe_serialize( $value ),
				),
				null,
				$this->settings[ $option ]['tab'],
				'updated'
			);
		}
	}

	/**
	 * Loads the WooCommerce admin settings.
	 */
	public function get_woocommerce_settings_fields() {
		if ( ! defined( 'WC_VERSION' ) || ! class_exists( 'WC_Admin_Settings' ) ) {
			return false;
		}

		if ( ! empty( $this->settings ) ) {
			return $this->settings;
		}

		$settings_cache_key = 'stream_connector_woocommerce_settings_' . sanitize_key( WC_VERSION );
		$settings_transient = get_transient( $settings_cache_key );

		if ( $settings_transient ) {
			$settings       = $settings_transient['settings'];
			$settings_pages = $settings_transient['settings_pages'];
		} else {
			global $woocommerce;

			$settings       = array();
			$settings_pages = array();

			foreach ( \WC_Admin_Settings::get_settings_pages() as $page ) {
				/**
				 * Get ID / Label of the page, since they're protected, by hacking into
				 * the callback filter for 'woocommerce_settings_tabs_array'.
				 */
				$info       = $page->add_settings_page( array() );
				$page_id    = key( $info );
				$page_label = current( $info );
				$sections   = $page->get_sections();

				if ( empty( $sections ) ) {
					$sections[''] = $page_label;
				}

				$settings_pages[ $page_id ] = $page_label;

				// Remove non-fields ( sections, titles and whatever ).
				$fields = array();

				foreach ( $sections as $section_key => $section_label ) {
					$_fields = array_filter(
						$page->get_settings( $section_key ),
						function ( $item ) {
							return isset( $item['id'] ) && ( ! in_array( $item['type'], array( 'title', 'sectionend' ), true ) );
						}
					);

					foreach ( $_fields as $field ) {
						$title                  = isset( $field['title'] ) ? $field['title'] : ( isset( $field['desc'] ) ? $field['desc'] : 'N/A' );
						$fields[ $field['id'] ] = array(
							'title'   => $title,
							'page'    => 'wc-settings',
							'tab'     => $page_id,
							'section' => $section_key,
							'type'    => esc_html__( 'setting', 'stream' ),
						);
					}
				}

				// Store fields in the global array to be searched later.
				$settings = array_merge( $settings, $fields );
			}

			// Provide additional context for each of the settings pages.
			array_walk(
				$settings_pages,
				function ( &$value ) {
					$value .= ' ' . esc_html__( 'Settings', 'stream' );
				}
			);

			// Load Payment Gateway Settings.
			$payment_gateway_settings = array();
			$payment_gateways         = $woocommerce->payment_gateways();

			foreach ( $payment_gateways->payment_gateways as $section_key => $payment_gateway ) {
				$title = $payment_gateway->title;
				$key   = $payment_gateway->plugin_id . $payment_gateway->id . '_settings';

				$payment_gateway_settings[ $key ] = array(
					'title'   => $title,
					'page'    => 'wc-settings',
					'tab'     => 'checkout',
					'section' => strtolower( $section_key ),
					'type'    => esc_html__( 'payment gateway', 'stream' ),
				);
			}

			$settings = array_merge( $settings, $payment_gateway_settings );

			// Load Shipping Method Settings.
			$shipping_method_settings = array();
			$shipping_methods         = $woocommerce->shipping();

			foreach ( (array) $shipping_methods->shipping_methods as $section_key => $shipping_method ) {
				$title = $shipping_method->title;
				$key   = $shipping_method->plugin_id . $shipping_method->id . '_settings';

				$shipping_method_settings[ $key ] = array(
					'title'   => $title,
					'page'    => 'wc-settings',
					'tab'     => 'shipping',
					'section' => strtolower( $section_key ),
					'type'    => esc_html__( 'shipping method', 'stream' ),
				);
			}

			$settings = array_merge( $settings, $shipping_method_settings );

			// Load Email Settings.
			$email_settings = array();
			$emails         = $woocommerce->mailer();

			foreach ( $emails->emails as $section_key => $email ) {
				$title = $email->title;
				$key   = $email->plugin_id . $email->id . '_settings';

				$email_settings[ $key ] = array(
					'title'   => $title,
					'page'    => 'wc-settings',
					'tab'     => 'email',
					'section' => strtolower( $section_key ),
					'type'    => esc_html__( 'email', 'stream' ),
				);
			}

			$settings = array_merge( $settings, $email_settings );

			// Tools page.
			$tools_page = array(
				'tools' => esc_html__( 'Tools', 'stream' ),
			);

			$settings_pages = array_merge( $settings_pages, $tools_page );

			// Cache the results.
			$settings_cache = array(
				'settings'       => $settings,
				'settings_pages' => $settings_pages,
			);

			set_transient( $settings_cache_key, $settings_cache, MINUTE_IN_SECONDS * 60 * 6 );
		}

		$custom_settings      = $this->get_custom_settings();
		$this->settings       = array_merge( $settings, $custom_settings );
		$this->settings_pages = $settings_pages;

		return $this->settings;
	}
}