[ label, color ] */ public static function groups() { return array( 'posts' => array( 'label' => __( 'Posts and pages', 'hookbird' ), 'color' => '#2271b1', ), 'users' => array( 'label' => __( 'Users and logins', 'hookbird' ), 'color' => '#8c5cd6', ), 'comments' => array( 'label' => __( 'Comments', 'hookbird' ), 'color' => '#00a32a', ), 'site' => array( 'label' => __( 'Plugins, themes and core', 'hookbird' ), 'color' => '#dba617', ), 'woocommerce' => array( 'label' => __( 'WooCommerce', 'hookbird' ), 'color' => '#7f54b3', ), 'custom' => array( 'label' => __( 'Custom', 'hookbird' ), 'color' => '#50575e', ), ); } /** * All event definitions. * * Each definition: label, group, description, title (template with {data.path} tokens), * fields (label => path), link (path to a URL), sample (sample data key), color (optional). * * @return array */ public static function events() { if ( null !== self::$events ) { return self::$events; } $events = array( 'post.published' => array( 'label' => __( 'Post published', 'hookbird' ), 'group' => 'posts', 'title' => __( 'Published: {post.title}', 'hookbird' ), 'fields' => array( __( 'Type', 'hookbird' ) => 'post.type', __( 'Author', 'hookbird' ) => 'post.author.name', __( 'Category', 'hookbird' ) => 'post.categories', ), 'link' => 'post.url', 'sample' => 'post', ), 'post.updated' => array( 'label' => __( 'Published post updated', 'hookbird' ), 'group' => 'posts', 'title' => __( 'Updated: {post.title}', 'hookbird' ), 'fields' => array( __( 'Type', 'hookbird' ) => 'post.type', __( 'Author', 'hookbird' ) => 'post.author.name', ), 'link' => 'post.url', 'sample' => 'post', ), 'post.trashed' => array( 'label' => __( 'Post moved to trash', 'hookbird' ), 'group' => 'posts', 'title' => __( 'Trashed: {post.title}', 'hookbird' ), 'fields' => array( __( 'Type', 'hookbird' ) => 'post.type', __( 'By', 'hookbird' ) => 'actor.name', ), 'link' => 'post.edit_url', 'sample' => 'post', 'color' => '#d63638', ), 'user.registered' => array( 'label' => __( 'User registered', 'hookbird' ), 'group' => 'users', 'title' => __( 'New user: {user.display_name}', 'hookbird' ), 'fields' => array( __( 'Email', 'hookbird' ) => 'user.email', __( 'Role', 'hookbird' ) => 'user.roles', ), 'link' => 'user.edit_url', 'sample' => 'user', ), 'user.updated' => array( 'label' => __( 'User profile updated', 'hookbird' ), 'group' => 'users', 'title' => __( 'Profile updated: {user.display_name}', 'hookbird' ), 'fields' => array( __( 'Email', 'hookbird' ) => 'user.email', __( 'Role', 'hookbird' ) => 'user.roles', ), 'link' => 'user.edit_url', 'sample' => 'user', ), 'user.role_changed' => array( 'label' => __( 'User role changed', 'hookbird' ), 'group' => 'users', 'title' => __( 'Role changed for {user.display_name}', 'hookbird' ), 'fields' => array( __( 'New role', 'hookbird' ) => 'role', __( 'Previous roles', 'hookbird' ) => 'previous_roles', ), 'link' => 'user.edit_url', 'sample' => 'user_role', 'color' => '#dba617', ), 'user.deleted' => array( 'label' => __( 'User deleted', 'hookbird' ), 'group' => 'users', 'title' => __( 'User deleted: {user.display_name}', 'hookbird' ), 'fields' => array( __( 'Email', 'hookbird' ) => 'user.email', __( 'By', 'hookbird' ) => 'actor.name', ), 'sample' => 'user', 'color' => '#d63638', ), 'user.login' => array( 'label' => __( 'User logged in', 'hookbird' ), 'group' => 'users', 'title' => __( '{user.display_name} logged in', 'hookbird' ), 'fields' => array( __( 'Role', 'hookbird' ) => 'user.roles', __( 'IP', 'hookbird' ) => 'ip', ), 'sample' => 'user_login', ), 'user.login_failed' => array( 'label' => __( 'Failed login attempt', 'hookbird' ), 'group' => 'users', 'title' => __( 'Failed login for "{username}"', 'hookbird' ), 'fields' => array( __( 'IP', 'hookbird' ) => 'ip', __( 'Reason', 'hookbird' ) => 'reason', ), 'sample' => 'login_failed', 'color' => '#d63638', ), 'user.password_reset' => array( 'label' => __( 'Password reset', 'hookbird' ), 'group' => 'users', 'title' => __( 'Password reset for {user.display_name}', 'hookbird' ), 'fields' => array( __( 'Email', 'hookbird' ) => 'user.email', ), 'sample' => 'user', ), 'comment.created' => array( 'label' => __( 'New comment', 'hookbird' ), 'group' => 'comments', 'title' => __( 'New comment on {post.title}', 'hookbird' ), 'fields' => array( __( 'From', 'hookbird' ) => 'comment.author', __( 'Status', 'hookbird' ) => 'comment.status', __( 'Comment', 'hookbird' ) => 'comment.content', ), 'link' => 'comment.url', 'sample' => 'comment', ), 'comment.approved' => array( 'label' => __( 'Comment approved', 'hookbird' ), 'group' => 'comments', 'title' => __( 'Comment approved on {post.title}', 'hookbird' ), 'fields' => array( __( 'From', 'hookbird' ) => 'comment.author', ), 'link' => 'comment.url', 'sample' => 'comment', ), 'plugin.activated' => array( 'label' => __( 'Plugin activated', 'hookbird' ), 'group' => 'site', 'title' => __( 'Plugin activated: {plugin.name}', 'hookbird' ), 'fields' => array( __( 'Version', 'hookbird' ) => 'plugin.version', __( 'By', 'hookbird' ) => 'actor.name', ), 'sample' => 'plugin', ), 'plugin.deactivated' => array( 'label' => __( 'Plugin deactivated', 'hookbird' ), 'group' => 'site', 'title' => __( 'Plugin deactivated: {plugin.name}', 'hookbird' ), 'fields' => array( __( 'Version', 'hookbird' ) => 'plugin.version', __( 'By', 'hookbird' ) => 'actor.name', ), 'sample' => 'plugin', 'color' => '#d63638', ), 'plugin.updated' => array( 'label' => __( 'Plugin updated', 'hookbird' ), 'group' => 'site', 'title' => __( 'Plugin updated: {plugin.name} {plugin.version}', 'hookbird' ), 'fields' => array( __( 'Previous version', 'hookbird' ) => 'plugin.previous_version', __( 'By', 'hookbird' ) => 'actor.name', ), 'sample' => 'plugin_update', ), 'plugin.deleted' => array( 'label' => __( 'Plugin deleted', 'hookbird' ), 'group' => 'site', 'title' => __( 'Plugin deleted: {plugin.file}', 'hookbird' ), 'fields' => array( __( 'By', 'hookbird' ) => 'actor.name', ), 'sample' => 'plugin', 'color' => '#d63638', ), 'theme.switched' => array( 'label' => __( 'Theme switched', 'hookbird' ), 'group' => 'site', 'title' => __( 'Theme switched to {theme.name}', 'hookbird' ), 'fields' => array( __( 'Previous theme', 'hookbird' ) => 'previous_theme.name', __( 'By', 'hookbird' ) => 'actor.name', ), 'sample' => 'theme', ), 'theme.updated' => array( 'label' => __( 'Theme updated', 'hookbird' ), 'group' => 'site', 'title' => __( 'Theme updated: {theme.name} {theme.version}', 'hookbird' ), 'fields' => array( __( 'By', 'hookbird' ) => 'actor.name', ), 'sample' => 'theme', ), 'core.updated' => array( 'label' => __( 'WordPress updated', 'hookbird' ), 'group' => 'site', 'title' => __( 'WordPress updated to {version}', 'hookbird' ), 'fields' => array(), 'sample' => 'core', ), 'order.created' => array( 'label' => __( 'Order created', 'hookbird' ), 'group' => 'woocommerce', 'title' => __( 'New order #{order.number}: {order.total_formatted}', 'hookbird' ), 'fields' => array( __( 'Customer', 'hookbird' ) => 'order.customer.name', __( 'Items', 'hookbird' ) => 'order.items_summary', __( 'Payment', 'hookbird' ) => 'order.payment_method_title', __( 'Status', 'hookbird' ) => 'order.status', ), 'link' => 'order.edit_url', 'sample' => 'order', ), 'order.paid' => array( 'label' => __( 'Order paid', 'hookbird' ), 'group' => 'woocommerce', 'title' => __( 'Payment received for order #{order.number}: {order.total_formatted}', 'hookbird' ), 'fields' => array( __( 'Customer', 'hookbird' ) => 'order.customer.name', __( 'Payment', 'hookbird' ) => 'order.payment_method_title', ), 'link' => 'order.edit_url', 'sample' => 'order', 'color' => '#00a32a', ), 'order.status_changed' => array( 'label' => __( 'Order status changed', 'hookbird' ), 'group' => 'woocommerce', 'title' => __( 'Order #{order.number}: {from} → {to}', 'hookbird' ), 'fields' => array( __( 'Customer', 'hookbird' ) => 'order.customer.name', __( 'Total', 'hookbird' ) => 'order.total_formatted', ), 'link' => 'order.edit_url', 'sample' => 'order_status', ), 'order.completed' => array( 'label' => __( 'Order completed', 'hookbird' ), 'group' => 'woocommerce', 'title' => __( 'Order #{order.number} completed', 'hookbird' ), 'fields' => array( __( 'Customer', 'hookbird' ) => 'order.customer.name', __( 'Total', 'hookbird' ) => 'order.total_formatted', ), 'link' => 'order.edit_url', 'sample' => 'order', 'color' => '#00a32a', ), 'order.refunded' => array( 'label' => __( 'Order refunded', 'hookbird' ), 'group' => 'woocommerce', 'title' => __( 'Refund of {refund.amount} on order #{order.number}', 'hookbird' ), 'fields' => array( __( 'Reason', 'hookbird' ) => 'refund.reason', __( 'Customer', 'hookbird' ) => 'order.customer.name', ), 'link' => 'order.edit_url', 'sample' => 'order_refund', 'color' => '#d63638', ), 'product.low_stock' => array( 'label' => __( 'Product low in stock', 'hookbird' ), 'group' => 'woocommerce', 'title' => __( 'Low stock: {product.name}', 'hookbird' ), 'fields' => array( __( 'Stock left', 'hookbird' ) => 'product.stock_quantity', __( 'SKU', 'hookbird' ) => 'product.sku', ), 'link' => 'product.edit_url', 'sample' => 'product', 'color' => '#dba617', ), 'product.out_of_stock' => array( 'label' => __( 'Product out of stock', 'hookbird' ), 'group' => 'woocommerce', 'title' => __( 'Out of stock: {product.name}', 'hookbird' ), 'fields' => array( __( 'SKU', 'hookbird' ) => 'product.sku', ), 'link' => 'product.edit_url', 'sample' => 'product_out', 'color' => '#d63638', ), ); /** * Register custom events so they appear in the endpoint editor and get readable chat messages. * * Example: * add_filter( 'hookbird_events', function ( $events ) { * $events['lead.created'] = array( * 'label' => 'Lead created', * 'group' => 'custom', * 'title' => 'New lead: {email}', * 'fields' => array( 'Source' => 'source' ), * ); * return $events; * } ); * * @param array $events Event definitions keyed by event key. */ $events = (array) apply_filters( 'hookbird_events', $events ); $clean = array(); foreach ( $events as $key => $definition ) { $key = Endpoints::sanitize_event_key( $key ); if ( '' === $key || false !== strpos( $key, '*' ) || ! is_array( $definition ) ) { continue; } $clean[ $key ] = wp_parse_args( $definition, array( 'label' => $key, 'group' => 'custom', 'title' => '', 'fields' => array(), 'link' => '', 'sample' => '', 'color' => '', ) ); } self::$events = $clean; return self::$events; } /** * One event definition. * * @param string $key Event key. * @return array|null */ public static function get( $key ) { $events = self::events(); return isset( $events[ $key ] ) ? $events[ $key ] : null; } /** * Events grouped for display. * * @return array group => [ label, color, events => [ key => def ] ] */ public static function grouped() { $groups = self::groups(); $out = array(); foreach ( self::events() as $key => $definition ) { $group = isset( $groups[ $definition['group'] ] ) ? $definition['group'] : 'custom'; if ( ! isset( $out[ $group ] ) ) { $out[ $group ] = $groups[ $group ] + array( 'events' => array() ); } $out[ $group ]['events'][ $key ] = $definition; } // Keep the group order defined in groups(). return array_merge( array_intersect_key( $groups, $out ), $out ); } /** * Accent colour for an event. * * @param string $key Event key. * @return string Hex colour. */ public static function color( $key ) { $definition = self::get( $key ); if ( $definition && ! empty( $definition['color'] ) ) { return $definition['color']; } $groups = self::groups(); $group = $definition && isset( $groups[ $definition['group'] ] ) ? $definition['group'] : 'custom'; return $groups[ $group ]['color']; } /** * Sample data for an event, used by "Send test" and to suggest condition fields. * * @param string $key Event key. * @return array */ public static function sample( $key ) { $definition = self::get( $key ); $type = $definition ? $definition['sample'] : ''; $site = home_url( '/' ); $admin = admin_url(); $actor = array( 'id' => 1, 'name' => 'Admin', ); $user = array( 'id' => 42, 'username' => 'jane', 'email' => 'jane@example.com', 'display_name' => 'Jane Doe', 'first_name' => 'Jane', 'last_name' => 'Doe', 'roles' => array( 'customer' ), 'registered_gmt' => gmdate( 'c' ), 'edit_url' => $admin . 'user-edit.php?user_id=42', ); $post = array( 'id' => 123, 'type' => 'post', 'title' => 'Hello from Hookbird', 'status' => 'publish', 'slug' => 'hello-from-hookbird', 'url' => $site . 'hello-from-hookbird/', 'edit_url' => $admin . 'post.php?post=123&action=edit', 'excerpt' => 'This is a sample post used to test your endpoint.', 'author' => array( 'id' => 1, 'name' => 'Admin', ), 'categories' => array( 'News' ), 'tags' => array( 'sample' ), 'date_gmt' => gmdate( 'c' ), 'modified_gmt' => gmdate( 'c' ), ); $order = array( 'id' => 1001, 'number' => '1001', 'status' => 'processing', 'currency' => 'USD', 'total' => 149.0, 'subtotal' => 135.0, 'total_tax' => 9.0, 'shipping_total' => 5.0, 'discount_total' => 0.0, 'total_formatted' => '149.00 USD', 'payment_method' => 'stripe', 'payment_method_title' => 'Credit card', 'customer_note' => '', 'created_gmt' => gmdate( 'c' ), 'customer' => array( 'id' => 42, 'name' => 'Jane Doe', 'email' => 'jane@example.com', 'phone' => '+1 555 0100', ), 'billing' => array( 'first_name' => 'Jane', 'last_name' => 'Doe', 'company' => '', 'address_1' => '1 Main Street', 'address_2' => '', 'city' => 'Springfield', 'state' => 'IL', 'postcode' => '62701', 'country' => 'US', 'email' => 'jane@example.com', 'phone' => '+1 555 0100', ), 'shipping' => array( 'first_name' => 'Jane', 'last_name' => 'Doe', 'company' => '', 'address_1' => '1 Main Street', 'address_2' => '', 'city' => 'Springfield', 'state' => 'IL', 'postcode' => '62701', 'country' => 'US', ), 'items' => array( array( 'product_id' => 11, 'variation_id' => 0, 'name' => 'Hoodie', 'sku' => 'HOODIE-01', 'quantity' => 2, 'total' => 90.0, ), array( 'product_id' => 12, 'variation_id' => 0, 'name' => 'Cap', 'sku' => 'CAP-01', 'quantity' => 1, 'total' => 45.0, ), ), 'items_summary' => '2 × Hoodie, 1 × Cap', 'item_count' => 3, 'coupons' => array(), 'edit_url' => $admin . 'admin.php?page=wc-orders&action=edit&id=1001', ); $product = array( 'id' => 11, 'name' => 'Hoodie', 'sku' => 'HOODIE-01', 'stock_quantity' => 2, 'stock_status' => 'instock', 'url' => $site . 'product/hoodie/', 'edit_url' => $admin . 'post.php?post=11&action=edit', ); switch ( $type ) { case 'post': return array( 'post' => $post, 'actor' => $actor, ); case 'user': return array( 'user' => $user, 'actor' => $actor, ); case 'user_role': return array( 'user' => $user, 'role' => 'editor', 'previous_roles' => array( 'customer' ), 'actor' => $actor, ); case 'user_login': return array( 'user' => $user, 'ip' => '203.0.113.10', 'user_agent' => 'Mozilla/5.0', ); case 'login_failed': return array( 'username' => 'admin', 'ip' => '203.0.113.66', 'user_agent' => 'Mozilla/5.0', 'reason' => 'incorrect_password', ); case 'comment': return array( 'comment' => array( 'id' => 77, 'author' => 'Sam', 'author_email' => 'sam@example.com', 'author_url' => '', 'content' => 'Great article, thanks!', 'status' => 'pending', 'type' => 'comment', 'url' => $post['url'] . '#comment-77', 'date_gmt' => gmdate( 'c' ), ), 'post' => array( 'id' => $post['id'], 'title' => $post['title'], 'url' => $post['url'], ), ); case 'plugin': return array( 'plugin' => array( 'file' => 'hello-dolly/hello.php', 'name' => 'Hello Dolly', 'version' => '1.7.2', 'author' => 'Matt Mullenweg', ), 'actor' => $actor, ); case 'plugin_update': return array( 'plugin' => array( 'file' => 'hello-dolly/hello.php', 'name' => 'Hello Dolly', 'version' => '1.7.3', 'previous_version' => '1.7.2', 'author' => 'Matt Mullenweg', ), 'actor' => $actor, ); case 'theme': return array( 'theme' => array( 'name' => 'Twenty Twenty-Five', 'stylesheet' => 'twentytwentyfive', 'version' => '1.3', ), 'previous_theme' => array( 'name' => 'Twenty Twenty-Four', 'stylesheet' => 'twentytwentyfour', 'version' => '1.3', ), 'actor' => $actor, ); case 'core': return array( 'version' => get_bloginfo( 'version' ), ); case 'order': return array( 'order' => $order ); case 'order_status': return array( 'order' => $order, 'from' => 'pending', 'to' => 'processing', ); case 'order_refund': return array( 'order' => $order, 'refund' => array( 'id' => 1002, 'amount' => '45.00 USD', 'reason' => 'Wrong size', ), ); case 'product': return array( 'product' => $product ); case 'product_out': $product['stock_quantity'] = 0; $product['stock_status'] = 'outofstock'; return array( 'product' => $product ); } return array( 'message' => __( 'This is a test event from Hookbird.', 'hookbird' ), 'example' => true, ); } /** * Flattened dot paths of a sample payload, for condition field suggestions. * * @param array $data Data. * @param string $prefix Prefix. * @return string[] */ public static function paths( array $data, $prefix = '' ) { $paths = array(); foreach ( $data as $key => $value ) { if ( is_int( $key ) ) { continue; } $path = '' === $prefix ? $key : $prefix . '.' . $key; if ( is_array( $value ) && $value && array_keys( $value ) !== range( 0, count( $value ) - 1 ) ) { $paths = array_merge( $paths, self::paths( $value, $path ) ); } else { $paths[] = $path; } } return $paths; } /** * All suggested condition paths across built-in events. * * @return string[] */ public static function all_paths() { $paths = array(); foreach ( array_keys( self::events() ) as $key ) { $paths = array_merge( $paths, self::paths( self::sample( $key ) ) ); } $paths = array_values( array_unique( $paths ) ); sort( $paths ); return $paths; } }