Skip to content

Creating a Custom Connector

Geoffrey K Taylor edited this page Jul 28, 2020 · 18 revisions

Introduction

Connectors are the mechanism by which Stream logs activity that's happening in WordPress.

Connectors are really just a simple way to encapsulate the instructions Stream needs to monitor actions taken by specific WordPress components, like Posts, as well as actions by specific plugins, like Jetpack.

A Connector is comprised of six primary components:

  1. Connector name/slug
  2. Connector label
  3. Action callbacks
  4. Action labels
  5. Context labels
  6. Callback functions

Understanding Contexts

A Connector should be thought of as a top-level Context, and Contexts listed within our Connector as second-level Contexts.

These second-level Contexts help to further categorize actions that are happening within the Connector we are tracking.

For example, if there is a custom post type called "Books" and there is a book post called "Moby Dick" that is updated, the Stream record data would look like this:

Summary: "Moby Dick" book updated

Contexts: Posts > Books

Action: Updated

The Connector Class

Every Connector registered in Stream must be a child class that inherits from the main WP_Stream\Connector abstract class where the required arguments for Connector methods are defined.

Connector Example

namespace My_Plugin;

class Connector extends \WP_Stream\Connector {
	/**
	 * Connector slug
	 *
	 * @var string
	 */
	public $name = 'my_plugin';

	/**
	 * Actions registered for this connector
	 *
	 * These are actions that My Plugin has created, we are defining them here to
	 * tell Stream to run a callback each time this action is fired so we can
	 * log information about what happened.
	 *
	 * @var array
	 */
	public $actions = array(
		'my_plugin_update_foo',
		'my_plugin_update_bar',
	);

	/**
	 * The minimum version required for My Plugin
	 *
	 * @const string
	 */
	const PLUGIN_MIN_VERSION = '1.0.0';

	/**
	 * Display an admin notice if plugin dependencies are not satisfied
	 *
	 * If My Plugin does not have the minimum required version number specified
	 * in the constant above, then Stream will display an admin notice for us.
	 *
	 * @return bool
	 */
	public function is_dependency_satisfied() {
		$version_compare = version_compare( My_Plugin_Class::$version, self::PLUGIN_MIN_VERSION, '>=' );
		if ( class_exists( 'My_Plugin_Class' ) && $version_compare ) {
			return true;
		}

		return false;
	}

	/**
	 * Return translated connector label
	 *
	 * @return string
	 */
	public function get_label() {
		return __( 'My Plugin', 'my-plugin-text-domain' );
	}

	/**
	 * Return translated context labels
	 *
	 * @return array
	 */
	public function get_context_labels() {
		return array(
			'foo'    => __( 'Foo', 'my-plugin-text-domain' ),
			'bar' => __( 'Bar', 'my-plugin-text-domain' ),
		);
	}

	/**
	 * Return translated action labels
	 *
	 * @return array
	 */
	public function get_action_labels() {
		return array(
			'created' => __( 'Created', 'my-plugin-text-domain' ),
			'updated' => __( 'Updated', 'my-plugin-text-domain' ),
		);
	}

	/**
	 * Add action links to Stream drop row in admin list screen
	 *
	 * This method is optional.
	 *
	 * @param array  $links  Previous links registered
	 * @param Record $record Stream record
	 *
	 * @return array Action links
	 */
	public function action_links( $links, $record ) {
		// Check if the Foo or Bar exists
		if ( $record->object_id && get_post_status( $record->object_id ) ) {
			$post_type_name = $this->get_post_type_name( get_post_type( $record->object_id ) );
			$action_link_text = sprintf(
				esc_html_x( 'Edit %s', 'Post type singular name', 'stream' ),
				$post_type_name
			);
			$links[ $action_link_text ] = get_edit_post_link( $record->object_id );
		}

		return $links;
	}

	/**
	 * Track create and update actions on Foos
	 *
	 * @param array $foo
	 * @param bool  $is_new
	 *
	 * @return void
	 */
	public function callback_my_plugin_update_foo( $foo, $is_new ) {
		$action = __( 'updated', 'my-plugin-text-domain' );
		if ( $is_new ) {
			$action = __( 'created', 'my-plugin-text-domain' );
		}
		$this->log(
			// Summary message
			sprintf(
				__( '"%1$s" foo %2$s', 'my-plugin-text-domain' ),
				$foo['title'],
				$action
			),
			// This array is compacted and saved as Stream meta
			array(
				'action' => $action,
				'id'     => $foo['id'],
				'title'  => $foo['title'],
			),
			$foo['id'], // Object ID
			'foo', // Context
			$action
		);
	}

	/**
	 * Track create and update actions on Bars
	 *
	 * @param array $bar
	 * @param bool  $is_new
	 *
	 * @return void
	 */
	public function callback_my_plugin_update_bar( $bar, $is_new ) {
		$action = __( 'updated', 'my-plugin-text-domain' );
		if ( $is_new ) {
			$action = __( 'created', 'my-plugin-text-domain' );
		}
		$this->log(
			// Summary message
			sprintf(
				__( '"%1$s" bar %2$s', 'my-plugin-text-domain' ),
				$bar['title'],
				$action
			),
			// This array is compacted and saved as Stream meta
			array(
				'action' => $action,
				'id'     => $bar['id'],
				'title'  => $bar['title'],
			),
			$bar['id'], // Object ID
			'bar', // Context
			$action // Action
		);
	}
}

Registering A Connector

Now we need to tell Stream to include our new child class every time it loads the rest of Connectors in Stream.

This will also make your custom Contexts and Actions appear in the Stream Records filter dropdown menus.

To register a Connector, hook into the wp_stream_connectors filter from inside your plugin.

/**
 * Register the My Plugin connector for Stream
 *
 * @filter wp_stream_connectors
 *
 * @param array $classes  Array of connector class names
 *
 * @return array
 */
function my_plugin_register_stream_connector( $classes ) {
	require plugin_dir_path( __FILE__ ) . '/class-wp-stream-connector-my-plugin.php';

	$class_name = '\My_Plugin\Connector';

	if ( ! class_exists( $class_name ) ) {
		return;
	}

	$stream = wp_stream_get_instance();
	$class = new $class_name();

	if ( ! method_exists( $class, 'is_dependency_satisfied' ) ) {
		return;
	}

	if ( $class->is_dependency_satisfied() ) {
		$classes[] = $class;
	}

	return $classes;
}
add_filter( 'wp_stream_connectors', 'my_plugin_register_stream_connector' );