<?php
define('ABORT_AFTER_CONFIG', true);
define('NO_OUTPUT_BUFFERING', true);
define('IGNORE_COMPONENT_CACHE', true);

define('MOODLE_MANAGER_ROLE_ID',1);
define('MOODLE_EDITING_TEACHER_ROLE_ID',3);
define('MOODLE_TEACHER_ROLE_ID',4);
define('DOCENTEN_GROUP_ID',2);

require_once(ROOT_DIR . 'lib/Application/Authentication/namespace.php');
require_once(ROOT_DIR . 'plugins/Authentication/Moodle/namespace.php');
require_once(ROOT_DIR . 'lib/Database/MySQL/namespace.php');

/**
 * Provides Moodle authentication/synchronization for phpScheduleIt
 * @see IAuthorization
 */
class Moodle extends Authentication implements IAuthentication
{
	/**
	 * @var IAuthentication
	 */
	private $authToDecorate;

	/**
	 * @var MoodleOptions
	 */
	private $options;

	/**
	 * @var IRegistration
	 */
	private $_registration;

	/**
	 * @var string
	 */
	private $password;

	/** @var moodle user */
	private $user;

	public function SetRegistration($registration)
	{
		$this->_registration = $registration;
	}

	private function GetRegistration()
	{
		if ($this->_registration == null)
		{
			$this->_registration = new Registration();
		}

		return $this->_registration;
	}

	/**
	 * @param IAuthentication $authentication Authentication class to decorate
	 */
	public function __construct(IAuthentication $authentication)
	{
		Log::Debug('Moodle authentication plugin - __construct');
		
		$this->authToDecorate = $authentication;

		if(!ServiceLocator::GetServer()->GetUserSession()->IsLoggedIn())
        {
    		$this->CheckForMoodleSession(); 
        }
 	}

	private function CheckForMoodleSession()
	{
		global $CFG;
	
		Log::Debug('Moodle authentication plugin - in CheckForMoodleSession');
	
		$this->options = new MoodleOptions();
        
		// phpScheduleIt can only 'see' Moodle cookies if they are on the same (main) domain
		// from Moodle 2.6 this can be set in Site Administration>Server>Session Handling (admin/settings.php?section=sessionhandling)
		$server = ServiceLocator::GetServer();
        if($moodleCookie = $server->GetCookie($this->options->GetMoodleCookieId()))
        {
            $moodlesid = $_COOKIE[$this->options->GetMoodleCookieId()];
			
            Log::Debug('Moodle authentication plugin - using Moodle session: '.$moodleCookie);
	
            require_once($this->options->GetPath().'config.php');

            Log::Debug('Moodle authentication plugin - Moodle config loaded from '.$this->options->GetPath().'config.php');

            // get Moodle username from existing Moodle session in database
			$moodledb = new Database(new MySqlConnection($CFG->dbuser, $CFG->dbpass, $CFG->dbhost, $CFG->dbname));
			$reader = $moodledb->Query(new GetMoodleSessionCommand($moodlesid) );
			if ($moodleuser = (object)$reader->GetRow())
			{
                Log::Debug('Moodle authentication plugin - valid Moodle user found: '.$moodleuser->username);

				$userRepository = new UserRepository();
				if( $this->user = $userRepository->LoadByUsername($moodleuser->username) )
				{
					$loginData = new LoginData();
					$webLoginContext = new WebLoginContext( $loginData );
					
					$userSession = $this->authToDecorate->Login($moodleuser->username, $webLoginContext );
					if ($userSession->IsLoggedIn())
					{
						$server->SetUserSession($userSession);
                        return true;
					}
				}
			}
		}
        else
        {
            Log::Debug('Moodle authentication plugin - no Moodle session found');
        }
		
		return false;
	}

	public function Validate($username, $password)
	{
		global $CFG;
		
		Log::Debug('Moodle authentication plugin - Validate');
		
		if( $username=='api' )
			return $this->authToDecorate->Validate($username, $password);

		Log::Debug('Attempting to authenticate user against Moodle. User=%s', $username);

        require_once($this->options->GetPath().'config.php');

        Log::Debug('Moodle authentication plugin - Moodle config loaded from '.$this->options->GetPath().'config.php');

		$moodledb = new Database(new MySqlConnection($CFG->dbuser, $CFG->dbpass, $CFG->dbhost, $CFG->dbname));
		
		// Note: only teachers are allowed to login using their Moodle account
		// This is checked by looking at mdl_role_assignments for users with a role_id = 3
		$reader = $moodledb->Query(new GetMoodleUserCommand($username));
		if ($moodleuser = (object)$reader->GetRow())
		{
    		require_once($this->options->GetPath() . 'lib/moodlelib.php');
        
			if (validate_internal_user_password($moodleuser,$password))
			{
				Log::Debug('Moodle authentication successful. User=%s', $username);
				$this->user = $moodleuser;
				$this->password = $password;
				return true;
			}
			else
			{
				Log::Debug('Moodle authentication failed. User=%s', $username);
				if ($this->options->RetryAgainstDatabase())
				{
					Log::Debug('Moodle authentication retrying against internal database');
					return $this->authToDecorate->Validate($username, $password);
				}
			}
		}
		
		return false;
	}

	public function Login($username, $loginContext)
	{
		Log::Debug('Moodle authentication plugin - Login in with username: %s', $username);
		if ($this->UserExists())
		{
			Log::Debug('Running Moodle user synchronization for username: %s', $username);
			$this->Synchronize();
		}
		else
		{
			Log::Debug('Skipping Moodle user synchronization, user not loaded');
		}

		return $this->authToDecorate->Login($username, $loginContext);
	}

	public function Logout(UserSession $user)
	{
		Log::Debug('Moodle authentication plugin - Logout');
		
		$this->authToDecorate->Logout($user);
	}

	public function AreCredentialsKnown()
	{
		Log::Debug('Moodle authentication plugin - AreCredentialsKnown');
		  
		return false;
	}

	private function UserExists()
	{
		Log::Debug('Moodle authentication plugin - UserExists');
		
		return ($this->user!=null) && $this->user->exists();
	}

	private function Synchronize()
	{
		Log::Debug('Moodle authentication plugin - Synchronize');
		// add the user
		$registration = $this->GetRegistration();
		$registration->Synchronize(new AuthenticatedUser(
                $this->user->username,
                $this->user->email,
                $this->user->firstname,
                $this->user->lastname,
                $this->password,
                Configuration::Instance()->GetKey(ConfigKeys::LANGUAGE),
				Configuration::Instance()->GetKey(ConfigKeys::SERVER_TIMEZONE),
				null, null, null));

		$userRepository = new UserRepository();
		$user = $userRepository->LoadByUsername($this->user->username);
		
		// grab the Docenten group
		$groupRepository = new GroupRepository();
		if( $group = $groupRepository->LoadById(DOCENTEN_GROUP_ID) )
		{
			// add user to group Docenten
			$group->AddUser($user->Id());
			$groupRepository->Update($group);
		}
	}
}

class GetMoodleUserCommand extends SqlCommand
{
	public function __construct($username)
	{
		parent::__construct('SELECT u.* FROM mdl_user u JOIN mdl_role_assignments a ON u.id=a.userid WHERE u.deleted=0 AND u.suspended=0 AND (a.roleid=@roleid0 OR a.roleid=@roleid1 OR a.roleid=@roleid2) AND u.username=@username');
		$this->AddParameter(new Parameter('@roleid0', MOODLE_MANAGER_ROLE_ID ));
		$this->AddParameter(new Parameter('@roleid1', MOODLE_EDITING_TEACHER_ROLE_ID ));
		$this->AddParameter(new Parameter('@roleid2', MOODLE_TEACHER_ROLE_ID ));
		$this->AddParameter(new Parameter('@username', $username));
	}
}

class GetMoodleSessionCommand extends SqlCommand
{
	// NB nog uitbreiden met check op ja/nee teacher of admin
	
	public function __construct($sid)
	{
		parent::__construct('SELECT u.* FROM mdl_sessions s JOIN mdl_user u ON s.userid=u.id AND NOT s.userid=0 WHERE s.state=0 AND s.sid=@sid AND u.suspended=0 AND u.deleted=0');
		$this->AddParameter(new Parameter('@sid', $sid));
	}
}


?>