<?php
# MantisBT - A PHP based bugtracking system

# MantisBT is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# MantisBT is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with MantisBT.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Mention API
 *
 * @package CoreAPI
 * @subpackage LanguageAPI
 * @copyright Copyright 2000 - 2002  Kenzaburo Ito - kenito@300baud.org
 * @copyright Copyright 2002  MantisBT Team - mantisbt-dev@lists.sourceforge.net
 * @link http://www.mantisbt.org
 *
 * @uses bug_api.php
 * @uses email_api.php
 */

require_api( 'bug_api.php' );
require_api( 'email_api.php' );

/**
 * Check if @ mentions feature is enabled or not.
 *
 * @return bool true enabled, false otherwise.
 */
function mention_enabled() {
	return config_get( 'mentions_enabled' ) != OFF;
}

/**
 * Get the tag to use for mentions.
 * @return string The mentions tag.
 */
function mentions_tag() {
	return config_get( 'mentions_tag', null, ALL_USERS, ALL_PROJECTS );
}

/**
 * A method that takes in a text argument and extracts all candidate @ mentions
 * from it.  The return list will not include the @ sign and will not include
 * duplicates.  This method is mainly for testability and it doesn't take into
 * consideration whether the @ mentions features is enabled or not.
 *
 * @param string $p_text The text to process.
 * @return array of @ mentions without the @ sign.
 * @private
 */
function mention_get_candidates( $p_text ) {
	if( is_blank( $p_text ) ) {
		return array();
	}

	static $s_pattern = null;
	if( $s_pattern === null ) {
		$t_quoted_tag = preg_quote( mentions_tag() );
		$s_pattern = '/(?:'
			# Negative lookbehind to ensure we have whitespace or start of
			# string before the tag - ensures we don't match a tag in the
			# middle of a word (e.g. e-mail address)
			. '(?<=^|[^\w])'
			# Negative lookbehind  to ensure we don't match multiple tags
			. '(?<!' . $t_quoted_tag . ')' . $t_quoted_tag
			. ')'
			# any word char or period, but must not end with period
			. '([\w.]*[\w])'
			# Lookforward to ensure next char is not a valid mention char or
			# the end of the string, or the mention tag
			. '(?=[^\w@]|$)'
			. '(?!$t_quoted_tag)'
			. '/';
	}

	preg_match_all( $s_pattern, $p_text, $t_mentions );

	return array_unique( $t_mentions[1] );
}

/**
 * Given a string find the @ mentioned users.  The return list is a valid
 * list of valid mentioned users.  The list will be empty if the mentions
 * feature is disabled.
 *
 * @param string $p_text The text to process.
 * @return array with valid usernames as keys and their ids as values.
 */
function mention_get_users( $p_text ) {
	if ( !mention_enabled() ) {
		return array();
	}

	$t_matches = mention_get_candidates( $p_text );
	if( empty( $t_matches )) {
		return array();
	}

	$t_mentioned_users = array();

	foreach( $t_matches as $t_candidate ) {
		if( $t_user_id = user_get_id_by_name( $t_candidate ) ) {
			if( false === $t_user_id ) {
				continue;
			}

			$t_mentioned_users[$t_candidate] = $t_user_id;
		}
	}

	return $t_mentioned_users;
}

/**
 * Process users that are mentioned on the specified bug.
 *
 * @param int $p_bug_id The bug id.
 * @param array $p_mentioned_user_ids An array of user ids
 * @param string $p_message The message containing the mentions.
 * @param array $p_removed_mentions_user_ids The list of ids removed due to lack of access to issue or note.
 * @return array array of user ids actually received mention notifications.
 */
function mention_process_user_mentions( $p_bug_id, $p_mentioned_user_ids, $p_message, $p_removed_mentions_user_ids ) {
	return email_user_mention( $p_bug_id, $p_mentioned_user_ids, $p_message, $p_removed_mentions_user_ids );
}

/**
 * Format and hyperlink mentions
 *
 * @param string $p_text The text to process.
 * @param bool $p_html true for html, false otherwise.
 * @return string The processed text.
 */
function mention_format_text( $p_text, $p_html = true ) {
	$t_mentioned_users = mention_get_users( $p_text );
	if( empty( $t_mentioned_users ) ) {
		return $p_text;
	}

	$t_mentions_tag = mentions_tag();
	$t_formatted_mentions = array();

	foreach( $t_mentioned_users as $t_username => $t_user_id  ) {
		$t_mention = $t_mentions_tag . $t_username;
		$t_mention_formatted = $t_mention;

		if( $p_html ) {
			$t_mention_formatted = string_display_line( $t_mention_formatted );

			$t_mention_formatted = '<a class="user" href="' . string_sanitize_url( 'view_user_page.php?id=' . $t_user_id, true ) . '">' . $t_mention_formatted . '</a>';

			if( !user_is_enabled( $t_user_id ) ) {
				$t_mention_formatted = '<s>' . $t_mention_formatted . '</s>';
			}

			$t_mention_formatted = '<span class="mention">' . $t_mention_formatted . '</span>';
		}

		$t_formatted_mentions[$t_mention] = $t_mention_formatted;
	}

	# Replace the mentions, ignoring existing anchor tags (otherwise
	# previously set mailto links would be processed as mentions,
	# corrupting the output
	$t_text = string_process_exclude_anchors(
		$p_text,
		function( $p_string ) use ( $t_formatted_mentions ) {
			return str_replace(
				array_keys( $t_formatted_mentions ),
				array_values( $t_formatted_mentions ),
				$p_string
			);
		}
	);

	return $t_text;
}

