0 ) { $spoken[] = sprintf( /* translators: %s: number of unread submissions. */ _n( '%s unread', '%s unread', $counts['unread'], 'accessible-forms-by-accessible-org' ), number_format_i18n( $counts['unread'] ) ); } if ( $counts['review'] > 0 ) { $spoken[] = sprintf( /* translators: %s: number of submissions held for review. */ _n( '%s needs review', '%s need review', $counts['review'], 'accessible-forms-by-accessible-org' ), number_format_i18n( $counts['review'] ) ); } $badge = sprintf( ' %3$s', (int) $total, esc_html( number_format_i18n( $total ) ), /* translators: list separator between badge counts, e.g. "3 unread, 2 need review". */ esc_html( implode( _x( ', ', 'badge count separator', 'accessible-forms-by-accessible-org' ), $spoken ) ) ); return $badge; } /** * Show the same badge on the plugin's top-level menu item, so waiting * messages are visible while the menu is collapsed. */ public static function badge_top_level_menu() { global $menu; $badge = self::menu_badge(); if ( '' === $badge || ! is_array( $menu ) ) { return; } foreach ( $menu as $position => $item ) { if ( isset( $item[0], $item[2] ) && 'edit.php?post_type=accessible_form' === $item[2] && false === strpos( $item[0], 'awaiting-mod' ) ) { $menu[ $position ][0] = $item[0] . $badge; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Appends a count badge to this plugin's own menu item. } } } /** * Markers for the newest failure from the last week: the newest failed * submission and the newest failed notification attempt. Either growing * means a new failure, which brings a dismissed notice back. * * @return array{entry:int, job:int} */ private static function latest_failure() { global $wpdb; // The window of Entries::recent_failure_clause(), written out in full: a recent submission, or a // failed job last tried in the last week (jobs are timed in UTC). // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Plugin-owned tables; must reflect the latest delivery state. $row = $wpdb->get_row( $wpdb->prepare( "SELECT MAX(e.id) AS entry, MAX(n.id) AS job FROM %i e LEFT JOIN %i n ON n.entry_id = e.id AND n.status = 'failed' WHERE e.mail_status = 'failed' AND (e.created_at >= %s OR (e.time_version & 1) = 0 OR e.id IN (SELECT entry_id FROM %i WHERE status = 'failed' AND next_at >= %s))", Entries::table_name(), Notifications::table(), Entries::recent_failure_cutoff(), Notifications::table(), gmdate( 'Y-m-d H:i:s', time() - WEEK_IN_SECONDS ) ), ARRAY_A ); return array( 'entry' => (int) ( $row['entry'] ?? 0 ), 'job' => (int) ( $row['job'] ?? 0 ), ); } /** * Notice about notifications that failed in the last 7 days. Each person * can dismiss it; it returns when another notification fails. */ public static function failed_mail_notice() { if ( ! current_user_can( 'read_accessible_form_entries' ) ) { return; } $failures = Entries::recent_mail_failures(); if ( $failures < 1 ) { return; } $latest = self::latest_failure(); $dismissed = get_user_meta( get_current_user_id(), self::DISMISSED_META . get_current_blog_id(), true ); if ( is_array( $dismissed ) && $latest['entry'] <= (int) ( $dismissed['entry'] ?? 0 ) && $latest['job'] <= (int) ( $dismissed['job'] ?? 0 ) ) { return; } // Not limited by arrival date: a recent failure can belong to an older submission. $review = EntriesTable::url( array( 'mail_status' => 'failed' ) ); $dismiss = wp_nonce_url( add_query_arg( array( 'action' => 'accessible_forms_dismiss_mail_notice', 'entry' => $latest['entry'], 'job' => $latest['job'], ), admin_url( 'admin-post.php' ) ), 'accessible_forms_dismiss_mail_notice' ); echo '
' . esc_html( sprintf( /* translators: %d: number of submissions whose notification failed in the last 7 days. */ _n( 'Across your forms, %d notification email failed to send in the last 7 days.', 'Across your forms, %d notification emails failed to send in the last 7 days.', $failures, 'accessible-forms-by-accessible-org' ), $failures ) ) . ' ' . esc_html__( 'The submissions are stored safely. Open an affected entry and check Notification history for the cause.', 'accessible-forms-by-accessible-org' ) . ' ' . esc_html( current_user_can( 'send_accessible_form_notifications' ) ? __( 'For an entry in Received, fix the cause, then choose “Queue notification again”.', 'accessible-forms-by-accessible-org' ) : __( 'Ask someone with notification access to fix the cause and queue the email again.', 'accessible-forms-by-accessible-org' ) ) . '
' . esc_html__( 'Review affected entries', 'accessible-forms-by-accessible-org' ) . ' '; if ( current_user_can( 'manage_accessible_forms_settings' ) ) { echo '' . esc_html__( 'Email settings', 'accessible-forms-by-accessible-org' ) . ' '; } echo '' . esc_html__( 'Dismiss until another notification fails', 'accessible-forms-by-accessible-org' ) . '