<?php


namespace IPS\chatbox\modules\front\chatbox;

/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
	header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
	exit;
}

/**
 * room
 */
class _room extends \IPS\Dispatcher\Controller
{
	/**
	 * Execute
	 *
	 * @return	void
	 */
	public function execute()
	{		
		# Load room
		try
		{
			$this->room = \IPS\chatbox\Rooms\Room::load( \IPS\Request::i()->id );
		}
		catch ( \OutOfRangeException $e )
		{
			\IPS\Output::i()->error( 'node_error', '2CHATBOX100/2', 404 );
		}

		parent::execute();		
	}

	/**
	 * ...
	 *
	 * @return	void
	 */
	protected function manage()
	{
		# Set breadcrumb
		$club = $this->room->club();
		
		if ( $club )
		{
			\IPS\core\FrontNavigation::$clubTabActive = TRUE;
			\IPS\Output::i()->breadcrumb = array();
			\IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( 'app=core&module=clubs&controller=directory', 'front', 'clubs_list' ), \IPS\Member::loggedIn()->language()->addToStack('module__core_clubs') );
			\IPS\Output::i()->breadcrumb[] = array( $club->url(), $club->name );
			\IPS\Output::i()->breadcrumb[] = array( $this->room->url(), $this->room->_title );
			
			if ( \IPS\Settings::i()->clubs_header == 'sidebar' )
			{
				\IPS\Output::i()->sidebar['contextual'] = \IPS\Theme::i()->getTemplate( 'clubs', 'core' )->header( $club, $this->room, 'sidebar' );
			}
		}
		else
		{
			\IPS\Output::i()->breadcrumb[] = array( NULL, $this->room->_title );
		}
		
		# Output
		\IPS\Output::i()->title	= $this->room->_title;
		
		if ( \IPS\Request::i()->full != '1' )
		{
			\IPS\Output::i()->output = $this->room->show();
		}
		else
		{
			\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( $this->room->show() ) );
		}
	}
	
	/*-------------------------------------------------------------------------*/
	// Csrf & ajax check
	/*-------------------------------------------------------------------------*/	
	protected function ajaxCheck()
	{
		\IPS\Session::i()->csrfCheck();
		
		if ( ! \IPS\IN_DEV )
		{
			if ( ! \IPS\Request::i()->isAjax() )
			{
				\IPS\Output::i()->error( 'chatbox_error_noajax', '2CHATBOX100/3', 403, '' );
			}		
		}
	}
	
	/*-------------------------------------------------------------------------*/
	// Check view permission
	/*-------------------------------------------------------------------------*/		
	protected function checkViewPerm()
	{
		if ( ! $this->room->_canView() )
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_no_perm_view' ) ) );
		}
		if ( $this->room->isBlocked() )
		{
			\IPS\Output::i()->json( array( 'type' => 'isBlocked', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_kick_out' ) ) );
		}	

		# Block spiders
		if( \IPS\Session\Front::i()->userAgent->spider )
		{
			\IPS\Output::i()->error( 'chatbox_error_no_perm_view', '2CHATBOX100/4', 403, '' );
		}		
	}

	/*-------------------------------------------------------------------------*/
	// Check chat permission
	/*-------------------------------------------------------------------------*/	
	protected function checkChatPerm()
	{
		if ( ! $this->room->canChat() )
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_no_perm_chat' ) ) );
		}
		if ( $this->room->isBlocked() )
		{
			\IPS\Output::i()->json( array( 'type' => 'isBlocked', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_kick_out' ) ) );
		}
	}	
	
	/*-------------------------------------------------------------------------*/
	// get msg by id
	/*-------------------------------------------------------------------------*/
	protected function getMsgByID( $id )
	{
		try
		{
			$chat = \IPS\chatbox\Rooms\Chat::load( $id );
			return $chat;
		}
		catch ( \OutOfRangeException $e ) 
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_nomsg' ) ) );
		}
	}
			
	/*-------------------------------------------------------------------------*/
	// Ping to keep us in chatters list
	/*-------------------------------------------------------------------------*/	 
	protected function ping()
	{
		# Ajax Check
		$this->ajaxCheck();
		
		# Check permission
		$this->checkViewPerm();

		# Remove inactive users
		$this->room->removeInactiveUsers();
		
		if ( ! \IPS\Member::loggedIn()->member_id && !$this->room->canChat() )
		{
			\IPS\Output::i()->json('OK');
		}
		
		# Chatters
		$chatters = $this->room->getChatters();

		# Update last ping
		$key = $this->room->userKey();
		
		if ( isset( $chatters[ $key ] ) )
		{
			# Update last Ping
			$chatters[ $key ]['last_ping'] = time();
			
			# Sync user's name & photo
			if ( $chatters[ $key ]['chatterID'] > 0 )
			{
				$mem = \IPS\Member::loggedIn();
				$chatters[ $key ]['chatterName']		= $mem->name;
				$chatters[ $key ]['chatterGroup']		= $mem->member_group_id;
				$chatters[ $key ]['chatterPhoto']		= (string) $mem->photo;
				$chatters[ $key ]['chatterNameFormat']	= \IPS\Application::load('chatbox')->userGroupFormat( $mem->group, $mem->name );
				$chatters[ $key ]['is_anon']			= $mem->isOnlineAnonymously();
			}

			$this->room->saveChatters( $chatters );
		}
		else
		{
			$chatters = $this->room->addUser();
		}
		
		# Ouput
		\IPS\Output::i()->json('OK');	
	}

	/*-------------------------------------------------------------------------*/
	// Get New Messages
	/*-------------------------------------------------------------------------*/	
	protected function getMSG()
	{
		# Ajax Check
		$this->ajaxCheck();
		
		# Check permission
		$this->checkViewPerm();
		
		# Options
		$loadMoreMode 	= (int) \IPS\Request::i()->loadMoreMode;
		$opts 			= $this->room->options();
		$useSQL 		= TRUE;
		
		# Check lastID
		$lastID = \IPS\Request::i()->lastID ? (int) \IPS\Request::i()->lastID : 0;

		if ( $loadMoreMode == 0 && $lastID > 0 )
		{
			$cacheLastID = (int) $this->room->cacheLastMsg()['lastChat'];

			if ( $cacheLastID > 0 && $cacheLastID <= $lastID )
			{
				$useSQL = FALSE;
				$newLastID = $cacheLastID;
			}
		}

		$return['cacheLevel'] = $useSQL ? 0 : 1;
		
		# Can report?
		$canReport = ( \IPS\Member::loggedIn()->group['g_can_report'] == '1' || \in_array( 'IPS\chatbox\Rooms\Chat', explode( ',', \IPS\Member::loggedIn()->group['g_can_report'] ) ) ) && \IPS\Settings::i()->chatbox_report == 1;
		
		# Get Chatters
		$chatters = $this->room->getChatters();
		
		if ( !$this->room->_canManage() )
		{
			foreach( $chatters as $k => $u )
			{
				unset( $chatters[$k]['ip'] );
			}
		}
				
		# Query
		if ( $useSQL )
		{
			$filterMems = \IPS\Application::load('chatbox')->filterMems();
			
			if ( $filterMems['ignored'] )
			{
				$where[] = array( 'chat_member_id NOT IN(' . implode( ",", $filterMems['ignored'] ) . ')' );
			}				

			$where[] = array( '( chat_room=? OR FIND_IN_SET( ?, chat_in_multiple_rooms ) )', $this->room->id, $this->room->id ); 
			
			if ( !$opts['announcement_on'] )
			{
				$where[] = array( "( chat_sys IS NULL OR chat_sys!='announcement' )" );
			}
			
			if ( ! $this->room->onDonate() )
			{
				$where[] = array( "( chat_sys IS NULL OR chat_sys!='donation' )" );
			}
			else
			{
				$cacheDonation = $this->room->cacheDonation();
			}
			
			if ( !$this->room->_canSeeOnline() || !$opts['online_on'] )
			{
				$where[] = array( "( chat_sys IS NULL OR ( chat_sys!='join' AND chat_sys!='leave' ) )" );
			}

			$where[] = $loadMoreMode == 1 ? array( 'chat_id<?', $lastID ) : array( 'chat_id>?', $lastID );

			$chats = iterator_to_array( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'chatbox_room_posts', $where, 'chat_time DESC', array( 0, (int) $opts['limit'] ) ), 'IPS\chatbox\Rooms\Chat' ) );

			if ( $chats )
			{
				$newLastID = $chats[0]->id;
				$chats = $opts['sort'] == 'asc' ? array_reverse( $chats ) : $chats;
				
				$files = \IPS\Application::load('chatbox')->showFiles( $chats, 'room' );
				$authors = \IPS\Application::load('chatbox')->joinAuthor( $chats );
				
				foreach( $chats as $chat )
				{					
					if ( $chat->member_id > 0 )	// Msg from member
					{
						$chatterID = $chat->member_id;
						$chatterName = $authors[ $chat->member_id ]['name'];
						$chatterKey = "user@" . $chatterID;
						$chatterPhoto = \IPS\Member::photoUrl( $authors[ $chat->member_id ] );
						$content = \IPS\Application::load('chatbox')->parseContent( $opts, $chat->content ) . $files[ $chat->id ];
					}
					elseif ( $chat->member_id == 0 ) // Msg from guest
					{
						$chatterID = 0;
						$chatterName = $chat->guestName;
						$chatterKey = "guest@" . $chatterName;
						$chatterPhoto = md5( $chatterName );
						$content = \IPS\Application::load('chatbox')->parseContent( $opts, $chat->content ) . $files[ $chat->id ];
					}
					else // Msg from sys
					{
						$chatterID = -1;
						$content = $chat->content;
						$v = NULL;
						
						if ( \in_array( $chat->sys, array( 'content', 'registration', 'donation' ) ) )
						{
							$v = json_decode( $content, true );				
						}	
						if ( $chat->sys == 'join' || $chat->sys == 'leave' )
						{
							$v = !$chat->guestName ? array( (string) $content ) : array( \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_guest_name' ) . " " . (string) $content );
						}						
						if ( $chat->sys == 'announcement' )
						{
							if ( $content == 'remove' )
							{
								$updateAnn = $content;
								$content = null;
								$v = 'removeAnn';
							}
							else
							{
								$updateAnn = $this->room->announcement();
								$v = array( (string) $this->room->url('readAnnouncement'), $updateAnn[0] );
							}
						}
						if ($v == 'removeAnn')
						{
							$content = \IPS\Member::loggedIn()->language()->addToStack('chatbox_sys_ann_remove');
						}
						else $content = \IPS\Member::loggedIn()->language()->addToStack('chatbox_sys_' . $chat->sys, FALSE, array( 'htmlsprintf' => $v ) );
					}
			
					$inDay = ( \IPS\Settings::i()->chatbox_time_moment && ( time() - $chat->time ) <= 86400 ) ? true : false;
					$timeFormat = $inDay && \IPS\Settings::i()->chatbox_time_moment ? \IPS\DateTime::ISO8601 : \IPS\Settings::i()->chatbox_time_format;
					
					$new[] = array(
						'id'				=> $chat->id,
						'chatterID'			=> $chatterID,
						'chatterName' 		=> $chatterName,
						'chatterKey' 		=> $chatterKey,
						'chatterPhoto' 		=> $chatterPhoto,
						'chatterNameFormat' => \IPS\Application::load('chatbox')->userGroupFormat( $authors[ $chat->member_id ]['groupData'], $chatterName ),
						'content' 			=> $content,
						'sys'				=> $chat->sys,
						'inDay'				=> $inDay,
						'time'				=> \IPS\DateTime::ts( $chat->time )->format( $timeFormat ),
						'donation'			=> $chatterID > 0 && $cacheDonation ? $cacheDonation[ $chatterID ] : 0,
						'canEdit'			=> $this->room->canEditMsg( $chat ),
						'canDelete'			=> $this->room->_canDelete( $chat->member_id ),
						'canReport'			=> $canReport,
					);
				}
			}
			else
			{
				$noOlder = 1;
				$newLastID = $cacheLastID > 0 ? $cacheLastID : null;
			}
		}

		# Return
		$return['content'] = $new;
		$return['lastID'] = $newLastID;
		
		if ( $loadMoreMode > 0 )
		{
			$return['noOlder'] = $noOlder;
		}		

		if ( $updateAnn )
		{
			$return['updateAnn'] = $updateAnn;
		}
		if ( $opts['showUserList'] == 1 && $this->room->_canSeeOnline() )
		{
			$return['chatters'] = $chatters;
		}		
		if ( $this->room->_canPlaySong() )
		{
			$return['song'] = $this->room->song;
		}

		# Update edited/deleted msg
		if ( isset( $chatters[ $this->room->userKey() ][ 'update' ] ) )
		{
			$return['update'] = $chatters[ $this->room->userKey() ][ 'update' ];
			unset( $chatters[ $this->room->userKey() ][ 'update' ] );
			$this->room->saveChatters( $chatters );
		}
		
		# Done
		\IPS\Output::i()->json( $return );	
	}
	
	/*-------------------------------------------------------------------------*/
	// Use editor to edit msg
	/*-------------------------------------------------------------------------*/	
	protected function editEditor()
	{
		# Get msg by ID
		$chat = $this->getMsgByID( \IPS\Request::i()->chatID );			

		# Check permission
		if ( ! $this->room->canEditMsg( $chat ) )
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_no_perm_edit' ) ) );
		}
		
		# Options
		$opts = $this->room->options();
		
		# Form
		$form = $this->room->editor( $chat );

		# Done
		if ( ! $opts['use_editor'] )
		{
			$form->class = 'ipsForm_noLabels ipsPadding cbCleanTextEdit';
		}
		else
		{
			$form->class = 'ipsForm_noLabels ipsPadding cbEditorEdit';
		}
		
		$output = (string) $form;
		
		if( \IPS\Request::i()->isAjax() )
		{
			\IPS\Output::i()->sendOutput( $output, 200, 'text/html', \IPS\Output::i()->httpHeaders );
		}
		else
		{
			\IPS\Output::i()->output = $output;
		}			
	}	

	/*-------------------------------------------------------------------------*/
	// Delete Message
	/*-------------------------------------------------------------------------*/		
	protected function deleteMSG()
	{
		# Ajax Check
		$this->ajaxCheck();
		
		# Get msg by ID
		$chat = $this->getMsgByID( \IPS\Request::i()->chatID );	

		# Check permission
		if ( ! $this->room->_canDelete( $chat->member_id ) )
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_no_perm_del' ) ) );
		}		

		# Save to datastore to update for all online users
		$this->room->markMsg( $chat->id );	
		
		# Delete
		if ( $chat->room > 0 )
		{
			$chat->delete();
		}
		else
		{
			if ( $chat->in_multiple_rooms === (string) $this->room->id )
			{	
				\IPS\Db::i()->delete( 'chatbox_room_posts', array( 'chat_id=?', $chat->id ) );
			}
			else
			{
				$r = explode( ",", $chat->in_multiple_rooms );
				$rooms = array_diff( $r, [ $this->room->id ] );
				\IPS\Db::i()->update( 'chatbox_room_posts', array( 'chat_in_multiple_rooms' => implode( ",", $rooms ) ), array( 'chat_id=?', $chat->id ) );
			}
		}
			
		# Done
		if ( \IPS\Request::i()->isAjax() )		
		{
			\IPS\Output::i()->json( 'OK' );
		}
		else
		{
			if ( \IPS\Request::i()->fromArchive == 1 )
			{
				\IPS\Output::i()->redirect( isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : \IPS\Http\Url::internal( '' ) );
			}
			else
			{
				\IPS\Output::i()->redirect( $this->room->url() );				
			}
		}
	}
		
	/*-------------------------------------------------------------------------*/
	// Delete all messages
	/*-------------------------------------------------------------------------*/			
	protected function cleanRoom()
	{
		# Ajax Check
		$this->ajaxCheck();
	
		# Check permission
		if ( ! $this->room->_canManage() )
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_no_perm_clean' ) ) );
		}

		# Update new message
		$this->room->cleanRoom();

		# Done
		\IPS\Output::i()->json( 'OK' );	
	}
	
	/*-------------------------------------------------------------------------*/
	// Announcement
	/*-------------------------------------------------------------------------*/			
	protected function makeAnnouncement()
	{
		# Check permission
		if ( ! $this->room->_canManage() )
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_no_perm_ann' ) ) );
		}

		# Form
		$form = new \IPS\Helpers\Form();
		$form->class = 'ipsForm_noLabels chatboxAnnForm';
		$form->add( new \IPS\Helpers\Form\YesNo( 'room_announcement_full', $this->room->announcement_full, FALSE ) );		
		$form->add( new \IPS\Helpers\Form\Translatable( 'room_announcement_title', NULL, FALSE, array( 'app' => 'chatbox', 'key' => "chatbox_room_{$this->room->id}_announcement_title" ) ) );
		$form->add( new \IPS\Helpers\Form\Translatable( 'room_announcement', NULL, FALSE, array( 'app' => 'chatbox', 'key' => "chatbox_room_{$this->room->id}_announcement", 'editor' => array( 'app' => 'chatbox', 'key' => 'Room', 'autoSaveKey' => "chatbox-room-{$this->room->id}-announcement", 'attachIds' => array( $this->room->id, NULL, 'chatbox_announcement' ) ) ) ) );

		if ( $values = $form->values() )
		{
			$this->room->announcement_full = (int) $values['room_announcement_full'];
			$this->room->save();
			
			if ( $values['room_announcement_title'] )
			{
				\IPS\Lang::saveCustom( 'chatbox', "chatbox_room_{$this->room->id}_announcement_title", $values['room_announcement_title'] );
			}
			else
			{
				\IPS\Lang::deleteCustom( 'chatbox', "chatbox_room_{$this->room->id}_announcement_title" );
			}
			
			if ( $values['room_announcement'] )
			{
				\IPS\Lang::saveCustom( 'chatbox', "chatbox_room_{$this->room->id}_announcement", $values['room_announcement'] );
			}
			else
			{
				\IPS\Lang::deleteCustom( 'chatbox', "chatbox_room_{$this->room->id}_announcement" );
			}
			
			$opts = $this->room->options();

			if ( $opts['announcement_on'] && isset( $values['room_announcement_title'] ) && $values['room_announcement'] )
			{
				$msgType = "announcement";
				\IPS\Db::i()->delete( 'chatbox_room_posts', array( 'chat_sys=? AND chat_room=?', $msgType, $this->room->id ) );
				$this->room->addSystemMsg( $msgType );
			}
			
			$ann = $this->room->announcement();
			
			\IPS\Output::i()->json( array(
				'message' 	=> \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_updated_announcement' ),
				'title'		=> $ann[0],
				'content'	=> $ann[1],
				'isFull'	=> $ann[2],
			) );
			
			return;
		}		

		# Done
		\IPS\Output::i()->output = $form->customTemplate( array( \call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) );
	}
	
	protected function readAnnouncement()
	{
		# Ajax Check
		$this->ajaxCheck();
		
		# Check permission
		$this->checkViewPerm();
		
		# Get announcement
		$ann = $this->room->announcement();

		if ( !$ann[0] )
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_no_ann' ) ) );
		}
		
		\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'room', 'chatbox', 'front' )->announcement( $ann[1] );
	}
	
	/*-------------------------------------------------------------------------*/
	// Blocked list
	/*-------------------------------------------------------------------------*/	
	protected function blockedChatters()
	{
		# Ajax Check
		$this->ajaxCheck();
	
		# Check permission
		if ( ! $this->room->_canManage() )
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_no_perm_block' ) ) );
		}
		
		# List
		$data = $this->room->getBlockedData();
	
		# Done
		\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'room', 'chatbox', 'front' )->blockedChatters( $data );
	}
	
	/*-------------------------------------------------------------------------*/
	// Block a user
	/*-------------------------------------------------------------------------*/
	protected function addToBlockedList()
	{
		# Ajax Check
		$this->ajaxCheck();
	
		# Check permission
		if ( ! $this->room->_canManage() )
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_no_perm_block' ) ) );
		}
		
		# Form
		$form = new \IPS\Helpers\Form();
		$form->class = 'ipsPadding chatboxBlockUser';
		$form->hiddenValues['key'] = \IPS\Request::i()->key;
		$form->hiddenValues['name'] = \IPS\Request::i()->name;
		$form->hiddenValues['ip'] = \IPS\Request::i()->ip;
		$form->hiddenValues['mod'] = \IPS\Member::loggedIn()->member_id;
		$form->add( new \IPS\Helpers\Form\Text( 'chatbox_block_reason', NULL, FALSE ) );
		$form->add( new \IPS\Helpers\Form\Number( 'chatbox_block_unblock', 0, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'chatbox_block_unblock_never' ) ) );
		$form->add( new \IPS\Helpers\Form\YesNo( 'chatbox_block_del', 0, FALSE ) );
		
		if ( $values = $form->values( TRUE ) )
		{
			$key 	= $values['key'];
			$name 	= $values['name'];
			$ip 	= $values['ip'];
			$mod 	= $values['mod'];

			if ( $values['chatbox_block_del'] == 1 )
			{
				\IPS\Task::queue( 'chatbox', 'deleteMsg', array( 'room' => $this->room->id, 'user' => $key ), 4 );
			}
			
			# Is blocked?
			$data = $this->room->getBlockedData();
			
			if ( \in_array( $key, array_keys($data) ) )
			{
				\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_in_blocked' ) ) );
			}

			# Block
			$data[ $key ] = array( 
				'chatterName' 	=> $name, 
				'ip' 			=> $ip, 
				'mod' 			=> $mod, 
				'reason' 		=> $values['chatbox_block_reason'], 
				'unblock' 		=> $values['chatbox_block_unblock'], 
				'time' 			=> time() 
			);
			
			# Save
			$this->room->blocked = json_encode( $data );
			$this->room->save();
			
			# Remove from online list
			$chatters = $this->room->getChatters();
			if ( \in_array( $key, array_keys( $chatters ) ) )
			{
				unset( $chatters[ $key ] );		
				$this->room->saveChatters( $chatters );
			}
			
			# Done
			\IPS\Output::i()->json( array(
				'message' 	=> \IPS\Member::loggedIn()->language()->addToStack('chatbox_blocked_done', FALSE, array( 'sprintf' => $name ) ),
				'deleted'	=> (int) $values['chatbox_block_del']
			) );			
		}
		
		# Done
		\IPS\Output::i()->output = (string) $form;
	}

	/*-------------------------------------------------------------------------*/
	// Unblock
	/*-------------------------------------------------------------------------*/	
	protected function unblock()
	{
		# Ajax Check
		$this->ajaxCheck();
	
		# Check permission
		if ( ! $this->room->_canManage() )
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_no_perm_block' ) ) );
		}

		# Unblock
		$data = array();
		
		if ( $this->room->blocked )
		{
			$data = json_decode( $this->room->blocked, true );
		}

		if ( !$data[ \IPS\Request::i()->key ] )
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_no_user' ) ) );
		}
		
		unset( $data[ \IPS\Request::i()->key ] );
		$this->room->blocked = $data ? json_encode( $data ) : null;
		$this->room->save();
		
		# Done
		\IPS\Output::i()->json('OK');
	}
	
	/*-------------------------------------------------------------------------*/
	// Get Chatters List
	/*-------------------------------------------------------------------------*/	
	protected function chattersList()
	{
		$this->ajaxCheck();
		
		$chatters = $this->room->getChatters();
		
		if ( !$this->room->_canManage() )
		{
			foreach( $chatters as $k => $u )
			{
				unset( $chatters[$k]['ip'] );
			}
		}
		
		$this->room->removeInactiveUsers();
		$count = $chatters ? \count( $chatters ) : 0;
		
		\IPS\Output::i()->json( array( 'list' => $chatters, 'count' => $count ) );
	}
	
	/*-------------------------------------------------------------------------*/
	// Change Name
	/*-------------------------------------------------------------------------*/		
	protected function changeName()
	{
		# Ajax Check
		$this->ajaxCheck();

		# Not guest?
		if ( \IPS\Member::loggedIn()->member_id )
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_not_guest' ) ) );
		}

		# Check name and save to cookie
		$name = $this->room->checkName( \IPS\Request::i()->name );

		# Check permission
		$this->checkViewPerm();
		
		# Position
		$inGlobal = \IPS\Request::i()->inGlobal == 1 ? true : false;
		$inWidget = \IPS\Request::i()->inWidget ? true : false;
		
		# Done
		\IPS\Output::i()->json( array( 
			'message'	=> \IPS\Member::loggedIn()->language()->addToStack('chatbox_welcome_guest', FALSE, array( 'sprintf' => $name ) ),
			'html' 		=> $this->room->show( $inWidget, $inGlobal ),
		) );
	}
		
	/*-------------------------------------------------------------------------*/
	// Play a song
	/*-------------------------------------------------------------------------*/	
	protected function selectSong()
	{
		# Ajax Check
		$this->ajaxCheck();
	
		# Check permission
		if ( ! $this->room->_canManage() || ! $this->room->_canPlaySong() )
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_no_perm_song' ) ) );
		}

		if ( $this->room->song )
		{
			$songData = json_decode( $this->room->song, TRUE );
			
			try
			{
				$song = \IPS\musicbox\Song::load( $songData['id'] );
				if ( $song->isPlaylist != 1 )
				{
					$playingSong = \IPS\Theme::i()->getTemplate( 'room', 'chatbox', 'front' )->songPlaying( $song );
				}
			}
			catch ( \OutOfRangeException $e ) 
			{
				$this->room->song = NULL;
				$this->room->save();
			}			
		}
		
		# Done
		\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'room', 'chatbox', 'front' )->songSearch( $this->room, $playingSong );
	}
	
	protected function searchSong()
	{
		# Ajax Check
		$this->ajaxCheck();
	
		# Check permission
		if ( ! $this->room->_canManage() || ! $this->room->_canPlaySong() )
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_no_perm_song' ) ) );
		}
		
		# Search
		$q = mb_strtolower( urldecode( \IPS\Request::i()->q ) );
		if ( !$q || mb_strlen( $q ) < 4 )
		{
			return '';
		}
		
		$perpage = 5;
		$page = isset( \IPS\Request::i()->page ) ? \intval( \IPS\Request::i()->page ) : 1;
		if( $page < 1 )
		{
			$page = 1;
		}
		
		$url = $this->room->url()->setQueryString( array( 'do' => 'searchSong', 'q' => $q ) );
		
		$where[] = array( "(song_title LIKE CONCAT( '%', ?, '%' ) OR song_singer LIKE CONCAT( '%', ?, '%' ))", $q, $q );
		$where[] = array( 'song_isPlaylist=0' );				
		$select = \IPS\Db::i()->select( '*', 'musicbox_songs', $where, 'song_id DESC', array( ( $page - 1 ) * $perpage, $perpage ) );
		$total = (int) \IPS\Db::i()->select( 'COUNT(*)', 'musicbox_songs', $where )->first();
		$pagination = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->pagination(
			$url,
			ceil( $total / $perpage ),
			$page,
			$perpage,
			TRUE
		);
		
		foreach( $select as $row )
		{
			$data[] = \IPS\musicbox\Song::constructFromData( $row );
		}

		# Output
		\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('mb_search_results');
		\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'room', 'chatbox', 'front' )->songResults( $data, $pagination );
	}
	
	protected function addSong()
	{
		# Ajax Check
		$this->ajaxCheck();
	
		# Check permission
		if ( ! $this->room->_canManage() || ! $this->room->_canPlaySong() )
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_no_perm_song' ) ) );
		}

		# Add song
		try
		{
			$song = \IPS\musicbox\Song::load( (int) \IPS\Request::i()->song );

			if ( $song->isPlaylist != 1 )
			{
				$data = array(
					'file'		=> $song->musicFile(),
					'id'		=> $song->id,
					'url'		=> (string) $song->url(),
					'name'		=> $song->title,
					'artist'	=> $song->singer,
					'thumbnail'	=> $song->thumbnail(),
				);
				$songData = json_encode( $data );
				$this->room->song = $songData;
				$this->room->save();
		
				\IPS\Output::i()->json( array( 
					'html' 		=> \IPS\Theme::i()->getTemplate( 'room', 'chatbox', 'front' )->songPlaying( $song ), 
					'song' 		=> $songData, 
					'message' 	=> \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_added_song' ) 
				) );
			}
		}
		catch ( \OutOfRangeException $e ) {}

		\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_no_song' ) ) );
	}
	
	protected function removeSong()
	{
		# Ajax Check
		$this->ajaxCheck();
	
		# Check permission
		if ( ! $this->room->_canManage() || ! $this->room->_canPlaySong() )
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_no_perm_song' ) ) );
		}

		# Remove song
		$this->room->song = NULL;
		$this->room->save();
		
		\IPS\Output::i()->json( 'OK' );
	}	

	/*-------------------------------------------------------------------------*/
	// Change room
	/*-------------------------------------------------------------------------*/	
	protected function changeRoom()
	{
		# Ajax Check
		$this->ajaxCheck();

		# Load room
		try
		{
			$room = \IPS\chatbox\Rooms\Room::load( \IPS\Request::i()->newroom );
		}
		catch ( \OutOfRangeException $e )
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_no_room' ) ) );
		}
		
		if ( \IPS\Request::i()->inGlobal == 1 || \IPS\Request::i()->inWidget )
		{
			if ( \IPS\Request::i()->inGlobal == 1 )
			{
				$inGlobal = TRUE;
				\IPS\Request::i()->setCookie( 'chatbox_inRoom', $room->id, NULL, FALSE );
			}
			if ( \IPS\Request::i()->inWidget )
			{
				$widgetConf = array( 'orientation' => \IPS\Request::i()->inWidget );
			}
			
			\IPS\Output::i()->json( array( 'html' => $room->show( $widgetConf, $inGlobal ), 'config' => $room->jsVars() ) );
		}
		else
		{
			\IPS\Output::i()->json( array( 'url' => (string) $room->url() ) );
		}
	}
	
	/*-------------------------------------------------------------------------*/
	// Archive msgs
	/*-------------------------------------------------------------------------*/		
	public function archive()
	{
		/* Permission */

		$title = \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_archive' );
		$url = $this->room->url()->setQueryString( array( 'do' => 'archive' ) );
		
		/* Search */
		$where[] = array( 'chat_sys IS NULL' ); 
		$where[] = array( '( chat_room=? OR FIND_IN_SET( ?, chat_in_multiple_rooms ) )', $this->room->id, $this->room->id ); 

		/* Init Table */
		$table = new \IPS\Helpers\Table\Db( 'chatbox_room_posts', $url, $where );
		//$table->joins = array( array( 'from' => 'chatbox_files', 'where' => "chatbox_files.file_from='room' AND chatbox_files.file_msg_id=chatbox_room_posts.chat_id" ) );
		$table->classes = array( 'chatboxArchive' );
		$table->langPrefix = 'chatbox_archive_';
		$table->sortBy = $table->sortBy ? $table->sortBy : 'chat_time';
		$table->advancedSearch = array(
			'chat_member_id'	=> \IPS\Helpers\Table\SEARCH_MEMBER,
			'chat_guestName'	=> \IPS\Helpers\Table\SEARCH_CONTAINS_TEXT,
			'chat_content'		=> \IPS\Helpers\Table\SEARCH_CONTAINS_TEXT,
			'chat_time'			=> \IPS\Helpers\Table\SEARCH_DATE_RANGE,
		);

		$table->include = array( 'chat_member_id', 'chat_content', 'chat_time' );
		$table->parsers = array(
			'chat_member_id'	=> function( $val, $row )
			{
				if ( $val > 0 )
				{
					return \IPS\Theme::i()->getTemplate('archive', 'chatbox', 'front')->chatter( \IPS\Member::load( $val ) );
				}
				else
				{
					return \IPS\Theme::i()->getTemplate('archive', 'chatbox', 'front')->guest( $row );
				}
			},
			'chat_content'	=> function ( $val, $row )
			{
				return \IPS\Application::load('chatbox')->parseContent( $opts, $val );
			},
			'chat_time'	=> function( $val )
			{
				return \IPS\DateTime::ts( $val )->format( \IPS\Settings::i()->chatbox_time_format );
			}	
		);
		$table->rowButtons = function( $row )
		{
			$msg = \IPS\chatbox\Rooms\Chat::constructFromData( $row );
			
			if ( $msg->container()->canEditMsg( $msg ) )
			{
				$button['edit'] = array(
					'icon'	=> 'pencil',
					'title'	=> 'edit',
					'link'	=> $msg->container()->url()->csrf()->setQueryString( array( 'do' => 'editEditor', 'chatID' => $msg->id, 'useForm' => 1 ) ),
					'data'	=> array( 'ipsDialog' => '', 'data-ipsDialog-size' => 'medium', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack( 'edit' ) )
				);
			}
			
			if ( $msg->container()->_canDelete( $msg->member_id ) )
			{
				$button['delete'] = array(
					'icon'	=> 'times',
					'title'	=> 'delete',
					'link'	=> $msg->container()->url()->csrf()->setQueryString( array( 'do' => 'deleteMSG', 'chatID' => $msg->id, 'fromArchive' => 1 ) ),
					'data'	=> array( 'data-confirm' => '', 'data-confirmSubMessage' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_confirmDelete' ) )
				);
			}
			
			if ( $msg->canReport() )
			{
				$button['report'] = array(
					'icon'	=> 'exclamation',
					'title'	=> 'chatbox_report',
					'link'	=> $msg->url('report'),
					'data'	=> array( 'ipsDialog' => '', 'data-ipsDialog-size' => 'medium', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_report' ) )
				);
			}
			
			return $button;
		};
		
		$table->limit = 20;
		$table->title = $title;
		$table->tableTemplate  = array( \IPS\Theme::i()->getTemplate( 'archive', 'chatbox', 'front' ), 'table_archive' );
		$table->rowsTemplate  = array( \IPS\Theme::i()->getTemplate( 'archive', 'chatbox', 'front' ), 'table_archive_rows' );

		/* Output */
		\IPS\Output::i()->breadcrumb[] = array( $this->room->url(), $this->room->_title );
		\IPS\Output::i()->breadcrumb[] = array( $url, $title );
		\IPS\Output::i()->title	= $title;
		\IPS\Output::i()->output = (string) $table;		
	}
	
	/*-------------------------------------------------------------------------*/
	// Donation
	/*-------------------------------------------------------------------------*/	
	protected function donate()
	{
		# Ajax Check
		$this->ajaxCheck();
		
		# Check permission
		if ( ! $this->room->canDonate()  )
		{
			\IPS\Output::i()->json( array( 'type' => 'error', 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'chatbox_error_no_perm_donate' ) ) );
		}
		
		# Form
		try
		{
			$goal = \IPS\nexus\Donation\Goal::load( $this->room->nexus_donate_goal );
		}
		catch ( \OutOfRangeException $e )
		{
			\IPS\Output::i()->error( 'node_error', '2CHATBOX100/5', 404, '' );
		}
		
		$form = new \IPS\Helpers\Form( 'donate', 'donate' );
		$form->class = 'cbDonationForm ipsForm_vertical';
		if ( !isset( \IPS\Request::i()->noDesc ) and $desc = \IPS\Member::loggedIn()->language()->get("nexus_donategoal_{$goal->id}_desc") )
		{
			$form->addMessage( $desc, '', FALSE );
		}
		
		$scData = json_decode( $this->room->nexus_donate_superchat, true );
		
		usort($scData, function ($v1, $v2) {
			return $v1['amount'] <=> $v2['amount'];
		});

		foreach( $scData as $op )
		{
			$options[ $op['amount'] ] = $op['amount'];
		}

		foreach( $scData as $tg )
		{
			$fieldID = str_replace(".", "_", $tg['amount']);
			$fieldID = str_replace(",", "_", $fieldID);
			$toggles[ $tg['amount'] ] = array( $fieldID );
		}

		$lang = \IPS\Member::loggedIn()->language();
		
		$form->add( new \IPS\Helpers\Form\Select( 'donate_amount', 0, TRUE, array( 
			'options' 	=> $options,
			'toggles'	=> $toggles
		), NULL, NULL, $goal->currency ) );
		
		foreach( $scData as $row )
		{
			$form->add( new \IPS\Helpers\Form\Custom( 'preview' . $row['amount'], NULL, FALSE, array( 'getHtml' => function( $element ) use( $row )
			{
				return "<div style='width: 100%; height: 40px; border-radius: 3px; background: " . $row['color'] . "'></div>";
			} ), NULL, NULL, NULL, $row['amount'] ) );
			$lang->words[ 'preview' . $row['amount'] ] = $lang->addToStack('chatbox_donate_preview');
		}
		
		if ( $values = $form->values() )
		{
			$item = new \IPS\nexus\extensions\nexus\Item\Donation( \IPS\Member::loggedIn()->language()->get( 'nexus_donategoal_' . $goal->_id ), new \IPS\nexus\Money( $values['donate_amount'], $goal->currency ) );
			$item->id = $goal->_id;
						
			$invoice = new \IPS\nexus\Invoice;
			$invoice->member = \IPS\nexus\Customer::loggedIn();
			$invoice->currency = $goal->currency;
			$invoice->addItem( $item );
			$invoice->return_uri = 'app=nexus&module=clients&controller=donations&thanks=1';			
			$invoice->save();
			
			\IPS\Output::i()->json( array( 'redirect' => $invoice->checkoutUrl() ) );
		}
		
		\IPS\Output::i()->title = $goal->_title;
		
		$output = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) );
		
		if( \IPS\Request::i()->isAjax() )
		{
			\IPS\Output::i()->sendOutput( $output, 200, 'text/html', \IPS\Output::i()->httpHeaders );
		}
		else
		{
			\IPS\Output::i()->output = $output;
		}
	}
	
	/*-------------------------------------------------------------------------*/
	// Leave room
	/*-------------------------------------------------------------------------*/	
	protected function leaveRoom()
	{
		# Ajax Check
		$this->ajaxCheck();
		
		# Check permission
		$this->checkViewPerm();

		# Leave
		$chatters = $this->room->getChatters();
		
		$key = $this->room->userKey();
		
		if ( isset( $chatters[$key] ) )
		{
			$user = $chatters[$key];

			$isGuest = (int) $user['chatterID'] > 0 ? false : true;
			
			$userName = $isGuest ? $user['chatterName'] : \IPS\Theme::i()->getTemplate( 'room', 'chatbox', 'front' )->chatterName(
				\IPS\Member\Group::load( $user['chatterGroup'] )->formatName( $user['chatterName'] ), 
				$user['chatterID'], 
				NULL 
			);
						
			$this->room->addSystemMsg( 'leave', $userName, $isGuest );
			
			unset( $chatters[$key] );
			$this->room->saveChatters( $chatters );			
		}
		
		# Done
		\IPS\Output::i()->json( 'OK' );
	}
	
	
		
}