diff --git a/includes/class-admin.php b/includes/class-admin.php index 8b2c933..649b59c 100644 --- a/includes/class-admin.php +++ b/includes/class-admin.php @@ -17,6 +17,9 @@ class WC_EDM_Admin { /** * Admin page slug. */ + /** Cache key for the dashboard card's counts. */ + const CARD_TRANSIENT = 'wc_edm_card_counts'; + const PAGE_SLUG = 'wc-email-discounts'; /** @@ -59,10 +62,60 @@ class WC_EDM_Admin { $this->audit = class_exists( 'WC_EDM_Admin_Audit' ) ? new WC_EDM_Admin_Audit() : null; add_action( 'admin_menu', array( $this, 'register_menu' ) ); + add_filter( 'wpmgr_dashboard_cards', array( $this, 'dashboard_card' ) ); add_action( 'admin_init', array( $this, 'handle_actions' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); } + /** + * This plugin's card on the shared Nolimit dashboard. + * + * The contract says implementations must not measure anything here, + * because this filter runs on an admin page load and a slow card makes + * somebody else's dashboard slow in a way they cannot attribute. Two + * COUNT(*) on small indexed tables is the most this may cost, and even + * that is cached: the dashboard is a page people leave open. + * + * @param array $cards The cards so far. + * @return array + */ + public function dashboard_card( $cards ) { + $counts = get_transient( self::CARD_TRANSIENT ); + + if ( ! is_array( $counts ) ) { + $counts = array( + 'groups' => WC_EDM_DB::count_groups(), + 'emails' => WC_EDM_DB::count_emails( true ), + ); + set_transient( self::CARD_TRANSIENT, $counts, 5 * MINUTE_IN_SECONDS ); + } + + // "warn", not "fail": a shop that has installed this and not set it up + // yet is not broken, it is unfinished. Reserving red for actual + // breakage is what keeps red worth reading. + $status = $counts['groups'] > 0 && $counts['emails'] > 0 ? 'ok' : 'warn'; + + $cards[] = array( + 'title' => __( 'Discounts', 'wc-email-discount' ), + 'status' => $status, + 'headline' => sprintf( + /* translators: %s: number of email addresses that currently receive a discount. */ + _n( '%s address gets a discount', '%s addresses get a discount', $counts['emails'], 'wc-email-discount' ), + number_format_i18n( $counts['emails'] ) + ), + 'lines' => array( + __( 'Groups', 'wc-email-discount' ) => number_format_i18n( $counts['groups'] ), + __( 'Active addresses', 'wc-email-discount' ) => number_format_i18n( $counts['emails'] ), + ), + 'url' => admin_url( 'admin.php?page=' . self::PAGE_SLUG ), + // The plugin's own colour. Deliberately not WooCommerce's brand + // purple; see the note in assets/icon.svg. + 'colour' => '#1a7a52', + ); + + return $cards; + } + /** * Register the page under the shared Nolimit menu. * diff --git a/tests/smoke.php b/tests/smoke.php index 8acd4a1..842783e 100644 --- a/tests/smoke.php +++ b/tests/smoke.php @@ -287,6 +287,33 @@ wc_edm_ok( 'it is not in the is_admin() branch of includes(), so nothing would define wpmgr_ensure_menu()' ); +// The card on the shared dashboard. Absent from it is a different failure from +// absent from the menu, and Kristian hit both in turn: the menu entry missing, +// then the card missing once the entry was there. +$wc_edm_cards = apply_filters( 'wpmgr_dashboard_cards', array() ); +$wc_edm_mine = array_values( + array_filter( + $wc_edm_cards, + static function ( $card ) { + return isset( $card['url'] ) && false !== strpos( $card['url'], WC_EDM_Admin::PAGE_SLUG ); + } + ) +); +wc_edm_ok( + 'this plugin puts a card on the shared dashboard', + 1 === count( $wc_edm_mine ), + count( $wc_edm_cards ) . ' card(s) registered in total' +); + +// Matched against the literal rather than the constant: the shared file +// renders any colour it is handed, so a card asserting its own constant would +// agree with itself after somebody put WooCommerce purple back. +wc_edm_is( + 'and it carries this plugin colour, not WooCommerce purple', + '#1a7a52', + isset( $wc_edm_mine[0]['colour'] ) ? $wc_edm_mine[0]['colour'] : null +); + // includes/shared-menu.php is byte-identical in all five plugins, and which // copy a site loads is decided by activation order. Editing it here would make // a site with three of five updated show one label or another depending on who