alerts/class-alert-type-highlight.php

Summary

Maintainability
A
2 hrs
Test Coverage
F
47%
<?php
/**
 * Highlight Alert type.
 *
 * @package WP_Stream
 */

namespace WP_Stream;

/**
 * Class Alert_Type_Highlight
 *
 * @package WP_Stream
 */
class Alert_Type_Highlight extends Alert_Type {

    /**
     * Main JS file script handle.
     */
    const SCRIPT_HANDLE = 'wp-stream-alert-highlight-js';

    /**
     * Remove Highlight Ajax action label.
     */
    const REMOVE_ACTION = 'stream_remove_highlight';

    /**
     * Remove Action nonce name.
     */
    const REMOVE_ACTION_NONCE = 'stream-remove-highlight';

    /**
     * Alert type name
     *
     * @var string
     */
    public $name = 'Highlight';

    /**
     * Alert type slug
     *
     * @var string
     */
    public $slug = 'highlight';

    /**
     * The single Alert ID.
     *
     * @var int|string
     */
    public $single_alert_id;

    /**
     * The Plugin
     *
     * @var Plugin
     */
    public $plugin;

    /**
     * Class Constructor
     *
     * @param Plugin $plugin Plugin object.
     * @return void
     */
    public function __construct( $plugin ) {
        parent::__construct( $plugin );
        $this->plugin = $plugin;
        if ( ! is_admin() ) {
            return;
        }
        add_filter(
            'wp_stream_record_classes',
            array(
                $this,
                'post_class',
            ),
            10,
            2
        );
        add_action(
            'admin_enqueue_scripts',
            array(
                $this,
                'enqueue_scripts',
            )
        );
        add_action(
            'wp_ajax_' . self::REMOVE_ACTION,
            array(
                $this,
                'ajax_remove_highlight',
            )
        );

        if ( ! empty( $this->plugin->connectors->connectors ) && is_array( $this->plugin->connectors->connectors ) ) {
            foreach ( $this->plugin->connectors->connectors as $connector ) {
                add_filter(
                    'wp_stream_action_links_' . $connector->name,
                    array(
                        $this,
                        'action_link_remove_highlight',
                    ),
                    10,
                    2
                );
            }
        }

        add_filter(
            'wp_stream_alerts_save_meta',
            array(
                $this,
                'add_alert_meta',
            ),
            10,
            2
        );
    }

    /**
     * Record that the Alert was triggered by a Record.
     *
     * In self::post_class() this value is checked so we can determine
     * if the class should be added to the Record's display.
     *
     * @param int|string $record_id Record that triggered alert.
     * @param array      $recordarr Record details.
     * @param object     $alert Alert options.
     * @return void
     */
    public function alert( $record_id, $recordarr, $alert ) {
        $recordarr['ID']       = $record_id;
        $this->single_alert_id = $alert->ID;
        if ( ! empty( $alert->alert_meta['color'] ) ) {
            $alert_meta = array(
                'highlight_color' => $alert->alert_meta['color'],
            );
            Alert::update_record_triggered_alerts( (object) $recordarr, $this->slug, $alert_meta );
        }
    }

    /**
     * Displays a settings form for the alert type
     *
     * @param Alert $alert Alert object for the currently displayed alert.
     * @return void
     */
    public function display_fields( $alert ) {
        $alert_meta = array();
        if ( is_object( $alert ) ) {
            $alert_meta = $alert->alert_meta;
        }
        $options = wp_parse_args(
            $alert_meta,
            array(
                'color' => 'yellow',
            )
        );

        $form = new Form_Generator();
        echo '<span class="wp_stream_alert_type_description">' . esc_html__( 'Highlight this alert on the Stream records page.', 'stream' ) . '</span>';
        echo '<label for="wp_stream_highlight_color"><span class="title">' . esc_html__( 'Color', 'stream' ) . '</span>';
        echo '<span class="input-text-wrap">';
        echo $form->render_field(
            'select',
            array(
                'name'    => 'wp_stream_highlight_color',
                'title'   => esc_attr( __( 'Highlight Color', 'stream' ) ),
                'options' => $this->get_highlight_options(),
                'value'   => $options['color'],
            )
        ); // Xss ok.
        echo '</span></label>';
    }

    /**
     * Lists available color options for alerts.
     *
     * @return array List of highlight color options.
     */
    public function get_highlight_options() {
        return array(
            'yellow' => __( 'Yellow', 'stream' ),
            'red'    => __( 'Red', 'stream' ),
            'green'  => __( 'Green', 'stream' ),
            'blue'   => __( 'Blue', 'stream' ),
        );
    }

    /**
     * Validates and saves form settings for later use.
     *
     * @param Alert $alert Alert object for the currently displayed alert.
     * @return void
     */
    public function save_fields( $alert ) {
        check_admin_referer( 'save_alert', 'wp_stream_alerts_nonce' );

        if ( empty( $_POST['wp_stream_highlight_color'] ) ) {
            $alert->alert_meta['color'] = 'yellow';
        }
        $input_color = sanitize_text_field( wp_unslash( $_POST['wp_stream_highlight_color'] ) );
        if ( ! array_key_exists( $input_color, $this->get_highlight_options() ) ) {
            $alert->alert_meta['color'] = 'yellow';
        } else {
            $alert->alert_meta['color'] = $input_color;
        }

    }

    /**
     * Apply highlight to records
     *
     * @param array  $classes List of classes being applied to the post.
     * @param object $record Record data.
     * @return array New list of classes.
     */
    public function post_class( $classes, $record ) {
        $record           = new Record( $record );
        $alerts_triggered = $record->get_meta( Alerts::ALERTS_TRIGGERED_META_KEY, true );
        if ( ! empty( $alerts_triggered[ $this->slug ]['highlight_color'] ) ) {
            $color = $alerts_triggered[ $this->slug ]['highlight_color'];
        }

        if ( empty( $color ) || ! is_string( $color ) ) {
            return $classes;
        }
        $classes[] = 'alert-highlight highlight-' . esc_attr( $color ) . ' record-id-' . $record->ID;

        return $classes;
    }

    /**
     * Maybe add the "Remove Highlight" action link.
     *
     * This will appear on highlighted items on
     * the Record List page.
     *
     * This is set to run for all Connectors
     * in self::__construct().
     *
     * @filter wp_stream_action_links_{ connector }
     *
     * @param array  $actions Action links.
     * @param object $record A record object.
     *
     * @return mixed
     */
    public function action_link_remove_highlight( $actions, $record ) {
        $record           = new Record( $record );
        $alerts_triggered = $record->get_meta( Alerts::ALERTS_TRIGGERED_META_KEY, true );
        if ( ! empty( $alerts_triggered[ $this->slug ] ) ) {
            $actions[ __( 'Remove Highlight', 'stream' ) ] = '#';
        }

        return $actions;
    }

    /**
     * Ajax action to remove highlight.
     *
     * This is fired from the "Remove Highlight"
     * action links on the Records list page.
     *
     * First, we validate and sanitize.
     *
     * Then, we get the Record meta and remove
     * the "highlight" array from the
     * "Alerts triggered" meta field.
     *
     * Finally, the meta field is updated.
     *
     * Note: this removes ALL highlights
     * that were triggered by this record, not just
     * those triggered on specific Alert (post) IDs.
     *
     * @action wp_ajax_stream_remove_highlight
     */
    public function ajax_remove_highlight() {
        check_ajax_referer( self::REMOVE_ACTION_NONCE, 'security' );

        $failure_message = __( 'Removing Highlight Failed', 'stream' );

        if ( empty( $_POST['recordId'] ) ) {
            wp_send_json_error( $failure_message );
        }

        $record_id = sanitize_text_field( wp_unslash( $_POST['recordId'] ) );

        if ( ! is_numeric( $record_id ) ) {
            wp_send_json_error( $failure_message );
        }
        $record_obj       = new \stdClass();
        $record_obj->ID   = $record_id;
        $record           = new Record( $record_obj );
        $alerts_triggered = $record->get_meta( Alerts::ALERTS_TRIGGERED_META_KEY, true );
        if ( isset( $alerts_triggered[ $this->slug ] ) ) {
            unset( $alerts_triggered[ $this->slug ] );
        }
        $record->update_meta( Alerts::ALERTS_TRIGGERED_META_KEY, $alerts_triggered );
        wp_send_json_success();
    }

    /**
     * Enqueue Highlight-specific scripts.
     *
     * @param string $page WP admin page.
     */
    public function enqueue_scripts( $page ) {
        if ( 'toplevel_page_wp_stream' === $page ) {
            $min = wp_stream_min_suffix();

            wp_register_script(
                self::SCRIPT_HANDLE,
                $this->plugin->locations['url'] . 'alerts/js/alert-type-highlight.' . $min . 'js',
                array(
                    'jquery',
                ),
                $this->plugin->get_version(),
                false
            );

            $exports = array(
                'ajaxUrl'      => admin_url( 'admin-ajax.php' ),
                'removeAction' => self::REMOVE_ACTION,
                'security'     => wp_create_nonce( self::REMOVE_ACTION_NONCE ),
            );

            wp_scripts()->add_data(
                self::SCRIPT_HANDLE,
                'data',
                sprintf( 'var _streamAlertTypeHighlightExports = %s;', wp_json_encode( $exports ) )
            );

            wp_add_inline_script( self::SCRIPT_HANDLE, 'streamAlertTypeHighlight.init();', 'after' );
            wp_enqueue_script( self::SCRIPT_HANDLE );
        }
    }

    /**
     * Add alert meta if this is a highlight alert
     *
     * @param array  $alert_meta The metadata to be inserted for this alert.
     * @param string $alert_type The type of alert being added or updated.
     *
     * @return mixed
     */
    public function add_alert_meta( $alert_meta, $alert_type ) {
        if ( $this->slug === $alert_type ) {
            $color = wp_stream_filter_input( INPUT_POST, 'wp_stream_highlight_color' );
            if ( empty( $color ) ) {
                $alert_meta['color'] = 'yellow';
            } else {
                $alert_meta['color'] = $color;
            }
        }

        return $alert_meta;
    }
}