text:
<?php

/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
	exit;
}

/**
* Compose private message
* Called from ucp_pm with mode == 'compose'
*/
function compose_pm($id, $mode, $action, $user_folders = array())
{
	global $template, $db, $auth, $user, $cache;
	global $phpbb_root_path, $phpEx, $config;
	global $request, $phpbb_dispatcher, $phpbb_container;

	// Damn php and globals - i know, this is horrible
	// Needed for handle_message_list_actions()
	global $refresh, $submit, $preview;

	include($phpbb_root_path . 'includes/functions_posting.' . $phpEx);
	include($phpbb_root_path . 'includes/functions_display.' . $phpEx);
	include($phpbb_root_path . 'includes/message_parser.' . $phpEx);

	if (!$action)
	{
		$action = 'post';
	}
	add_form_key('ucp_pm_compose');

	// Grab only parameters needed here
	$to_user_id		= request_var('u', 0);
	$to_group_id	= request_var('g', 0);
	$msg_id			= request_var('p', 0);
	$draft_id		= request_var('d', 0);
	$lastclick		= request_var('lastclick', 0);

	// Reply to all triggered (quote/reply)
	$reply_to_all	= request_var('reply_to_all', 0);

	$address_list	= $request->variable('address_list', array('' => array(0 => '')));

	$preview	= (isset($_POST['preview'])) ? true : false;
	$save		= (isset($_POST['save'])) ? true : false;
	$load		= (isset($_POST['load'])) ? true : false;
	$cancel		= (isset($_POST['cancel']) && !isset($_POST['save'])) ? true : false;
	$delete		= (isset($_POST['delete'])) ? true : false;

	$remove_u	= (isset($_REQUEST['remove_u'])) ? true : false;
	$remove_g	= (isset($_REQUEST['remove_g'])) ? true : false;
	$add_to		= (isset($_REQUEST['add_to'])) ? true : false;
	$add_bcc	= (isset($_REQUEST['add_bcc'])) ? true : false;

	$refresh	= isset($_POST['add_file']) || isset($_POST['delete_file']) || $save || $load
		|| $remove_u || $remove_g || $add_to || $add_bcc;
	$submit = $request->is_set_post('post') && !$refresh && !$preview;

	$action		= ($delete && !$preview && !$refresh && $submit) ? 'delete' : $action;
	$select_single = ($config['allow_mass_pm'] && $auth->acl_get('u_masspm')) ? false : true;

	$error = array();
	$current_time = time();

	// Was cancel pressed? If so then redirect to the appropriate page
	if ($cancel || ($current_time - $lastclick < 2 && $submit))
	{
		if ($msg_id)
		{
			redirect(append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&mode=view&action=view_message&p=' . $msg_id));
		}
		redirect(append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm'));
	}

	// Since viewtopic.php language entries are used in several modes,
	// we include the language file here
	$user->add_lang('viewtopic');

	/**
	* Modify the default vars before composing a PM
	*
	* @event core.ucp_pm_compose_modify_data
	* @var	int		msg_id					post_id in the page request
	* @var	int		to_user_id				The id of whom the message is to
	* @var	int		to_group_id				The id of the group the message is to
	* @var	bool	submit					Whether the form has been submitted
	* @var	bool	preview					Whether the user is previewing the PM or not
	* @var	string	action					One of: post, reply, quote, forward, quotepost, edit, delete, smilies
	* @var	bool	delete					Whether the user is deleting the PM
	* @var	int		reply_to_all			Value of reply_to_all request variable.
	* @since 3.1.4-RC1
	*/
	$vars = array(
		'msg_id',
		'to_user_id',
		'to_group_id',
		'submit',
		'preview',
		'action',
		'delete',
		'reply_to_all',
	);
	extract($phpbb_dispatcher->trigger_event('core.ucp_pm_compose_modify_data', compact($vars)));

	// Output PM_TO box if message composing
	if ($action != 'edit')
	{
		// Add groups to PM box
		if ($config['allow_mass_pm'] && $auth->acl_get('u_masspm_group'))
		{
			$sql = 'SELECT g.group_id, g.group_name, g.group_type
				FROM ' . GROUPS_TABLE . ' g';

			if (!$auth->acl_gets('a_group', 'a_groupadd', 'a_groupdel'))
			{
				$sql .= ' LEFT JOIN ' . USER_GROUP_TABLE . ' ug
					ON (
						g.group_id = ug.group_id
						AND ug.user_id = ' . $user->data['user_id'] . '
						AND ug.user_pending = 0
					)
					WHERE (g.group_type <> ' . GROUP_HIDDEN . ' OR ug.user_id = ' . $user->data['user_id'] . ')';
			}

			$sql .= ($auth->acl_gets('a_group', 'a_groupadd', 'a_groupdel')) ? ' WHERE ' : ' AND ';

			$sql .= 'g.group_receive_pm = 1
				ORDER BY g.group_type DESC, g.group_name ASC';
			$result = $db->sql_query($sql);

			$group_options = '';
			while ($row = $db->sql_fetchrow($result))
			{
				$group_options .= '<option' . (($row['group_type'] == GROUP_SPECIAL) ? ' class="sep"' : '') . ' value="' . $row['group_id'] . '">' . (($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['group_name']] : $row['group_name']) . '</option>';
			}
			$db->sql_freeresult($result);
		}

		$template->assign_vars(array(
			'S_SHOW_PM_BOX'		=> true,
			'S_ALLOW_MASS_PM'	=> ($config['allow_mass_pm'] && $auth->acl_get('u_masspm')) ? true : false,
			'S_GROUP_OPTIONS'	=> ($config['allow_mass_pm'] && $auth->acl_get('u_masspm_group')) ? $group_options : '',
			'U_FIND_USERNAME'	=> append_sid("{$phpbb_root_path}memberlist.$phpEx", "mode=searchuser&form=postform&field=username_list&select_single=" . (int) $select_single),
		));
	}

	$sql = '';
	$folder_id = 0;

	// What is all this following SQL for? Well, we need to know
	// some basic information in all cases before we do anything.
	switch ($action)
	{
		case 'post':
			if (!$auth->acl_get('u_sendpm'))
			{
				trigger_error('NO_AUTH_SEND_MESSAGE');
			}
		break;

		case 'reply':
		case 'quote':
		case 'forward':
		case 'quotepost':
			if (!$msg_id)
			{
				trigger_error('NO_MESSAGE');
			}

			if (!$auth->acl_get('u_sendpm'))
			{
				trigger_error('NO_AUTH_SEND_MESSAGE');
			}

			if ($action == 'quotepost')
			{
				$sql = 'SELECT p.post_id as msg_id, p.forum_id, p.post_text as message_text, p.poster_id as author_id, p.post_time as message_time, p.bbcode_bitfield, p.bbcode_uid, p.enable_sig, p.enable_smilies, p.enable_magic_url, t.topic_title as message_subject, u.username as quote_username
					FROM ' . POSTS_TABLE . ' p, ' . TOPICS_TABLE . ' t, ' . USERS_TABLE . " u
					WHERE p.post_id = $msg_id
						AND t.topic_id = p.topic_id
						AND u.user_id = p.poster_id";
			}
			else
			{
				$sql = 'SELECT t.folder_id, p.*, u.username as quote_username
					FROM ' . PRIVMSGS_TO_TABLE . ' t, ' . PRIVMSGS_TABLE . ' p, ' . USERS_TABLE . ' u
					WHERE t.user_id = ' . $user->data['user_id'] . "
						AND p.author_id = u.user_id
						AND t.msg_id = p.msg_id
						AND p.msg_id = $msg_id";
			}
		break;

		case 'edit':
			if (!$msg_id)
			{
				trigger_error('NO_MESSAGE');
			}

			// check for outbox (not read) status, we do not allow editing if one user already having the message
			$sql = 'SELECT p.*, t.folder_id
				FROM ' . PRIVMSGS_TO_TABLE . ' t, ' . PRIVMSGS_TABLE . ' p
				WHERE t.user_id = ' . $user->data['user_id'] . '
					AND t.folder_id = ' . PRIVMSGS_OUTBOX . "
					AND t.msg_id = $msg_id
					AND t.msg_id = p.msg_id";
		break;

		case 'delete':
			if (!$auth->acl_get('u_pm_delete'))
			{
				trigger_error('NO_AUTH_DELETE_MESSAGE');
			}

			if (!$msg_id)
			{
				trigger_error('NO_MESSAGE');
			}

			$sql = 'SELECT msg_id, pm_unread, pm_new, author_id, folder_id
				FROM ' . PRIVMSGS_TO_TABLE . '
				WHERE user_id = ' . $user->data['user_id'] . "
					AND msg_id = $msg_id";
		break;

		case 'smilies':
			generate_smilies('window', 0);
		break;

		default:
			trigger_error('NO_ACTION_MODE', E_USER_ERROR);
		break;
	}

	if ($action == 'forward' && (!$config['forward_pm'] || !$auth->acl_get('u_pm_forward')))
	{
		trigger_error('NO_AUTH_FORWARD_MESSAGE');
	}

	if ($action == 'edit' && !$auth->acl_get('u_pm_edit'))
	{
		trigger_error('NO_AUTH_EDIT_MESSAGE');
	}

	if ($sql)
	{
		/**
		* Alter sql query to get message for user to write the PM
		*
		* @event core.ucp_pm_compose_compose_pm_basic_info_query_before
		* @var	string	sql						String with the query to be executed
		* @var	array	forum_list				List of forums that contain the posts
		* @var	int		visibility_const		Integer with one of the possible ITEM_* constant values
		* @var	int		msg_id					topic_id in the page request
		* @var	int		to_user_id				The id of whom the message is to
		* @var	int		to_group_id				The id of the group whom the message is to
		* @var	bool	submit					Whether the user is sending the PM or not
		* @var	bool	preview					Whether the user is previewing the PM or not
		* @var	string	action					One of: post, reply, quote, forward, quotepost, edit, delete, smilies
		* @var	bool	delete					Whether the user is deleting the PM
		* @var	int		reply_to_all			Value of reply_to_all request variable.
		* @var	string	limit_time_sql			String with the SQL code to limit the time interval of the post (Note: May be empty string)
		* @var	string	sort_order_sql			String with the ORDER BY SQL code used in this query
		* @since 3.1.0-RC5
		*/
		$vars = array(
			'sql',
			'forum_list',
			'visibility_const',
			'msg_id',
			'to_user_id',
			'to_group_id',
			'submit',
			'preview',
			'action',
			'delete',
			'reply_to_all',
			'limit_time_sql',
			'sort_order_sql',
		);
		extract($phpbb_dispatcher->trigger_event('core.ucp_pm_compose_compose_pm_basic_info_query_before', compact($vars)));

		$result = $db->sql_query($sql);
		$post = $db->sql_fetchrow($result);
		$db->sql_freeresult($result);

		if (!$post)
		{
			// If editing it could be the recipient already read the message...
			if ($action == 'edit')
			{
				$sql = 'SELECT p.*, t.folder_id
					FROM ' . PRIVMSGS_TO_TABLE . ' t, ' . PRIVMSGS_TABLE . ' p
					WHERE t.user_id = ' . $user->data['user_id'] . "
						AND t.msg_id = $msg_id
						AND t.msg_id = p.msg_id";
				$result = $db->sql_query($sql);
				$post = $db->sql_fetchrow($result);
				$db->sql_freeresult($result);

				if ($post)
				{
					trigger_error('NO_EDIT_READ_MESSAGE');
				}
			}

			trigger_error('NO_MESSAGE');
		}

		if ($action == 'quotepost')
		{
			if (($post['forum_id'] && !$auth->acl_get('f_read', $post['forum_id'])) || (!$post['forum_id'] && !$auth->acl_getf_global('f_read')))
			{
				trigger_error('NOT_AUTHORISED');
			}

			/**
			* Get the result of querying for the post to be quoted in the pm message
			*
			* @event core.ucp_pm_compose_quotepost_query_after
			* @var	string	sql					The original SQL used in the query
			* @var	array	post				Associative array with the data of the quoted post
			* @var	array	msg_id				The post_id that was searched to get the message for quoting
			* @var	int		visibility_const	Visibility of the quoted post (one of the possible ITEM_* constant values)
			* @var	int		topic_id			Topic ID of the quoted post
			* @var	int		to_user_id			Users the message is sent to
			* @var	int		to_group_id			Groups the message is sent to
			* @var	bool	submit				Whether the user is sending the PM or not
			* @var	bool	preview				Whether the user is previewing the PM or not
			* @var	string	action				One of: post, reply, quote, forward, quotepost, edit, delete, smilies
			* @var	bool	delete				If deleting message
			* @var	int		reply_to_all		Value of reply_to_all request variable.
			* @since 3.1.0-RC5
			*/
			$vars = array(
				'sql',
				'post',
				'msg_id',
				'visibility_const',
				'topic_id',
				'to_user_id',
				'to_group_id',
				'submit',
				'preview',
				'action',
				'delete',
				'reply_to_all',
			);
			extract($phpbb_dispatcher->trigger_event('core.ucp_pm_compose_quotepost_query_after', compact($vars)));

			// Passworded forum?
			if ($post['forum_id'])
			{
				$sql = 'SELECT forum_id, forum_name, forum_password
					FROM ' . FORUMS_TABLE . '
					WHERE forum_id = ' . (int) $post['forum_id'];
				$result = $db->sql_query($sql);
				$forum_data = $db->sql_fetchrow($result);
				$db->sql_freeresult($result);

				if (!empty($forum_data['forum_password']))
				{
					login_forum_box($forum_data);
				}
			}
		}

		$msg_id			= (int) $post['msg_id'];
		$folder_id		= (isset($post['folder_id'])) ? $post['folder_id'] : 0;
		$message_text	= (isset($post['message_text'])) ? $post['message_text'] : '';

		if ((!$post['author_id'] || ($post['author_id'] == ANONYMOUS && $action != 'delete')) && $msg_id)
		{
			trigger_error('NO_AUTHOR');
		}

		if ($action == 'quotepost')
		{
			// Decode text for message display
			decode_message($message_text, $post['bbcode_uid']);
		}

		if ($action != 'delete')
		{
			$enable_urls = $post['enable_magic_url'];
			$enable_sig = (isset($post['enable_sig'])) ? $post['enable_sig'] : 0;

			$message_attachment = (isset($post['message_attachment'])) ? $post['message_attachment'] : 0;
			$message_subject = $post['message_subject'];
			$message_time = $post['message_time'];
			$bbcode_uid = $post['bbcode_uid'];

			$quote_username = (isset($post['quote_username'])) ? $post['quote_username'] : '';
			$icon_id = (isset($post['icon_id'])) ? $post['icon_id'] : 0;

			if (($action == 'reply' || $action == 'quote' || $action == 'quotepost') && !sizeof($address_list) && !$refresh && !$submit && !$preview)
			{
				// Add the original author as the recipient if quoting a post or only replying and not having checked "reply to all"
				if ($action == 'quotepost' || !$reply_to_all)
				{
					$address_list = array('u' => array($post['author_id'] => 'to'));
				}
				else
				{
					// We try to include every previously listed member from the TO Header - Reply to all
					$address_list = rebuild_header(array('to' => $post['to_address']));

					// Add the author (if he is already listed then this is no shame (it will be overwritten))
					$address_list['u'][$post['author_id']] = 'to';

					// Now, make sure the user itself is not listed. ;)
					if (isset($address_list['u'][$user->data['user_id']]))
					{
						unset($address_list['u'][$user->data['user_id']]);
					}
				}
			}
			else if ($action == 'edit' && !sizeof($address_list) && !$refresh && !$submit && !$preview)
			{
				// Rebuild TO and BCC Header
				$address_list = rebuild_header(array('to' => $post['to_address'], 'bcc' => $post['bcc_address']));
			}

			if ($action == 'quotepost')
			{
				$check_value = 0;
			}
			else
			{
				$check_value = (($post['enable_bbcode']+1) << 8) + (($post['enable_smilies']+1) << 4) + (($enable_urls+1) << 2) + (($post['enable_sig']+1) << 1);
			}
		}
	}
	else
	{
		$message_attachment = 0;
		$message_text = $message_subject = '';

		if ($to_user_id && $to_user_id != ANONYMOUS && $action == 'post')
		{
			$address_list['u'][$to_user_id] = 'to';
		}
		else if ($to_group_id && $action == 'post')
		{
			$address_list['g'][$to_group_id] = 'to';
		}
		$check_value = 0;
	}

	if (($to_group_id || isset($address_list['g'])) && (!$config['allow_mass_pm'] || !$auth->acl_get('u_masspm_group')))
	{
		trigger_error('NO_AUTH_GROUP_MESSAGE');
	}

	if ($action == 'edit' && !$refresh && !$preview && !$submit)
	{
		if (!($message_time > time() - ($config['pm_edit_time'] * 60) || !$config['pm_edit_time']))
		{
			trigger_error('CANNOT_EDIT_MESSAGE_TIME');
		}
	}

	if ($action == 'post')
	{
		$template->assign_var('S_NEW_MESSAGE', true);
	}

	if (!isset($icon_id))
	{
		$icon_id = 0;
	}

	$message_parser = new parse_message();
	$plupload = $phpbb_container->get('plupload');
	$message_parser->set_plupload($plupload);

	$message_parser->message = ($action == 'reply') ? '' : $message_text;
	unset($message_text);

	$s_action = append_sid("{$phpbb_root_path}ucp.$phpEx", "i=$id&mode=$mode&action=$action", true, $user->session_id);
	$s_action .= (($folder_id) ? "&f=$folder_id" : '') . (($msg_id) ? "&p=$msg_id" : '');

	// Delete triggered ?
	if ($action == 'delete')
	{
		// Folder id has been determined by the SQL Statement
		// $folder_id = request_var('f', PRIVMSGS_NO_BOX);

		// Do we need to confirm ?
		if (confirm_box(true))
		{
			delete_pm($user->data['user_id'], $msg_id, $folder_id);

			// jump to next message in "history"? nope, not for the moment. But able to be included later.
			$meta_info = append_sid("{$phpbb_root_path}ucp.$phpEx", "i=pm&folder=$folder_id");
			$message = $user->lang['MESSAGE_DELETED'];

			meta_refresh(3, $meta_info);
			$message .= '

' . sprintf($user->lang['RETURN_FOLDER'], '', '');
			trigger_error($message);
		}
		else
		{
			$s_hidden_fields = array(
				'p'			=> $msg_id,
				'f'			=> $folder_id,
				'action'	=> 'delete'
			);

			// "{$phpbb_root_path}ucp.$phpEx?i=pm&mode=compose"
			confirm_box(false, 'DELETE_MESSAGE', build_hidden_fields($s_hidden_fields));
		}

		redirect(append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&mode=view&action=view_message&p=' . $msg_id));
	}

	// Get maximum number of allowed recipients
	$sql = 'SELECT MAX(g.group_max_recipients) as max_recipients
		FROM ' . GROUPS_TABLE . ' g, ' . USER_GROUP_TABLE . ' ug
		WHERE ug.user_id = ' . $user->data['user_id'] . '
			AND ug.user_pending = 0
			AND ug.group_id = g.group_id';
	$result = $db->sql_query($sql);
	$max_recipients = (int) $db->sql_fetchfield('max_recipients');
	$db->sql_freeresult($result);

	$max_recipients = (!$max_recipients) ? $config['pm_max_recipients'] : $max_recipients;

	// If this is a quote/reply "to all"... we may increase the max_recpients to the number of original recipients
	if (($action == 'reply' || $action == 'quote') && $max_recipients && $reply_to_all)
	{
		// We try to include every previously listed member from the TO Header
		$list = rebuild_header(array('to' => $post['to_address']));

		// Can be an empty array too ;)
		$list = (!empty($list['u'])) ? $list['u'] : array();
		$list[$post['author_id']] = 'to';

		if (isset($list[$user->data['user_id']]))
		{
			unset($list[$user->data['user_id']]);
		}

		$max_recipients = ($max_recipients < sizeof($list)) ? sizeof($list) : $max_recipients;

		unset($list);
	}

	// Handle User/Group adding/removing
	handle_message_list_actions($address_list, $error, $remove_u, $remove_g, $add_to, $add_bcc);

	// Check mass pm to group permission
	if ((!$config['allow_mass_pm'] || !$auth->acl_get('u_masspm_group')) && !empty($address_list['g']))
	{
		$address_list = array();
		$error[] = $user->lang['NO_AUTH_GROUP_MESSAGE'];
	}

	// Check mass pm to users permission
	if ((!$config['allow_mass_pm'] || !$auth->acl_get('u_masspm')) && num_recipients($address_list) > 1)
	{
		$address_list = get_recipients($address_list, 1);
		$error[] = $user->lang('TOO_MANY_RECIPIENTS', 1);
	}

	// Check for too many recipients
	if (!empty($address_list['u']) && $max_recipients && sizeof($address_list['u']) > $max_recipients)
	{
		$address_list = get_recipients($address_list, $max_recipients);
		$error[] = $user->lang('TOO_MANY_RECIPIENTS', $max_recipients);
	}

	// Always check if the submitted attachment data is valid and belongs to the user.
	// Further down (especially in submit_post()) we do not check this again.
	$message_parser->get_submitted_attachment_data();

	if ($message_attachment && !$submit && !$refresh && !$preview && $action == 'edit')
	{
		// Do not change to SELECT *
		$sql = 'SELECT attach_id, is_orphan, attach_comment, real_filename, filesize
			FROM ' . ATTACHMENTS_TABLE . "
			WHERE post_msg_id = $msg_id
				AND in_message = 1
				AND is_orphan = 0
			ORDER BY filetime DESC";
		$result = $db->sql_query($sql);
		$message_parser->attachment_data = array_merge($message_parser->attachment_data, $db->sql_fetchrowset($result));
		$db->sql_freeresult($result);
	}

	if (!in_array($action, array('quote', 'edit', 'delete', 'forward')))
	{
		$enable_sig		= ($config['allow_sig'] && $config['allow_sig_pm'] && $auth->acl_get('u_sig') && $user->optionget('attachsig'));
		$enable_smilies	= ($config['allow_smilies'] && $auth->acl_get('u_pm_smilies') && $user->optionget('smilies'));
		$enable_bbcode	= ($config['allow_bbcode'] && $auth->acl_get('u_pm_bbcode') && $user->optionget('bbcode'));
		$enable_urls	= true;
	}

	$enable_magic_url = $drafts = false;

	// User own some drafts?
	if ($auth->acl_get('u_savedrafts') && $action != 'delete')
	{
		$sql = 'SELECT draft_id
			FROM ' . DRAFTS_TABLE . '
			WHERE forum_id = 0
				AND topic_id = 0
				AND user_id = ' . $user->data['user_id'] .
				(($draft_id) ? " AND draft_id <> $draft_id" : '');
		$result = $db->sql_query_limit($sql, 1);
		$row = $db->sql_fetchrow($result);
		$db->sql_freeresult($result);

		if ($row)
		{
			$drafts = true;
		}
	}

	if ($action == 'edit')
	{
		$message_parser->bbcode_uid = $bbcode_uid;
	}

	$bbcode_status	= ($config['allow_bbcode'] && $config['auth_bbcode_pm'] && $auth->acl_get('u_pm_bbcode')) ? true : false;
	$smilies_status	= ($config['allow_smilies'] && $config['auth_smilies_pm'] && $auth->acl_get('u_pm_smilies')) ? true : false;
	$img_status		= ($config['auth_img_pm'] && $auth->acl_get('u_pm_img')) ? true : false;
	$flash_status	= ($config['auth_flash_pm'] && $auth->acl_get('u_pm_flash')) ? true : false;
	$url_status		= ($config['allow_post_links']) ? true : false;

	// Save Draft
	if ($save && $auth->acl_get('u_savedrafts'))
	{
		$subject = utf8_normalize_nfc(request_var('subject', '', true));
		$subject = (!$subject && $action != 'post') ? $user->lang['NEW_MESSAGE'] : $subject;
		$message = utf8_normalize_nfc(request_var('message', '', true));

		if ($subject && $message)
		{
			if (confirm_box(true))
			{
				$sql = 'INSERT INTO ' . DRAFTS_TABLE . ' ' . $db->sql_build_array('INSERT', array(
					'user_id'		=> $user->data['user_id'],
					'topic_id'		=> 0,
					'forum_id'		=> 0,
					'save_time'		=> $current_time,
					'draft_subject'	=> $subject,
					'draft_message'	=> $message
					)
				);
				$db->sql_query($sql);

				$redirect_url = append_sid("{$phpbb_root_path}ucp.$phpEx", "i=pm&mode=$mode");

				meta_refresh(3, $redirect_url);
				$message = $user->lang['DRAFT_SAVED'] . '

' . sprintf($user->lang['RETURN_UCP'], '', '');

				trigger_error($message);
			}
			else
			{
				$s_hidden_fields = build_hidden_fields(array(
					'mode'		=> $mode,
					'action'	=> $action,
					'save'		=> true,
					'subject'	=> $subject,
					'message'	=> $message,
					'u'			=> $to_user_id,
					'g'			=> $to_group_id,
					'p'			=> $msg_id)
				);
				$s_hidden_fields .= build_address_field($address_list);

				confirm_box(false, 'SAVE_DRAFT', $s_hidden_fields);
			}
		}
		else
		{
			if (utf8_clean_string($subject) === '')
			{
				$error[] = $user->lang['EMPTY_MESSAGE_SUBJECT'];
			}

			if (utf8_clean_string($message) === '')
			{
				$error[] = $user->lang['TOO_FEW_CHARS'];
			}
		}

		unset($subject, $message);
	}

	// Load Draft
	if ($draft_id && $auth->acl_get('u_savedrafts'))
	{
		$sql = 'SELECT draft_subject, draft_message
			FROM ' . DRAFTS_TABLE . "
			WHERE draft_id = $draft_id
				AND topic_id = 0
				AND forum_id = 0
				AND user_id = " . $user->data['user_id'];
		$result = $db->sql_query_limit($sql, 1);

		if ($row = $db->sql_fetchrow($result))
		{
			$message_parser->message = $row['draft_message'];
			$message_subject = $row['draft_subject'];

			$template->assign_var('S_DRAFT_LOADED', true);
		}
		else
		{
			$draft_id = 0;
		}
		$db->sql_freeresult($result);
	}

	// Load Drafts
	if ($load && $drafts)
	{
		load_drafts(0, 0, $id, $action, $msg_id);
	}

	if ($submit || $preview || $refresh)
	{
		if (($submit || $preview) && !check_form_key('ucp_pm_compose'))
		{
			$error[] = $user->lang['FORM_INVALID'];
		}
		$subject = utf8_normalize_nfc(request_var('subject', '', true));
		$message_parser->message = utf8_normalize_nfc(request_var('message', '', true));

		$icon_id			= request_var('icon', 0);

		$enable_bbcode 		= (!$bbcode_status || isset($_POST['disable_bbcode'])) ? false : true;
		$enable_smilies		= (!$smilies_status || isset($_POST['disable_smilies'])) ? false : true;
		$enable_urls 		= (isset($_POST['disable_magic_url'])) ? 0 : 1;
		$enable_sig			= (!$config['allow_sig'] ||!$config['allow_sig_pm']) ? false : ((isset($_POST['attach_sig'])) ? true : false);

		/**
		* Modify private message
		*
		* @event core.ucp_pm_compose_modify_parse_before
		* @var	bool	enable_bbcode		Whether or not bbcode is enabled
		* @var	bool	enable_smilies		Whether or not smilies are enabled
		* @var	bool	enable_urls			Whether or not urls are enabled
		* @var	bool	enable_sig			Whether or not signature is enabled
		* @var	string	subject				PM subject text
		* @var	object	message_parser		The message parser object
		* @var	bool	submit				Whether or not the form has been sumitted
		* @var	bool	preview				Whether or not the signature is being previewed
		* @var	array	error				Any error strings
		* @since 3.1.10-RC1
		*/
		$vars = array(
			'enable_bbcode',
			'enable_smilies',
			'enable_urls',
			'enable_sig',
			'subject',
			'message_parser',
			'submit',
			'preview',
			'error',
		);
		extract($phpbb_dispatcher->trigger_event('core.ucp_pm_compose_modify_parse_before', compact($vars)));

		if ($submit)
		{
			$status_switch	= (($enable_bbcode+1) << 8) + (($enable_smilies+1) << 4) + (($enable_urls+1) << 2) + (($enable_sig+1) << 1);
			$status_switch = ($status_switch != $check_value);
		}
		else
		{
			$status_switch = 1;
		}

		// Parse Attachments - before checksum is calculated
		$message_parser->parse_attachments('fileupload', $action, 0, $submit, $preview, $refresh, true);

		if (sizeof($message_parser->warn_msg) && !($remove_u || $remove_g || $add_to || $add_bcc))
		{
			$error[] = implode('
', $message_parser->warn_msg);
			$message_parser->warn_msg = array();
		}

		// Parse message
		$message_parser->parse($enable_bbcode, ($config['allow_post_links']) ? $enable_urls : false, $enable_smilies, $img_status, $flash_status, true, $config['allow_post_links']);

		// On a refresh we do not care about message parsing errors
		if (sizeof($message_parser->warn_msg) && !$refresh)
		{
			$error[] = implode('
', $message_parser->warn_msg);
		}

		if ($action != 'edit' && !$preview && !$refresh && $config['flood_interval'] && !$auth->acl_get('u_ignoreflood'))
		{
			// Flood check
			$last_post_time = $user->data['user_lastpost_time'];

			if ($last_post_time)
			{
				if ($last_post_time && ($current_time - $last_post_time) < intval($config['flood_interval']))
				{
					$error[] = $user->lang['FLOOD_ERROR'];
				}
			}
		}

		// Subject defined
		if ($submit)
		{
			if (utf8_clean_string($subject) === '')
			{
				$error[] = $user->lang['EMPTY_MESSAGE_SUBJECT'];
			}

			if (!sizeof($address_list))
			{
				$error[] = $user->lang['NO_RECIPIENT'];
			}
		}

		// Store message, sync counters
		if (!sizeof($error) && $submit)
		{
			$pm_data = array(
				'msg_id'				=> (int) $msg_id,
				'from_user_id'			=> $user->data['user_id'],
				'from_user_ip'			=> $user->ip,
				'from_username'			=> $user->data['username'],
				'reply_from_root_level'	=> (isset($post['root_level'])) ? (int) $post['root_level'] : 0,
				'reply_from_msg_id'		=> (int) $msg_id,
				'icon_id'				=> (int) $icon_id,
				'enable_sig'			=> (bool) $enable_sig,
				'enable_bbcode'			=> (bool) $enable_bbcode,
				'enable_smilies'		=> (bool) $enable_smilies,
				'enable_urls'			=> (bool) $enable_urls,
				'bbcode_bitfield'		=> $message_parser->bbcode_bitfield,
				'bbcode_uid'			=> $message_parser->bbcode_uid,
				'message'				=> $message_parser->message,
				'attachment_data'		=> $message_parser->attachment_data,
				'filename_data'			=> $message_parser->filename_data,
				'address_list'			=> $address_list
			);

			// ((!$message_subject) ? $subject : $message_subject)
			$msg_id = submit_pm($action, $subject, $pm_data);

			$return_message_url = append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&mode=view&p=' . $msg_id);
			$inbox_folder_url = append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&folder=inbox');
			$outbox_folder_url = append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&folder=outbox');

			$folder_url = '';
			if (($folder_id > 0) && isset($user_folders[$folder_id]))
			{
				$folder_url = append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&folder=' . $folder_id);
			}

			$return_box_url = ($action === 'post' || $action === 'edit') ? $outbox_folder_url : $inbox_folder_url;
			$return_box_lang = ($action === 'post' || $action === 'edit') ? 'PM_OUTBOX' : 'PM_INBOX';

			$save_message = ($action === 'edit') ? $user->lang['MESSAGE_EDITED'] : $user->lang['MESSAGE_STORED'];
			$message = $save_message . '

' . $user->lang('VIEW_PRIVATE_MESSAGE', '', '');

			$last_click_type = 'CLICK_RETURN_FOLDER';
			if ($folder_url)
			{
				$message .= '

' . sprintf($user->lang['CLICK_RETURN_FOLDER'], '', '', $user_folders[$folder_id]['folder_name']);
				$last_click_type = 'CLICK_GOTO_FOLDER';
			}
			$message .= '

' . sprintf($user->lang[$last_click_type], '', '', $user->lang[$return_box_lang]);

			meta_refresh(3, $return_message_url);
			trigger_error($message);
		}

		$message_subject = $subject;
	}

	// Preview
	if (!sizeof($error) && $preview)
	{
		$preview_message = $message_parser->format_display($enable_bbcode, $enable_urls, $enable_smilies, false);

		$preview_signature = $user->data['user_sig'];
		$preview_signature_uid = $user->data['user_sig_bbcode_uid'];
		$preview_signature_bitfield = $user->data['user_sig_bbcode_bitfield'];

		// Signature
		if ($enable_sig && $config['allow_sig'] && $preview_signature)
		{
			$parse_sig = new parse_message($preview_signature);
			$parse_sig->bbcode_uid = $preview_signature_uid;
			$parse_sig->bbcode_bitfield = $preview_signature_bitfield;

			$parse_sig->format_display($config['allow_sig_bbcode'], $config['allow_sig_links'], $config['allow_sig_smilies']);
			$preview_signature = $parse_sig->message;
			unset($parse_sig);
		}
		else
		{
			$preview_signature = '';
		}

		// Attachment Preview
		if (sizeof($message_parser->attachment_data))
		{
			$template->assign_var('S_HAS_ATTACHMENTS', true);

			$update_count = array();
			$attachment_data = $message_parser->attachment_data;

			parse_attachments(false, $preview_message, $attachment_data, $update_count, true);

			foreach ($attachment_data as $i => $attachment)
			{
				$template->assign_block_vars('attachment', array(
					'DISPLAY_ATTACHMENT'	=> $attachment)
				);
			}
			unset($attachment_data);
		}

		$preview_subject = censor_text($subject);

		if (!sizeof($error))
		{
			$template->assign_vars(array(
				'PREVIEW_SUBJECT'		=> $preview_subject,
				'PREVIEW_MESSAGE'		=> $preview_message,
				'PREVIEW_SIGNATURE'		=> $preview_signature,

				'S_DISPLAY_PREVIEW'		=> true)
			);
		}
		unset($message_text);
	}

	// Decode text for message display
	$bbcode_uid = (($action == 'quote' || $action == 'forward') && !$preview && !$refresh && (!sizeof($error) || (sizeof($error) && !$submit))) ? $bbcode_uid : $message_parser->bbcode_uid;

	$message_parser->decode_message($bbcode_uid);

	if (($action == 'quote' || $action == 'quotepost') && !$preview && !$refresh && !$submit)
	{
		if ($action == 'quotepost')
		{
			$post_id = request_var('p', 0);
			if ($config['allow_post_links'])
			{
				$message_link = "[url=" . generate_board_url() . "/viewtopic.$phpEx?p={$post_id}#p{$post_id}]{$user->lang['SUBJECT']}{$user->lang['COLON']} {$message_subject}[/url]\n\n";
			}
			else
			{
				$message_link = $user->lang['SUBJECT'] . $user->lang['COLON'] . ' ' . $message_subject . " (" . generate_board_url() . "/viewtopic.$phpEx?p={$post_id}#p{$post_id})\n\n";
			}
		}
		else
		{
			$message_link = '';
		}
		$message_parser->message = $message_link . '[quote="' . $quote_username . '"]' . censor_text(trim($message_parser->message)) . "[/quote]\n";
	}

	if (($action == 'reply' || $action == 'quote' || $action == 'quotepost') && !$preview && !$refresh)
	{
		$message_subject = ((!preg_match('/^Re:/', $message_subject)) ? 'Re: ' : '') . censor_text($message_subject);
	}

	if ($action == 'forward' && !$preview && !$refresh && !$submit)
	{
		$fwd_to_field = write_pm_addresses(array('to' => $post['to_address']), 0, true);

		if ($config['allow_post_links'])
		{
			$quote_username_text = '[url=' . generate_board_url() . "/memberlist.$phpEx?mode=viewprofile&u={$post['author_id']}]{$quote_username}[/url]";
		}
		else
		{
			$quote_username_text = $quote_username . ' (' . generate_board_url() . "/memberlist.$phpEx?mode=viewprofile&u={$post['author_id']})";
		}

		$forward_text = array();
		$forward_text[] = $user->lang['FWD_ORIGINAL_MESSAGE'];
		$forward_text[] = sprintf($user->lang['FWD_SUBJECT'], censor_text($message_subject));
		$forward_text[] = sprintf($user->lang['FWD_DATE'], $user->format_date($message_time, false, true));
		$forward_text[] = sprintf($user->lang['FWD_FROM'], $quote_username_text);
		$forward_text[] = sprintf($user->lang['FWD_TO'], implode($user->lang['COMMA_SEPARATOR'], $fwd_to_field['to']));

		$message_parser->message = implode("\n", $forward_text) . "\n\n[quote="{$quote_username}"]\n" . censor_text(trim($message_parser->message)) . "\n[/quote]";
		$message_subject = ((!preg_match('/^Fwd:/', $message_subject)) ? 'Fwd: ' : '') . censor_text($message_subject);
	}

	$attachment_data = $message_parser->attachment_data;
	$filename_data = $message_parser->filename_data;
	$message_text = $message_parser->message;

	// MAIN PM PAGE BEGINS HERE

	// Generate smiley listing
	generate_smilies('inline', 0);

	// Generate PM Icons
	$s_pm_icons = false;
	if ($config['enable_pm_icons'])
	{
		$s_pm_icons = posting_gen_topic_icons($action, $icon_id);
	}

	// Generate inline attachment select box
	posting_gen_inline_attachments($attachment_data);

	// Build address list for display
	// array('u' => array($author_id => 'to'));
	if (sizeof($address_list))
	{
		// Get Usernames and Group Names
		$result = array();
		if (!empty($address_list['u']))
		{
			$sql = 'SELECT user_id as id, username as name, user_colour as colour
				FROM ' . USERS_TABLE . '
				WHERE ' . $db->sql_in_set('user_id', array_map('intval', array_keys($address_list['u']))) . '
				ORDER BY username_clean ASC';
			$result['u'] = $db->sql_query($sql);
		}

		if (!empty($address_list['g']))
		{
			$sql = 'SELECT g.group_id AS id, g.group_name AS name, g.group_colour AS colour, g.group_type
				FROM ' . GROUPS_TABLE . ' g';

			if (!$auth->acl_gets('a_group', 'a_groupadd', 'a_groupdel'))
			{
				$sql .= ' LEFT JOIN ' . USER_GROUP_TABLE . ' ug
					ON (
						g.group_id = ug.group_id
						AND ug.user_id = ' . $user->data['user_id'] . '
						AND ug.user_pending = 0
					)
					WHERE (g.group_type <> ' . GROUP_HIDDEN . ' OR ug.user_id = ' . $user->data['user_id'] . ')';
			}

			$sql .= ($auth->acl_gets('a_group', 'a_groupadd', 'a_groupdel')) ? ' WHERE ' : ' AND ';

			$sql .= 'g.group_receive_pm = 1
				AND ' . $db->sql_in_set('g.group_id', array_map('intval', array_keys($address_list['g']))) . '
				ORDER BY g.group_name ASC';

			$result['g'] = $db->sql_query($sql);
		}

		$u = $g = array();
		$_types = array('u', 'g');
		foreach ($_types as $type)
		{
			if (isset($result[$type]) && $result[$type])
			{
				while ($row = $db->sql_fetchrow($result[$type]))
				{
					if ($type == 'g')
					{
						$row['name'] = ($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['name']] : $row['name'];
					}

					${$type}[$row['id']] = array('name' => $row['name'], 'colour' => $row['colour']);
				}
				$db->sql_freeresult($result[$type]);
			}
		}

		// Now Build the address list
		$plain_address_field = '';
		foreach ($address_list as $type => $adr_ary)
		{
			foreach ($adr_ary as $id => $field)
			{
				if (!isset(${$type}[$id]))
				{
					unset($address_list[$type][$id]);
					continue;
				}

				$field = ($field == 'to') ? 'to' : 'bcc';
				$type = ($type == 'u') ? 'u' : 'g';
				$id = (int) $id;

				$tpl_ary = array(
					'IS_GROUP'	=> ($type == 'g') ? true : false,
					'IS_USER'	=> ($type == 'u') ? true : false,
					'UG_ID'		=> $id,
					'NAME'		=> ${$type}[$id]['name'],
					'COLOUR'	=> (${$type}[$id]['colour']) ? '#' . ${$type}[$id]['colour'] : '',
					'TYPE'		=> $type,
				);

				if ($type == 'u')
				{
					$tpl_ary = array_merge($tpl_ary, array(
						'U_VIEW'		=> get_username_string('profile', $id, ${$type}[$id]['name'], ${$type}[$id]['colour']),
						'NAME_FULL'		=> get_username_string('full', $id, ${$type}[$id]['name'], ${$type}[$id]['colour']),
					));
				}
				else
				{
					$tpl_ary = array_merge($tpl_ary, array(
						'U_VIEW'		=> append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=group&g=' . $id),
					));
				}

				$template->assign_block_vars($field . '_recipient', $tpl_ary);
			}
		}
	}

	// Build hidden address list
	$s_hidden_address_field = build_address_field($address_list);

	$bbcode_checked		= (isset($enable_bbcode)) ? !$enable_bbcode : (($config['allow_bbcode'] && $auth->acl_get('u_pm_bbcode')) ? !$user->optionget('bbcode') : 1);
	$smilies_checked	= (isset($enable_smilies)) ? !$enable_smilies : (($config['allow_smilies'] && $auth->acl_get('u_pm_smilies')) ? !$user->optionget('smilies') : 1);
	$urls_checked		= (isset($enable_urls)) ? !$enable_urls : 0;
	$sig_checked		= $enable_sig;

	switch ($action)
	{
		case 'post':
			$page_title = $user->lang['POST_NEW_PM'];
		break;

		case 'quote':
			$page_title = $user->lang['POST_QUOTE_PM'];
		break;

		case 'quotepost':
			$page_title = $user->lang['POST_PM_POST'];
		break;

		case 'reply':
			$page_title = $user->lang['POST_REPLY_PM'];
		break;

		case 'edit':
			$page_title = $user->lang['POST_EDIT_PM'];
		break;

		case 'forward':
			$page_title = $user->lang['POST_FORWARD_PM'];
		break;

		default:
			trigger_error('NO_ACTION_MODE', E_USER_ERROR);
		break;
	}

	$s_hidden_fields = '<input type="hidden" name="lastclick" value="' . $current_time . '" />';
	$s_hidden_fields .= (isset($check_value)) ? '<input type="hidden" name="status_switch" value="' . $check_value . '" />' : '';
	$s_hidden_fields .= ($draft_id || isset($_REQUEST['draft_loaded'])) ? '<input type="hidden" name="draft_loaded" value="' . ((isset($_REQUEST['draft_loaded'])) ? $request->variable('draft_loaded', 0) : $draft_id) . '" />' : '';

	$form_enctype = (@ini_get('file_uploads') == '0' || strtolower(@ini_get('file_uploads')) == 'off' || !$config['allow_pm_attach'] || !$auth->acl_get('u_pm_attach')) ? '' : ' enctype="multipart/form-data"';

	// Start assigning vars for main posting page ...
	$template->assign_vars(array(
		'L_POST_A'					=> $page_title,
		'L_ICON'					=> $user->lang['PM_ICON'],
		'L_MESSAGE_BODY_EXPLAIN'	=> $user->lang('MESSAGE_BODY_EXPLAIN', (int) $config['max_post_chars']),

		'SUBJECT'				=> (isset($message_subject)) ? $message_subject : '',
		'MESSAGE'				=> $message_text,
		'BBCODE_STATUS'			=> ($bbcode_status) ? sprintf($user->lang['BBCODE_IS_ON'], '', '') : sprintf($user->lang['BBCODE_IS_OFF'], '', ''),
		'IMG_STATUS'			=> ($img_status) ? $user->lang['IMAGES_ARE_ON'] : $user->lang['IMAGES_ARE_OFF'],
		'FLASH_STATUS'			=> ($flash_status) ? $user->lang['FLASH_IS_ON'] : $user->lang['FLASH_IS_OFF'],
		'SMILIES_STATUS'		=> ($smilies_status) ? $user->lang['SMILIES_ARE_ON'] : $user->lang['SMILIES_ARE_OFF'],
		'URL_STATUS'			=> ($url_status) ? $user->lang['URL_IS_ON'] : $user->lang['URL_IS_OFF'],
		'MAX_FONT_SIZE'			=> (int) $config['max_post_font_size'],
		'MINI_POST_IMG'			=> $user->img('icon_post_target', $user->lang['PM']),
		'ERROR'					=> (sizeof($error)) ? implode('
', $error) : '',
		'MAX_RECIPIENTS'		=> ($config['allow_mass_pm'] && ($auth->acl_get('u_masspm') || $auth->acl_get('u_masspm_group'))) ? $max_recipients : 0,

		'S_COMPOSE_PM'			=> true,
		'S_EDIT_POST'			=> ($action == 'edit'),
		'S_SHOW_PM_ICONS'		=> $s_pm_icons,
		'S_BBCODE_ALLOWED'		=> ($bbcode_status) ? 1 : 0,
		'S_BBCODE_CHECKED'		=> ($bbcode_checked) ? ' checked="checked"' : '',
		'S_SMILIES_ALLOWED'		=> $smilies_status,
		'S_SMILIES_CHECKED'		=> ($smilies_checked) ? ' checked="checked"' : '',
		'S_SIG_ALLOWED'			=> ($config['allow_sig'] && $config['allow_sig_pm'] && $auth->acl_get('u_sig')),
		'S_SIGNATURE_CHECKED'	=> ($sig_checked) ? ' checked="checked"' : '',
		'S_LINKS_ALLOWED'		=> $url_status,
		'S_MAGIC_URL_CHECKED'	=> ($urls_checked) ? ' checked="checked"' : '',
		'S_SAVE_ALLOWED'		=> ($auth->acl_get('u_savedrafts') && $action != 'edit') ? true : false,
		'S_HAS_DRAFTS'			=> ($auth->acl_get('u_savedrafts') && $drafts),
		'S_FORM_ENCTYPE'		=> $form_enctype,
		'S_ATTACH_DATA'			=> json_encode($message_parser->attachment_data),

		'S_BBCODE_IMG'			=> $img_status,
		'S_BBCODE_FLASH'		=> $flash_status,
		'S_BBCODE_QUOTE'		=> true,
		'S_BBCODE_URL'			=> $url_status,

		'S_POST_ACTION'				=> $s_action,
		'S_HIDDEN_ADDRESS_FIELD'	=> $s_hidden_address_field,
		'S_HIDDEN_FIELDS'			=> $s_hidden_fields,

		'S_CLOSE_PROGRESS_WINDOW'	=> isset($_POST['add_file']),
		'U_PROGRESS_BAR'			=> append_sid("{$phpbb_root_path}posting.$phpEx", 'f=0&mode=popup'),
		'UA_PROGRESS_BAR'			=> addslashes(append_sid("{$phpbb_root_path}posting.$phpEx", 'f=0&mode=popup')),
	));

	// Build custom bbcodes array
	display_custom_bbcodes();

	// Show attachment box for adding attachments if true
	$allowed = ($auth->acl_get('u_pm_attach') && $config['allow_pm_attach'] && $form_enctype);

	if ($allowed)
	{
		$max_files = ($auth->acl_gets('a_', 'm_')) ? 0 : (int) $config['max_attachments_pm'];
		$plupload->configure($cache, $template, $s_action, false, $max_files);
	}

	// Attachment entry
	posting_gen_attachment_entry($attachment_data, $filename_data, $allowed);

	// Message History
	if ($action == 'reply' || $action == 'quote' || $action == 'forward')
	{
		if (message_history($msg_id, $user->data['user_id'], $post, array(), true))
		{
			$template->assign_var('S_DISPLAY_HISTORY', true);
		}
	}
}

/**
* For composing messages, handle list actions
*/
function handle_message_list_actions(&$address_list, &$error, $remove_u, $remove_g, $add_to, $add_bcc)
{
	global $auth, $db, $user;
	global $request;

	// Delete User [TO/BCC]
	if ($remove_u && $request->variable('remove_u', array(0 => '')))
	{
		$remove_user_id = array_keys($request->variable('remove_u', array(0 => '')));

		if (isset($remove_user_id[0]))
		{
			unset($address_list['u'][(int) $remove_user_id[0]]);
		}
	}

	// Delete Group [TO/BCC]
	if ($remove_g && $request->variable('remove_g', array(0 => '')))
	{
		$remove_group_id = array_keys($request->variable('remove_g', array(0 => '')));

		if (isset($remove_group_id[0]))
		{
			unset($address_list['g'][(int) $remove_group_id[0]]);
		}
	}

	// Add Selected Groups
	$group_list = request_var('group_list', array(0));

	// Build usernames to add
	$usernames = request_var('username', '', true);
	$usernames = (empty($usernames)) ? array() : array($usernames);

	$username_list = request_var('username_list', '', true);
	if ($username_list)
	{
		$usernames = array_merge($usernames, explode("\n", $username_list));
	}

	// If add to or add bcc not pressed, users could still have usernames listed they want to add...
	if (!$add_to && !$add_bcc && (sizeof($group_list) || sizeof($usernames)))
	{
		$add_to = true;

		global $refresh, $submit, $preview;

		$refresh = true;
		$submit = false;

		// Preview is only true if there was also a message entered
		if (request_var('message', ''))
		{
			$preview = true;
		}
	}

	// Add User/Group [TO]
	if ($add_to || $add_bcc)
	{
		$type = ($add_to) ? 'to' : 'bcc';

		if (sizeof($group_list))
		{
			foreach ($group_list as $group_id)
			{
				$address_list['g'][$group_id] = $type;
			}
		}

		// User ID's to add...
		$user_id_ary = array();

		// Reveal the correct user_ids
		if (sizeof($usernames))
		{
			$user_id_ary = array();
			user_get_id_name($user_id_ary, $usernames, array(USER_NORMAL, USER_FOUNDER, USER_INACTIVE));

			// If there are users not existing, we will at least print a notice...
			if (!sizeof($user_id_ary))
			{
				$error[] = $user->lang['PM_NO_USERS'];
			}
		}

		// Add Friends if specified
		$friend_list = array_keys($request->variable('add_' . $type, array(0)));
		$user_id_ary = array_merge($user_id_ary, $friend_list);

		foreach ($user_id_ary as $user_id)
		{
			if ($user_id == ANONYMOUS)
			{
				continue;
			}

			$address_list['u'][$user_id] = $type;
		}
	}

	// Check for disallowed recipients
	if (!empty($address_list['u']))
	{
		$can_ignore_allow_pm = $auth->acl_gets('a_', 'm_') || $auth->acl_getf_global('m_');

		// Administrator deactivated users check and we need to check their
		//		PM status (do they want to receive PM's?)
		// 		Only check PM status if not a moderator or admin, since they
		//		are allowed to override this user setting
		$sql = 'SELECT user_id, user_allow_pm
			FROM ' . USERS_TABLE . '
			WHERE ' . $db->sql_in_set('user_id', array_keys($address_list['u'])) . '
				AND (
						(user_type = ' . USER_INACTIVE . '
						AND user_inactive_reason = ' . INACTIVE_MANUAL . ')
						' . ($can_ignore_allow_pm ? '' : ' OR user_allow_pm = 0') . '
					)';

		$result = $db->sql_query($sql);

		$removed_no_pm = $removed_no_permission = false;
		while ($row = $db->sql_fetchrow($result))
		{
			if (!$can_ignore_allow_pm && !$row['user_allow_pm'])
			{
				$removed_no_pm = true;
			}
			else
			{
				$removed_no_permission = true;
			}

			unset($address_list['u'][$row['user_id']]);
		}
		$db->sql_freeresult($result);

		// print a notice about users not being added who do not want to receive pms
		if ($removed_no_pm)
		{
			$error[] = $user->lang['PM_USERS_REMOVED_NO_PM'];
		}

		// print a notice about users not being added who do not have permission to receive PMs
		if ($removed_no_permission)
		{
			$error[] = $user->lang['PM_USERS_REMOVED_NO_PERMISSION'];
		}

		if (!sizeof(array_keys($address_list['u'])))
		{
			return;
		}

		// Check if users have permission to read PMs
		$can_read = $auth->acl_get_list(array_keys($address_list['u']), 'u_readpm');
		$can_read = (empty($can_read) || !isset($can_read[0]['u_readpm'])) ? array() : $can_read[0]['u_readpm'];
		$cannot_read_list = array_diff(array_keys($address_list['u']), $can_read);
		if (!empty($cannot_read_list))
		{
			foreach ($cannot_read_list as $cannot_read)
			{
				unset($address_list['u'][$cannot_read]);
			}

			$error[] = $user->lang['PM_USERS_REMOVED_NO_PERMISSION'];
		}

		// Check if users are banned
		$banned_user_list = phpbb_get_banned_user_ids(array_keys($address_list['u']), false);
		if (!empty($banned_user_list))
		{
			foreach ($banned_user_list as $banned_user)
			{
				unset($address_list['u'][$banned_user]);
			}

			$error[] = $user->lang['PM_USERS_REMOVED_NO_PERMISSION'];
		}
	}
}

/**
* Build the hidden field for the recipients. Needed, as the variable is not read via request_var.
*/
function build_address_field($address_list)
{
	$s_hidden_address_field = '';
	foreach ($address_list as $type => $adr_ary)
	{
		foreach ($adr_ary as $id => $field)
		{
			$s_hidden_address_field .= '<input type="hidden" name="address_list[' . (($type == 'u') ? 'u' : 'g') . '][' . (int) $id . ']" value="' . (($field == 'to') ? 'to' : 'bcc') . '" />';
		}
	}
	return $s_hidden_address_field;
}

/**
* Return number of private message recipients
*/
function num_recipients($address_list)
{
	$num_recipients = 0;

	foreach ($address_list as $field => $adr_ary)
	{
		$num_recipients += sizeof($adr_ary);
	}

	return $num_recipients;
}

/**
* Get number of 'num_recipients' recipients from first position
*/
function get_recipients($address_list, $num_recipients = 1)
{
	$recipient = array();

	$count = 0;
	foreach ($address_list as $field => $adr_ary)
	{
		foreach ($adr_ary as $id => $type)
		{
			if ($count >= $num_recipients)
			{
				break 2;
			}
			$recipient[$field][$id] = $type;
			$count++;
		}
	}

	return $recipient;
}


text:
package tripbook.main;

import java.io.InputStream;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.Process;
import android.preference.PreferenceManager;
import android.support.v4.app.NavUtils;
import android.text.InputType;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class SignUpActivity extends Activity {

	//Animation anim;

	HttpPost httppost;
    StringBuffer buffer;
    HttpResponse response;
    HttpClient httpclient;
    List<NameValuePair> nameValuePairs;
    ProgressDialog dialog = null;
    Animation anim,rot;
	EditText etemail,pass,passagain;
	Button btsignup;
	String empty="";
	String email,pass1,user1;
	InputStream is=null;
	String result=null;
	String line=null;
	int code;
	private Menu optionMenu;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);

		setContentView(R.layout.activity_sign_up_screen);

		/*
		//
		 mGoogleApiClient = new GoogleApiClient.Builder(this)
	        .addConnectionCallbacks((com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks) this)
	        .addOnConnectionFailedListener((com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener) this)
	        .addApi(Plus.API)
	        .addScope(Plus.SCOPE_PLUS_LOGIN)
	        .build();

		*/

		 getActionBar().setDisplayHomeAsUpEnabled(true);
		etemail=(EditText) findViewById(R.id.etEmail);

		pass=(EditText) findViewById(R.id.etPass);
		passagain=(EditText) findViewById(R.id.etPassag);
		rot=AnimationUtils.loadAnimation(getApplicationContext(), R.anim.fade_out);
		//rot.setAnimationListener((AnimationListener)this);

		//
		//* Track whether the sign-in button has been clicked so that we know to resolve
		// * all issues preventing sign-in without waiting.
		 //*/

		/* Store the connection result from onConnectionFailed callbacks so that we can
		 * resolve them when the user clicks sign-in.
		 */

		/* A helper method to resolve the current ConnectionResult error. */

		pass.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub

			}
		});

passagain.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub

			}
		});

		btsignup=(Button) findViewById(R.id.btnSearch);
		btsignup.setOnClickListener(new View.OnClickListener() {
			@Override
            public void onClick(View v) {
				setBoolenSharPref("skippressed",false,getBaseContext());

            	String k1=pass.getText().toString();
            	String k2=etemail.getText().toString();
            	String k3=passagain.getText().toString();
            	if(!haveNetworkConnection())
            	{
            		Toast.makeText(SignUpActivity.this,"OOhh!!No Internet", Toast.LENGTH_SHORT).show();
            	}
            	else if((k1.equals("")||(k2.equals(""))||(k3.equals("")))){
            		//op
            		Toast.makeText(SignUpActivity.this,"Please Enter the Required Fields", Toast.LENGTH_SHORT).show();
            		//op.setTextColor(getResources().getColor(R.color.red));
            		//op.setText("Please Enter Required Field") ;

            	}
            	else if(!emailValidator(etemail.getText().toString()))
            	{
            		Toast.makeText(SignUpActivity.this,"Invalid Email Addres",Toast.LENGTH_SHORT).show();
            	}

            	else if(!k1.equals(k3)){
            		Toast.makeText(SignUpActivity.this,"Password Mismatch",Toast.LENGTH_SHORT).show();
            	}
            	else{
               // dialog = ProgressDialog.show(SignUpActivity.this, "",
                  //      "Please Wait...", true);
                //dialog.setCanceledOnTouchOutside(true);
                 setRefreshState(true);
                 btsignup.setClickable(false);
            		new Thread(new Runnable() {
                        public void run() {
                        	Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                        	login();
                        }
                      }).start();
            }}
        });
    }

	/*private void resolveSignInError() {
		  if (mConnectionResult.hasResolution()) {
		    try {
		      mIntentInProgress = true;
		      startIntentSenderForResult(mConnectionResult.getResolution().getIntentSender(),
		          RC_SIGN_IN, null, 0, 0, 0);
		    } catch (SendIntentException e) {
		      // The intent was canceled before it was sent.  Return to the default
		      // state and attempt to connect to get an updated ConnectionResult.
		      mIntentInProgress = false;
		      mGoogleApiClient.connect();
		    }
		  }}*/

	/*
	protected void onStart() {
	    super.onStart();
	    mGoogleApiClient.connect();
	  }

	protected void onStop() {
	    super.onStop();

	    if (mGoogleApiClient.isConnected()) {
	      mGoogleApiClient.disconnect();
	    }}

	public void onConnectionFailed(ConnectionResult result) {
		  if (!mIntentInProgress && result.hasResolution()) {
		    try {
		      mIntentInProgress = true;
		      startIntentSenderForResult(result.getResolution().getIntentSender(),
		          RC_SIGN_IN, null, 0, 0, 0);
		    } catch (SendIntentException e) {
		      // The intent was canceled before it was sent.  Return to the default
		      // state and attempt to connect to get an updated ConnectionResult.
		      mIntentInProgress = false;
		      mGoogleApiClient.connect();
		    }
		  }
		}

		public void onConnected(Bundle connectionHint) {
		  // We've resolved any connection errors.  mGoogleApiClient can be used to
		  // access Google APIs on behalf of the user.
		}

		protected void onActivityResult(int requestCode, int responseCode, Intent intent) {
			  if (requestCode == RC_SIGN_IN) {
			    mIntentInProgress = false;

			    if (!mGoogleApiClient.isConnecting()) {
			      mGoogleApiClient.connect();
			    }
			  }
			}
		public void onConnectionSuspended(int cause) {
			  mGoogleApiClient.connect();
			}

	*/

    void login(){
        try{

            httpclient=new DefaultHttpClient();
            httppost= new HttpPost("http://192.168.43.207/SEtest-Mobile/signup.php"); // make sure the url is correct.
            //add your data
            nameValuePairs = new ArrayList<NameValuePair>(3);
            // Always use the same variable name for posting i.e the android side variable name and php side variable name should be similar,
            nameValuePairs.add(new BasicNameValuePair("register-username",etemail.getText().toString().trim()));
             // $Edittext_value = $_POST['Edittext_value'];
            nameValuePairs.add(new BasicNameValuePair("register-password",pass.getText().toString().trim()));

            httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
            //Execute HTTP Post Request
            response=httpclient.execute(httppost);
            response.getEntity().consumeContent();
          //m here....
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            final String response = httpclient.execute(httppost, responseHandler).toString().replaceAll("\n","");
            System.out.println("Response : " + response);
            runOnUiThread(new Runnable() {
                public void run() {
                	//op.getOnFocusChangeListener().onFocusChange(welcome, true);

                	etemail.setText("Response: " + response);
                	 setRefreshState(false);
                     btsignup.setClickable(true);
                   // dialog.dismiss();
                }
            });

            if(response.toString().equals("1")){
                runOnUiThread(new Runnable() {
                    public void run() {

                    	String p=pass.getText().toString();
                    	String e=etemail.getText().toString();
                    	setDefaults("namestore",e, getBaseContext());
    	                setDefaults("passstore",p, getBaseContext());
    	                setDefaults("email",e,getBaseContext());
                        Toast.makeText(SignUpActivity.this,"Success", Toast.LENGTH_SHORT).show();
                    }
                });

                startActivity(new Intent(SignUpActivity.this, MainActivity.class));
                SignUpActivity.this.finish();
                overridePendingTransition(R.anim.fade_in,0);
            }else{
                showAlert();
            }

        }catch(Exception e){
          //  dialog.dismiss();
            System.out.println("Exception : " + e.getMessage());
        }
    }
    public void showAlert(){
        SignUpActivity.this.runOnUiThread(new Runnable() {
            public void run() {
                AlertDialog.Builder builder = new AlertDialog.Builder(SignUpActivity.this);
                builder.setTitle("Signup Error.");
                builder.setMessage("Please try again.")
                       .setCancelable(false)
                       .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                           public void onClick(DialogInterface dialog, int id) {
                           }
                       });
                AlertDialog alert = builder.create();
                alert.setCanceledOnTouchOutside(true);

                alert.show();

            }
        });
    }
	  @Override
			public boolean onCreateOptionsMenu(Menu menu) {
			this.optionMenu=menu;
		  getMenuInflater().inflate(R.menu.menu1, menu);
				return true;
			}

			@Override
			public boolean onOptionsItemSelected(MenuItem item) {
				// toggle nav drawer on selecting action bar app icon/title

				// Handle action bar actions click
				switch (item.getItemId()) {
				case R.id.action_refresh:
				{
					etemail.setText(empty);

					pass.setText(empty);
					passagain.setText(empty);
					//pass.setTransformationMethod(PasswordTransformationMethod.getInstance());
					//pass.setTransformationMethod(PasswordTransformationMethod.getInstance());
					//passagain.setTransformationMethod(PasswordTransformationMethod.getInstance());
					btsignup.startAnimation(rot);
					return true;}
				case R.id.action_skip:
					{//setBoolenSharPref("skippressed",true,getBaseContext());
						Intent i=new Intent(SignUpActivity.this,TakeDemo.class);
					startActivity(i);
					overridePendingTransition(R.anim.fade_in,0);
					SignUpActivity.this.finish();
					return true;
					}
				case android.R.id.home:
			        NavUtils.navigateUpFromSameTask(this);
			        return true;

				default:
					return super.onOptionsItemSelected(item);
				}
			}
			  private boolean haveNetworkConnection() {
			        boolean haveConnectedWifi = false;
			        boolean haveConnectedMobile = false;

			        ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
			        NetworkInfo[] netInfo = cm.getAllNetworkInfo();
			        for (NetworkInfo ni : netInfo) {
			            if (ni.getTypeName().equalsIgnoreCase("WIFI"))
			                if (ni.isConnected())
			                    haveConnectedWifi = true;
			            if (ni.getTypeName().equalsIgnoreCase("MOBILE"))
			                if (ni.isConnected())
			                    haveConnectedMobile = true;
			        }
			        return haveConnectedWifi || haveConnectedMobile;
			    }
			  public void setRefreshState(final boolean refreshing)

				{

					if(optionMenu!=null){

						final MenuItem refreshItem=optionMenu.findItem(R.id.action_refresh);
						if(refreshItem!=null)
						{

							if(refreshing){refreshItem.setActionView(R.layout.refresh);}
							else{refreshItem.setActionView(null);}
						}
					}

				}
			  public static void setDefaults(String key, String value, Context context) {
				    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
				    SharedPreferences.Editor editor = prefs.edit();
				    editor.putString(key, value);
				    editor.commit();
				}
				public static String getDefaults(String key, Context context) {
				    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
				    return preferences.getString(key, null);
				}
				public static void setBoolenSharPref(String key,boolean val,Context context){
					SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
				    SharedPreferences.Editor editor = prefs.edit();
				    editor.putBoolean(key,val);
				    editor.commit();

				}
				public static class Validate{
					Validate(){

					}
				public final static boolean isValidEmail(CharSequence target) {
					  return !TextUtils.isEmpty(target) && android.util.Patterns.EMAIL_ADDRESS.matcher(target).matches();
					}
				}

				 public static boolean emailValidator(final String mailAddress) {

			            Pattern pattern;
			            Matcher matcher;

			            final String EMAIL_PATTERN = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";

			            pattern = Pattern.compile(EMAIL_PATTERN);
			            matcher = pattern.matcher(mailAddress);
			            return matcher.matches();

			        }

				 public static boolean usernameValidator(final String userName) {

			            Pattern pattern;
			            Matcher matcher;

			            final String USERNAME_PATTERN ="^(?i)[a-z0-9_-]{4,15}$";

			            pattern = Pattern.compile(USERNAME_PATTERN);
			            matcher = pattern.matcher(userName);
			            return matcher.matches();

			        }
				 public static boolean getBoolSharPref(String key,Context context)
					{
						 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
						/* Boolean tmp = null;
						 tmp = preferences.getBoolean("key", tmp.getBoolean(""));
						 if(tmp==true){return true;}
						 else{
							 return false;
						 }*/

						return preferences.getBoolean(key, false);

					}

				 /*
				@Override
				public void onDisconnected() {
					// TODO Auto-generated method stub

				}*/

}

redpajama_set_name: RedPajamaGithub


text:
local strings = {}
local iters

-- xsplit(str, [sep=" "], [nmax])
-- Break a string into pieces, Python-style (returns iterator)
function strings.xsplit(str, sep, nmax)
    sep = sep or " "
    local pos = 1
    local nmatch = 0
    return function ()
        if pos == -1 then return nil
        elseif nmatch == nmax then
            local r = str:sub(pos)
            pos = -1
            return r, nmatch + 1
        else
            local a, b = str:find(sep, pos, true)
            if a then
                -- next match
                local r = str:sub(pos, a - 1)
                pos = b + 1
                nmatch = nmatch + 1
                return r, nmatch
            else
                -- no more matches
                local r
                if pos <= #str then r = str:sub(pos) end
                pos = -1
                return r, nmatch + 1
            end
        end
    end
end

-- split(str, [sep=" "], [nmax])
-- Break a string into pieces, Python-style (returns table)
function strings.split(str, sep, nmax)
    if not iters then iters = require "charon.iters"; end
    return iters.totable(strings.xsplit(str, sep, nmax))
end

-- True if s starts with s2
function strings.starts_with(s, s2)
    return s:sub(1, #s2) == s2
end

-- True if s ends with s2
function strings.ends_with(s, s2)
    return s:sub(-#s2) == s2
end

----- IO -----

function strings.printf(fmt, ...)
    return strings.fprintf(io.stdout, fmt, ...)
end

function strings.printfln(fmt, ...)
    return strings.fprintf(io.stdout, fmt.."\n", ...)
end

function strings.fprintf(f, fmt, ...)
    f:write(string.format(fmt, ...))
end

function strings.errorf(fmt, ...)
    return error(string.format(fmt, ...))
end

return strings


text:
package com.mysql.proxy.codes;

import java.nio.ByteBuffer;

import org.apache.log4j.Logger;

import com.alibaba.fastjson.JSON;
import com.mysql.proxy.util.NettyUtil;
import com.seaboat.mysql.protocol.AuthPacket;
import com.seaboat.mysql.protocol.HandshakePacket;
import com.seaboat.mysql.protocol.MysqlMessage;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

public class MySqlPackEnCoder  extends MessageToByteEncoder {

    private static final Logger log  = Logger.getLogger(MySqlPackEnCoder.class);

    @Override
    protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
        if(msg instanceof MysqlMessage){
            log.debug("接受到命令报文");

        }else if(msg instanceof AuthPacket){
            log.debug("认证报文");
            log.debug(JSON.toJSONString((AuthPacket)msg));
            AuthPacket hs=(AuthPacket)msg;
            ByteBuffer buffer = ByteBuffer.allocate(256);
            hs.write(buffer);
            buffer.flip();
            ctx.writeAndFlush(NettyUtil.toByteBuf(buffer));
        }else if(msg instanceof HandshakePacket){
            HandshakePacket hs = (HandshakePacket)msg;
            ByteBuffer buffer = ByteBuffer.allocate(256);
            hs.write(buffer);
            buffer.flip();
            ctx.writeAndFlush(NettyUtil.toByteBuf(buffer));

    }else if(msg instanceof byte[]){
            log.debug("字节报文");
            byte[] data=(byte[])msg;
            log.debug("报文:"+new String(data));
            out.writeBytes(data);
        }else if(msg instanceof ByteBuf){
            ByteBuf buf=(ByteBuf)msg;
            buf.resetReaderIndex();
            log.debug("ByteBuf报文");
            out.writeBytes(buf);
        }else{
            log.debug("不认识的类型"+msg.getClass().getName());

        }
    }

}


text:
/*
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
/*
 * This code was generated by https://github.com/googleapis/google-api-java-client-services/
 * Modify at your own risk.
 */

package com.google.api.services.firestore.v1.model;

/**
 * A reference to a field, such as `max(messages.time) as max_time`.
 *
 *
 This is the Java data model class that specifies how to parse/serialize into the JSON that is
 * transmitted over HTTP when working with the Cloud Firestore API. For a detailed explanation see:
 * https://developers.google.com/api-client-library/java/google-http-java-client/json
 *

 *
 * @author Google, Inc.
 */
@SuppressWarnings("javadoc")
public final class FieldReference extends com.google.api.client.json.GenericJson {

  /**
   * The value may be {@code null}.
   */
  @com.google.api.client.util.Key
  private java.lang.String fieldPath;

  /**
   * @return value or {@code null} for none
   */
  public java.lang.String getFieldPath() {
    return fieldPath;
  }

  /**
   * @param fieldPath fieldPath or {@code null} for none
   */
  public FieldReference setFieldPath(java.lang.String fieldPath) {
    this.fieldPath = fieldPath;
    return this;
  }

  @Override
  public FieldReference set(String fieldName, Object value) {
    return (FieldReference) super.set(fieldName, value);
  }

  @Override
  public FieldReference clone() {
    return (FieldReference) super.clone();
  }

}


text:
package mblog.core.persist.utils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;

import mblog.base.lang.Consts;
import mblog.core.data.AccountProfile;
import mblog.core.data.Attach;
import mblog.core.data.AuthMenu;
import mblog.core.data.CardTransactionRecord;
import mblog.core.data.Comment;
import mblog.core.data.Favor;
import mblog.core.data.Feeds;
import mblog.core.data.FriendLink;
import mblog.core.data.Goods;
import mblog.core.data.GoodsOther;
import mblog.core.data.Group;
import mblog.core.data.MyPOS;
import mblog.core.data.Notify;
import mblog.core.data.Point;
import mblog.core.data.PointDetail;
import mblog.core.data.PointRule;
import mblog.core.data.Post;
import mblog.core.data.Role;
import mblog.core.data.Tag;
import mblog.core.data.User;
import mblog.core.persist.entity.AttachPO;
import mblog.core.persist.entity.AuthMenuPO;
import mblog.core.persist.entity.CardTransactionRecordPO;
import mblog.core.persist.entity.CommentPO;
import mblog.core.persist.entity.FavorPO;
import mblog.core.persist.entity.FeedsPO;
import mblog.core.persist.entity.FriendLinkPO;
import mblog.core.persist.entity.GoodsOtherPO;
import mblog.core.persist.entity.GoodsPO;
import mblog.core.persist.entity.GroupPO;
import mblog.core.persist.entity.MyPOSPO;
import mblog.core.persist.entity.NotifyPO;
import mblog.core.persist.entity.PointDetailPO;
import mblog.core.persist.entity.PointPO;
import mblog.core.persist.entity.PointRulePO;
import mblog.core.persist.entity.PostPO;
import mblog.core.persist.entity.RolePO;
import mblog.core.persist.entity.TagPO;
import mblog.core.persist.entity.UserPO;

/**
 * @author langhsu
 *
 */
public class BeanMapUtils {
	public static String[] USER_IGNORE = new String[]{"password", "extend", "roles"};

	public static String[] POST_IGNORE_LIST = new String[]{"markdown", "content"};

	public static User copy(UserPO po, int level) {
		if (po == null) {
			return null;
		}
		User ret = new User();
		BeanUtils.copyProperties(po, ret, USER_IGNORE);

		if (level > 0) {
			List<RolePO> rolePOs = po.getRoles();
			List<Role> roles = new ArrayList<Role>();
			for(RolePO rolePo :rolePOs){
				Role role = copy(rolePo);
				roles.add(role);
			}
			ret.setRoles(roles);
		}
		return ret;
	}

	public static AccountProfile copyPassport(UserPO po) {
		AccountProfile passport = new AccountProfile(po.getId(), po.getUsername());
		passport.setName(po.getName());
		passport.setEmail(po.getEmail());
		passport.setAvatar(po.getAvatar());
		passport.setLastLogin(po.getLastLogin());
		passport.setStatus(po.getStatus());
		passport.setActiveEmail(po.getActiveEmail());

		List<AuthMenu> menus = new ArrayList<AuthMenu>();
		List<RolePO> rolePOs = po.getRoles();
		List<Role> roles = new ArrayList<Role>();
		for(RolePO rolePo :rolePOs){
			Role role = copy(rolePo);
			roles.add(role);
		}
		for(Role role : roles){
			List<AuthMenu> authMenus = role.getAuthMenus();
			menus.addAll(authMenus);
		}
		passport.setAuthMenus(menus);
		return passport;
	}

	public static Comment copy(CommentPO po) {
		Comment ret = new Comment();
		BeanUtils.copyProperties(po, ret);
		return ret;
	}

	public static Post copy(PostPO po, int level) {
		Post d = new Post();
		if (level > 0) {
			BeanUtils.copyProperties(po, d);
		} else {
			BeanUtils.copyProperties(po, d, POST_IGNORE_LIST);
		}
		return d;
	}

	public static Attach copy(AttachPO po) {
		Attach ret = new Attach();
		BeanUtils.copyProperties(po, ret);
		return ret;
	}

	public static Tag copy(TagPO po) {
		if (po == null) {
			return null;
		}
		Tag ret = new Tag();
		BeanUtils.copyProperties(po, ret);
		return ret;
	}

	public static List<Tag> convertTags(long postId, String tags) {
		if (StringUtils.isBlank(tags)) {
			return Collections.emptyList();
		}
		List<Tag> ret = new ArrayList<>();
		String[] ts = StringUtils.split(tags, Consts.SEPARATOR);

		for (String t : ts) {
			Tag tag = new Tag();
			tag.setName(t);
			tag.setLastPostId(postId);
			tag.setPosts(1);
			ret.add(tag);
		}

		return ret;
	}

	public static Group copy(GroupPO po) {
		Group r = new Group();
		BeanUtils.copyProperties(po, r);
		return r;
	}

	public static AuthMenu copy(AuthMenuPO po,String... ignore){
		AuthMenu am = new AuthMenu();
		List<AuthMenu> children = new ArrayList<AuthMenu>();
		BeanUtils.copyProperties(po, am,"parent","children");
		List<String> ignoreList = Arrays.asList(ignore);
		if(po.getParent()!=null && !ignoreList.contains("parent")){
			am.setParent(BeanMapUtils.copy(po.getParent()));
		}
		for(AuthMenuPO child :po.getChildren()){
			AuthMenu childAuthMenu = BeanMapUtils.copy(child,"parent");
			childAuthMenu.setParent(am);
			children.add(childAuthMenu);
		}
		am.setChildren(children);
		return am;
	}

	public static Role copy(RolePO po){
		Role r = new Role();
		BeanUtils.copyProperties(po, r,"users","authMenus");
		List<AuthMenu> authMenus = new ArrayList<AuthMenu>();
		for(AuthMenuPO authMenuPO : po.getAuthMenus()){
			AuthMenu authMenu = BeanMapUtils.copy(authMenuPO);
			authMenus.add(authMenu);
		}
		r.setAuthMenus(authMenus);
		return r;
	}

	public static Feeds copy(FeedsPO po) {
		Feeds ret = new Feeds();
		BeanUtils.copyProperties(po, ret);
		return ret;
	}

	public static Notify copy(NotifyPO po) {
		Notify ret = new Notify();
		BeanUtils.copyProperties(po, ret);
		return ret;
	}

	public static Favor copy(FavorPO po) {
		Favor ret = new Favor();
		BeanUtils.copyProperties(po, ret);
		return ret;
	}

	public static FriendLink copy(FriendLinkPO po) {
		FriendLink ret = new FriendLink();
		BeanUtils.copyProperties(po, ret);
		return ret;
	}

	public static CardTransactionRecord copy(CardTransactionRecordPO po) {
		CardTransactionRecord ret = new CardTransactionRecord();
		BeanUtils.copyProperties(po, ret);
		return ret;
	}

	public static MyPOS copy(MyPOSPO po) {
		MyPOS ret = new MyPOS();
		BeanUtils.copyProperties(po, ret);
		return ret;
	}

	public static Point copy(PointPO po) {
		Point ret = new Point();
		BeanUtils.copyProperties(po, ret);
		return ret;
	}

	public static PointDetail copy(PointDetailPO po) {
		PointDetail ret = new PointDetail();
		BeanUtils.copyProperties(po, ret);
		return ret;
	}

	public static PointRule copy(PointRulePO po) {
		PointRule ret = new PointRule();
		BeanUtils.copyProperties(po, ret);
		return ret;
	}

	public static Goods copy(GoodsPO po) {
		Goods ret = new Goods();
		BeanUtils.copyProperties(po, ret);
		return ret;
	}

	public static GoodsOther copy(GoodsOtherPO po) {
		GoodsOther ret = new GoodsOther();
		BeanUtils.copyProperties(po, ret);
		return ret;
	}

}


text:
from __future__ import absolute_import

import pytest

import bokeh.util.callback_manager as cbm

class _Good(object):
    def __init__(self):
        self.last_name = None
        self.last_old = None
        self.last_new = None
    def __call__(self, name, old, new):
        self.method(name, old, new)
    def method(self, name, old, new):
        self.last_name = name
        self.last_old = old
        self.last_new = new

class _Bad(object):
    def __call__(self, x, y): pass
    def method(self, x, y): pass

def _good(x,y,z): pass
def _bad(x,y): pass

def test_creation():
    m = cbm.CallbackManager()
    assert len(m._callbacks) == 0

def test_on_change_good_method():
    m = cbm.CallbackManager()
    good = _Good()
    m.on_change('foo', good.method)
    assert len(m._callbacks) == 1
    assert m._callbacks['foo'] == [good.method]

def test_on_change_good_functor():
    m = cbm.CallbackManager()
    good = _Good()
    m.on_change('foo', good)
    assert len(m._callbacks) == 1
    assert m._callbacks['foo'] == [good]

def test_on_change_good_function():
    m = cbm.CallbackManager()
    m.on_change('foo', _good)
    assert len(m._callbacks) == 1
    assert m._callbacks['foo'] == [_good]

def test_on_change_good_lambda():
    m = cbm.CallbackManager()
    good = lambda x, y, z: x
    m.on_change('foo', good)
    assert len(m._callbacks) == 1
    assert m._callbacks['foo'] == [good]

def test_on_change_good_closure():
    def good(x,y,z): pass
    m = cbm.CallbackManager()
    m.on_change('foo', good)
    assert len(m._callbacks) == 1
    assert len(m._callbacks['foo']) == 1

def test_on_change_bad_method():
    m = cbm.CallbackManager()
    bad = _Bad()
    with pytest.raises(ValueError):
        m.on_change('foo', bad.method)
    assert len(m._callbacks) == 1
    assert len(m._callbacks['foo']) == 0

def test_on_change_bad_functor():
    m = cbm.CallbackManager()
    bad = _Bad()
    with pytest.raises(ValueError):
        m.on_change('foo', bad)
    assert len(m._callbacks) == 1
    assert len(m._callbacks['foo']) == 0

def test_on_change_bad_function():
    m = cbm.CallbackManager()
    with pytest.raises(ValueError):
        m.on_change('foo', _bad)
    assert len(m._callbacks) == 1
    assert len(m._callbacks['foo']) == 0

def test_on_change_bad_lambda():
    m = cbm.CallbackManager()
    with pytest.raises(ValueError):
        m.on_change('foo', lambda x, y: x)
    assert len(m._callbacks) == 1
    assert len(m._callbacks['foo']) == 0

def test_on_change_bad_closure():
    def bad(x,y): pass
    m = cbm.CallbackManager()
    with pytest.raises(ValueError):
        m.on_change('foo', bad)
    assert len(m._callbacks) == 1
    assert len(m._callbacks['foo']) == 0

def test_on_change_same_attr_twice_multiple_calls():
    def good1(x,y,z): pass
    def good2(x,y,z): pass
    m1 = cbm.CallbackManager()
    m1.on_change('foo', good1)
    m1.on_change('foo', good2)
    assert len(m1._callbacks) == 1
    assert m1._callbacks['foo'] == [good1, good2]

def test_on_change_same_attr_twice_one_call():
    def good1(x,y,z): pass
    def good2(x,y,z): pass
    m2 = cbm.CallbackManager()
    m2.on_change('foo', good1, good2)
    assert len(m2._callbacks) == 1
    assert m2._callbacks['foo'] == [good1, good2]

def test_on_change_different_attrs():
    def good1(x,y,z): pass
    def good2(x,y,z): pass
    m1 = cbm.CallbackManager()
    m1.on_change('foo', good1)
    m1.on_change('bar', good2)
    assert len(m1._callbacks) == 2
    assert m1._callbacks['foo'] == [good1]
    assert m1._callbacks['bar'] == [good2]

def test_trigger():
    m = cbm.CallbackManager()
    good = _Good()
    m.on_change('foo', good.method)
    m.trigger('foo', 42, 43)
    assert good.last_name == 'foo'
    assert good.last_old == 42
    assert good.last_new == 43

def test_trigger_with_two_callbacks():
    m = cbm.CallbackManager()
    good1 = _Good()
    good2 = _Good()
    m.on_change('foo', good1.method)
    m.on_change('foo', good2.method)
    m.trigger('foo', 42, 43)
    assert good1.last_name == 'foo'
    assert good1.last_old == 42
    assert good1.last_new == 43
    assert good2.last_name == 'foo'
    assert good2.last_old == 42
    assert good2.last_new == 43


text:
import pytest

# LOCAL
from astropy.cosmology import Cosmology
from astropy.cosmology.core import _COSMOLOGY_CLASSES
from astropy.cosmology.io.table import from_table, to_table
from astropy.table import QTable, Table, vstack

from .base import ToFromDirectTestBase, ToFromTestMixinBase

###############################################################################

class ToFromTableTestMixin(ToFromTestMixinBase):
    """
    Tests for a Cosmology[To/From]Format with ``format="astropy.table"``.
    This class will not be directly called by :mod:`pytest` since its name does
    not begin with ``Test``. To activate the contained tests this class must
    be inherited in a subclass. Subclasses must define a :func:`pytest.fixture`
    ``cosmo`` that returns/yields an instance of a |Cosmology|.
    See ``TestCosmology`` for an example.
    """

    def test_to_table_bad_index(self, from_format, to_format):
        """Test if argument ``index`` is incorrect"""
        tbl = to_format("astropy.table")

        # single-row table and has a non-0/None index
        with pytest.raises(IndexError, match="index 2 out of range"):
            from_format(tbl, index=2, format="astropy.table")

        # string index where doesn't match
        with pytest.raises(KeyError, match="No matches found for key"):
            from_format(tbl, index="row 0", format="astropy.table")

    # -----------------------

    def test_to_table_failed_cls(self, to_format):
        """Test failed table type."""
        with pytest.raises(TypeError, match="'cls' must be"):
            to_format("astropy.table", cls=list)

    @pytest.mark.parametrize("tbl_cls", [QTable, Table])
    def test_to_table_cls(self, to_format, tbl_cls):
        tbl = to_format("astropy.table", cls=tbl_cls)
        assert isinstance(tbl, tbl_cls)  # test type

    # -----------------------

    @pytest.mark.parametrize("in_meta", [True, False])
    def test_to_table_in_meta(self, cosmo_cls, to_format, in_meta):
        """Test where the cosmology class is placed."""
        tbl = to_format("astropy.table", cosmology_in_meta=in_meta)

        # if it's in metadata, it's not a column. And vice versa.
        if in_meta:
            assert tbl.meta["cosmology"] == cosmo_cls.__qualname__
            assert "cosmology" not in tbl.colnames  # not also a column
        else:
            assert tbl["cosmology"][0] == cosmo_cls.__qualname__
            assert "cosmology" not in tbl.meta

    # -----------------------

    def test_to_table(self, cosmo_cls, cosmo, to_format):
        """Test cosmology -> astropy.table."""
        tbl = to_format("astropy.table")

        # Test properties of Table.
        assert isinstance(tbl, QTable)
        assert tbl.meta["cosmology"] == cosmo_cls.__qualname__
        assert tbl["name"] == cosmo.name
        assert tbl.indices  # indexed

        # Test each Parameter column has expected information.
        for n in cosmo.__parameters__:
            P = getattr(cosmo_cls, n)  # Parameter
            col = tbl[n]  # Column

            # Compare the two
            assert col.info.name == P.name
            assert col.info.description == P.__doc__
            assert col.info.meta == (cosmo.meta.get(n) or {})

    # -----------------------

    def test_from_not_table(self, cosmo, from_format):
        """Test not passing a Table to the Table parser."""
        with pytest.raises((TypeError, ValueError)):
            from_format("NOT A TABLE", format="astropy.table")

    def test_tofrom_table_instance(self, cosmo_cls, cosmo, from_format, to_format):
        """Test cosmology -> astropy.table -> cosmology."""
        tbl = to_format("astropy.table")

        # add information
        tbl["mismatching"] = "will error"

        # tests are different if the last argument is a **kwarg
        if tuple(cosmo._init_signature.parameters.values())[-1].kind == 4:
            got = from_format(tbl, format="astropy.table")

            assert got.__class__ is cosmo_cls
            assert got.name == cosmo.name
            assert "mismatching" not in got.meta

            return  # don't continue testing

        # read with mismatching parameters errors
        with pytest.raises(TypeError, match="there are unused parameters"):
            from_format(tbl, format="astropy.table")

        # unless mismatched are moved to meta
        got = from_format(tbl, format="astropy.table", move_to_meta=True)
        assert got == cosmo
        assert got.meta["mismatching"] == "will error"

        # it won't error if everything matches up
        tbl.remove_column("mismatching")
        got = from_format(tbl, format="astropy.table")
        assert got == cosmo

        # and it will also work if the cosmology is a class
        # Note this is not the default output of ``to_format``.
        tbl.meta["cosmology"] = _COSMOLOGY_CLASSES[tbl.meta["cosmology"]]
        got = from_format(tbl, format="astropy.table")
        assert got == cosmo

        # also it auto-identifies 'format'
        got = from_format(tbl)
        assert got == cosmo

    def test_fromformat_table_subclass_partial_info(
        self, cosmo_cls, cosmo, from_format, to_format
    ):
        """
        Test writing from an instance and reading from that class.
        This works with missing information.
        """
        # test to_format
        tbl = to_format("astropy.table")
        assert isinstance(tbl, QTable)

        # partial information
        tbl.meta.pop("cosmology", None)
        del tbl["Tcmb0"]

        # read with the same class that wrote fills in the missing info with
        # the default value
        got = cosmo_cls.from_format(tbl, format="astropy.table")
        got2 = from_format(tbl, format="astropy.table", cosmology=cosmo_cls)
        got3 = from_format(
            tbl, format="astropy.table", cosmology=cosmo_cls.__qualname__
        )

        assert (got == got2) and (got2 == got3)  # internal consistency

        # not equal, because Tcmb0 is changed, which also changes m_nu
        assert got != cosmo
        assert got.Tcmb0 == cosmo_cls._init_signature.parameters["Tcmb0"].default
        assert got.clone(name=cosmo.name, Tcmb0=cosmo.Tcmb0, m_nu=cosmo.m_nu) == cosmo
        # but the metadata is the same
        assert got.meta == cosmo.meta

    @pytest.mark.parametrize("add_index", [True, False])
    def test_tofrom_table_mutlirow(self, cosmo_cls, cosmo, from_format, add_index):
        """Test if table has multiple rows."""
        # ------------
        # To Table

        cosmo1 = cosmo.clone(name="row 0")
        cosmo2 = cosmo.clone(name="row 2")
        tbl = vstack(
            [c.to_format("astropy.table") for c in (cosmo1, cosmo, cosmo2)],
            metadata_conflicts="silent",
        )

        assert isinstance(tbl, QTable)
        assert tbl.meta["cosmology"] == cosmo_cls.__qualname__
        assert tbl[1]["name"] == cosmo.name

        # whether to add an index. `from_format` can work with or without.
        if add_index:
            tbl.add_index("name", unique=True)

        # ------------
        # From Table

        # it will error on a multi-row table
        with pytest.raises(ValueError, match="need to select a specific row"):
            from_format(tbl, format="astropy.table")

        # unless the index argument is provided
        got = from_format(tbl, index=1, format="astropy.table")
        assert got == cosmo

        # the index can be a string
        got = from_format(tbl, index=cosmo.name, format="astropy.table")
        assert got == cosmo

        # when there's more than one cosmology found
        tbls = vstack([tbl, tbl], metadata_conflicts="silent")
        with pytest.raises(ValueError, match="more than one"):
            from_format(tbls, index=cosmo.name, format="astropy.table")

    @pytest.mark.parametrize("format", [True, False, None, "astropy.table"])
    def test_is_equivalent_to_table(self, cosmo, to_format, format):
        """Test :meth:`astropy.cosmology.Cosmology.is_equivalent`.

        This test checks that Cosmology equivalency can be extended to any
        Python object that can be converted to a Cosmology -- in this case
        a |Table|.
        """
        obj = to_format("astropy.table")
        assert not isinstance(obj, Cosmology)

        is_equiv = cosmo.is_equivalent(obj, format=format)
        assert is_equiv is (True if format is not False else False)

class TestToFromTable(ToFromDirectTestBase, ToFromTableTestMixin):
    """Directly test ``to/from_table``."""

    def setup_class(self):
        self.functions = {"to": to_table, "from": from_table}


text:
namespace blink {

class PositionTest : public EditingTestBase {};

TEST_F(PositionTest, NodeAsRangeLastNodeNull) {
  EXPECT_EQ(nullptr, Position().nodeAsRangeLastNode());
  EXPECT_EQ(nullptr, PositionInFlatTree().nodeAsRangeLastNode());
}

TEST_F(PositionTest, editingPositionOfWithEditingIgnoresContent) {
  const char* bodyContent =
      "<textarea id=textarea></textarea>12";
  setBodyContent(bodyContent);
  Node* textarea = document().getElementById("textarea");

  EXPECT_EQ(Position::beforeNode(textarea),
            Position::editingPositionOf(textarea, 0));
  EXPECT_EQ(Position::afterNode(textarea),
            Position::editingPositionOf(textarea, 1));
  EXPECT_EQ(Position::afterNode(textarea),
            Position::editingPositionOf(textarea, 2));

  // Change DOM tree to
  // <textarea id=textarea>12</textarea>
  Node* child1 = document().getElementById("child1");
  Node* child2 = document().getElementById("child2");
  textarea->appendChild(child1);
  textarea->appendChild(child2);

  EXPECT_EQ(Position::beforeNode(textarea),
            Position::editingPositionOf(textarea, 0));
  EXPECT_EQ(Position::afterNode(textarea),
            Position::editingPositionOf(textarea, 1));
  EXPECT_EQ(Position::afterNode(textarea),
            Position::editingPositionOf(textarea, 2));
  EXPECT_EQ(Position::afterNode(textarea),
            Position::editingPositionOf(textarea, 3));
}

TEST_F(PositionTest, NodeAsRangeLastNode) {
  const char* bodyContent = "
11

33
";
  setBodyContent(bodyContent);
  Node* p1 = document().getElementById("p1");
  Node* p2 = document().getElementById("p2");
  Node* p3 = document().getElementById("p3");
  Node* body = EditingStrategy::parent(*p1);
  Node* t1 = EditingStrategy::firstChild(*p1);
  Node* t3 = EditingStrategy::firstChild(*p3);

  EXPECT_EQ(body, Position::inParentBeforeNode(*p1).nodeAsRangeLastNode());
  EXPECT_EQ(t1, Position::inParentBeforeNode(*p2).nodeAsRangeLastNode());
  EXPECT_EQ(p2, Position::inParentBeforeNode(*p3).nodeAsRangeLastNode());
  EXPECT_EQ(t1, Position::inParentAfterNode(*p1).nodeAsRangeLastNode());
  EXPECT_EQ(p2, Position::inParentAfterNode(*p2).nodeAsRangeLastNode());
  EXPECT_EQ(t3, Position::inParentAfterNode(*p3).nodeAsRangeLastNode());
  EXPECT_EQ(t3, Position::afterNode(p3).nodeAsRangeLastNode());

  EXPECT_EQ(body,
            PositionInFlatTree::inParentBeforeNode(*p1).nodeAsRangeLastNode());
  EXPECT_EQ(t1,
            PositionInFlatTree::inParentBeforeNode(*p2).nodeAsRangeLastNode());
  EXPECT_EQ(p2,
            PositionInFlatTree::inParentBeforeNode(*p3).nodeAsRangeLastNode());
  EXPECT_EQ(t1,
            PositionInFlatTree::inParentAfterNode(*p1).nodeAsRangeLastNode());
  EXPECT_EQ(p2,
            PositionInFlatTree::inParentAfterNode(*p2).nodeAsRangeLastNode());
  EXPECT_EQ(t3,
            PositionInFlatTree::inParentAfterNode(*p3).nodeAsRangeLastNode());
  EXPECT_EQ(t3, PositionInFlatTree::afterNode(p3).nodeAsRangeLastNode());
}

TEST_F(PositionTest, NodeAsRangeLastNodeShadow) {
  const char* bodyContent =
      "
00112233
";
  const char* shadowContent =
      "<content select=#two></content><content "
      "select=#one></content>";
  setBodyContent(bodyContent);
  ShadowRoot* shadowRoot = setShadowContent(shadowContent, "host");

  Node* host = document().getElementById("host");
  Node* n1 = document().getElementById("one");
  Node* n2 = document().getElementById("two");
  Node* t0 = EditingStrategy::firstChild(*host);
  Node* t1 = EditingStrategy::firstChild(*n1);
  Node* t2 = EditingStrategy::firstChild(*n2);
  Node* t3 = EditingStrategy::lastChild(*host);
  Node* a = shadowRoot->getElementById("a");

  EXPECT_EQ(t0, Position::inParentBeforeNode(*n1).nodeAsRangeLastNode());
  EXPECT_EQ(t1, Position::inParentBeforeNode(*n2).nodeAsRangeLastNode());
  EXPECT_EQ(t1, Position::inParentAfterNode(*n1).nodeAsRangeLastNode());
  EXPECT_EQ(t2, Position::inParentAfterNode(*n2).nodeAsRangeLastNode());
  EXPECT_EQ(t3, Position::afterNode(host).nodeAsRangeLastNode());

  EXPECT_EQ(t2,
            PositionInFlatTree::inParentBeforeNode(*n1).nodeAsRangeLastNode());
  EXPECT_EQ(a,
            PositionInFlatTree::inParentBeforeNode(*n2).nodeAsRangeLastNode());
  EXPECT_EQ(t1,
            PositionInFlatTree::inParentAfterNode(*n1).nodeAsRangeLastNode());
  EXPECT_EQ(t2,
            PositionInFlatTree::inParentAfterNode(*n2).nodeAsRangeLastNode());
  EXPECT_EQ(t1, PositionInFlatTree::afterNode(host).nodeAsRangeLastNode());
}

TEST_F(PositionTest, ToPositionInFlatTreeWithActiveInsertionPoint) {
  const char* bodyContent = "
001122
";
  const char* shadowContent =
      "<content select=#one "
      "id='content'></content><content></content>";
  setBodyContent(bodyContent);
  ShadowRoot* shadowRoot = setShadowContent(shadowContent, "host");
  Element* anchor = shadowRoot->getElementById("a");

  EXPECT_EQ(PositionInFlatTree(anchor, 0),
            toPositionInFlatTree(Position(anchor, 0)));
  EXPECT_EQ(PositionInFlatTree(anchor, 1),
            toPositionInFlatTree(Position(anchor, 1)));
  EXPECT_EQ(PositionInFlatTree(anchor, PositionAnchorType::AfterChildren),
            toPositionInFlatTree(Position(anchor, 2)));
}

TEST_F(PositionTest, ToPositionInFlatTreeWithInactiveInsertionPoint) {
  const char* bodyContent = "
<content></content>
";
  setBodyContent(bodyContent);
  Element* anchor = document().getElementById("p");

  EXPECT_EQ(PositionInFlatTree(anchor, 0),
            toPositionInFlatTree(Position(anchor, 0)));
  EXPECT_EQ(PositionInFlatTree(anchor, PositionAnchorType::AfterChildren),
            toPositionInFlatTree(Position(anchor, 1)));
}

// This test comes from "editing/style/block-style-progress-crash.html".
TEST_F(PositionTest, ToPositionInFlatTreeWithNotDistributed) {
  setBodyContent("<progress id=sample>foo</progress>");
  Element* sample = document().getElementById("sample");

  EXPECT_EQ(PositionInFlatTree(sample, PositionAnchorType::AfterChildren),
            toPositionInFlatTree(Position(sample, 0)));
}

TEST_F(PositionTest, ToPositionInFlatTreeWithShadowRoot) {
  const char* bodyContent = "
001122
";
  const char* shadowContent = "<content select=#one></content>";
  setBodyContent(bodyContent);
  ShadowRoot* shadowRoot = setShadowContent(shadowContent, "host");
  Element* host = document().getElementById("host");

  EXPECT_EQ(PositionInFlatTree(host, 0),
            toPositionInFlatTree(Position(shadowRoot, 0)));
  EXPECT_EQ(PositionInFlatTree(host, PositionAnchorType::AfterChildren),
            toPositionInFlatTree(Position(shadowRoot, 1)));
  EXPECT_EQ(PositionInFlatTree(host, PositionAnchorType::AfterChildren),
            toPositionInFlatTree(
                Position(shadowRoot, PositionAnchorType::AfterChildren)));
  EXPECT_EQ(PositionInFlatTree(host, PositionAnchorType::BeforeChildren),
            toPositionInFlatTree(
                Position(shadowRoot, PositionAnchorType::BeforeChildren)));
  EXPECT_EQ(PositionInFlatTree(host, PositionAnchorType::AfterAnchor),
            toPositionInFlatTree(
                Position(shadowRoot, PositionAnchorType::AfterAnchor)));
  EXPECT_EQ(PositionInFlatTree(host, PositionAnchorType::BeforeAnchor),
            toPositionInFlatTree(
                Position(shadowRoot, PositionAnchorType::BeforeAnchor)));
}

TEST_F(PositionTest,
       ToPositionInFlatTreeWithShadowRootContainingSingleContent) {
  const char* bodyContent = "
001122
";
  const char* shadowContent = "<content select=#one></content>";
  setBodyContent(bodyContent);
  ShadowRoot* shadowRoot = setShadowContent(shadowContent, "host");
  Element* host = document().getElementById("host");

  EXPECT_EQ(PositionInFlatTree(host, 0),
            toPositionInFlatTree(Position(shadowRoot, 0)));
  EXPECT_EQ(PositionInFlatTree(host, PositionAnchorType::AfterChildren),
            toPositionInFlatTree(Position(shadowRoot, 1)));
}

TEST_F(PositionTest, ToPositionInFlatTreeWithEmptyShadowRoot) {
  const char* bodyContent = "
001122
";
  const char* shadowContent = "";
  setBodyContent(bodyContent);
  ShadowRoot* shadowRoot = setShadowContent(shadowContent, "host");
  Element* host = document().getElementById("host");

  EXPECT_EQ(PositionInFlatTree(host, PositionAnchorType::AfterChildren),
            toPositionInFlatTree(Position(shadowRoot, 0)));
}

}  // namespace blink


text:
\documentclass[simplex.tex]{subfiles}
% NO NEED TO INPUT PREAMBLES HERE
% packages are inherited; you can compile this on its own

\begin{document}
\onlyinsubfile{
\thispagestyle{empty}

%%%% Table of Contents
\tableofcontents

%%%% Publications
\bibliographystyle{IEEEtran}
\begin{spacing}{0.5}
\section*{Publications, Presentations, and Talks}
%\vspace{-20pt}
\nocite{*}
{\footnotesize	\bibliography{simplex}}
\end{spacing}
%%%% End Publications
}

\subsection{Your title here}

{\Large Your content here}
%%% Your content here
%%% Your content here
%%% Your content here
%%% Your content here
%%% Your content here
%%% Your content here
%%% Your content here
%%% Your content here

\newpage
\begin{spacing}{0.5}
{\footnotesize
%\nocitereferences{*}
\bibliographystylereferences{IEEEtran}
\bibliographyreferences{references}
}
\end{spacing}

\end{document}


text:
export default function getContainerRoot (doc, containerPath, nodeId) {
  let current = doc.get(nodeId)
  const containerId = containerPath[0]
  while (current) {
    const parent = current.getParent()
    if (parent && parent.id === containerId) {
      return current
    }
    current = parent
  }
}


text:
Contributing to Skia
====================

Here some ways you can get involved and help us improve Skia.

Report Bugs
-----------

Find bugs to fix or report new bugs in the [Skia issue tracker](http://bug.skia.org/).
You can also search the [Chromium issue tracker](http://code.google.com/p/chromium/issues/list) for bugs related to graphics or Skia.

Test
----

Write an application or tool that will exercise the Skia code differently than our
current set of tests and verify that Skia works as expected.  Draw something
interesting and profile it to find ways to speed up Skia's implementation.
We cannot always fix issues or support every scenario, but we welcome any bugs
found so we can assess and prioritize them.  (If you find _and_ fix a bug, even better!)

Contribute Code
---------------

Whether you develop a new feature or a fix for an existing bug in the Skia code base,
you will need a committer to review and approve the change.  There are some steps that
can speed up the review process:
Keep your code submissions small and targeted.
When possible, have a fellow contributor review your change in advance of submission.
Propose new features to the project leads by opening a feature bug or posting to
skia-discuss ahead of development.  For more information, see [How to submit a patch](./submit).

For background on the project and an outline of the types of roles interested parties
can take on, see [Project Roles](../../roles).

Anyone contributing code to Skia must sign a Contributor License Agreement and ensure
they are listed in the AUTHORS file:
Individual contributors can complete the [Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual) online.
If you are contributing on behalf of a corporation, fill out the [Corporate Contributor License Agreement](https://developers.google.com/open-source/cla/corporate)
and send it in as described on that page.
If it is your first time submitting code or you have not previously done so, add your
(or your organization's) name and contact info to the [AUTHORS file](https://skia.googlesource.com/skia/+/master/AUTHORS) as a part
of your CL.
REVIEWERS: Before you LGTM a change, verify that the contributor is listed in the AUTHORS file.
If they are not, a Googler must ensure that the individual or their corporation has signed the
CLA by searching [go/cla-signers](https://goto.google.com/cla-signers).
Then have an entry added to the AUTHORS file with the CL.