<?php
/**
 * GravityView Admin notices
 *
 * @package   GravityView
 * @license   GPL2+
 * @author    GravityKit <hello@gravitykit.com>
 * @link      http://www.gravitykit.com
 * @copyright Copyright 2015, Katz Web Services, Inc.
 *
 * @since 1.12
 */

/**
 * When the plugin is activated, flush dismissed notices
 *
 * @since 1.15.1
 */
register_activation_hook( GRAVITYVIEW_FILE, array( 'GravityView_Admin_Notices', 'flush_dismissed_notices' ) );

/**
 * Handle displaying and storing of admin notices for GravityView
 *
 * @since 1.12
 */
class GravityView_Admin_Notices {

	/**
	 * @var array
	 */
	private static $admin_notices = array();

	private static $dismissed_notices = array();

	function __construct() {

		$this->add_hooks();
	}

	function add_hooks() {
		add_action( 'network_admin_notices', array( $this, 'dismiss_notice' ), 50 );
		add_action( 'admin_notices', array( $this, 'dismiss_notice' ), 50 );
		add_action( 'admin_notices', array( $this, 'admin_notice' ), 100 );
		add_action( 'network_admin_notices', array( $this, 'admin_notice' ), 100 );
	}

	/**
	 * Clear out the dismissed notices when the plugin gets activated
	 *
	 * @see register_activation_hook
	 * @since 1.15.1
	 * @return void
	 */
	public static function flush_dismissed_notices() {
		delete_transient( 'gravityview_dismissed_notices' );
	}

	/**
	 * Dismiss a GravityView notice - stores the dismissed notices for 16 weeks
	 *
	 * @since 1.12
	 * @return void
	 */
	public function dismiss_notice() {

		// No dismiss sent
		if ( empty( $_GET['gv-dismiss'] ) || empty( $_GET['notice'] ) ) {
			return;
		}

		// Invalid nonce
		if ( ! wp_verify_nonce( $_GET['gv-dismiss'], 'dismiss' ) ) {
			return;
		}

		$notice_id = esc_attr( $_GET['notice'] );

		// don't display a message if use has dismissed the message for this version
		$dismissed_notices = (array) get_transient( 'gravityview_dismissed_notices' );

		$dismissed_notices[] = $notice_id;

		$dismissed_notices = array_unique( $dismissed_notices );

		// Remind users every week
		set_transient( 'gravityview_dismissed_notices', $dismissed_notices, WEEK_IN_SECONDS );
	}

	/**
	 * Has the notice been dismissed already in the admin?
	 *
	 * If the passed notice array has a `dismiss` key, the notice is dismissable. If it's dismissable,
	 * we check against other notices that have already been dismissed.
	 *
	 * @since 1.12
	 * @see GravityView_Admin_Notices::dismiss_notice()
	 * @see GravityView_Admin_Notices::add_notice()
	 * @param  string $notice            Notice array, set using `add_notice()`.
	 * @return boolean                   True: show notice; False: hide notice
	 */
	private function is_notice_dismissed( $notice ) {

		// There are no dismissed notices.
		if ( empty( self::$dismissed_notices ) ) {
			return false;
		}

		// Has the
		$is_dismissed = ! empty( $notice['dismiss'] ) && in_array( $notice['dismiss'], self::$dismissed_notices );

		return $is_dismissed ? true : false;
	}

	/**
	 * Get admin notices
	 *
	 * @since 1.12
	 * @return array
	 */
	public static function get_notices() {
		return self::$admin_notices;
	}

	/**
	 * Handle whether to display notices in Multisite based on plugin activation status
	 *
	 * @uses \GV\Plugin::is_network_activated
	 *
	 * @since 1.12
	 *
	 * @return bool True: show the notices; false: don't show
	 */
	private function check_show_multisite_notices() {

		if ( ! is_multisite() ) {
			return true;
		}

		// It's network activated but the user can't manage network plugins; they can't do anything about it.
		if ( gravityview()->plugin->is_network_activated() && ! is_main_site() ) {
			return false;
		}

		// or they don't have admin capabilities
		if ( ! is_super_admin() ) {
			return false;
		}

		return true;
	}

	/**
	 * Outputs the admin notices generated by the plugin
	 *
	 * @uses GVCommon::has_cap()
	 * @since 1.12
	 *
	 * @return void
	 */
	public function admin_notice() {

		/**
		 * Modify the notices displayed in the admin.
		 *
		 * @since 1.12
		 *
		 * @param array $notices Array of notices to display.
		 */
		$notices = apply_filters( 'gravityview/admin/notices', self::$admin_notices );

		if ( empty( $notices ) || ! $this->check_show_multisite_notices() ) {
			return;
		}

		// don't display a message if use has dismissed the message for this version
		// TODO: Use get_user_meta instead of get_transient
		self::$dismissed_notices = isset( $_GET['show-dismissed-notices'] ) ? array() : (array) get_transient( 'gravityview_dismissed_notices' );

		$output = '';

		foreach ( $notices as $notice ) {

			// If the user doesn't have the capability to see the warning
			if ( isset( $notice['cap'] ) && false === GVCommon::has_cap( $notice['cap'] ) ) {
				gravityview()->log->debug( 'Notice not shown because user does not have the capability to view it.', array( 'data' => $notice ) );
				continue;
			}

			if ( true === $this->is_notice_dismissed( $notice ) ) {
				gravityview()->log->debug( 'Notice not shown because the notice has already been dismissed.', array( 'data' => $notice ) );
				continue;
			}

			$output .= '<div id="message" style="position:relative" class="notice ' . gravityview_sanitize_html_class( $notice['class'] ) . '">';

			// Too cute to leave out.
			$output .= gravityview_get_floaty();

			if ( ! empty( $notice['title'] ) ) {
				$output .= '<h3>' . esc_html( $notice['title'] ) . '</h3>';
			}

			$message = isset( $notice['message'] ) ? $notice['message'] : '';

			if ( ! empty( $notice['dismiss'] ) ) {

				$dismiss = esc_attr( $notice['dismiss'] );

				$url = esc_url(
					add_query_arg(
						array(
							'gv-dismiss' => wp_create_nonce( 'dismiss' ),
							'notice'     => $dismiss,
						)
					)
				);

				$align    = is_rtl() ? 'alignleft' : 'alignright';
				$message .= '<a href="' . $url . '" data-notice="' . $dismiss . '" class="' . $align . ' button button-link">' . esc_html__( 'Dismiss', 'gk-gravityview' ) . '</a></p>';
			}

			$output .= wpautop( $message );

			$output .= '<div class="clear"></div>';
			$output .= '</div>';

		}

		echo $output;

		unset( $output, $align, $message, $notices );

		// reset the notices handler
		self::$admin_notices = array();
	}

	/**
	 * Add a notice to be displayed in the admin.
	 *
	 * @since 1.12 Moved from {@see GravityView_Admin::add_notice() }
	 * @since 1.15.1 Allows for `cap` key, passing capability required to show the message
	 * @param array $notice {
	 *      @type string       $class    HTML class to be used for the notice. Default: 'error'
	 *      @type string       $message  Notice message, not escaped. Allows HTML.
	 *      @type string       $dismiss  Unique key used to determine whether the notice has been dismissed. Set to false if not dismissable.
	 *      @type string|array $cap      The capability or caps required for an user to see the notice
	 * }
	 * @return void
	 */
	public static function add_notice( $notice = array() ) {

		if ( ! isset( $notice['message'] ) ) {
			gravityview()->log->error( 'Notice not set', array( 'data' => $notice ) );
			return;
		}

		$notice['class'] = empty( $notice['class'] ) ? 'error' : $notice['class'];

		self::$admin_notices[] = $notice;
	}
}

new GravityView_Admin_Notices();
