prepare("SELECT * FROM users WHERE id=?");
    $stmt->bind_param("d", $id);
    $stmt->execute();
    $query = $stmt->get_result();
    if (!$query->num_rows)
    {
      throw new Exception("User doesn't exist.");
      return;
    }
    $result = $query->fetch_array();
    $this->id = $id;
    $this->active = $result['active'];
    $this->name = $result['name'];
    $this->email = $result['email'];
    $this->surname = $result['surname'];
    $this->username = $result['username'];
    $this->rank = $result['permission'];
  }
  /**
   * Returns username of this user
   * @return String username
   */
  public function get_username()
  {
    return $this->username;
  }
  
  /**
   * Returns whether this user is active
   * @return Boolean user active status
   */
  public function is_active()
  {
    return $this->active;
  }
  
  /**
   * Returns rank of this user
   * @return int rank
   */
  public function get_rank()
  {
    return $this->rank;
  }
  
  /**
   * Returns full name of this user
   * @return String name in "Name Surname" format
   */
  public function get_name()
  {
    return $this->name . " " . $this->surname;
  }
  /**
   * Toggles active status of this user. First checks if the user
   * making the change has permission to do that.
   * @return void
   */
  public function toggle()
  {
    global $mysqli, $message, $user;
    $id = $_GET['id'];
    if ($this->id!=$_SESSION['user'] && $user->get_rank()<=1 && ($user->get_rank()<$this->rank))
    {
      $stmt = $mysqli->prepare("UPDATE users SET active = !active WHERE id=?");
      $stmt->bind_param("i", $this->id);
      $stmt->execute();
      $stmt->close();
      header("Location: ".WEB_URL."/admin/?do=user&id=".$id);
    }else{
      $message = _("You don't have the permission to do that!");
    }
  }
  /**
   * Processes submitted form and adds user unless problem is encountered, 
   * calling this is possible only for Superadmin (other ranks cannot add users)
   * or when the installation script is being run. Also checks requirements
   * for username and email being unique and char limits.
   * @return void
   */
  public static function add()
  {
    global $user, $message, $mysqli;
    if (INSTALL_OVERRIDE || $user->get_rank()==0)
    {
      if (strlen(trim($_POST['name']))==0) {
        $messages[] = _("Name");
      }
      if(strlen(trim($_POST['surname']))==0) {
        $messages[] = _("Surname");
      }
      if(strlen(trim($_POST['email']))==0) {
        $messages[] = _("Email");
      }
      if(strlen(trim($_POST['password']))==0) {
        $messages[] = _("Password");
      }
      if(!isset($_POST['permission']))
      {
        $messages[] = _("Rank");
      }
      if (isset($messages)){
        $message = "Please enter ".implode(", ", $messages);
        return;
      }
      
      $name = $_POST['name'];
      $surname = $_POST['surname'];
      $username = $_POST['username'];
      $email = $_POST['email'];
      $pass = $_POST['password'];
        
      if (!filter_var($email, FILTER_VALIDATE_EMAIL))
      {
        $message = "Invalid email!";
        return;
      }
      $variables = array();
      if (strlen($name)>50){
        $variables[] = 'name: 50';
      }
      if (strlen($surname)>50){
        $variables[] = 'surname: 50';
      }
      if (strlen($username)>50){
        $variables[] = 'username: 50';
      }
      if (strlen($email)>60){
        $variables[] = 'email: 60';
      }
      if (!empty($variables))
      {
        $message = _("Please mind the following character limits: ");
        $message .= implode(", ", $variables);
        return;
      }
      $salt = uniqid(mt_rand(), true);
      $hash = hash('sha256', $pass.$salt);
      $permission = $_POST['permission'];
      
      $stmt = $mysqli->prepare("INSERT INTO users values (NULL, ?, ?, ?, ?, ?, ?, ?, 1)");
      $stmt->bind_param("ssssssi", $email, $username, $name, $surname, $hash, $salt, $permission);
      $stmt->execute();
      if ($stmt->affected_rows==0)
      {
        $message = _("Username or email already used");
        return;
      }
      $to      = $email;
      $subject = _('User account created').' - '.NAME;
      $msg = sprintf(_("Hi %s!
"."Your account has been created. You can login with your email address at %s with password %s - please change it as soon as possible."), $name." ".$surname,WEB_URL."/admin", WEB_URL."/admin", $pass);
      $headers = "Content-Type: text/html; charset=utf-8 ".PHP_EOL;
      $headers .= "MIME-Version: 1.0 ".PHP_EOL;
      $headers .= "From: ".MAILER_NAME.' <'.MAILER_ADDRESS.'>'.PHP_EOL;
      $headers .= "Reply-To: ".MAILER_NAME.' <'.MAILER_ADDRESS.'>'.PHP_EOL; 
      mail($to, $subject, $msg, $headers);
      if (!INSTALL_OVERRIDE) 
      {
        header("Location: ".WEB_URL."/admin/?do=settings");
      }
    }
    else {
      $message = _("You don't have the permission to do that!");
    }
  }
  /**
   * Processes submitted form and logs user in, unless the user is deactivated or wrong
   * password or email has been submitted. The script doesn't let anyone know which
   * field was wrong as it is not possible to verify email address from outside admin panel,
   * so this actually helps with security :)
   * @return void
   */
  public static function login()
  {
    global $message, $mysqli;
    if (!isset($_POST['email']) && !isset($_POST['pass']))
    {
      return;
    }
    if ((!isset($_POST['email']) || !isset($_POST['pass'])))
    {
      $message = _("Please fill in your email and password!");
      return;
    }
    $email = $_POST['email'];
    $pass = $_POST['pass'];
    $stmt = $mysqli->prepare("SELECT id,password_salt as salt,active FROM users WHERE email=?");
    $stmt->bind_param("s", $email);
    $stmt->execute();
    $query = $stmt->get_result();
    if ($query->num_rows<1)
    {
      $message = _("Wrong email or password");
      return;
    }
      
    $result = $query->fetch_assoc();
    $salt = $result["salt"];
    $id =  $result["id"];
    $active =  $result["active"];
    if (!$active)
    {
      $message = _("Your account has been disabled. Please contact administrator.");
      return;
    }
    $hash = hash('sha256', $pass.$salt);
    $stmt = $mysqli->prepare("SELECT count(*) as count FROM users WHERE id=? AND password_hash=?");
    $stmt->bind_param("is", $id, $hash);
    $stmt->execute();
    $query = $stmt->get_result();
    if (!$query->fetch_assoc()['count'])
    {
      $message = _("Wrong email or password");
      return;
    }
    if (isset($_POST['remember'])&&$_POST['remember'])
    {
      $year = strtotime('+356 days', time());
      $token = Token::add($id, 'remember', $year);
      setcookie('token', $token, $year, "/");
      setcookie('user', $id, $year, "/");
    }
    $_SESSION['user'] = $id;
    header("Location: ".WEB_URL."/admin");
  }
  /**
   * Checks whether token is valid (this means is in database and associated
   * with the user) and sets session data if it is, so user remains logged in.
   * The script deletes the token either way.
   * @return void
   */
  public static function restore_session()
  {
    global $message;
    $id = $_COOKIE['user'];
    $token = $_COOKIE['token'];
    if (Token::validate($token, $id, "remember"))
    {
      $year = strtotime('+356 days', time());
      unset($_COOKIE['token']);
      $_SESSION['user'] = $id;
      $new_token = Token::add($id, 'remember', $year);
      setcookie('token', $new_token, $year, "/");
      setcookie('user', $id, $year, "/");
    }
    else
    {
      unset($_COOKIE['user']);
      unset($_COOKIE['token']);
      setcookie('user', null, -1, '/');
      setcookie('token', null, -1, '/');
      $message = _("Invalid token detected, please login again!");
    }
    
    Token::delete($token);
  }
  /**
   * Renders settings for this user so it can be displayed in admin panel.
   * @return void
   */
  public function render_user_settings()
  {
    global $permissions, $user;
    ?>