<?php
require_once('Login.class.php');
require_once('Auth.class.php');
require_once('DB.class.php');

Router::register('users/new'  , 'Users', 'new_user');
Router::register('users/index', 'Users', 'index_file');
Router::register('users'      , 'Users', 'index_dir');
Router::register('users/*'    , 'Users', 'individual');

class Users extends Controller {
	// Index Views ///////////////////////////////////////////////
	
	public function index($routed, $remainder) {
		return $this->index_dir($routed, $remainder);
	}
	
	/**
	 * Handle POSTing a new user, or GETing the index.
	 */
	public function index_dir($routed, $remainder) {
		$method = $_SERVER['REQUEST_METHOD'];
		switch ($method) {
		case 'POST':
			// We're POSTing a new user.
			if ($this->registrationOpen()) {
				$this->create_user();
			} else {
				$this->showView('users/new-locked', array());
				exit();
			}
			break;
		case 'HEAD': // fall-through to GET
		case 'GET':
			// We're GETing the index.
			$this->show_index($routed, $remainder);
			break;
		}
	}
	
	/**
	 * Handle PUTing an updated user index, or GETing the index.
	 */
	public function index_file($routed, $remainder) {
		$method = $_SERVER['REQUEST_METHOD'];
		switch ($method) {
		case 'PUT': $_POST = $_PUT;
		case 'POST':
			// We're PUTing an updated user index.
			$this->update_users();
			break;
		}
		$this->show_index($routed, $remainder);
	}
	
	// Other Views ///////////////////////////////////////////////
	
	/**
	 * Handle GETing the new user form.
	 * 
	 * I would have named this `new', but that's a keyword.
	 */
	public function new_user($routed, $vars) {
		// since there will never be a remainder to `users/new', we can
		// use that parameter to pass in some data.
		if (Login::isLoggedIn()) {
			$this->showView('users/new-logged-in', array());
			exit();
		}
		if (!$this->registrationOpen()) {
			$this->showView('users/new-locked', array());
			exit();
		}
		if (!isset($vars['errors'])) $vars['errors'] = array();
		
		global $mm;
		$pm = $mm->pluginManager();
		$db = $mm->database();
		
		$vars['antispam_html'] = $pm->callHook('antispam_html');
		$vars['userlist'] = $db->getSysConf('anon_userlist');
		$this->showView('users/new', $vars);
	}
	
	public function individual($routed, $remainder) {
		global $mm; // also used for pluginmanager
		$db = $mm->database();
		$pm = $mm->pluginManager();

		$username = implode('/', $remainder);
		if ($username == 'all') {
			$uids = $db->listUsers();
		} else {
			$uids = array($db->getUID($username));
		}

		$vars = array();
		
		if (count($uids)<2) {
			$user = Auth::getObj($uid);
			
			if ($user->isGroup()) $uid = false; // ignore groups.
			
			if ($uid===false) {
				$this->http404($routed, $remainder);
				exit();
			}
			if (!$user->canRead()) {
				$this->http401($routed, $remainder);
				exit();
			}
			
			$method = $_SERVER['REQUEST_METHOD'];
			switch ($method) {
			case 'PUT': $_POST = $_PUT;
			case 'POST':
				// We're PUTing updated user info.
				if ($user->canEdit()) {
					$vars = $this->update_user($user);
				}
				break;
			}
		}
		
		$config_options = array();
		$pm->callHook('userConfig', &$config_options);

		$vars['users'] = array();
		foreach ($uids as $uid) {
			$vars['users'][] = Auth::getObj($uid);
		}
		$vars['username'] = $username;
		$vars['config_options'] = $config_options;
		$vars['groups'] = $db->listGroupNames();
		require_once('ContactMethod.class.php');
		$this->showView('users/individual', $vars);
	}
	
	public function http404($routed, $remainder) {
		$username = implode('/', $remainder);
		$this->showView('users/404',
		                array('username'=>$username));
	}
	
	public function http401($routed, $remainder) {
		$this->showView('users/401', array('uid'=>Login::isLoggedIn()));
	}
	
	// Other Functions ///////////////////////////////////////////
	
	/**
	 * This will parse POST data to create a new user.
	 * If successfull it will show a message saying so.
	 * If not successfull, it will re-show the new-user form with errors
	 * explained.
	 */
	private function create_user() {
		global $mm;
		$db = $mm->database();
		$pm = $mm->pluginManager();
		
		$vars = array();
		@$vars['username' ] = $_POST['auth_name'];
		@$vars['password1'] = $_POST['auth_password'       ];
		@$vars['password2'] = $_POST['auth_password_verify'];
		@$vars['email']     = $_POST['user_email'];
		
		$vars['errors'] = array();
		if ($db->getUID($vars['username'])!==false)
			$vars['errors'][] = 'user exists';
		if (!Auth::isNameLegal($vars['username']))
			$vars['errors'][] = 'illegal name';
		$matches = ($vars['password1'] == $vars['password2']);
		if (!$matches) {
			$vars['errors'][] = 'pw mixmatch';
		}
		if ($matches && $vars['password2'] == '') {
			$vars['errors'][] = 'no pw';
		}
		if ($vars['email'] == '') {
			$vars['errors'][] = 'no email';
		}
		foreach ($pm->callHook('antispam_verify') as $plugin=>$valid) {
			if (!$valid) $vars['errors'][] = 'plugin_'.$plugin;
		}
		
		if (count($vars['errors']) > 0) {
			$this->new_user($routed, $vars);
		} else {
			$username = $vars['username'];
			$password = $vars['password1'];
			$uid = $db->addUser($username, $password);
			if ($uid===false) {
				$this->showView('users/500');
			} else {
				Login::login($username, $password);
				DB::set('users', $uid, 'email', $vars['email']);
				$this->showView('users/created',
				                array('username'=>$username));
			}
		}
	}
	
	/**
	 * This will parse POST (really, PUT) data to update a single user
	 */
	private function update_user($user) {
		$vars = array();
		
		$username = $user->getName();
		// Change the username /////////////////////////////////////////
		if (isset($_POST['auth_name'])) {
			$new_name = $_POST['auth_name'];
			if ($new_name != $username) {
				$changed_name = $user->setName($new_name);
				$username = $user->getName();
				$vars['changed name'] = $changed_name;
			}
		}
		
		// Change the password /////////////////////////////////////////
		@$password1 = $_POST['auth_password'          ];
		@$password2 = $_POST['auth_password'.'_verify'];
		
		// Check the verify box, not main box, so that we don't get
		// tripped by browsers annoyingly autocompleting the password.
		$is_set = ($password2 != '');
		
		if ($is_set) {
			$matches = ( $password1 == $password2 );
			if ($matches) {
				$user->setPassword($password1);
				$vars['pw updated'] = true;
			} else {
				$vars['pw mixmatch'] = true;
			}
		}
		
		// Change information //////////////////////////////////////////
		$config_options = array();
		global $mm;
		$mm->pluginManager()->callHook('userConfig', &$config_options);
		
		foreach ($config_options as $group=>$options) {
			foreach ($options as $option) {
				$this->confText($user, $option[0]);
			}
		}
		
		// Change contact info /////////////////////////////////////////
		global $CONTACT_METHODS;
		foreach ($CONTACT_METHODS as $method) {
			$this->confText($user, $method->addr_slug);
		}
		$this->confArray($user, 'use');
		
		// Change groups ///////////////////////////////////////////////
		$this->confArray($user, 'groups');
		
		return $vars;
	}
	
	private function confArray($user, $key) {
		if (isset($_POST[$key]) && is_array($_POST[$key])) {
			$user->setConfArray($key, $_POST[$key]);
		}
	}
	
	private function confText($user, $name) {
		if (isset($_POST["user_$name"])) {
			$user->setConf($name, $_POST["user_$name"]);
		}
	}
	
	
	/**
	 * This will parse POST (really, PUT) data to update multiple users.
	 */
	private function update_users() {
		$attribs = $this->getIndexAttribs();
		$form = new Form(null, null);
		foreach ($attribs as $attrib) {
			$key = $attrib['key'];
			if (isset($_POST[$key]) && is_array($_POST[$key])) {
				$old = $_POST['_old'][$key];
				foreach ($_POST[$key] as $uid => $value) {
					@$value_base = $old[$uid];
					$set = DB::set('users', $uid, $key, $value, $value_base);
					if (is_string($set)) {
						echo "<pre>\n";
						echo "Error: Value changed elsewhere, ".
							"and I don't have real handling ".
							"for this yet.\n";
						echo "UID: $uid\n";
						echo "Name: ".$user->getName()."\n";
						echo "Key: $key\n";
						echo "Value: Original  : ";
						var_dump($value_base);
						echo "Value: Other edit: ";
						var_dump($value_fork);
						echo "Value: This edit : ";
						var_dump($value);
						echo "</pre>";
					}
				}
			}
		}
	}
	
	/**
	 * This will show the user index.
	 */
	private function show_index($routed, $remainder) {
		global $mm; $db = $mm->database();
		
		$logged_in_user = Auth::getObj(Login::isLoggedIn());
		$anon_userlist = $db->getSysConf('anon_userlist')=='true';
		if (!$anon_userlist && !$logged_in_user->isUser()) {
			$this->http401($routed, $remainder);
			exit();
		}
		
		$vars = array();
		$vars['attribs'] = $this->getIndexAttribs();
		$vars['users'] = array();
		$uids = $db->listUsers();
		foreach ($uids as $uid) {
			$vars['users'][$uid] = array();
			foreach ($vars['attribs'] as $attrib) {
				$key = $attrib['key'];
				$props = DB::get('users', $uid, $key);
				$vars['users'][$uid][$key] = $props;
			}
		}
		$this->showView('users/index', $vars);
	}
	
	function attrib($key, $name, $type='string') {
		return array('key'=>$key, 'name'=>$name, 'type'=>$type);
	}
	private function getIndexAttribs() {
		$user = Auth::getObj(Login::isLoggedIn());
		
		$attribs = array();
		$attribs[] = $this->attrib('auth_uid', 'UID');
		if ($user->isUser()) {
			$attribs[] = $this->attrib('auth_user', 'Active', 'bool');
			if ($user->isAdmin()) {
				$attribs[] = $this->attrib('auth_admin', 'Admin', 'bool');
				$attribs[] = $this->attrib('auth_delete', 'Delete', 'bool');
			}
			$attribs[] = $this->attrib('lastname','Last');
			$attribs[] = $this->attrib('firstname','First');
			$attribs[] = $this->attrib('hsclass','Class of');
			$attribs[] = $this->attrib('phone','Phone number');
			$attribs[] = $this->attrib('email','Email');
		}
		$attribs[] = $this->attrib('auth_name', 'Username');

		return $attribs;
	}
	
	private function registrationOpen() {
		global $mm; $db = $mm->database();
		$val = $db->getSysConf('registration_open');
		switch ($val) {
		case 'true': return true;
		case 'false': return false;
		default: return true;
		}
	}
}