<?php
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;

if ( !class_exists( 'CommunityAlly_Members_Setup' ) ) :
class CommunityAlly_Members_Setup {
	const SEND_TO_ARG = 'communityally-send-to';
	const PROFILE_MENU = array(
			'profile' => true,
			'groups' => true,
			'settings' => true
		);
	// <editor-fold defaultstate="collapsed" desc="Add member shortcodes">
	public static function add_shortcodes() {
		add_shortcode('communityally_members_header', array(__CLASS__, 'shortcode_members_header'));
		add_shortcode('communityally_members_navigation', array(__CLASS__, 'shortcode_members_navigation'));
		add_shortcode('communityally_members_content', array(__CLASS__, 'shortcode_members_content'));

		add_shortcode('communityally_members_display', array(__CLASS__, 'shortcode_members_display'));

		add_shortcode('communityally_single_activity', array(__CLASS__, 'shortcode_single_activity'));
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Add actions">
	public static function add_actions() {
		add_action('wp_ajax_communityally_notifications_bulk_action', array(__CLASS__, 'ajax_notifications_bulk_action'));
		add_action('wp_ajax_communityally_members_groups_list', array(__CLASS__, 'ajax_get_groups_list'));
		add_action('wp_ajax_communityally_members_upload_cover_image', array(__CLASS__, 'ajax_upload_cover_image'));
		add_action('wp_ajax_communityally_members_delete_cover_image', array(__CLASS__, 'ajax_delete_cover_image'));
		add_action('wp_ajax_communityally_members_edit_notification_preference', array(__CLASS__, 'ajax_update_notification_preference'));

		add_action('wp_ajax_communityally_members_get_notification_list', array(__CLASS__, 'ajax_get_notification_list'));
		add_action('wp_ajax_nopriv_communityally_members_get_notification_list', array(__CLASS__, 'ajax_get_notification_list'));

		add_action('wp_ajax_communityally_messages_send_message', array(__CLASS__, 'ajax_send_private_message'));
		add_action('wp_ajax_communityally_messages_reply_message', array(__CLASS__, 'ajax_reply_private_message'));

		add_action('wp_ajax_communityally_messages_bulk_action', array(__CLASS__, 'ajax_messages_bulk_action'));
		add_action('wp_ajax_communityally_messages_update_list', array(__CLASS__, 'ajax_messages_update_list'));
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Add Members / Single activty page rewrite rules">
	public static function add_rewrite_rules() {
		$general_settings = CommunityAllySettings::get_general_settings();
		if (!empty($general_settings['members-page-slug'])) {
			add_rewrite_rule('^' . $general_settings['members-page-slug'] . '/([^/]*)/?$',
				'index.php?pagename=' . $general_settings['members-page-slug'] . '&ca_subpage=$matches[1]', 'top');
			add_rewrite_rule('^' . $general_settings['members-page-slug'] . '/([^/]*)/([^/]*)/?$',
				'index.php?pagename=' . $general_settings['members-page-slug'] . '&ca_subpage=$matches[1]&ca_user=$matches[2]', 'top');
		}
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Member header shortcode">
	private static function generate_members_header($display_user_id) {
		ob_start();
		include (dirname(__FILE__) . '/template/header.php');
		// Get the output buffer contents.
		$content = ob_get_clean();
		return $content;
	}
	public static function shortcode_members_header($atts) {
		extract(shortcode_atts(array(
			'user_id' => '',
			'request' => 'encrypt',
			'context' => 'local'
		), $atts, 'communityally_members_header'));
		$display_user_id = self::get_display_user_id($user_id, $request, $context);

		if (empty($display_user_id)) {
			return false;
		}
		return self::generate_members_header($display_user_id);
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Member profile shortcuts">
	public static function generate_member_profile_item_shortcuts($display_user_id) {
		$display_user_id = (false === $display_user_id) ? get_current_user_id() : intval($display_user_id);
		ob_start();
		include (dirname(__FILE__) . '/template/member-profile-item-shortcut-template.php');
		$content = ob_get_clean();
		return $content;
	}
	// </editor-fold>
	
	// <editor-fold defaultstate="collapsed" desc="Member navigation shortcode">
	private static function generate_members_navigation($display_user_id, $nav_items, $subpage) {
		ob_start();
		include (dirname(__FILE__) . '/template/navigation.php');
		// Get the output buffer contents.
		$content = ob_get_clean();
		return $content;
	}
	/** top level and sub menu separation for flexible*/
	private static function generate_members_sub_nav($display_user_id, $nav_items, $subpage){
		ob_start();
		include (dirname(__FILE__) . '/template/sub-nav-template.php');
		// Get the output buffer contents.
		$content = ob_get_clean();
		return $content;
	}
	public static function shortcode_members_navigation($atts) {
		extract(shortcode_atts(array(
			'user_id' => '',
			'request' => 'encrypt',
			'context' => 'local',
			'tab' => '',
		), $atts, 'communityally_members_navigation'));
		$display_user_id = self::get_display_user_id($user_id, $request, $context);

		if (empty($display_user_id)) {
			return false;
		}

		list($nav_items, $subpage) = self::get_nav_items($display_user_id, $tab);
		$content = self::generate_members_navigation($display_user_id, $nav_items, $subpage); // main navigation
		$content .= self::generate_members_sub_nav($display_user_id, $nav_items, $subpage); // sub menu navigation
		return $content;
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Member content shortcode">
	private static function generate_members_content($subpage, $display_user_id, $current_user_id, $args = array()) {
		$thread_id = false;
		if ('messages-inbox' === $subpage) {
			$thread_id = get_query_var('ca_user');
			if (empty($thread_id)) {
				if (!empty($args['view-message'])) {
					$thread_id = $args['view-message'];
				}
			}
			if (!empty($thread_id)) {
				$subpage = 'messages-reply';
			}
		}
		ob_start();
		include (dirname(__FILE__) . '/template/' . $subpage . '.php');
		// Get the output buffer contents.
		$content = ob_get_clean();
		return $content;
	}
	public static function shortcode_members_content($atts) {
		extract(shortcode_atts(array(
			'user_id' => '',
			'request' => 'encrypt',
			'context' => 'local',
			'tab' => '',
		), $atts, 'communityally_members_content'));
		$display_user_id = self::get_display_user_id($user_id, $request, $context);
		$current_user_id = get_current_user_id();

		if (empty($display_user_id)) {
			return false;
		}
		$display_user_id = intval($display_user_id);

		list($nav_items, $subpage) = self::get_nav_items($display_user_id, $tab);

		$args = array();
		if (!empty($_REQUEST['view-message'])) {
			$args['view-message'] = $_REQUEST['view-message'];
		}
		$content = self::generate_members_content($subpage, $display_user_id, $current_user_id, $args);
		return $content;
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Member full display shortcode">
	public static function shortcode_members_display($atts) {
		extract(shortcode_atts(array(
			'user_id' => '',
			'request' => 'encrypt',
			'context' => 'local',
			'tab' => '',
		), $atts, 'communityally_members_display'));
		$display_user_id = self::get_display_user_id($user_id, $request, $context);
		$current_user_id = get_current_user_id();

		if (empty($display_user_id)) {
			return false;
		}
		$display_user_id = intval($display_user_id);

		list($nav_items, $subpage) = self::get_nav_items($display_user_id, $tab);

		$args = array();
		if (!empty($_REQUEST['view-message'])) {
			$args['view-message'] = $_REQUEST['view-message'];
		}
		$header = self::generate_members_header($display_user_id);
		$top_level_navigation = self::generate_members_navigation($display_user_id, $nav_items, $subpage); // members display
		$sub_nav = self::generate_members_sub_nav($display_user_id, $nav_items, $subpage);
		$member_content = self::generate_members_content($subpage, $display_user_id, $current_user_id, $args);
		ob_start();
		include (dirname(__FILE__) . '/template/member-profile-template.php');
		$content= ob_get_clean();
		return $content;
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Single activity shortcode">
	public static function shortcode_single_activity($atts) {
		extract(shortcode_atts(array(
			'activity_id' => '',
		), $atts, 'communityally_single_activity'));
		if (empty($activity_id)) {
			$activity_id = get_query_var('ca_subpage');
		}
		ob_start();
		include (dirname(__FILE__) . '/template/single-activity.php');
		// Get the output buffer contents.
		$content = ob_get_clean();
		return $content;
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Generate members navigation items and current subpage">
	private static function get_nav_items($user_id = false, $subpage = false) {
		$general_settings = CommunityAllySettings::get_general_settings();
		$permalink = get_permalink($general_settings['select-members-page']);
		$permalink = trailingslashit($permalink);

		$current_user_id = get_current_user_id();
		if (false === $user_id) {
			$display_user_id = $current_user_id;
		} else {
			$display_user_id = intval($user_id);
		}
		$user_arg = '';
		if ($current_user_id !== $display_user_id) {
			$user_arg = apply_filters('accessally_get_encrypted_user_request_arg', $display_user_id, 'communityally');
			$user_arg .= '/';
		}

		$result = array(
				'profile' => array(
					'name' => _x( 'Profile', 'Member profile main navigation', 'buddypress' ),
					'url' => $permalink . $user_arg,
				),
			);
		if ($current_user_id === $display_user_id) {
			$result['activity'] = array(
					'name' => _x( 'Favorites', 'My Account Activity sub nav', 'buddypress' ),
					'url' => $permalink . 'activity/' . $user_arg,
				);

			$message_count = BP_Messages_Thread::get_inbox_count($display_user_id);

			$result['messages'] = array(
					'name' => __( 'Messages', 'buddypress' ),
					'url' => $permalink . 'messages/' . $user_arg,
					'count' => number_format_i18n($message_count),
					'count-class' => $message_count > 0 ? 'count' : 'no-count',
					'default-child' => 'messages-inbox',
				);
			$result['messages-inbox'] = array(
					'name' => __('All Messages', 'communityally'),
					'url' => $permalink . 'messages-inbox/' . $user_arg,
					'parent' => 'messages',
				);
			$result['messages-starred'] = array(
					'name' => __('Starred', 'buddypress'),
					'url' => $permalink . 'messages-starred/' . $user_arg,
					'parent' => 'messages',
				);
			$result['messages-sent'] = array(
					'name' => __('Sent', 'buddypress'),
					'url' => $permalink . 'messages-sent/' . $user_arg,
					'parent' => 'messages',
				);
			$result['messages-compose'] = array(
					'name' => __('Compose', 'communityally'),
					'url' => $permalink . 'messages-compose/' . $user_arg,
					'parent' => 'messages',
				);

			$result['notifications'] = array(
					'name' => _x( 'Notifications', 'Profile screen nav', 'buddypress' ),
					'url' => $permalink . 'notifications/' . $user_arg,
					'count' => bp_notifications_get_unread_notification_count($display_user_id),
					'count-class' => 'communityally-notification-count',
					'default-child' => 'notifications-unread',
				);
			$result['notifications-unread'] = array(
					'name' => __('Unread', 'buddypress'),
					'url' => $permalink . 'notifications-unread/' . $user_arg,
					'parent' => 'notifications',
				);
			$result['notifications-read'] = array(
					'name' => __('Read', 'buddypress'),
					'url' => $permalink . 'notifications-read/' . $user_arg,
					'parent' => 'notifications',
				);
			$result['groups'] = array(
					'name' => _x( 'Groups', 'My Account Activity sub nav', 'buddypress' ),
					'url' => $permalink . 'groups/' . $user_arg,
				);
			$result['settings'] = array(
					'name' => __( 'Settings', 'buddypress' ),
					'url' => $permalink . 'settings/' . $user_arg,
				);
		}
		if (empty($subpage)) {
			$subpage = get_query_var('ca_subpage');
		}
		if (!isset($result[$subpage])) {
			$subpage = 'profile';
		}
		// show the default child page if the parent page is selected
		if (!empty($result[$subpage]['default-child'])) {
			$subpage = $result[$subpage]['default-child'];
		}
		if (!empty($result[$subpage]['parent'])) {
			$parent_page_slug = $result[$subpage]['parent'];
			$result[$parent_page_slug]['selected'] = true;
		}
		$result[$subpage]['selected'] = true;
		return array($result, $subpage);
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Ajax: notification bulk action">
	public static function ajax_notifications_bulk_action() {
		$result = array(
			'status' => 'error',
			'message' => 'Please refresh the page and retry.',
			);
		try {
			if (!isset($_POST['notification_bulk_action']) || !isset($_POST['ids']) || !isset($_POST['nonce']) ||
				!wp_verify_nonce($_POST['nonce'], 'communityally-update')) {
				throw new Exception('Missing parameter for bulk update.');
			}
			$action = $_POST['notification_bulk_action'];
			$notification_ids = explode('|', $_POST['ids']);

			$current_user_id = get_current_user_id();
			$message = '';
			foreach ($notification_ids as $id) {
				if (!bp_notifications_check_notification_access($current_user_id, $id)) {
					continue;
				}
				switch ( $action ) {
					case 'delete' :
						BP_Notifications_Notification::delete(array('id' => $id));
						break;
					case 'read' :
						BP_Notifications_Notification::update(
							array('is_new' => false),
							array('id' => $id)
						);
						break;
					case 'unread' :
						BP_Notifications_Notification::update(
							array('is_new' => true),
							array('id' => $id)
						);
						break;
				}
			}
			switch ( $action ) {
				case 'delete' :
					$message = __('Notifications marked as read', 'buddypress');
					break;
				case 'read' :
					$message = __('Notifications marked as read', 'buddypress');
					break;
				case 'unread' :
					$message = __('Notifications marked as unread.', 'buddypress');
					break;
			}

			$code = '';
			if (empty($_POST['silent'])) {
				$subpage_slug = 'notifications-unread';
				if (!empty($_POST['notification_type']) && 'read' === $_POST['notification_type']) {
					$subpage_slug = 'notifications-read';
				}
				$code = self::generate_members_content($subpage_slug, $current_user_id, $current_user_id,
					array(
						'notice' => $message,
					));
			}
			$result = array('status' => 'success', 'code' => $code);
		} catch (Exception $e) {
			$result['status'] = 'error';
			$result['message'] = $e->getMessage();
		}
		echo json_encode($result);
		die();
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Ajax: get member's group list">
	public static function ajax_get_groups_list() {
		$result = array(
			'status' => 'error',
			'message' => 'Please refresh the page and retry.',
			);
		try {
			if (!isset($_POST['user_id']) || !isset($_POST['nonce']) ||
				!wp_verify_nonce($_POST['nonce'], 'communityally-update')) {
				throw new Exception('Missing parameter.');
			}

			$display_user_id = intval($_POST['user_id']);
			$current_user_id = get_current_user_id();
			$subpage_slug = 'groups';
			list($nav_items, $subpage) = self::get_nav_items($display_user_id);

			// check if the user has permission to view the groups tab
			if (!isset($nav_items[$subpage_slug])) {
				throw new Exception('Missing parameter.');
			}
			$args = array();
			if (!empty($_POST['order'])) {
				$args['group-order-by'] = $_POST['order'];
			}
			if (!empty($_POST['page'])) {
				// the page string is prefixed with #
				$page_str = str_replace('#', '', $_POST['page']);
				$args['page'] = max(1, intval($page_str));
			}

			$code = self::generate_members_content($subpage_slug, $display_user_id, $current_user_id, $args);

			$result = array('status' => 'success', 'code' => $code);
		} catch (Exception $e) {
			$result['status'] = 'error';
			$result['message'] = $e->getMessage();
		}
		echo json_encode($result);
		die();
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Upload member cover image">
	public static function ajax_upload_cover_image() {
		if (!bp_is_post_request()) {
			wp_die();
		}

		check_admin_referer( 'bp-uploader' );

		// Sending the json response will be different if the current Plupload runtime is html4.
		$is_html4 = ! empty( $_POST['html4' ] );

		$cover_image_attachment = new BP_Attachment_Cover_Image(array(
			'action' => 'communityally_members_upload_cover_image',
			'object' => 'members',
			'object_id' => get_current_user_id(),
		));
		$uploaded = $cover_image_attachment->upload( $_FILES );

		if ( ! empty( $uploaded['error'] ) ) {
			// Upload error response.
			bp_attachments_json_response( false, $is_html4, array(
				'type'    => 'upload_error',
				'message' => sprintf(
					/* translators: %s: the upload error message */
					__( 'Upload Failed! Error was: %s', 'buddypress' ),
					$uploaded['error']
				),
			) );
		}

		$error_message = __( 'There was a problem uploading the cover image.', 'buddypress' );

		$upload_dir = $cover_image_attachment->upload_dir_filter();

		$cover_dir = $upload_dir['path'];

		if ( 1 === validate_file( $cover_dir ) || ! is_dir( $cover_dir ) ) {
			// Upload error response.
			bp_attachments_json_response( false, $is_html4, array(
				'type'    => 'upload_error',
				'message' => $error_message,
			) );
		}

		/*
		 * Generate the cover image so that it fit to feature's dimensions
		 *
		 * Unlike the avatar, uploading and generating the cover image is happening during
		 * the same Ajax request, as we already instantiated the BP_Attachment_Cover_Image
		 * class, let's use it.
		 */
		$cover = bp_attachments_cover_image_generate_file( array(
			'file'            => $uploaded['file'],
			'component'       => 'members',
			'cover_image_dir' => $cover_dir
		), $cover_image_attachment );

		if ( ! $cover ) {
			bp_attachments_json_response( false, $is_html4, array(
				'type'    => 'upload_error',
				'message' => $error_message,
			) );
		}

		$cover_url = $upload_dir['url'] . '/' . $cover['cover_basename'];

		// Set the name of the file.
		$name       = $_FILES['file']['name'];
		$name_parts = pathinfo( $name );
		$name       = trim( substr( $name, 0, - ( 1 + strlen( $name_parts['extension'] ) ) ) );

		// Finally return the cover image url to the UI.
		bp_attachments_json_response( true, $is_html4, array(
			'name'          => $name,
			'url'           => $cover_url,
		) );
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Delete member cover image">
	public static function ajax_delete_cover_image() {
		$result = array(
			'status' => 'error',
			'message' => 'Please refresh the page and retry.',
			);
		try {
			if (!isset($_POST['nonce']) ||
				!wp_verify_nonce($_POST['nonce'], 'communityally-update')) {
				throw new Exception('Missing parameter.');
			}

			$current_user_id = get_current_user_id();
			$args = array(
				'object'  => 'members',
				'item_id' => $current_user_id,
			);
			if (! bp_attachments_current_user_can('edit_cover_image', $args)) {
				throw new Exception('You do not have permission to change the cover image');
			}
			$delete_result = bp_attachments_delete_file(array('item_id' => $current_user_id, 'object_dir' => 'members', 'type' => 'cover-image'));
			if (!$delete_result) {
				throw new Exception('Delete cover image failed.');
			}
			$result = array('status' => 'success');
		} catch (Exception $e) {
			$result['status'] = 'error';
			$result['message'] = $e->getMessage();
		}
		echo json_encode($result);
		die();
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Ajax: update notification preference">
	public static function ajax_update_notification_preference() {
		$result = array(
			'status' => 'error',
			'message' => 'Please refresh the page and retry.',
			);
		try {
			if (!isset($_POST['info']) || !isset($_POST['nonce']) ||
				!wp_verify_nonce($_POST['nonce'], 'communityally-update')) {
				throw new Exception('Missing parameter.');
			}

			$current_user_id = get_current_user_id();

			$settings = self::proper_parse_str(urldecode($_POST['info']));
			bp_settings_update_notification_settings($current_user_id, $settings);

			// Switch feedback for super admins.
			if ( bp_is_my_profile() ) {
				bp_core_add_message( __( 'Your notification settings have been saved.',        'buddypress' ), 'success' );
			} else {
				bp_core_add_message( __( "This user's notification settings have been saved.", 'buddypress' ), 'success' );
			}
			$code = self::generate_members_content('settings', $current_user_id, $current_user_id,
				array(
					'selected-tab' => 'notifications',
					'notice' => __('Your notification settings have been saved.', 'buddypress'),
				));

			$result = array('status' => 'success', 'code' => $code);
		} catch (Exception $e) {
			$result['status'] = 'error';
			$result['message'] = $e->getMessage();
		}
		echo json_encode($result);
		die();
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Utility: parse ajax input value">
	private static function recursive_assign_array_value($result, $name, $value) {
		$to_assign = &$result;
		if (strpos($name, '[') > 0) {
			$parts = explode('[', $name);
			for ($i = 0; $i < count($parts) -1; ++$i) {
				$path = $parts[$i];
				if (!empty($path)) {
					$path = str_replace(']', '', $path);
					if (!isset($to_assign[$path])) {
						$to_assign[$path] = array();
					}
					$to_assign = &$to_assign[$path];
				}
			}

			$name = $parts[count($parts) -1];
			$name = str_replace(']', '', $name);
		}
		if(isset($to_assign[$name])) {
			if(is_array($to_assign[$name])) {
				$to_assign[$name][] = $value;
			} else {
				$to_assign[$name] = array($to_assign[$name], $value);
			}
		} else {
			$to_assign[$name] = $value;
		}
		return $result;
	}
	private static function proper_parse_str($str) {
		$arr = array();

		// apostrophe and other special characters are escaped by slash, so it needs to be removed
		$str = stripslashes($str);

		# split on outer delimiter
		$pairs = explode('&', $str);

		# loop through each pair
		foreach ($pairs as $i) {
			# split into name and value
			list($name,$value) = explode('=', $i, 2);

			$name = urldecode($name);
			$value = urldecode($value);

			$arr = self::recursive_assign_array_value($arr, $name, $value);
		}
		return $arr;
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Create members / activity pages">
	public static function create_new_members_page() {
		$new_post_param = array(
			'post_title' => 'Members',
			'post_type' => 'page',
			'post_status' => 'publish',
			);
		$is_block_editor = function_exists('register_block_type');
		if ($is_block_editor) {
			$new_post_param['post_content'] = '<!-- wp:accessally-gutenberg/shortcode {"shortcode_type":"communityally-members","current_post_id":"0"} /-->';
		} else {
			$new_post_param['post_content'] = '[communityally_members_header][communityally_members_navigation][communityally_members_content]';
		}

		$post_id = wp_insert_post($new_post_param);
		apply_filters('accessally_set_page_permission', array('require-login' => 'yes'), $post_id);
		return $post_id;
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Generate link for Members page">
	public static function get_members_page_url($subpage, $display_user_id = false) {
		list($valid_nav_items, $selected_subpage) = self::get_nav_items($display_user_id);
		if (isset($valid_nav_items[$subpage])) {
			return $valid_nav_items[$subpage]['url'];
		}
		return '';
	}
	private static function get_user_id_from_query_arg() {
		$user_arg = get_query_var('ca_user');
		if (empty($user_arg)) {	// there is only 1 path segment, so $subpage might be the user argument
			$subpage = get_query_var('ca_subpage');
			$user_arg = $subpage;
		}
		$user_id = apply_filters('accessally_get_decrypted_user_id', $user_arg, 'communityally');
		return $user_id;
	}
	private static function get_display_user_id($user_id, $request, $context) {
		$display_user_id = apply_filters('accessally_get_current_user_id_for_display', $user_id, $request, $context, false);
		if (false === $display_user_id) {	// no user ID is supplied via the shortcode methods
			$display_user_id = self::get_user_id_from_query_arg();
		}
		if (false === $display_user_id) {
			return get_current_user_id();
		}
		return $display_user_id;
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Generate link for private message reply page">
	public static function get_private_message_reply_url($thread_id) {
		$url = self::get_members_page_url('messages-inbox');
		return $url . $thread_id . '/';
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Generate link for single activity page">
	public static function get_single_activity_page_url($activity_id) {
		$activities = bp_activity_get(array(
			'in' => array($activity_id),
			'show_hidden' => true,
			'display_comments' => 'stream',
			));
		if (empty($activities['activities'])) {
			return '#';
		}
		$activity = $activities['activities'][0];
		$group_id = $activity->item_id;
		$anchor = '#activity-' . $activity_id;
		if ('activity_comment' === $activity->type) {
			$parent_activities = bp_activity_get(array(
				'in' => array($activity->item_id),
				'show_hidden' => true,
				'display_comments' => 'stream',
				));
			if (empty($parent_activities['activities'])) {
				return '#';
			}
			$group_id = $parent_activities['activities'][0]->item_id;
			$anchor = '#acomment-' . $activity_id;
		}
		$group_permalink = CommunityAlly_Groups_Setup::get_group_permalink($group_id);
		$single_activity_permalink = trailingslashit($group_permalink) . CommunityAlly_Groups_Setup::SINGLE_ACTIVITY_PREFIX . $activity_id;
		return trailingslashit($single_activity_permalink) . $anchor;
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="clear cached data">
	public static function clear_cache() {
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Ajax: get member's notification list">
	public static function ajax_get_notification_list() {
		$result = array(
			'status' => 'error',
			'message' => 'Please refresh the page and retry.',
			);
		try {
			if (!isset($_POST['status']) || !isset($_POST['user_id']) || !isset($_POST['nonce']) ||
				!wp_verify_nonce($_POST['nonce'], 'communityally-update')) {
				throw new Exception('Missing parameter.');
			}

			$display_user_id = intval($_POST['user_id']);
			$current_user_id = get_current_user_id();
			$subpage_slug = 'notifications-' . $_POST['status'];
			list($nav_items, $subpage) = self::get_nav_items($display_user_id);

			// check if the user has permission to view the notifications tab
			if (!isset($nav_items[$subpage_slug])) {
				throw new Exception('Missing parameter.');
			}
			$args = array();
			if (!empty($_POST['page'])) {
				// the page string is prefixed with #
				$page_str = str_replace('#', '', $_POST['page']);
				$args['page'] = max(1, intval($page_str));
			}

			$code = self::generate_members_content($subpage_slug, $display_user_id, $current_user_id, $args);

			$result = array('status' => 'success', 'code' => $code);
		} catch (Exception $e) {
			$result['status'] = 'error';
			$result['message'] = $e->getMessage();
		}
		echo json_encode($result);
		die();
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Generate notification description">
	public static function generate_notification_description($notification) {
		$action = $notification->component_action;
		$user_fullname = bp_core_get_user_displayname($notification->secondary_item_id);
		$name_with_css = '<span class="communityally-notification-user-name">' . esc_html($user_fullname) .'</span>';
		$link_url = '';

		$text = '';
		switch ($action) {
			case 'new_at_mention':
				$activity_id = $notification->item_id;
				$link_url = self::get_single_activity_page_url($activity_id);

				/* translators: 1: the user display name */
				$text = sprintf(esc_html__( '%1$s mentioned you', 'buddypress' ), $name_with_css);
			break;

			case 'update_reply':
				$activity_id = $notification->item_id;
				$link_url = self::get_single_activity_page_url($activity_id);

				/* translators: 1: the user display name */
				$text = sprintf(esc_html__( '%1$s commented on one of your updates', 'buddypress' ), $name_with_css);
			break;

			case 'comment_reply':
				$activity_id = $notification->item_id;
				$link_url = self::get_single_activity_page_url($activity_id);
				/* translators: 1: the user display name */
				$text = sprintf(esc_html__( '%1$s replied to one of your activity comments', 'buddypress' ), $name_with_css);
			break;

			case 'new_message':
				// Get message thread ID.
				$message = new BP_Messages_Message($notification->item_id);
				$thread_id = $message->thread_id;
				$link_url = self::get_private_message_reply_url($thread_id);

				/* translators: %s: member name */
				$text = sprintf(esc_html__( '%s sent you a new private message', 'buddypress' ), $name_with_css);
			break;
		}

		$description = '<a target="_blank" href="' . esc_url($link_url) . '">' . $text . '</a>';
		return $description;
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Get send private message link">
	public static function generate_send_private_message_url($target_user_id) {
		$compose_url = self::get_members_page_url('messages-compose');
		$encrypted_user_id = apply_filters('accessally_get_encrypted_user_request_arg', $target_user_id, 'communityally');
		$url = add_query_arg(self::SEND_TO_ARG, $encrypted_user_id, $compose_url);
		return $url;
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Map raw content to mentioned structure text">
	public static function convert_mentioned_content($content, $mention_mapping) {
		$mentioned_users = array();
		if (!empty($mention_mapping)) {
			foreach ($mention_mapping as $user_id => $name) {
				// "&" symbol in the display name will be converted to &amp; by filter bp_activity_content_before_save -> bp_activity_filter_kses
				$name = str_replace('&', '&amp;', $name);

				if (false !== strpos($content, $name)) {
					$mentioned_users[$user_id] = $name;
					$content = str_replace($name, '[mention:' . $user_id . ']', $content);
				}
			}
		}
		return array($content, $mentioned_users);
	}
	public static function convert_tinymce_mentioned_content($content) {
		$pattern = '/<span class="atwho-inserted" data-communityally-mention="([0-9]+)" contenteditable="false">@(.*)<\/span>/';
		preg_match_all($pattern, $content, $matches);
		foreach ($matches[0] as $index => $span_code) {
			$content = str_replace($span_code, '[mention:' . $matches[1][$index] . ']', $content);
		}
		$pattern = '/<span class="atwho-inserted" contenteditable="false" data-communityally-mention="([0-9]+)">@(.*)<\/span>/';
		preg_match_all($pattern, $content, $matches);
		foreach ($matches[0] as $index => $span_code) {
			$content = str_replace($span_code, '[mention:' . $matches[1][$index] . ']', $content);
		}
		return $content;
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Process raw input with mentions">
	public static function sanitize_mentioned_content($content = false) {
		if (false === $content) {
			$content = '';
			if (!empty($_POST['content'])) {
				$content = $_POST['content'];
			}
			$content = stripslashes($content);
		}
		$content = str_replace('\0', '*chr0*', $content);
		$content = str_replace(chr(0), '*chr0*', $content);
		return $content;
	}
	public static function extract_mention_mapping($mention_input = false) {
		if (false === $mention_input) {
			$mention_input = '';
			if (!empty($_POST['mentions'])) {
				$mention_input = $_POST['mentions'];
				$mention_input = stripslashes($mention_input);
			}
		}
		$raw_array = json_decode($mention_input, true);
		$mention_mapping = array();
		if (is_array($raw_array)) {
			$mention_mapping = $raw_array;
			foreach ($mention_mapping as $user_id => $name) {
				$mention_mapping[$user_id] = str_replace(chr(0), '*chr0*', $name);
			}
		}
		return $mention_mapping;
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Ajax callback: send private message">
	public static function ajax_send_private_message() {
		try{
			if (!isset($_POST['input']) ||
				!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'communityally-send-private-message')) {
				throw new Exception('The page is outdated. Please reload this page.');
			}
			$data = stripslashes($_POST['input']);
			$input = CommunityAllyUtilities::convert_setting_string_to_array($data);

			if (empty($input['recipient'])) {
				throw new Exception(__('Your message was not sent. Please enter at least one username.', 'buddypress'));
			}
			if (empty($input['subject'])) {
				throw new Exception(__('Your message was not sent. Please enter a subject line.', 'buddypress'));
			}
			if (empty($input['message_content'])) {
				throw new Exception(__('Your message was not sent. Please enter some content.', 'buddypress'));
			}

			$recipients = $input['recipient'];
			// Validate recipients
			if (empty($recipients)) {
				throw new Exception(__('Your message was not sent. Please enter at least one username.', 'buddypress'));
			}

			$message_text= self::convert_tinymce_mentioned_content($input['message_content']);

			$send = messages_new_message(array(
				'recipients' => $recipients,
				'subject'    => $input['subject'],
				'content'    => $message_text,
				'error_type' => 'wp_error',
			));

			if (!is_int($send)) {
				throw new Exception($send->get_error_message());
			}
			$result = array(
				'status' => 'success',
				'redirect' => self::get_members_page_url('messages-sent'),
				'message' => __('Message successfully sent.', 'buddypress'),
				);
			echo json_encode($result);
		} catch (Exception $ex) {
			$error = array('status' => 'error', 'message' => $ex->getMessage());
			echo json_encode($error);
		}
		die();
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Ajax callback: reply to private message">
	public static function ajax_reply_private_message() {
		try{
			if (!isset($_POST['thread_id']) ||!isset($_POST['input']) ||
				!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'communityally-reply-private-message-' . $_POST['thread_id'])) {
				throw new Exception('The page is outdated. Please reload this page.');
			}
			$data = stripslashes($_POST['input']);
			$input = CommunityAllyUtilities::convert_setting_string_to_array($data);

			$thread_id = $_POST['thread_id'];
			if (empty($input['message_content'])) {
				throw new Exception(__('Your message was not sent. Please enter some content.', 'buddypress'));
			}

			$message_text = self::convert_tinymce_mentioned_content($input['message_content']);

			$current_user_id = get_current_user_id();
			messages_new_message(array(
				'thread_id' => $thread_id,
				'content' => $message_text,
				'sender_id' => $current_user_id,
			));

			$code = self::generate_members_content('messages-inbox', $current_user_id, $current_user_id, array('view-message' => $thread_id));
			$result = array(
				'status' => 'success',
				'code' => $code,
				'message' => __('Message successfully sent.', 'buddypress'),
				);
			echo json_encode($result);
		} catch (Exception $ex) {
			$error = array('status' => 'error', 'message' => $ex->getMessage());
			echo json_encode($error);
		}
		die();
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Ajax: messages bulk action">
	public static function ajax_messages_bulk_action() {
		$result = array(
			'status' => 'error',
			'message' => 'Please refresh the page and retry.',
			);
		try {
			if (!isset($_POST['bulk_action']) || !isset($_POST['ids']) || !isset($_POST['nonce']) ||
				!wp_verify_nonce($_POST['nonce'], 'communityally-update')) {
				throw new Exception('Missing parameter for bulk update.');
			}
			$action = $_POST['bulk_action'];
			$thread_ids = explode('|', $_POST['ids']);

			$current_user_id = get_current_user_id();
			foreach ($thread_ids as $id) {
				switch ( $action ) {
					case 'read':
						if (messages_check_thread_access($id, $current_user_id)) {
							messages_mark_thread_read($id);
						}
						break;
					case 'unread':
						if (messages_check_thread_access($id, $current_user_id)) {
							messages_mark_thread_unread($id);
						}
						break;
					case 'delete':
						messages_delete_thread($id, $current_user_id);
						break;
					case 'add-star':
						$thread_id = $id;
						$message_id = 0;
						if (0 === strpos($id, 'message-')) {
							$message_id = substr($id, 8);
							$thread_id = messages_get_message_thread_id($message_id);
						} else {
							$thread = new BP_Messages_thread($id);
							$message_id = $thread->messages[0]->id;
						}
						bp_messages_star_set_action(array(
							'action' => 'star',
							'thread_id' => $thread_id,
							'message_id' => $message_id,
							'user_id' => $current_user_id,
						));
						break;
					case 'remove-star':
						$thread_id = $id;
						$message_id = 0;
						$bulk = true;
						if (0 === strpos($id, 'message-')) {
							$message_id = substr($id, 8);
							$thread_id = messages_get_message_thread_id($message_id);
							$bulk = false;
						}
						bp_messages_star_set_action(array(
							'action' => 'unstar',
							'thread_id' => $thread_id,
							'message_id' => $message_id,
							'user_id' => $current_user_id,
							'bulk' => $bulk,
						));
						break;
				}
			}

			$message = '';
			$count = count($thread_ids);
			switch ( $action ) {
				case 'read':
					$message = __('Messages marked as read', 'buddypress');
					break;
				case 'unread':
					$message = __('Messages marked as unread', 'buddypress');
					break;
				case 'delete':
					$message = __('Messages deleted.', 'buddypress');
					break;
				case 'add-star':
					$message = sprintf(_n( '%s message was successfully starred', '%s messages were successfully starred', $count, 'buddypress'), $count);
					break;
				case 'remove-star':
					$message = sprintf(_n( '%s message was successfully unstarred', '%s messages were successfully unstarred', $count, 'buddypress'), $count);
					break;
			}
			$inbox_url = self::get_members_page_url('messages-inbox', $current_user_id);
			$result = array('status' => 'success', 'message' => $message, 'inbox' => $inbox_url);
		} catch (Exception $e) {
			$result['status'] = 'error';
			$result['message'] = $e->getMessage();
		}
		echo json_encode($result);
		die();
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Ajax: update message list">
	public static function ajax_messages_update_list() {
		$result = array(
			'status' => 'error',
			'message' => 'Please refresh the page and retry.',
			);
		try {
			if (!isset($_POST['box']) || !isset($_POST['nonce']) ||
				!wp_verify_nonce($_POST['nonce'], 'communityally-update')) {
				throw new Exception('Missing parameter for update.');
			}
			$subpage_slug = 'messages-' . $_POST['box'];
			$current_user_id = get_current_user_id();
			list($nav_items, $subpage) = self::get_nav_items($current_user_id);

			// check if the user has permission to view the notifications tab
			if (!isset($nav_items[$subpage_slug])) {
				throw new Exception('Missing parameter.');
			}
			$args = array();
			if (!empty($_POST['page'])) {
				// the page string is prefixed with #
				$page_str = str_replace('#', '', $_POST['page']);
				$args['page'] = max(1, intval($page_str));
			}
			$input = array();
			if (isset($_POST['input'])) {
				$data = stripslashes($_POST['input']);
				$input = CommunityAllyUtilities::convert_setting_string_to_array($data);
			}

			// process search terms here : search for name and topic
			if (!empty($input['search'])) {
				$args['search_terms'] = $input['search'];
			}

			$code = self::generate_members_content($subpage_slug, $current_user_id, $current_user_id, $args);

			$result = array('status' => 'success', 'code' => $code);
		} catch (Exception $e) {
			$result['status'] = 'error';
			$result['message'] = $e->getMessage();
		}
		echo json_encode($result);
		die();
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Generate private message popup code">
	public static function generate_user_link_popup_code() {
		if (!CommunityAllySettings::is_messaging_enabled()) {
			return '';
		}
		ob_start();
		include (dirname(__FILE__) . '/template/private-message-popup.php');
		// Get the output buffer contents.
		$content = ob_get_clean();
		return $content;
	}
	// </editor-fold>

	// <editor-fold defaultstate="collapsed" desc="Prevent multiple emails from the same event">
	private static $sent_email_history = array();
	public static function to_send_notification_email($to_address, $email_type) {
		$filtered_to_address = array();
		if (is_array($to_address)) {
			foreach ($to_address as $address) {
				$to_send = self::to_send_notification_email_for_user($address, $email_type);
				if ($to_send) {
					$filtered_to_address []= $address;
				}
			}
		} else {
			$to_send = self::to_send_notification_email_for_user($to_address, $email_type);
			if ($to_send) {
				$filtered_to_address []= $to_address;
			}
		}
		return $filtered_to_address;
	}
	private static function resolve_notification_email_address_to_user_id($email_or_user) {
		// User ID, email address or WP_User object.
		if (is_int($email_or_user)) {
			return $email_or_user;
		}
		if (is_string($email_or_user) && is_email($email_or_user)) {
			$wp_user = get_user_by('email', $email_or_user);
			if ($wp_user) {
				return $wp_user->ID;
			}
			return 0;
		}
		if (is_a($email_or_user, 'WP_User')) {
			return $email_or_user->ID;
		}
		return 0;
	}
	private static function to_send_notification_email_for_user($email_or_user, $email_type) {
		$user_id = self::resolve_notification_email_address_to_user_id($email_or_user);
		// we couldn't resolve the address to a user_id, so let the email be sent
		if ($user_id <= 0) {
			return true;
		}
		// define sets of equivalent email types, so sending one will stop others from being sent
		$email_groups = array(
			array('groups-at-message', 'activity-at-message', 'activity-comment-author', 'activity-comment'),
			);
		if (isset(self::$sent_email_history[$user_id])) {
			$already_sent = self::$sent_email_history[$user_id];
			foreach ($email_groups as $equivalent_types) {
				if (in_array($email_type, $equivalent_types)) {
					$shared_types = array_intersect($already_sent, $equivalent_types);
					if (!empty($shared_types)) {
						return false;
					}
				}
			}
			self::$sent_email_history[$user_id] []= $email_type;
		} else {
			self::$sent_email_history[$user_id] = array($email_type);
		}
		return true;
	}
	// </editor-fold>
}
endif; // CommunityAlly_Members_Setup.