<?php
/**
 * This file implements Comment handling functions.
  *
 * This file is part of the b2evolution/evocms project - {@link http://b2evolution.net/}.
 * See also {@link https://github.com/b2evolution/b2evolution}.
 *
 * @license GNU GPL v2 - {@link http://b2evolution.net/about/gnu-gpl-license}
 *
 * @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}.
 * Parts of this file are copyright (c)2004-2005 by Daniel HAHLER - {@link http://thequod.de/contact}.
 *
 * @package evocore
 *
 * @todo implement CommentCache based on LinkCache
 */
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );

load_class( 'comments/model/_comment.class.php', 'Comment' );

/**
 * Generic comments/trackbacks/pingbacks/webmentions counting
 *
 * @todo check this in a multiblog page...
 * @todo This should support visibility: at least in the default front office (_feedback.php), there should only the number of visible comments/trackbacks get used ({@link Item::feedback_link()}).
 *
 * @param integer
 * @param string what to count
 * @param mixed string or array to count comments with this/these status(es)
 * @param boolean set true to count expired comments, leave on false otherwise
 */
function generic_ctp_number( $post_id, $mode = 'comments', $status = 'published', $count_expired = false, $filter_by_perm = true )
{
	global $DB, $debug, $postdata, $cache_ctp_number, $preview, $servertimenow, $blog;

	if( $preview )
	{ // we are in preview mode, no comments yet!
		return 0;
	}

	$show_statuses = ( is_admin_page() || ( ! $filter_by_perm ) ) ? get_visibility_statuses( 'keys', array( 'trash', 'redirected' ) ) : get_inskin_statuses( $blog, 'comment' );
	$filter_index = $filter_by_perm ? 0 : 1;
	if( !isset($cache_ctp_number) || !isset($cache_ctp_number[$filter_index][$post_id]) )
	{ // we need a query to count comments
		$count_SQL = new SQL();
		$count_SQL->SELECT( 'comment_item_ID, comment_type, comment_status, COUNT(*) AS type_count' );
		$count_SQL->FROM( 'T_comments' );
		$count_SQL->GROUP_BY( 'comment_item_ID, comment_type, comment_status' );
		if( !empty( $blog ) )
		{
			$count_SQL->WHERE( statuses_where_clause( $show_statuses, 'comment_', $blog, 'blog_comment!', $filter_by_perm ) );
		}

		if( !$count_expired )
		{
			$count_SQL->FROM_add( 'LEFT JOIN T_items__item_settings as expiry_setting ON comment_item_ID = iset_item_ID AND iset_name = "comment_expiry_delay"' );
			$count_SQL->WHERE_and( 'expiry_setting.iset_value IS NULL OR expiry_setting.iset_value = "" OR TIMESTAMPDIFF(SECOND, comment_date, '.$DB->quote( date2mysql( $servertimenow ) ).') < expiry_setting.iset_value' );
		}
	}

	// init statuses count array
	$statuses_array = array( 'published' => 0, 'community' => 0, 'protected' => 0, 'private' => 0, 'review' => 0, 'draft' => 0, 'deprecated' => 0, 'trash' => 0, 'total' => 0 );

	/*
	 * Make sure cache is loaded for current display list:
	 */
	if( !isset($cache_ctp_number) || !isset($cache_ctp_number[$filter_index]) )
	{
		global $postIDlist, $postIDarray;

		// if( $debug ) echo "LOADING generic_ctp_number CACHE for posts: $postIDlist<br />";

		if( ! empty( $postIDlist ) )	// This can happen when displaying a featured post of something that's not in the MainList
		{
			foreach( $postIDarray as $tmp_post_id)
			{	// Initializes each post to nocount!
				$cache_ctp_number[$filter_index][$tmp_post_id] = array(
						'comments'   => $statuses_array,
						'trackbacks' => $statuses_array,
						'pingbacks'  => $statuses_array,
						'feedbacks'  => $statuses_array,
						'metas'      => $statuses_array,
						'webmentions'=> $statuses_array,
					);
			}

			$countall_SQL = $count_SQL;
			$countall_SQL->WHERE_and( 'comment_item_ID IN ('.$postIDlist.')' );

			foreach( $DB->get_results( $countall_SQL->get() ) as $row )
			{
				// detail by status, tyep and post:
				$cache_ctp_number[$filter_index][$row->comment_item_ID][$row->comment_type.'s'][$row->comment_status] = $row->type_count;

				// Total for type on post:
				$cache_ctp_number[$filter_index][$row->comment_item_ID][$row->comment_type.'s']['total'] += $row->type_count;

				if( $row->comment_type != 'meta' )
				{ // Exclude internal comments from feedbacks
					// Total for status on post:
					$cache_ctp_number[$filter_index][$row->comment_item_ID]['feedbacks'][$row->comment_status] += $row->type_count;

					// Total for post:
					$cache_ctp_number[$filter_index][$row->comment_item_ID]['feedbacks']['total'] += $row->type_count;
				}
			}
		}
	}
	/*	else
	{
		echo "cache set";
	}*/


	if( !isset($cache_ctp_number[$filter_index][$post_id]) )
	{ // this should be extremely rare...
		// echo "CACHE not set for $post_id";

		// Initializes post to nocount!
		$cache_ctp_number[$filter_index][intval($post_id)] = array(
				'comments'   => $statuses_array,
				'trackbacks' => $statuses_array,
				'pingbacks'  => $statuses_array,
				'feedbacks'  => $statuses_array,
				'metas'      => $statuses_array,
				'webmentions'=> $statuses_array,
			);

		$count_SQL->WHERE_and( 'comment_item_ID = '.intval($post_id) );

		foreach( $DB->get_results( $count_SQL->get() ) as $row )
		{
			// detail by status, type and post:
			$cache_ctp_number[$filter_index][$row->comment_item_ID][$row->comment_type.'s'][$row->comment_status] = $row->type_count;

			// Total for type on post:
			$cache_ctp_number[$filter_index][$row->comment_item_ID][$row->comment_type.'s']['total'] += $row->type_count;

			if( $row->comment_type != 'meta' )
			{ // Exclude internal comments from feedbacks
				// Total for status on post:
				$cache_ctp_number[$filter_index][$row->comment_item_ID]['feedbacks'][$row->comment_status] += $row->type_count;

				// Total for post:
				$cache_ctp_number[$filter_index][$row->comment_item_ID]['feedbacks']['total'] += $row->type_count;
			}
		}
	}

	if( ! in_array( $mode, array( 'comments', 'trackbacks', 'pingbacks', 'metas', 'webmentions' ) ) )
	{
		$mode = 'feedbacks';
	}

	if( is_array( $status ) )
	{ // $status is an array and probably contains more then one visibility status
		$result = 0;
		foreach( $status as $one_status )
		{
			if( isset( $cache_ctp_number[$filter_index][$post_id][$mode][$one_status] ) )
			{
				$result = $result + $cache_ctp_number[$filter_index][$post_id][$mode][$one_status];
			}
		}
	}
	elseif( isset( $cache_ctp_number[$filter_index][$post_id][$mode][$status] ) )
	{ // $status is a string with one visibility status
		$result = $cache_ctp_number[$filter_index][$post_id][$mode][$status];
	}
	else
	{ // $status is not recognized return total feedback number
		$result = $cache_ctp_number[$filter_index][$post_id][$mode]['total'];
	}

	// pre_dump( $cache_ctp_number[$filter_index][$post_id] );

	return $result;
}


/**
 * Get a Comment by ID. Exits if the requested comment does not exist!
 *
 * @param integer
 * @param boolean
 * @return Comment
 */
function & Comment_get_by_ID( $comment_ID, $halt_on_error = true )
{
	$CommentCache = & get_CommentCache();
	return $CommentCache->get_by_ID( $comment_ID, $halt_on_error );
}


/*
 * last_comments_title(-)
 *
 * @movedTo _obsolete092.php
 */


/***** Comment tags *****/

/**
 * comments_number(-)
 *
 * @deprecated deprecated by {@link Item::feedback_link()}, used in _edit_showposts.php
 */
function comments_number( $zero='#', $one='#', $more='#', $post_ID = NULL )
{
	if( $zero == '#' ) $zero = T_('Leave a comment');
	if( $one == '#' ) $one = T_('1 comment');
	if( $more == '#' ) $more = T_('%d comments');

	if( empty( $post_ID ) )
	{
		global $id;
		$post_ID = $id;
	}

	// attila> This function is called only from the backoffice ( _item_list_full.view.php ).
	// There we always have to show all comments.
	$number = generic_ctp_number( $post_ID, 'comments', 'total' );
	if ($number == 0)
	{
		$blah = $zero;
	}
	elseif ($number == 1)
	{
		$blah = $one;
	}
	elseif ($number  > 1)
	{
		$n = $number;
		$more = str_replace('%d', $n, $more);
		$blah = $more;
	}
	echo $blah;
}

/**
 * Get advanced perm for comment moderation on this blog
 *
 * @param int blog ID
 * @return array statuses - current user has permission to moderate comments with these statuses
 */
function get_allowed_statuses( $blog )
{
	$statuses = array();

	if( check_user_perm( 'blog_draft_comments', 'edit', false, $blog ) )
	{
		$statuses[] = 'draft';
	}

	if( check_user_perm( 'blog_published_comments', 'edit', false, $blog ) )
	{
		$statuses[] = 'published';
	}

	if( check_user_perm( 'blog_deprecated_comments', 'edit', false, $blog ) )
	{
		$statuses[] = 'deprecated';
	}

	return $statuses;
}


/**
 * Create comment form submit buttons
 *
 * Note: Publsih in only displayed when comment is in draft status
 *
 * @param $Form
 * @param $edited_Comment
 *
 */
function echo_comment_buttons( $Form, $edited_Comment )
{
	global $Collection, $Blog, $AdminUI;

	if( $edited_Comment->is_meta() )
	{ // Internal comments don't have a status, Display only one button to update
		$Form->submit( array( 'actionArray[update]', T_('Save Changes!'), 'SaveButton' ) );
	}
	else
	{ // Normal comments have a status, Display the buttons to change it and update
		if( $edited_Comment->status != 'trash' )
		{
			// ---------- VISIBILITY ----------
			echo T_('Visibility').get_manual_link( 'visibility-status' ).': ';
			// Get those statuses which are not allowed for the current User to create comments in this blog
			if( $edited_Comment->is_meta() )
			{	// Don't restrict statuses for internal comments:
				$restricted_statuses = array();
			}
			else
			{	// Restrict statuses for normal comments:
				$comment_Item = & $edited_Comment->get_Item();
				// Comment status cannot be more than post status, restrict it:
				$restrict_max_allowed_status = ( $comment_Item ? $comment_Item->status : '' );
				$restricted_statuses = get_restricted_statuses( $Blog->ID, 'blog_comment!', 'edit', $edited_Comment->status, $restrict_max_allowed_status );
			}
			$exclude_statuses = array_merge( $restricted_statuses, array( 'redirected', 'trash' ) );
			// Get allowed visibility statuses
			$status_options = get_visibility_statuses( '', $exclude_statuses );

			if( isset( $AdminUI, $AdminUI->skin_name ) && $AdminUI->skin_name == 'bootstrap' )
			{ // Use dropdown for bootstrap skin
				$status_icon_options = get_visibility_statuses( 'icons', $exclude_statuses );
				$Form->hidden( 'comment_status', $edited_Comment->status );
				echo '<div class="btn-group dropup comment_status_dropdown" data-toggle="tooltip" data-placement="left" data-container="body" title="'.get_status_tooltip_title( $edited_Comment->status ).'">';
				echo '<button type="button" class="btn btn-status-'.$edited_Comment->status.' dropdown-toggle" data-toggle="dropdown" aria-expanded="false" id="comment_status_dropdown">'
								.'<span>'.$status_options[ $edited_Comment->status ].'</span>'
							.' <span class="caret"></span></button>';
				echo '<ul class="dropdown-menu" role="menu" aria-labelledby="comment_status_dropdown">';
				foreach( $status_options as $status_key => $status_title )
				{
					echo '<li rel="'.$status_key.'" role="presentation"><a href="#" role="menuitem" tabindex="-1">'.$status_icon_options[ $status_key ].' <span>'.$status_title.'</span></a></li>';
				}
				echo '</ul>';
				echo '</div>';
			}
			else
			{ // Use standard select element for other skins
				echo '<select name="comment_status">';
				foreach( $status_options as $status_key => $status_title )
				{
					echo '<option value="'.$status_key.'"'
								.( $edited_Comment->status == $status_key ? ' selected="selected"' : '' )
								.' class="btn-status-'.$status_key.'">'
							.$status_title
						.'</option>';
				}
				echo '</select>';
			}
		}

		echo '<span class="btn-group">';

		// ---------- SAVE BUTTONS ----------
		$Form->submit( array( 'actionArray[update_edit]', /* TRANS: This is the value of an input submit button */ T_('Save & edit'), 'SaveEditButton btn-status-'.$edited_Comment->status ) );
		$Form->submit( array( 'actionArray[update]', T_('Save'), 'SaveButton btn-status-'.$edited_Comment->status ) );

		echo '</span>';
	}
}


/**
 * Display buttons to update a comment
 *
 * @param object Form
 * @param object edited Comment
 * @param string Max allowed status
 * @param string Action: 'update' - for button titles like 'Save as Public!', 'comments_visibility' - for button titles like 'Set visibility to Public'
 */
function echo_comment_status_buttons( $Form, $edited_Comment = NULL, $max_allowed_status = '', $action = 'update' )
{
	global $Collection, $Blog;

	if( $edited_Comment !== NULL )
	{	// If the edited comment is defined, e-g on edit form:
		if( $edited_Comment->is_meta() )
		{	// Don't suggest to change a status of internal comment:
			$Form->submit( array( 'actionArray['.$action.']', T_('Save Changes!'), 'SaveButton', '' ) );
			return;
		}

		$comment_Item = & $edited_Comment->get_Item();
		$comment_status = $edited_Comment->status;
	}
	else
	{	// If comment is not defined, e-g on action for several comments from list:
		$comment_Item = NULL;
		$comment_status = $max_allowed_status;
	}

	// Comment status cannot be more than post status, restrict it:
	$restrict_max_allowed_status = ( $comment_Item ? $comment_Item->status : $max_allowed_status );

	// Get those statuses which are not allowed for the current User to create posts in this blog
	$exclude_statuses = array_merge( get_restricted_statuses( $Blog->ID, 'blog_comment!', 'edit', $comment_status, $restrict_max_allowed_status ), array( 'redirected', 'trash' ) );
	// Get allowed visibility statuses:
	if( $action == 'comments_visibility' )
	{
		$status_options = get_visibility_statuses( '', $exclude_statuses );
		foreach( $status_options as $status_key => $status_title )
		{
			$status_options[ $status_key ] = sprintf( T_('Set visibility to %s'), $status_title );
		}
	}
	else // 'update'
	{
		$status_options = get_visibility_statuses( 'button-titles', $exclude_statuses );
		$status_options = array_map( 'T_', $status_options );
	}

	if( empty( $status_options ) )
	{	// If current User has no permission to edit to any status:
		return;
	}

	$status_icon_options = get_visibility_statuses( 'icons', $exclude_statuses );

	if( ! isset( $status_options[ $comment_status ] ) )
	{	// Check if the status is allowed for current User:
		foreach( $status_options as $status_key => $status_title )
		{
			$comment_status = $status_key;
			break;
		}
	}

	$Form->hidden( 'comment_status', $comment_status );
	echo '<div class="btn-group dropup comment_status_dropdown">';
	echo '<button type="submit" class="btn btn-status-'.$comment_status.'" name="actionArray['.$action.']">'
				.'<span>'.$status_options[ $comment_status ].'</span>'
			.'</button>'
			.'<button type="button" class="btn btn-status-'.$comment_status.' dropdown-toggle" data-toggle="dropdown" aria-expanded="false" id="comment_status_dropdown">'
				.'<span class="caret"></span>'
			.'</button>';
	echo '<ul class="dropdown-menu" role="menu" aria-labelledby="comment_status_dropdown">';
	foreach( $status_options as $status_key => $status_title )
	{
		echo '<li rel="'.$status_key.'" role="presentation"><a href="#" role="menuitem" tabindex="-1">'.$status_icon_options[ $status_key ].' <span>'.$status_title.'</span></a></li>';
	}
	echo '</ul>';
	echo '</div>';
}


/**
 * Add a javascript ban action icon after the given url
 *
 * @param string url
 * @return string the url with ban icon
 */
function add_jsban( $url )
{
	global $admin_url;

	$ban_domain = get_ban_domain( $url );
	$ban_url = $admin_url.'?ctrl=antispam&amp;action=ban&amp;keyword='.rawurlencode( $ban_domain ).'&amp;'.url_crumb('antispam');

	return '<a id="ban_url" href="'.$ban_url.'" onclick="ban_url(\''.$ban_domain.'\'); return false;">'.get_icon( 'ban' ).'</a>';
}


/**
 * Add a javascript ban action icon after each url in the given content
 *
 * @param string Comment content
 * @return string the content with a ban icon after each url if the user has spamblacklist permission, the incoming content otherwise
 */
function add_ban_icons( $content )
{
	if( ! check_user_perm( 'spamblacklist', 'edit' ) )
	{ // Current user has no permission to edit the spam contents
		return $content;
	}

	if( stristr( $content, '<code' ) !== false || stristr( $content, '<pre' ) !== false )
	{ // Add icons only outside <code> and <pre>
		return callback_on_non_matching_blocks( $content,
				'~<(code|pre)[^>]+class="codeblock"[^>]*>.*?</\1>~is',
				'add_ban_icons_callback' );
	}
	else
	{
		return add_ban_icons_callback( $content );
	}
}


/**
 * Callback function to add a javascript ban action icon after each url in the given content
 *
 * @param string Comment content
 * @return string the content with a ban icon after each url if the user has spamblacklist permission, the incoming content otherwise
 */
function add_ban_icons_callback( $content )
{
	$atags = get_atags( $content );
	$imgtags = get_imgtags( $content );
	$urls = get_urls( $content );
	$result = '';
	$from = 0; // current processing position
	$length = 0; // current url or tag length
	$i = 0; // url counter
	$j = 0; // "a" tag counter
	$k = 0; // "img" tag counter

	// Remove the duplicated <a> tags from array
	$atags_fixed = array();
	foreach( $atags as $atag )
	{
		if( preg_match( '#href="([^"]+)"#', $atag, $matches ) && ! isset( $atags_fixed[ $matches[1] ] ) )
		{
			$atags_fixed[ $matches[1] ] = $atag;
		}
	}
	$atags = array();
	foreach( $atags_fixed as $atag )
	{
		$atags[] = $atag;
	}

	$used_urls = array();
	while( isset( $urls[$i] ) )
	{ // there is unprocessed url
		$url = $urls[$i];
		if( validate_url( $url, 'posting', false ) )
		{ // skip not valid urls
			$i++;
			continue;
		}
		if( in_array( $url, $used_urls ) )
		{ // skip already passed url
			$i++;
			continue;
		}
		$used_urls[] = $url;
		while( isset( $imgtags[$k] ) && ( strpos( $content, $imgtags[$k] ) < $from ) )
		{ // skip already passed img tags
			$k++;
		}

		$pos = strpos( $content, $url, $from );
		$length = utf8_strlen($url);
		$i++;

		// check img tags
		if( isset( $imgtags[$k] ) && ( strpos( $imgtags[$k], $url ) !== false )
			&& ( $pos > strpos( $content, $imgtags[$k], $from ) ) )
		{ // current url is inside the img tag, we need to skip this url.
			$result .= substr( $content, $from, $pos + $length - $from );
			$from = $pos + $length;
			$k++;
			continue;
		}

		// check a tags
		if( isset($atags[$j]) )
		{ // there is unprocessed "a" tag
			$tag = $atags[$j];
			if( ( ( $urlpos = strpos( $tag, $url ) ) !== false ) && ( $pos > strpos( $content, $tag, $from ) ) )
			{ // the url is inside the current tag, we have to add ban icon after the tag
				$pos = strpos( $content, $tag, $from );
				$length = strlen($tag);
				while( isset($urls[$i]) && ( ( $urlpos = strpos( $tag, $urls[$i], $urlpos + 1 ) ) !== false ) )
				{ // skip all other urls from this tag
					$i++;
				}
				$j++;
			}
		}
		// add processed part and ban icon to result and set current position
		$result .= substr( $content, $from, $pos + $length - $from );
		$from = $pos + $length;
		$result .= add_jsban( $url );
	}

	// add the end of the content to the result
	$result .= substr( $content, $from, strlen($content) - $from );
	return $result;
}


/**
 * Get opentrash link
 *
 * @param boolean check permission or not. Should be false only if it was already checked.
 * @param boolean show "Open recycle bin" link even if there is no comment with 'trash' status
 * @param array Additional params
 * @return Open recycle bin link if user has the corresponding 'blogs' - 'editall' permission, empty string otherwise
 */
function get_opentrash_link( $check_perm = true, $force_show = false, $params = array() )
{
	global $admin_url, $DB, $blog;

	$params = array_merge( array(
			'before' => '<div id="recycle_bin" class="pull-right">',
			'after'  => ' </div>',
			'class'  => 'action_icon btn btn-default btn-sm',
		), $params );

	$show_recycle_bin = ( !$check_perm || check_user_perm( 'blogs', 'editall' ) );
	if( $show_recycle_bin && ( !$force_show ) )
	{ // get number of trash comments:
		$SQL = new SQL( 'Get number of trash comments for open trash link' );
		$SQL->SELECT( 'COUNT( comment_ID )' );
		$SQL->FROM( 'T_comments' );
		$SQL->FROM_add( 'INNER JOIN T_items__item ON comment_item_ID = post_ID' );
		$SQL->FROM_add( 'INNER JOIN T_categories ON post_main_cat_ID = cat_ID' );
		$SQL->WHERE( 'comment_status = "trash"' );
		if( isset( $blog ) )
		{
			$SQL->WHERE_and( 'cat_blog_ID = '.$DB->quote( $blog ) );
		}
		$show_recycle_bin = ( $DB->get_var( $SQL ) > 0 );
	}

	$result = $params['before'];
	if( $show_recycle_bin )
	{ // show "Open recycle bin"
		global $CommentList;
		$comment_list_param_prefix = 'cmnt_fullview_';
		if( !empty( $CommentList->param_prefix ) )
		{
			$comment_list_param_prefix = $CommentList->param_prefix;
		}
		$result .= action_icon( T_('Open recycle bin'), 'recycle_full',
						$admin_url.'?ctrl=comments&amp;blog='.$blog.'&amp;'.$comment_list_param_prefix.'show_statuses[]=trash', ' '.T_('Open recycle bin'), 5, 3, array( 'class' => $params['class'] ) );
	}
	return $result.$params['after'];
}


/**
 * Display disabled comment form
 *
 * @param string Blog allow comments settings value
 * @param string Item url, where this comment form should be displayed
 * @param array Skin params
 */
function echo_disabled_comments( $allow_comments_value, $item_url, $params = array() )
{
	global $Settings;

	$params = array_merge( array(
			'comments_disabled_text_member'     => T_( 'You must be a member of this blog to comment.' ),
			'comments_disabled_text_registered' => T_( 'In order to leave a comment' ),
			'comments_disabled_text_validated'  => T_( 'You must activate your account before you can leave a comment.' ),
			'form_comment_text'                 => T_('Comment text'),
			'form_class_comment'                => 'bComment',
		), $params );

	if( empty( $params['form_params'] ) )
	{
		$params['form_params'] = array();
	}

	$params['form_params'] = array_merge( array(
			'comments_disabled_before' => '<div class="comment_posting_disabled_msg"><p>',
			'comments_disabled_after' => '</p></div>',
			'formstart'      => '',
			'formend'        => '',
			'fieldset_begin' => '<fieldset>',
			'fieldset_end'   => '</fieldset>',
			'fieldstart'     => '',
			'fieldend'       => '',
			'labelstart'     => '<div class="evo_label"><label for="p">',
			'labelend'       => '</label></div>',
			'inputstart'     => '<div class="evo_input">',
			'inputend'       => '</div>',
		), $params['form_params'] );

	switch( $allow_comments_value )
	{
		case 'member':
			$disabled_text = $params['comments_disabled_text_member'];
			$form_disabled_text = $disabled_text;
			break;

		case 'registered':
			$disabled_text = $params['comments_disabled_text_registered'];
			$form_disabled_text = T_( 'You must be logged in to leave a comment.' );
			break;

		default:
			// case any or never, in this case comment form is already displayed, or comments are not allowed at all.
			return;
	}

	$login_link = '';
	$activateinfo_link = '';
	$is_logged_in = is_logged_in();
	if( !$is_logged_in )
	{ // user is not logged in
		$login_link = '<a class="btn btn-primary btn-sm" href="'.get_login_url( 'cannot comment', $item_url ).'">'.T_( 'Log in now!' ).'</a>';
	}
	elseif( check_user_status( 'can_be_validated' ) )
	{ // logged in but the account is not activated
		$disabled_text = $params['comments_disabled_text_validated'];
		$form_disabled_text = $disabled_text;
		$activateinfo_link = '<a href="'.get_activate_info_url( $item_url, '&amp;' ).'">'.T_( 'More info &raquo;' ).'</a>';
	}
	// else -> user is logged in and account was activated

	if( ( !$is_logged_in ) && ( $Settings->get( 'newusers_canregister' ) == 'yes' ) && ( $Settings->get( 'registration_is_public' ) ) )
	{
		$register_link = '<a class="btn btn-primary btn-sm" href="'.get_user_register_url( $item_url, 'reg to post comment' ).'">'.T_( 'Register now!' ).'</a>';
	}

	// disabled comment form
	echo '<form class="'.$params['form_class_comment'].'" action="">';

	echo $params['form_params']['formstart'];

	echo $params['form_params']['fieldset_begin'];

	echo $params['form_params']['comments_disabled_before'];
	if( $is_logged_in )
	{
		echo $disabled_text.' '.$activateinfo_link;
	}
	else
	{ // not logged in, add login and register links
		echo $disabled_text.' '.$login_link.( ! empty( $register_link ) ? ' '.T_('or').' '.$register_link : '' );
	}
	echo $params['form_params']['comments_disabled_after'];

	echo $params['form_params']['fieldset_end'];

	echo $params['form_params']['fieldset_begin'];
	echo $params['form_params']['fieldstart'];
	echo $params['form_params']['labelstart'].$params['form_comment_text'].':'.$params['form_params']['labelend'];
	echo $params['form_params']['inputstart'];
	echo '<textarea id="p" class="bComment form_textarea_input" rows="5" name="p" cols="40" disabled="disabled">'.$form_disabled_text.'</textarea>';
	echo $params['form_params']['inputend'];
	echo $params['form_params']['fieldend'];
	echo $params['form_params']['fieldset_end'];

	echo $params['form_params']['formend'];

	// margin at the bottom of the form
	echo '<div style="margin-top:10px"></div>';
	echo '</form>';
}


/**
 * Save Comment object into the current Session
 *
 * @param object Comment
 * @param string Kind of session var: 'unsaved' or 'preview'
 * @param string Comment type: Meta or Normal
 */
function save_comment_to_session( $Comment, $kind = 'unsaved', $type = '' )
{
	global $Session;

	if( $type != 'meta' )
	{	// Use default type if it is not allowed:
		$type = '';
	}

	$Session->set( 'core.'.$kind.'_Comment'.$type, $Comment );
}


/**
 * Get Comment object from the current Session
 *
 * @param string Kind of session var: 'unsaved' or 'preview'
 * @param string Comment type: Meta or Normal
 * @return Comment|NULL Comment object if Session core.unsaved_Comment param is set, NULL otherwise
 */
function get_comment_from_session( $kind = 'unsaved', $type = '' )
{
	global $Session, $b2evo_session_comments;

	if( $type != 'meta' )
	{	// Use default type if it is not allowed:
		$type = '';
	}

	$session_var_name = 'core.'.$kind.'_Comment'.$type;

	if( ! isset( $b2evo_session_comments[ $session_var_name ] ) )
	{	// Set Comment to cache array:
		if( ! is_array( $b2evo_session_comments ) )
		{	// Initialize array for caching:
			$b2evo_session_comments = array();
		}

		if( ( $Comment = $Session->get( $session_var_name ) ) && $Comment instanceof Comment )
		{	// If Comment is detected for current Session:

			// Delete Comment to clear Session data:
			$Session->delete( $session_var_name );
		}
		else
		{	// Comment is not detected, Return NULL:
			$Comment = NULL;
		}

		// Cache Comment in global var:
		$b2evo_session_comments[ $session_var_name ] = $Comment;
	}

	return $b2evo_session_comments[ $session_var_name ];
}


/**
 * Dispay the replies of a comment on front-office
 *
 * @param integer Comment ID
 * @param array Template params
 * @param integer Level
 */
function display_comment_replies( $comment_ID, $params = array(), $level = 1 )
{
	global $CommentReplies;

	$params = array_merge( array(
			'comment_template'    => '_item_comment.inc.php',
			'preview_block_start' => '',
			'preview_start'       => '<div class="bComment" id="comment_preview">',
			'preview_end'         => '</div>',
			'preview_block_end'   => '',
			'comment_start'       => '<div class="bComment">',
			'comment_end'         => '</div>',
			'comment_error_start' => '<div class="bComment" id="comment_error">',
			'comment_error_end'   => '</div>',
			'link_to'             => 'userurl>userpage', // 'userpage' or 'userurl' or 'userurl>userpage' or 'userpage>userurl'
			'author_link_text'    => 'name', // avatar_name | avatar_login | only_avatar | name | login | nickname | firstname | lastname | fullname | preferredname
		), $params );

	if( isset( $CommentReplies[ $comment_ID ] ) )
	{ // This comment has the replies
		foreach( $CommentReplies[ $comment_ID ] as $Comment )
		{ // Loop through the replies:
			if( empty( $Comment->ID ) )
			{ // Get html tag of the comment block of preview
				$comment_start = $Comment->email_is_detected ? $params['comment_error_start'] : $params['preview_start'];
			}
			else
			{ // Get html tag of the comment block of existing comment
				$comment_start = $params['comment_start'];
			}

			// Set margin left for each sub level comment
			$attrs = ' style="margin-left:'.( 20 * $level ).'px"';
			if( strpos( $comment_start, 'class="' ) === false )
			{ // Add a class attribute for the replied comment
				$attrs .= ' class="replied_comment"';
			}
			else
			{ // Add a class name for the replied comment
				$comment_start = str_replace( 'class="', 'class="replied_comment ', $comment_start );
			}
			$comment_start = str_replace( '>', $attrs.'>', $comment_start );

			if( ! empty( $Comment->ID ) )
			{ // Comment from DB
				skin_include( $params['comment_template'], array_merge( $params, array(
						'Comment'          => & $Comment,
						'comment_start'    => $comment_start
					) ) );
			}
			else
			{ // PREVIEW comment
				skin_include( $params['comment_template'], array_merge( $params, array(
						'Comment'              => & $Comment,
						'comment_block_start'  => $Comment->email_is_detected ? '' : $params['preview_block_start'],
						'comment_start'        => $comment_start,
						'comment_end'          => $Comment->email_is_detected ? $params['comment_error_end'] : $params['preview_end'],
						'comment_block_end'    => $Comment->email_is_detected ? '' : $params['preview_block_end'],
						'author_link_text'     => $params['author_link_text'],
					) ) );
			}

			// Display the rest replies recursively
			display_comment_replies( $Comment->ID, $params, $level + 1 );
		}
	}
}


/**
 * Dispay the replies of a comment on back-office
 *
 * @param integer Comment ID
 * @param array Params
 * @param integer Level
 */
function echo_comment_replies( $comment_ID, $params, $level = 1 )
{
	global $CommentReplies;

	if( ! isset( $CommentReplies[ $comment_ID ] ) )
	{	// This comment has no replies, Exit here:
		return false;
	}

	$params = array_merge( array(
			'redirect_to'        => NULL,
			'save_context'       => false,
			'display_meta_title' => false,
		), $params );

	foreach( $CommentReplies[ $comment_ID ] as $Comment )
	{	// Loop through the replies:

		// Display a comment:
		echo_comment( $Comment, $params['redirect_to'], $params['save_context'], $Comment->get_inlist_order(), $params['display_meta_title'], $level );

		// Display the rest replies recursively:
		echo_comment_replies( $Comment->ID, $params, $level + 1 );
	}
}


/**
 * JS Behaviour: Output JavaScript code to reply the comments
 *
 * @param object Item
 */
function echo_comment_reply_js( $Item )
{
	global $Collection, $Blog;

	if( ! isset( $Blog ) || ! $Blog->get_setting( 'threaded_comments' ) )
	{	// If threaded comments are not enabled for current Collection:
		return false;
	}

	$js_config = array(
			'item_ID' => $Item->ID,
			'reply_button_msg' => TS_('Reply to this comment'),
			'link_back_url' => url_add_param( $Item->get_permanent_url(), 'reply_ID=\' + comment_ID + \'&amp;redir=no', '&amp;', false ),
			'link_back_specific_comment_msg' => TS_('You are currently replying to a specific comment'),
			'link_back_current_comment_msg' => TS_('You are currently replying to this comment'),
		);

	expose_var_to_js( 'evo_init_comment_reply_config', $js_config );
}


/**
 * JS Behaviour: Output JavaScript code to moderate the comments
 * Vote on the comment
 * Change a status of the comment
 */
function echo_comment_moderate_js()
{
	if( !is_logged_in( false ) )
	{
		return false;
	}

	global $Collection, $Blog;

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

	load_funcs( 'comments/model/_comment_js.funcs.php' );
}


/**
 * Check to display a form to mass delete the comments
 *
 * @param object Comment List
 * @return boolean TRUE - if a form is available
 */
function check_comment_mass_delete( $CommentList )
{
	if( ! check_user_perm( 'blogs', 'all' ) )
	{	// Check permission
		return false;
	}

	if( $CommentList->get_total_rows() == 0 )
	{	// No comments for current list
		return false;
	}

	// Form is available to mass delete the comments
	return true;
}


/**
 * Check if autocomplete username is enabled
 * 
 * @param object Comment object
 * @return boolean TRUE - if autocomplete username is enabled
 */
function check_autocomplete_usernames( $Comment )
{
	global $Settings, $Collection, $Blog;

	if( $Comment->is_meta() )
	{	// Always enable autocomplete username for internal comments
		return true;
	}
	elseif( is_admin_page() && ! empty( $Blog ) )
	{	// Check setting in the Back office or when Blog is not defined
		return $Blog->get_setting( 'autocomplete_usernames' );
	}
	else
	{	// Check setting in the Front office for current blog & skin
		$SkinCache = & get_SkinCache();
		$skin = & $SkinCache->get_by_ID( $Blog->get( 'skin_ID' ) );
		return $skin->get_setting( 'autocomplete_usernames' );
	}

	return false;
}


/**
 * Dispay a form to mass delete the comments
 *
 * @param object Comment List
 */
function display_comment_mass_delete( $CommentList )
{

	$action = param_action();

	if( $action != 'mass_delete' )
	{	// Display this form only for action "mass_delete"
		return;
	}

	if( !check_comment_mass_delete( $CommentList ) )
	{	// Form is not available
		return;
	}

	require dirname(__FILE__).'/../views/_comment_mass.form.php';
}


/**
 * Delete the comments
 *
 * @param string Type of deleting:
 *               'recycle' - to move into recycle bin
 *               'delete' - to delete permanently
 * @param string sql query to get deletable comment ids
 */
function comment_mass_delete_process( $mass_type, $deletable_comments_query )
{
	if( $mass_type != 'recycle' && $mass_type != 'delete' )
	{ // Incorrect action
		return;
	}

	global $DB, $cache_comments_has_replies, $cache_items_user_data, $cache_postcats;

	/**
	 * Disable log queries because it increases the memory and stops the process with error "Allowed memory size of X bytes exhausted..."
	 */
	$DB->log_queries = false;

	$Form = new Form();

	$Form->begin_form( 'fform' );

	$Form->begin_fieldset( T_('Mass deleting log') );

	echo T_('The comments are deleting...');
	evo_flush();

	$CommentCache = & get_CommentCache();
	$ItemCache = & get_ItemCache();
	$ChapterCache = & get_ChapterCache();

	// Get the comments by 1000 to avoid an exhausting of memory
	$deletable_comment_ids = $DB->get_col( $deletable_comments_query.' LIMIT 1000' );
	while( !empty( $deletable_comment_ids ) )
	{
		// Get the first slice of the deletable comment ids list
		$ids = array_splice( $deletable_comment_ids, 0, 100 );
		// Make sure the CommentCache is empty
		$CommentCache->clear();
		// Load deletable comment ids
		$CommentCache->load_list( $ids );

		while( ( $iterator_Comment = & $CommentCache->get_next() ) != NULL )
		{ // Delete all comments from CommentCache
			$iterator_Comment->dbdelete( ($mass_type == 'delete') );
		}

		// Display progress dot
		echo ' .';
		evo_flush();

		if( empty( $deletable_comment_ids ) )
		{
			// Clear all caches to save memory
			$ItemCache->clear();
			$ChapterCache->clear();
			$cache_comments_has_replies = array();
			$cache_items_user_data = array();
			$cache_postcats = array();

			// Get new portion of deletable comments
			$deletable_comment_ids = $DB->get_col( $deletable_comments_query.' LIMIT 1000' );
		}
	}

	echo ' OK';

	$Form->end_form();

	// Clear a comment cache
	$CommentCache->clear();
}


/**
 * Move all child comments to new post by parent comment ID
 *
 * @param integer/array Parent comment IDs
 * @param integer Item ID
 * @param boolean TRUE to convert a comment to root/top comment of the post
 */
function move_child_comments_to_item( $comment_IDs, $item_ID, $convert_to_root = true )
{
	global $DB;

	$child_comments_SQL = new SQL();
	$child_comments_SQL->SELECT( 'comment_ID' );
	$child_comments_SQL->FROM( 'T_comments' );
	$child_comments_SQL->WHERE( 'comment_in_reply_to_cmt_ID IN ( '.$DB->quote( $comment_IDs ).' )' );
	$child_comments_IDs = $DB->get_col( $child_comments_SQL->get() );

	if( empty( $child_comments_IDs ) )
	{ // No child comments, Exit here
		return;
	}

	// Move the child comments recursively
	move_child_comments_to_item( $child_comments_IDs, $item_ID, false );

	// Update item ID to new
	if( $convert_to_root )
	{ // Make these comments as root comments (remove their parent depending)
		$update_sql = ', comment_in_reply_to_cmt_ID = NULL';
	}
	$DB->query( 'UPDATE T_comments
		SET comment_item_ID = '.$DB->quote( $item_ID ).$update_sql.'
		WHERE comment_in_reply_to_cmt_ID IN ( '.$DB->quote( $comment_IDs ).' )' );
}


/**
 * Display comments results table
 *
 * @param array Params
 */
function comments_results_block( $params = array() )
{
	// Make sure we are not missing any param:
	$params = array_merge( array(
			'edited_User'          => NULL,
			'results_param_prefix' => 'actv_comment_',
			'results_title'        => T_('Comments posted by the user'),
			'results_no_text'      => T_('User has not posted any comment yet'),
			'action'               => '',
		), $params );

	if( !is_logged_in() )
	{	// Only logged in users can access to this function
		return;
	}

	if( ! check_user_perm( 'users', 'moderate' ) )
	{	// Check minimum permission:
		return;
	}

	$edited_User = $params['edited_User'];
	if( !$edited_User )
	{	// No defined User, probably the function is calling from AJAX request
		$user_ID = param( 'user_ID', 'integer', 0 );
		if( empty( $user_ID ) )
		{	// Bad request, Exit here
			return;
		}
		$UserCache = & get_UserCache();
		if( ( $edited_User = & $UserCache->get_by_ID( $user_ID, false ) ) === false )
		{	// Bad request, Exit here
			return;
		}
	}

	global $DB, $AdminUI;

	param( 'user_tab', 'string', '', true );
	param( 'user_ID', 'integer', 0, true );


	$SQL = new SQL();
	$SQL->SELECT( '*' );
	$SQL->FROM( 'T_comments' );
	$SQL->WHERE( 'comment_author_user_ID = '.$DB->quote( $edited_User->ID ) );

	// Create result set:
	$comments_Results = new Results( $SQL->get(), $params['results_param_prefix'], 'D' );
	$comments_Results->Cache = & get_CommentCache();
	$comments_Results->title = $params['results_title'];
	$comments_Results->no_results_text = $params['results_no_text'];

	if( $params['action'] != 'view' && $comments_Results->get_total_rows() > 0 && $edited_User->has_comment_to_delete() )
	{	// Display action icon to delete all records if at least one record exists & current user can delete at least one comment posted by user
		$comments_Results->global_icon( sprintf( T_('Delete all comments posted by %s'), $edited_User->login ), 'recycle', '?ctrl=user&amp;user_tab=activity&amp;action=delete_all_comments&amp;user_ID='.$edited_User->ID.'&amp;'.url_crumb('user'), ' '.T_('Delete all'), 3, 4 );
	}

	// Initialize Results object
	comments_results( $comments_Results, array(
			'field_prefix' => 'comment_',
			'display_kind' => false,
			'display_additional_columns' => true,
			'plugin_table_name' => 'activity',
			'display_spam' => false,
		) );

	if( is_ajax_content() )
	{	// init results param by template name
		if( !isset( $params[ 'skin_type' ] ) || ! isset( $params[ 'skin_name' ] ) )
		{
			debug_die( 'Invalid ajax results request!' );
		}
		$comments_Results->init_params_by_skin( $params[ 'skin_type' ], $params[ 'skin_name' ] );
	}

	$results_params = $AdminUI->get_template( 'Results' );
	$display_params = array(
		'before' => str_replace( '>', ' style="margin-top:25px" id="comments_result">', $results_params['before'] ),
	);
	$comments_Results->display( $display_params );

	if( !is_ajax_content() )
	{	// Create this hidden div to get a function name for AJAX request
		echo '<div id="'.$params['results_param_prefix'].'ajax_callback" style="display:none">'.__FUNCTION__.'</div>';
	}
}


/**
 * Initialize Results object for comments list
 *
 * @param object Results
 * @param array Params
 */
function comments_results( & $comments_Results, $params = array() )
{
	// Make sure we are not missing any param:
	$params = array_merge( array(
			'field_prefix' => '',
			'display_date' => true,
			'display_permalink' => true,
			'display_item' => false,
			'display_status' => false,
			'display_kind' => true,
			'display_author' => true,
			'display_url' => true,
			'display_email' => true,
			'display_ip' => true,
			'display_additional_columns' => false,
			'plugin_table_name' => '',
			'display_spam' => true,
			'display_visibility' => true,
			'display_actions' => true,
			'col_post' => T_('Post'),
		), $params );

	if( $params['display_date'] )
	{	// Display Date column
		$td = '<span class="date">@date()@</span>';
		if( $params['display_permalink'] )
		{
			$td = '@get_permanent_link( get_icon(\'permalink\') )@ '.$td;
		}
		$comments_Results->cols[] = array(
				'th' => T_('Date'),
				'order' => $params['field_prefix'].'date',
				'default_dir' => 'D',
				'th_class' => 'shrinkwrap',
				'td_class' => 'shrinkwrap',
				'td' => $td,
			);
	}

	if( $params['display_item'] )
	{	// Display Date column
		$td = '%{Obj}->get_permanent_item_link()%';
		if( $params['display_status'] )
		{ // Dislpay status banner
			$td = '%{Obj}->get_status( "styled" )%'.$td;
		}
		$comments_Results->cols[] = array(
				'th' => $params['col_post'],
				'order' => $params['field_prefix'].'item_ID',
				'td' => $td,
			);
	}

	if( $params['display_kind'] )
	{	// Display Kind column
		$comments_Results->cols[] = array(
				'th' => T_('Kind'),
				'order' => $params['field_prefix'].'type',
				'th_class' => 'nowrap',
				'td' => '%get_type( {Obj} )%',
			);
	}

	if( $params['display_author'] )
	{	// Display Author column
		$comments_Results->cols[] = array(
				'th' => T_('Author'),
				'order' => $params['field_prefix'].'author',
				'th_class' => 'nowrap',
				'td' => '%get_author( {Obj} )%',
			);
	}

	if( $params['display_url'] )
	{	// Display Url column
		$comments_Results->cols[] = array(
				'th' => T_('URL'),
				'order' => $params['field_prefix'].'author_url',
				'th_class' => 'nowrap',
				'td' => '%get_url( {Obj} )%',
			);
	}

	if( $params['display_email'] )
	{	// Display Email column
		$comments_Results->cols[] = array(
				'th' => T_('Email'),
				'order' => $params['field_prefix'].'author_email',
				'th_class' => 'nowrap',
				'td_class' => 'nowrap',
				'td' => '%get_author_email( {Obj} )%',
			);
	}

	if( $params['display_ip'] )
	{	// Display IP column
		$comments_Results->cols[] = array(
				'th' => T_('IP'),
				'order' => $params['field_prefix'].'author_IP',
				'th_class' => 'nowrap',
				'td_class' => 'nowrap',
				'td' => '%get_author_ip( {Obj}, "'.$comments_Results->param_prefix.'" )%',
			);
	}


	if( $params['display_additional_columns'] )
	{	// Display additional columns from the Plugins
		global $Plugins;
		$Plugins->trigger_event( 'GetAdditionalColumnsTable', array(
			'table'   => $params['plugin_table_name'],
			'column'  => $params['field_prefix'].'author_IP',
			'Results' => $comments_Results ) );
	}

	if( $params['display_spam'] )
	{	// Display Spam karma column
		$comments_Results->cols[] = array(
				'th' => T_('Spam karma'),
				'order' => $params['field_prefix'].'spam_karma',
				'th_class' => 'shrinkwrap',
				'td_class' => 'shrinkwrap',
				'td' => '%get_spam_karma( {Obj} )%'
			);
	}

	if( $params['display_visibility'] )
	{	// Display Visibility column
		$comments_Results->cols[] = array(
				'th' => T_('Visibility'),
				'order' => $params['field_prefix'].'status',
				'th_class' => 'nowrap',
				'td_class' => 'shrinkwrap',
				'td' => '%{Obj}->get_status( "styled" )%',
			);
	}

	if( $params['display_actions'] )
	{	// Display Actions column
		$comments_Results->cols[] = array(
				'th' => T_('Actions'),
				'th_class' => 'shrinkwrap',
				'td_class' => 'shrinkwrap',
				'td' => '%comment_edit_actions( {Obj} )%'
			);
	}
}


/**
 * JS Behaviour: Output JavaScript code to change Item of the Comment
 */
function echo_comment_change_item_js()
{
	global $Blog, $admin_url, $evo_comment_change_item_js_initialized;

	if( ! empty( $evo_comment_change_item_js_initialized ) )
	{	// Don't initialize this JS code twice on same page:
		return;
	}

	// Set flag to know this is initialized:
	$evo_comment_change_item_js_initialized = true;

	// Initialize JavaScript to build and open window:
	echo_modalwindow_js();

	// Initialize JavaScript for item selector window:
	echo_item_selector_js();
?>
<script>
function evo_comment_change_item_load_window( item_ID )
{
	return evo_item_selector_load_window( item_ID,
		[ '<?php echo TS_('Link to Another Post'); ?>', '<?php echo T_('Link to Another Post'); ?>' ],
		false,
		[ { 'text': '<?php echo TS_('Link comment to this post'); ?>', 'id': 'evo_comment_change_item_btn', 'class': 'btn btn-primary' } ],
		<?php echo empty( $Blog ) ? 0 : $Blog->ID; // Default collection ?>,
		'collections', { list_in_frontoffice: 'all', per_page: -1 }
	);
}

// Submit form to link the Comment to another Item:
jQuery( document ).on( 'click', '#evo_comment_change_item_btn', function()
{
	var post = jQuery( '#evo_item_selector_dest_post_ID' ).data( 'post' )

	// Update info field link:
	var item_link = jQuery('.comment_item_title')[0];
	item_link.innerHTML = post.title;
	item_link.href = '<?php echo $admin_url; ?>?ctrl=items&blog=' + jQuery( '#evo_item_selector_colls_list' ).find( ':selected' ).data( 'coll-id' ) + '&p=' + jQuery( '#evo_item_selector_dest_post_ID').val();

	// Update moveto_post field:
	jQuery( 'input[name="moveto_post"]' ).val( jQuery('#evo_item_selector_dest_post_ID').val() );

	// Close main modal window:
	closeModalWindow();
} );
</script>
<?php
}


/**
 * Handles anonymous comment cookies
 * 
 * @param boolean True to set cookie, false otherwise
 * @param string Value of anonymous comment author
 * @param string Value of anonymous comment author's email
 * @param string Value of anonymous comment author's URL
 * @param boolean True if anonymous URLs are allowed
 */
function handle_comment_cookies( $set_cookies, $author, $email = '', $url = '', $allow_anon_url = false )
{
	global $cookie_name, $cookie_email, $cookie_url, $cookie_expires, $cookie_expired;

	if( !is_logged_in() )
	{
		if( $set_cookies )
		{	// Set cookies:
			if ($email == '')
				$email = ' '; // this to make sure a cookie is set for 'no email'
			if ($url == '')
				$url = ' '; // this to make sure a cookie is set for 'no url'

			// fplanque: made cookies available for whole site
			evo_setcookie( $cookie_name, $author, $cookie_expires, '', '', false, true );
			evo_setcookie( $cookie_email, $email, $cookie_expires, '', '', false, true );
			if( $allow_anon_url )
			{
				evo_setcookie( $cookie_url, $url, $cookie_expires, '', '', false, true );
			}
		}
		else
		{	// Erase cookies:
			if( !empty( $_COOKIE[$cookie_name] ) )
			{
				evo_setcookie( $cookie_name, '', $cookie_expired, '', '', false, true );
			}
			if( !empty( $_COOKIE[$cookie_email] ) )
			{
				evo_setcookie( $cookie_email, '', $cookie_expired, '', '', false, true );
			}
			if( !empty( $_COOKIE[$cookie_url] ) )
			{
				evo_setcookie( $cookie_url, '', $cookie_expired, '', '', false, true );
			}
		}
	}

	// Send the predefined cookies:
	evo_sendcookies();
}


/**
 * Helper functions to display Comments results.
 * New ( not display helper ) functions must be created above comments_results function
 */

/**
 * Get comment type.
 *
 * @param object Comment
 * @return string Comment type OR '---' if user has no permission to moderate this comment
 */
function get_type( $Comment )
{
	if( $Comment->can_be_displayed() || check_user_perm( 'comment!CURSTATUS', 'moderate', false, $Comment ) )
	{
		return $Comment->get( 'type' );
	}
	else
	{
		return '<span class="dimmed">---</span>';
	}
}


/**
 * Get comment author
 *
 * @param object Comment
 * @return string Comment author OR '---' if user has no permission to moderate this comment
 */
function get_author( $Comment )
{
	if( $Comment->can_be_displayed() || check_user_perm( 'comment!CURSTATUS', 'moderate', false, $Comment ) )
	{
		$author_User = $Comment->get_author_User();
		if( $author_User != NULL )
		{	// author is a registered user
			return $author_User->get_identity_link();
		}
		// author is not a registered user
		return $Comment->get_author( array( 'link_to' => 'userpage' )  );
	}
	else
	{
		return '<span class="dimmed">---</span>';
	}
}


/**
 * Get comment author url
 *
 * @param object Comment
 * @return string Comment url OR '---' if user has no permission to moderate this comment
 */
function get_url( $Comment )
{
	if( check_user_perm( 'comment!CURSTATUS', 'moderate', false, $Comment ) )
	{
		return $Comment->author_url_with_actions( NULL, false );
	}
	else
	{
		return '<span class="dimmed">---</span>';
	}
}


/**
 * Get comment author email
 *
 * @param object Comment
 * @return string Comment author email OR '---' if user has no permission to moderate this comment
 */
function get_author_email( $Comment )
{
	if( check_user_perm( 'comment!CURSTATUS', 'moderate', false, $Comment ) )
	{
		return $Comment->get_author_email();
	}
	else
	{
		return '<span class="dimmed">---</span>';
	}
}


/**
 * Get comment author IP
 *
 * @param object Comment
 * @return string Comment author IP OR '---' if user has no permission to moderate this comment
 */
function get_author_ip( $Comment, $param_prefix = '' )
{
	if( check_user_perm( 'comment!CURSTATUS', 'moderate', false, $Comment ) )
	{
		if( empty( $Comment->author_IP ) )
		{
			return '';
		}
		else
		{
			$antispam_icon = get_icon( 'lightning', 'imgtag', array( 'title' => T_( 'Go to edit this IP address in antispam control panel' ) ) );
			$antispam_link = ' '.implode( ', ', get_linked_ip_list( array( $Comment->author_IP ), NULL, $antispam_icon ) );

			$filter_IP_url = regenerate_url( 'filter', $param_prefix.'author_IP='.$Comment->get( 'author_IP' ) );
			$country = $Comment->get_ip_country( ' ' );

			return '<a href="'.$filter_IP_url.'">'.$Comment->get( 'author_IP' ).'</a>'.$antispam_link.$country;
		}
	}
	else
	{
		return '<span class="dimmed">---</span>';
	}
}


/**
 * Get comment spam karma
 *
 * @param object Comment
 * @return string Comment spam karma OR '---' if user has no permission to moderate this comment
 */
function get_spam_karma( $Comment )
{
	if( check_user_perm( 'comment!CURSTATUS', 'moderate', false, $Comment ) )
	{
		return $Comment->get( 'spam_karma' );
	}
	else
	{
		return '<span class="dimmed">---</span>';
	}
}


/**
 * Get comment status
 *
 * @param object Comment
 * @return string Comment status OR '---' if user has no permission to edit this comment
 */
function get_colored_status( $Comment )
{
	return '<span class="tdComment'.$Comment->get( 'status' ).'">'.$Comment->get( 't_status' ).'</span>';
}


/**
 * Get template for the styled status of comment or item
 *
 * @param string Status value
 * @param string Status title
 * @param string Status class
 * @return string Styled template for status
 */
// DEPRECATED: instead use something like: $Item->format_status( array(	'template' => '<div class="evo_status__banner evo_status__$status$">$status_title$</div>' ) );
function get_styled_status( $status_value, $status_title, $status_class = '' )
{
	return '<div class="floatright">'
		.'<span class="note status_'.$status_value.'">'
		.'<span'.( empty( $status_class ) ? '' : ' class="'.$status_class.'"' ).'>'.format_to_output( $status_title ).'</span>'
		.'</span>'
		.'</div>';
}


/**
 * Get the edit actions for comment
 *
 * @param object Comment
 * @return string The edit actions
 */
function comment_edit_actions( $Comment )
{
	global $admin_url;

	$r = '';
	if( !is_logged_in() )
	{
		return $r;
	}

	$user_has_edit_perm = check_user_perm( 'comment!CURSTATUS', 'edit', false, $Comment );
	$user_has_delete_perm = check_user_perm( 'comment!CURSTATUS', 'delete', false, $Comment );

	if( $user_has_edit_perm || $user_has_delete_perm )
	{ // Display edit and delete button if current user has the rights:
		$redirect_to = rawurlencode( regenerate_url( 'comment_ID,action', 'filter=restore', '', '&' ) );

		if( $user_has_edit_perm )
		{ // Display edit button only if current user can edit comment with current status
			$Comment->get_Item();
			$item_Blog = & $Comment->Item->get_Blog();
			if( $item_Blog->get_setting( 'in_skin_editing' ) && !is_admin_page() )
			{
				$edit_url = url_add_param( $item_Blog->gen_blogurl(), 'disp=edit_comment&amp;c='.$Comment->ID );
			}
			else
			{
				$edit_url = $admin_url.'?ctrl=comments&amp;comment_ID='.$Comment->ID.'&amp;action=edit&amp;redirect_to='.$redirect_to;
			}
			$r .= action_icon( TS_('Edit this comment').'...', 'properties', $edit_url );
		}

		if( $user_has_delete_perm )
		{ // Display delete/recycle button because current user has permission to delete/recycle this comment
			$params = array();
			if( $Comment->status == 'trash' )
			{ // Comment is already in the recycle bin, display delete action and add js confirm
				$title = T_('Delete this comment').'!';
				$params['onclick'] = "return confirm('".TS_('You are about to delete this comment!\\nThis cannot be undone!')."')";
			}
			else
			{ // Comment will be moved into the recycle bin
				$title = T_('Recycle this comment!');
			}

			$r .=  action_icon( $title, 'recycle',
				$admin_url.'?ctrl=comments&amp;comment_ID='.$Comment->ID.'&amp;action=delete&amp;'.url_crumb('comment')
				.'&amp;redirect_to='.$redirect_to, NULL, NULL, NULL, $params );
		}
	}

	return $r;
}

/**
 * End of helper functions block to display Comments results.
 * New ( not display helper ) functions must be created above comments_results function
 */

?>
